diff --git a/doc/ffmpeg-doc.texi b/doc/ffmpeg-doc.texi index cae8250e0d..56ee8022bc 100644 --- a/doc/ffmpeg-doc.texi +++ b/doc/ffmpeg-doc.texi @@ -234,6 +234,18 @@ set bottom crop band size (in pixels) set left crop band size (in pixels) @item -cropright size set right crop band size (in pixels) +@item -padtop size +set top pad band size (in pixels) +@item -padbottom size +set bottom pad band size (in pixels) +@item -padleft size +set left pad band size (in pixels) +@item -padright size +set right pad band size (in pixels) +@item -padcolor color +set right pad band size (hex). The value for pad color is expressed +as a six digit hexidecimal number where the first two digits represent red, +middle two digits green and last two digits blue. Defaults to 000000 (black) @item -vn disable video recording @item -bt tolerance diff --git a/ffmpeg.c b/ffmpeg.c index e1a2b18d86..80f9119c3e 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -72,6 +72,11 @@ static int frame_width = 160; static int frame_height = 128; static float frame_aspect_ratio = 0; static enum PixelFormat frame_pix_fmt = PIX_FMT_YUV420P; +static int frame_padtop = 0; +static int frame_padbottom = 0; +static int frame_padleft = 0; +static int frame_padright = 0; +static int padcolor[3] = {16,128,128}; /* default to black */ static int frame_topBand = 0; static int frame_bottomBand = 0; static int frame_leftBand = 0; @@ -222,6 +227,12 @@ typedef struct AVOutputStream { int topBand; /* cropping area sizes */ int leftBand; + int video_pad; /* video_resample and video_pad are mutually exclusive */ + int padtop; /* padding area sizes */ + int padbottom; + int padleft; + int padright; + /* audio only */ int audio_resample; ReSampleContext *resample; /* for audio resampling */ @@ -497,6 +508,40 @@ static void pre_process_video_frame(AVInputStream *ist, AVPicture *picture, void /* we begin to correct av delay at this threshold */ #define AV_DELAY_MAX 0.100 + +/* Expects img to be yuv420 */ +static void fill_pad_region(AVPicture* img, int height, int width, + int padtop, int padbottom, int padleft, int padright, int *color) { + + int i, y, shift; + uint8_t *optr; + + for (i = 0; i < 3; i++) { + shift = (i == 0) ? 0 : 1; + + if (padtop || padleft) { + memset(img->data[i], color[i], (((img->linesize[i] * padtop) + + padleft) >> shift)); + } + + if (padleft || padright) { + optr = img->data[i] + (img->linesize[i] * (padtop >> shift)) + + (img->linesize[i] - (padright >> shift)); + + for (y = 0; y < ((height - (padtop + padbottom)) >> shift); y++) { + memset(optr, color[i], (padleft + padright) >> shift); + optr += img->linesize[i]; + } + } + + if (padbottom) { + optr = img->data[i] + (img->linesize[i] * ((height - padbottom) >> shift)); + memset(optr, color[i], ((img->linesize[i] * padbottom) >> shift)); + } + } +} + + static void do_video_out(AVFormatContext *s, AVOutputStream *ost, AVInputStream *ist, @@ -579,7 +624,8 @@ static void do_video_out(AVFormatContext *s, return; /* convert pixel format if needed */ - target_pixfmt = ost->video_resample ? PIX_FMT_YUV420P : enc->pix_fmt; + target_pixfmt = ost->video_resample || ost->video_pad + ? PIX_FMT_YUV420P : enc->pix_fmt; if (dec->pix_fmt != target_pixfmt) { int size; @@ -601,12 +647,19 @@ static void do_video_out(AVFormatContext *s, formatted_picture = (AVPicture *)in_picture; } - /* XXX: resampling could be done before raw format convertion in + /* XXX: resampling could be done before raw format conversion in some cases to go faster */ /* XXX: only works for YUV420P */ if (ost->video_resample) { final_picture = &ost->pict_tmp; img_resample(ost->img_resample_ctx, final_picture, formatted_picture); + + if (ost->padtop || ost->padbottom || ost->padleft || ost->padright) { + fill_pad_region(final_picture, enc->height, enc->width, + ost->padtop, ost->padbottom, ost->padleft, ost->padright, + padcolor); + } + if (enc->pix_fmt != PIX_FMT_YUV420P) { int size; @@ -642,6 +695,51 @@ static void do_video_out(AVFormatContext *s, picture_crop_temp.linesize[1] = formatted_picture->linesize[1]; picture_crop_temp.linesize[2] = formatted_picture->linesize[2]; final_picture = &picture_crop_temp; + } else if (ost->video_pad) { + final_picture = &ost->pict_tmp; + + for (i = 0; i < 3; i++) { + uint8_t *optr, *iptr; + int shift = (i == 0) ? 0 : 1; + int y, yheight; + + /* set offset to start writing image into */ + optr = final_picture->data[i] + (((final_picture->linesize[i] * + ost->padtop) + ost->padleft) >> shift); + iptr = formatted_picture->data[i]; + + yheight = (enc->height - ost->padtop - ost->padbottom) >> shift; + for (y = 0; y < yheight; y++) { + /* copy unpadded image row into padded image row */ + memcpy(optr, iptr, formatted_picture->linesize[i]); + optr += final_picture->linesize[i]; + iptr += formatted_picture->linesize[i]; + } + } + + fill_pad_region(final_picture, enc->height, enc->width, + ost->padtop, ost->padbottom, ost->padleft, ost->padright, + padcolor); + + if (enc->pix_fmt != PIX_FMT_YUV420P) { + int size; + + av_free(buf); + /* create temporary picture */ + size = avpicture_get_size(enc->pix_fmt, enc->width, enc->height); + buf = av_malloc(size); + if (!buf) + return; + final_picture = &picture_format_temp; + avpicture_fill(final_picture, buf, enc->pix_fmt, enc->width, enc->height); + + if (img_convert(final_picture, enc->pix_fmt, + &ost->pict_tmp, PIX_FMT_YUV420P, + enc->width, enc->height) < 0) { + fprintf(stderr, "pixel format conversion not handled\n"); + goto the_end; + } + } } else { final_picture = formatted_picture; } @@ -1267,10 +1365,15 @@ static int av_encode(AVFormatContext **output_files, frame_topBand == 0 && frame_bottomBand == 0 && frame_leftBand == 0 && - frame_rightBand == 0) + frame_rightBand == 0 && + frame_padtop == 0 && + frame_padbottom == 0 && + frame_padleft == 0 && + frame_padright == 0) { ost->video_resample = 0; ost->video_crop = 0; + ost->video_pad = 0; } else if ((codec->width == icodec->width - (frame_leftBand + frame_rightBand)) && (codec->height == icodec->height - @@ -1280,6 +1383,20 @@ static int av_encode(AVFormatContext **output_files, ost->video_crop = 1; ost->topBand = frame_topBand; ost->leftBand = frame_leftBand; + } else if ((codec->width == icodec->width + + (frame_padleft + frame_padright)) && + (codec->height == icodec->height + + (frame_padtop + frame_padbottom))) { + ost->video_resample = 0; + ost->video_crop = 0; + ost->video_pad = 1; + ost->padtop = frame_padtop; + ost->padleft = frame_padleft; + ost->padbottom = frame_padbottom; + ost->padright = frame_padright; + if( avpicture_alloc( &ost->pict_tmp, PIX_FMT_YUV420P, + codec->width, codec->height ) ) + goto fail; } else { ost->video_resample = 1; ost->video_crop = 0; // cropping is handled as part of resample @@ -1291,7 +1408,15 @@ static int av_encode(AVFormatContext **output_files, ost->st->codec.width, ost->st->codec.height, ist->st->codec.width, ist->st->codec.height, frame_topBand, frame_bottomBand, - frame_leftBand, frame_rightBand); + frame_leftBand, frame_rightBand, + frame_padtop, frame_padbottom, + frame_padleft, frame_padright); + + ost->padtop = frame_padtop; + ost->padleft = frame_padleft; + ost->padbottom = frame_padbottom; + ost->padright = frame_padright; + } ost->encoding_needed = 1; ist->decoding_needed = 1; @@ -1809,6 +1934,93 @@ static void opt_frame_size(const char *arg) } } + +#define SCALEBITS 10 +#define ONE_HALF (1 << (SCALEBITS - 1)) +#define FIX(x) ((int) ((x) * (1<> SCALEBITS) + +#define RGB_TO_U(r1, g1, b1, shift)\ +(((- FIX(0.16874) * r1 - FIX(0.33126) * g1 + \ + FIX(0.50000) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) + +#define RGB_TO_V(r1, g1, b1, shift)\ +(((FIX(0.50000) * r1 - FIX(0.41869) * g1 - \ + FIX(0.08131) * b1 + (ONE_HALF << shift) - 1) >> (SCALEBITS + shift)) + 128) + +static void opt_pad_color(const char *arg) { + /* Input is expected to be six hex digits similar to + how colors are expressed in html tags (but without the #) */ + int rgb = strtol(arg, NULL, 16); + int r,g,b; + + r = (rgb >> 16); + g = ((rgb >> 8) & 255); + b = (rgb & 255); + + padcolor[0] = RGB_TO_Y(r,g,b); + padcolor[1] = RGB_TO_U(r,g,b,0); + padcolor[2] = RGB_TO_V(r,g,b,0); +} + +static void opt_frame_pad_top(const char *arg) +{ + frame_padtop = atoi(arg); + if (frame_padtop < 0) { + fprintf(stderr, "Incorrect top pad size\n"); + exit(1); + } + if ((frame_padtop % 2) != 0) { + fprintf(stderr, "Top pad size must be a multiple of 2\n"); + exit(1); + } +} + +static void opt_frame_pad_bottom(const char *arg) +{ + frame_padbottom = atoi(arg); + if (frame_padbottom < 0) { + fprintf(stderr, "Incorrect bottom pad size\n"); + exit(1); + } + if ((frame_padbottom % 2) != 0) { + fprintf(stderr, "Bottom pad size must be a multiple of 2\n"); + exit(1); + } +} + + +static void opt_frame_pad_left(const char *arg) +{ + frame_padleft = atoi(arg); + if (frame_padleft < 0) { + fprintf(stderr, "Incorrect left pad size\n"); + exit(1); + } + if ((frame_padleft % 2) != 0) { + fprintf(stderr, "Left pad size must be a multiple of 2\n"); + exit(1); + } +} + + +static void opt_frame_pad_right(const char *arg) +{ + frame_padright = atoi(arg); + if (frame_padright < 0) { + fprintf(stderr, "Incorrect right pad size\n"); + exit(1); + } + if ((frame_padright % 2) != 0) { + fprintf(stderr, "Right pad size must be a multiple of 2\n"); + exit(1); + } +} + + static void opt_frame_pix_fmt(const char *arg) { frame_pix_fmt = avcodec_get_pix_fmt(arg); @@ -2237,8 +2449,8 @@ static void opt_input_file(const char *filename) ap->channels = audio_channels; ap->frame_rate = frame_rate; ap->frame_rate_base = frame_rate_base; - ap->width = frame_width; - ap->height = frame_height; + ap->width = frame_width + frame_padleft + frame_padright; + ap->height = frame_height + frame_padtop + frame_padbottom; ap->image_format = image_format; ap->pix_fmt = frame_pix_fmt; @@ -2454,8 +2666,8 @@ static void opt_output_file(const char *filename) video_enc->frame_rate = frame_rate; video_enc->frame_rate_base = frame_rate_base; - video_enc->width = frame_width; - video_enc->height = frame_height; + video_enc->width = frame_width + frame_padright + frame_padleft; + video_enc->height = frame_height + frame_padtop + frame_padbottom; video_enc->sample_aspect_ratio = av_d2q(frame_aspect_ratio*frame_height/frame_width, 255); video_enc->pix_fmt = frame_pix_fmt; @@ -3164,6 +3376,11 @@ const OptionDef options[] = { { "cropbottom", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_crop_bottom}, "set bottom crop band size (in pixels)", "size" }, { "cropleft", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_crop_left}, "set left crop band size (in pixels)", "size" }, { "cropright", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_crop_right}, "set right crop band size (in pixels)", "size" }, + { "padtop", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_pad_top}, "set top pad band size (in pixels)", "size" }, + { "padbottom", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_pad_bottom}, "set bottom pad band size (in pixels)", "size" }, + { "padleft", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_pad_left}, "set left pad band size (in pixels)", "size" }, + { "padright", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_pad_right}, "set right pad band size (in pixels)", "size" }, + { "padcolor", HAS_ARG | OPT_VIDEO, {(void*)opt_pad_color}, "set color of pad bands (Hex 000000 thru FFFFFF)", "color" }, { "g", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_gop_size}, "set the group of picture size", "gop_size" }, { "intra", OPT_BOOL | OPT_EXPERT | OPT_VIDEO, {(void*)&intra_only}, "use only intra frames"}, { "vn", OPT_BOOL | OPT_VIDEO, {(void*)&video_disable}, "disable video" }, @@ -3217,7 +3434,7 @@ const OptionDef options[] = { { "bug", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_workaround_bugs}, "workaround not auto detected encoder bugs", "param" }, { "ps", HAS_ARG | OPT_EXPERT, {(void*)opt_packet_size}, "set packet size in bits", "size" }, { "error", HAS_ARG | OPT_EXPERT, {(void*)opt_error_rate}, "error rate", "rate" }, - { "strict", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_strict}, "how strictly to follow the standarts", "strictness" }, + { "strict", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_strict}, "how strictly to follow the standards", "strictness" }, { "sameq", OPT_BOOL | OPT_VIDEO, {(void*)&same_quality}, "use same video quality as source (implies VBR)" }, { "pass", HAS_ARG | OPT_VIDEO, {(void*)&opt_pass}, "select the pass number (1 or 2)", "n" }, diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 909dec1545..10040b2067 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -17,7 +17,7 @@ extern "C" { #define FFMPEG_VERSION_INT 0x000408 #define FFMPEG_VERSION "0.4.8" -#define LIBAVCODEC_BUILD 4707 +#define LIBAVCODEC_BUILD 4708 #define LIBAVCODEC_VERSION_INT FFMPEG_VERSION_INT #define LIBAVCODEC_VERSION FFMPEG_VERSION @@ -1821,7 +1821,10 @@ ImgReSampleContext *img_resample_init(int output_width, int output_height, ImgReSampleContext *img_resample_full_init(int owidth, int oheight, int iwidth, int iheight, int topBand, int bottomBand, - int leftBand, int rightBand); + int leftBand, int rightBand, + int padtop, int padbottom, + int padleft, int padright); + void img_resample(ImgReSampleContext *s, AVPicture *output, const AVPicture *input); diff --git a/libavcodec/imgresample.c b/libavcodec/imgresample.c index a18645e33e..14fdb1059e 100644 --- a/libavcodec/imgresample.c +++ b/libavcodec/imgresample.c @@ -45,7 +45,10 @@ #define LINE_BUF_HEIGHT (NB_TAPS * 4) struct ImgReSampleContext { - int iwidth, iheight, owidth, oheight, topBand, bottomBand, leftBand, rightBand; + int iwidth, iheight, owidth, oheight; + int topBand, bottomBand, leftBand, rightBand; + int padtop, padbottom, padleft, padright; + int pad_owidth, pad_oheight; int h_incr, v_incr; int16_t h_filters[NB_PHASES][NB_TAPS] __align8; /* horizontal filters */ int16_t v_filters[NB_PHASES][NB_TAPS] __align8; /* vertical filters */ @@ -532,6 +535,7 @@ static void component_resample(ImgReSampleContext *s, &s->v_filters[phase_y][0]); src_y += s->v_incr; + output += owrap; } } @@ -572,13 +576,16 @@ static void build_filter(int16_t *filter, float factor) ImgReSampleContext *img_resample_init(int owidth, int oheight, int iwidth, int iheight) { - return img_resample_full_init(owidth, oheight, iwidth, iheight, 0, 0, 0, 0); + return img_resample_full_init(owidth, oheight, iwidth, iheight, + 0, 0, 0, 0, 0, 0, 0, 0); } ImgReSampleContext *img_resample_full_init(int owidth, int oheight, int iwidth, int iheight, int topBand, int bottomBand, - int leftBand, int rightBand) + int leftBand, int rightBand, + int padtop, int padbottom, + int padleft, int padright) { ImgReSampleContext *s; @@ -593,19 +600,30 @@ ImgReSampleContext *img_resample_full_init(int owidth, int oheight, s->oheight = oheight; s->iwidth = iwidth; s->iheight = iheight; + s->topBand = topBand; s->bottomBand = bottomBand; s->leftBand = leftBand; s->rightBand = rightBand; - s->h_incr = ((iwidth - leftBand - rightBand) * POS_FRAC) / owidth; - s->v_incr = ((iheight - topBand - bottomBand) * POS_FRAC) / oheight; - - build_filter(&s->h_filters[0][0], (float) owidth / (float) (iwidth - leftBand - rightBand)); - build_filter(&s->v_filters[0][0], (float) oheight / (float) (iheight - topBand - bottomBand)); + s->padtop = padtop; + s->padbottom = padbottom; + s->padleft = padleft; + s->padright = padright; + + s->pad_owidth = owidth - (padleft + padright); + s->pad_oheight = oheight - (padtop + padbottom); + + s->h_incr = ((iwidth - leftBand - rightBand) * POS_FRAC) / s->pad_owidth; + s->v_incr = ((iheight - topBand - bottomBand) * POS_FRAC) / s->pad_oheight; + + build_filter(&s->h_filters[0][0], (float) s->pad_owidth / + (float) (iwidth - leftBand - rightBand)); + build_filter(&s->v_filters[0][0], (float) s->pad_oheight / + (float) (iheight - topBand - bottomBand)); return s; - fail: +fail: av_free(s); return NULL; } @@ -614,13 +632,20 @@ void img_resample(ImgReSampleContext *s, AVPicture *output, const AVPicture *input) { int i, shift; + uint8_t* optr; - for(i=0;i<3;i++) { + for (i=0;i<3;i++) { shift = (i == 0) ? 0 : 1; - component_resample(s, output->data[i], output->linesize[i], - s->owidth >> shift, s->oheight >> shift, - input->data[i] + (input->linesize[i] * (s->topBand >> shift)) + (s->leftBand >> shift), - input->linesize[i], ((s->iwidth - s->leftBand - s->rightBand) >> shift), + + optr = output->data[i] + (((output->linesize[i] * + s->padtop) + s->padleft) >> shift); + + component_resample(s, optr, output->linesize[i], + s->pad_owidth >> shift, s->pad_oheight >> shift, + input->data[i] + (input->linesize[i] * + (s->topBand >> shift)) + (s->leftBand >> shift), + input->linesize[i], ((s->iwidth - s->leftBand - + s->rightBand) >> shift), (s->iheight - s->topBand - s->bottomBand) >> shift); } }