diff options
-rw-r--r-- | contrib/ffmpeg/module.defs | 2 | ||||
-rw-r--r-- | gtk/src/callbacks.c | 3 | ||||
-rw-r--r-- | gtk/src/ghb.ui | 2 | ||||
-rw-r--r-- | gtk/src/hb-backend.c | 18 | ||||
-rw-r--r-- | gtk/src/makedeps.py | 2 | ||||
-rw-r--r-- | libhb/common.h | 9 | ||||
-rw-r--r-- | libhb/encavcodec.c | 99 | ||||
-rw-r--r-- | libhb/muxmkv.c | 11 | ||||
-rw-r--r-- | libhb/muxmp4.c | 42 | ||||
-rw-r--r-- | libhb/scan.c | 2 | ||||
-rw-r--r-- | libhb/work.c | 19 | ||||
-rw-r--r-- | test/test.c | 12 |
12 files changed, 162 insertions, 59 deletions
diff --git a/contrib/ffmpeg/module.defs b/contrib/ffmpeg/module.defs index 40c32003e..b524e27a7 100644 --- a/contrib/ffmpeg/module.defs +++ b/contrib/ffmpeg/module.defs @@ -20,9 +20,9 @@ FFMPEG.CONFIGURE.extra = \ --enable-bzlib \ --enable-encoder=ac3 \ --enable-encoder=mpeg4 \ + --enable-encoder=mpeg2video \ --enable-encoder=snow \ --enable-gpl \ - --enable-muxer=ipod \ --enable-zlib \ --cc="$(FFMPEG.GCC.gcc)" \ --extra-cflags="$(call fn.ARGS,FFMPEG.GCC,*archs *sysroot *minver ?extra) -I$(call fn.ABSOLUTE,$(CONTRIB.build/)include)" \ diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index 4ba8cdd8b..a2854dcad 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -4618,7 +4618,8 @@ format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud) } } break; - case HB_VCODEC_FFMPEG: + case HB_VCODEC_FFMPEG_MPEG4: + case HB_VCODEC_FFMPEG_MPEG2: { return g_strdup_printf("QP: %d", (int)val); } break; diff --git a/gtk/src/ghb.ui b/gtk/src/ghb.ui index 98675dd05..6284e52fd 100644 --- a/gtk/src/ghb.ui +++ b/gtk/src/ghb.ui @@ -3545,7 +3545,7 @@ no-fast-pskip=0:no-dct-decimate=0:cabac=1</property> <object class="GtkLabel" id="label75"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes"><small><b>Current FFMpeg MPEG-4 Advanced Option String</b></small></property> + <property name="label" translatable="yes"><small><b>Current FFMpeg Advanced Option String</b></small></property> <property name="use_markup">True</property> </object> </child> diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index fbcd4a679..b4ff3e8d2 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -239,7 +239,8 @@ combo_opts_t denoise_opts = static options_map_t d_vcodec_opts[] = { {"H.264 (x264)", "x264", HB_VCODEC_X264, ""}, - {"MPEG-4 (FFmpeg)", "ffmpeg", HB_VCODEC_FFMPEG, ""}, + {"MPEG-4 (FFmpeg)", "ffmpeg", HB_VCODEC_FFMPEG_MPEG4, ""}, + {"MPEG-2 (FFmpeg)", "ffmpeg2",HB_VCODEC_FFMPEG_MPEG2, ""}, {"VP3 (Theora)", "theora", HB_VCODEC_THEORA, ""}, }; combo_opts_t vcodec_opts = @@ -736,7 +737,8 @@ ghb_vquality_range( *inverted = TRUE; } break; - case HB_VCODEC_FFMPEG: + case HB_VCODEC_FFMPEG_MPEG2: + case HB_VCODEC_FFMPEG_MPEG4: { *min = 1; *max = 31; @@ -2854,7 +2856,8 @@ ghb_build_advanced_opts_string(GValue *settings) } } break; - case HB_VCODEC_FFMPEG: + case HB_VCODEC_FFMPEG_MPEG2: + case HB_VCODEC_FFMPEG_MPEG4: { gchar *opts = ghb_settings_get_string(settings, "lavcOption"); if (opts != NULL) @@ -4014,7 +4017,7 @@ ghb_validate_video(signal_user_data_t *ud) return FALSE; } g_free(message); - vcodec = HB_VCODEC_FFMPEG; + vcodec = HB_VCODEC_FFMPEG_MPEG4; ghb_ui_update(ud, "VideoEncoder", ghb_int64_value(vcodec)); } return TRUE; @@ -4339,7 +4342,8 @@ ghb_validate_vquality(GValue *settings) max = 30; } break; - case HB_VCODEC_FFMPEG: + case HB_VCODEC_FFMPEG_MPEG2: + case HB_VCODEC_FFMPEG_MPEG4: { min = 1; max = 8; @@ -4620,7 +4624,7 @@ add_job(hb_handle_t *h, GValue *js, gint unique_id, gint titleindex) if ((job->mux == HB_MUX_MP4 ) && (job->vcodec == HB_VCODEC_THEORA)) { // mp4/theora combination is not supported. - job->vcodec = HB_VCODEC_FFMPEG; + job->vcodec = HB_VCODEC_FFMPEG_MPEG4; } if ((job->vcodec == HB_VCODEC_X264) && (job->mux == HB_MUX_MP4)) { @@ -4880,7 +4884,7 @@ add_job(hb_handle_t *h, GValue *js, gint unique_id, gint titleindex) extra_opts = g_strdup_printf("%s:weightb=0", turbo_x264_opts); } } - else if (job->vcodec == HB_VCODEC_FFMPEG) + else if (job->vcodec == HB_VCODEC_FFMPEG_MPEG4) { extra_opts = g_strdup_printf("%s", turbo_lavc_opts); } diff --git a/gtk/src/makedeps.py b/gtk/src/makedeps.py index fd8db094a..2164ab4e7 100644 --- a/gtk/src/makedeps.py +++ b/gtk/src/makedeps.py @@ -47,7 +47,7 @@ dep_map = ( DepEntry("PictureAutoCrop", "PictureLeftCrop", "FALSE", False, False), DepEntry("PictureAutoCrop", "PictureRightCrop", "FALSE", False, False), DepEntry("VideoEncoder", "x264_tab", "x264", False, True), - DepEntry("VideoEncoder", "lavc_mpeg4_tab", "ffmpeg", False, True), + DepEntry("VideoEncoder", "lavc_mpeg4_tab", "ffmpeg|ffmpeg2", False, True), DepEntry("VideoEncoder", "Mp4iPodCompatible", "x264", False, False), DepEntry("AudioEncoderActual", "AudioBitrate", "ac3pass|dtspass", True, False), DepEntry("AudioEncoderActual", "AudioSamplerate", "ac3pass|dtspass", True, False), diff --git a/libhb/common.h b/libhb/common.h index fc7b13916..b8045234c 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -223,9 +223,12 @@ struct hb_job_s advanced_opts: string of extra advanced encoder options areBframes: boolean to note if b-frames are included in advanced_opts */ #define HB_VCODEC_MASK 0x0000FF -#define HB_VCODEC_FFMPEG 0x000001 -#define HB_VCODEC_X264 0x000002 -#define HB_VCODEC_THEORA 0x000004 +#define HB_VCODEC_X264 0x000001 +#define HB_VCODEC_THEORA 0x000002 +#define HB_VCODEC_FFMPEG_MPEG4 0x000010 +#define HB_VCODEC_FFMPEG HB_VCODEC_FFMPEG_MPEG4 +#define HB_VCODEC_FFMPEG_MPEG2 0x000020 +#define HB_VCODEC_FFMPEG_MASK 0x0000F0 int vcodec; float vquality; diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c index e70d9279b..b2cebee64 100644 --- a/libhb/encavcodec.c +++ b/libhb/encavcodec.c @@ -47,7 +47,7 @@ void encavcodecClose( hb_work_object_t * ); hb_work_object_t hb_encavcodec = { WORK_ENCAVCODEC, - "MPEG-4 encoder (libavcodec)", + "FFMPEG encoder (libavcodec)", encavcodecInit, encavcodecWork, encavcodecClose @@ -57,17 +57,34 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) { AVCodec * codec; AVCodecContext * context; - int rate_num, rate_den; + AVRational fps; hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; - codec = avcodec_find_encoder( CODEC_ID_MPEG4 ); + switch ( w->codec_param ) + { + case CODEC_ID_MPEG4: + { + hb_log("encavcodecInit: MPEG-4 ASP encoder"); + } break; + case CODEC_ID_MPEG2VIDEO: + { + hb_log("encavcodecInit: MPEG-2 encoder"); + } break; + default: + { + hb_error("encavcodecInit: unsupported encoder!"); + return 1; + } + } + + codec = avcodec_find_encoder( w->codec_param ); if( !codec ) { - hb_log( "hb_work_encavcodec_init: avcodec_find_encoder " + hb_log( "encavcodecInit: avcodec_find_encoder " "failed" ); } context = avcodec_alloc_context3( codec ); @@ -79,45 +96,64 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) if( job->pass == 2 ) { hb_interjob_t * interjob = hb_interjob_get( job->h ); - rate_num = interjob->vrate_base; - rate_den = interjob->vrate; + fps.den = interjob->vrate_base; + fps.num = interjob->vrate; } else { - rate_num = job->vrate_base; - rate_den = job->vrate; + fps.den = job->vrate_base; + fps.num = job->vrate; } - // If the rate_den is 27000000, there's a good chance this is + // If the fps.num is 27000000, there's a good chance this is // a standard rate that we have in our hb_video_rates table. // Because of rounding errors and approximations made while // measuring framerate, the actual value may not be exact. So // we look for rates that are "close" and make an adjustment - // to rate_num. - if (rate_den == 27000000) + // to fps.den. + if (fps.num == 27000000) { int ii; for (ii = 0; ii < hb_video_rates_count; ii++) { - if (abs(rate_num - hb_video_rates[ii].rate) < 10) + if (abs(fps.den - hb_video_rates[ii].rate) < 10) { - rate_num = hb_video_rates[ii].rate; + fps.den = hb_video_rates[ii].rate; break; } } } - hb_reduce(&rate_num, &rate_den, rate_num, rate_den); - if ((rate_num & ~0xFFFF) || (rate_den & ~0xFFFF)) + hb_reduce(&fps.den, &fps.num, fps.den, fps.num); + + // Check that the framerate is supported. If not, pick the closest. + // The mpeg2 codec only supports a specific list of frame rates. + if (codec->supported_framerates) { - hb_log( "encavcodec: truncating framerate %d / %d", - rate_num, rate_den ); + AVRational supported_fps; + supported_fps = codec->supported_framerates[av_find_nearest_q_idx(fps, codec->supported_framerates)]; + if (supported_fps.num != fps.num || supported_fps.den != fps.den) + { + hb_log( "encavcodec: framerate %d / %d is not supported. Using %d / %d.", + fps.num, fps.den, supported_fps.num, supported_fps.den ); + fps = supported_fps; + } } - while ((rate_num & ~0xFFFF) || (rate_den & ~0xFFFF)) + else if ((fps.num & ~0xFFFF) || (fps.den & ~0xFFFF)) { - rate_num >>= 1; - rate_den >>= 1; + // This may only be required for mpeg4 video. But since + // our only supported options are mpeg2 and mpeg4, there is + // no need to check codec type. + hb_log( "encavcodec: truncating framerate %d / %d", + fps.num, fps.den ); + while ((fps.num & ~0xFFFF) || (fps.den & ~0xFFFF)) + { + fps.num >>= 1; + fps.den >>= 1; + } } - context->time_base = (AVRational) { rate_num, rate_den }; + + context->time_base.den = fps.num; + context->time_base.num = fps.den; context->gop_size = 10 * (int)( (double)job->vrate / (double)job->vrate_base + 0.5 ); /* @@ -178,7 +214,9 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) { /* Rate control */ context->bit_rate = 1000 * job->vbitrate; - context->bit_rate_tolerance = 10 * context->bit_rate; + // ffmpeg's mpeg2 encoder requires that the bit_rate_tolerance be >= + // bitrate * fps + context->bit_rate_tolerance = context->bit_rate * av_q2d(fps) + 1; } else { @@ -257,10 +295,11 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) if( hb_avcodec_open( context, codec ) ) { - hb_log( "hb_work_encavcodec_init: avcodec_open failed" ); + hb_log( "encavcodecInit: avcodec_open failed" ); } pv->context = context; + job->areBframes = 0; if ( context->has_b_frames ) { job->areBframes = 1; @@ -284,7 +323,7 @@ void encavcodecClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; - if( pv->context ) + if( pv->context && pv->context->codec ) { hb_deep_log( 2, "encavcodec: closing libavcodec" ); avcodec_flush_buffers( pv->context ); @@ -506,6 +545,12 @@ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, } buf = process_delay_list( pv, buf ); } + + if( job->pass == 1 ) + { + /* Write stats */ + fprintf( pv->file, "%s", pv->context->stats_out ); + } } else { @@ -516,12 +561,6 @@ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, av_free( frame ); - if( job->pass == 1 ) - { - /* Write stats */ - fprintf( pv->file, "%s", pv->context->stats_out ); - } - *buf_out = buf; return HB_WORK_OK; diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c index 63f3e94cd..287f677fa 100644 --- a/libhb/muxmkv.c +++ b/libhb/muxmkv.c @@ -103,13 +103,20 @@ static int MKVInit( hb_mux_object_t * m ) if (job->areBframes) track->minCache = 1; break; - case HB_VCODEC_FFMPEG: + case HB_VCODEC_FFMPEG_MPEG4: track->codecID = MK_VCODEC_MP4ASP; track->codecPrivate = job->config.mpeg4.bytes; track->codecPrivateSize = job->config.mpeg4.length; if (job->areBframes) track->minCache = 1; break; + case HB_VCODEC_FFMPEG_MPEG2: + track->codecID = MK_VCODEC_MPEG2; + track->codecPrivate = job->config.mpeg4.bytes; + track->codecPrivateSize = job->config.mpeg4.length; + if (job->areBframes) + track->minCache = 1; + break; case HB_VCODEC_THEORA: { int i; @@ -451,7 +458,7 @@ static int MKVMux( hb_mux_object_t * m, hb_mux_data_t * mux_data, mk_addFrameData(m->file, mux_data->track, buf->data, buf->size); mk_setFrameFlags(m->file, mux_data->track, timecode, (((job->vcodec == HB_VCODEC_X264 || - job->vcodec == HB_VCODEC_FFMPEG) && + (job->vcodec & HB_VCODEC_FFMPEG_MASK)) && mux_data == job->mux_data) ? (buf->frametype == HB_FRAME_IDR) : ((buf->frametype & HB_FRAME_KEY) != 0)), 0 ); diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c index 9387d6499..19a14a0b1 100644 --- a/libhb/muxmp4.c +++ b/libhb/muxmp4.c @@ -154,7 +154,7 @@ static int MP4Init( hb_mux_object_t * m ) MP4AddIPodUUID(m->file, mux_data->track); } } - else /* FFmpeg or XviD */ + else if ( job->vcodec == HB_VCODEC_FFMPEG_MPEG4 ) /* FFmpeg MPEG-4 */ { MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 ); mux_data->track = MP4AddVideoTrack( m->file, 90000, @@ -182,6 +182,37 @@ static int MP4Init( hb_mux_object_t * m ) return 0; } } + else if ( job->vcodec == HB_VCODEC_FFMPEG_MPEG2 ) /* FFmpeg MPEG-2 */ + { + mux_data->track = MP4AddVideoTrack( m->file, 90000, + MP4_INVALID_DURATION, job->width, job->height, + MP4_MPEG2_VIDEO_TYPE ); + if (mux_data->track == MP4_INVALID_TRACK_ID) + { + hb_error("muxmp4.c: MP4AddVideoTrack failed!"); + *job->die = 1; + return 0; + } + + /* Tune track chunk duration */ + if( !MP4TuneTrackDurationPerChunk( m, mux_data->track )) + { + return 0; + } + + /* VOL from FFmpeg */ + if (!(MP4SetTrackESConfiguration( m->file, mux_data->track, + job->config.mpeg4.bytes, job->config.mpeg4.length ))) + { + hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!"); + *job->die = 1; + return 0; + } + } + else + { + hb_error("muxmp4.c: Unsupported video encoder!"); + } // COLR atom for color and gamma correction. // Per the notes at: @@ -887,7 +918,7 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, /* Video */ if( job->vcodec == HB_VCODEC_X264 || - job->vcodec == HB_VCODEC_FFMPEG ) + ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) ) { if ( buf && buf->start < buf->renderOffset ) { @@ -907,7 +938,7 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, return 0; if( job->vcodec == HB_VCODEC_X264 || - job->vcodec == HB_VCODEC_FFMPEG ) + ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) ) { // x264 supplies us with DTS, so offset is PTS - DTS offset = buf->start - buf->renderOffset; @@ -945,7 +976,7 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, } if( job->vcodec == HB_VCODEC_X264 || - job->vcodec == HB_VCODEC_FFMPEG ) + ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) ) { // x264 supplies us with DTS if ( m->delay_buf ) @@ -1017,7 +1048,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, } /* Here's where the sample actually gets muxed. */ - if( ( job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_FFMPEG ) + if( ( job->vcodec == HB_VCODEC_X264 || + ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) ) && mux_data == job->mux_data ) { /* Compute dependency flags. diff --git a/libhb/scan.c b/libhb/scan.c index 901551f55..89fd90b97 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -275,7 +275,7 @@ static void ScanFunc( void * _data ) job->keep_ratio = 1; - job->vcodec = HB_VCODEC_FFMPEG; + job->vcodec = HB_VCODEC_FFMPEG_MPEG4; job->vquality = -1.0; job->vbitrate = 1000; job->pass = 0; diff --git a/libhb/work.c b/libhb/work.c index c8caff7b8..e8c2a4e1e 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -264,8 +264,12 @@ void hb_display_job_info( hb_job_t * job ) /* Video encoder */ switch( job->vcodec ) { - case HB_VCODEC_FFMPEG: - hb_log( " + encoder: FFmpeg" ); + case HB_VCODEC_FFMPEG_MPEG4: + hb_log( " + encoder: FFmpeg MPEG-4" ); + break; + + case HB_VCODEC_FFMPEG_MPEG2: + hb_log( " + encoder: FFmpeg MPEG-2" ); break; case HB_VCODEC_X264: @@ -452,7 +456,7 @@ static void do_job( hb_job_t * job ) { hb_set_anamorphic_size(job, &job->width, &job->height, &job->anamorphic.par_width, &job->anamorphic.par_height); - if( job->vcodec == HB_VCODEC_FFMPEG ) + if( job->vcodec & HB_VCODEC_FFMPEG_MASK ) { /* Just to make working with ffmpeg even more fun, lavc's MPEG-4 encoder can't handle PAR values >= 255, @@ -722,8 +726,13 @@ static void do_job( hb_job_t * job ) /* Video encoder */ switch( job->vcodec ) { - case HB_VCODEC_FFMPEG: + case HB_VCODEC_FFMPEG_MPEG4: w = hb_get_work( WORK_ENCAVCODEC ); + w->codec_param = CODEC_ID_MPEG4; + break; + case HB_VCODEC_FFMPEG_MPEG2: + w = hb_get_work( WORK_ENCAVCODEC ); + w->codec_param = CODEC_ID_MPEG2VIDEO; break; case HB_VCODEC_X264: w = hb_get_work( WORK_ENCX264 ); @@ -993,7 +1002,7 @@ static void do_job( hb_job_t * job ) w = muxer; } - hb_buffer_t * buf_in, * buf_out; + hb_buffer_t * buf_in, * buf_out = NULL; while ( !*job->die && !*w->done && w->status != HB_WORK_DONE ) { diff --git a/test/test.c b/test/test.c index e957c7cd7..c6363a036 100644 --- a/test/test.c +++ b/test/test.c @@ -61,7 +61,7 @@ static char * decomb_opt = 0; static int rotate = 0; static char * rotate_opt = 0; static int grayscale = 0; -static int vcodec = HB_VCODEC_FFMPEG; +static int vcodec = HB_VCODEC_FFMPEG_MPEG4; static hb_list_t * audios = NULL; static hb_audio_config_t * audio = NULL; static int num_audio_tracks = 0; @@ -3169,7 +3169,15 @@ static int ParseOptions( int argc, char ** argv ) case 'e': if( !strcasecmp( optarg, "ffmpeg" ) ) { - vcodec = HB_VCODEC_FFMPEG; + vcodec = HB_VCODEC_FFMPEG_MPEG4; + } + else if( !strcasecmp( optarg, "ffmpeg4" ) ) + { + vcodec = HB_VCODEC_FFMPEG_MPEG4; + } + else if( !strcasecmp( optarg, "ffmpeg2" ) ) + { + vcodec = HB_VCODEC_FFMPEG_MPEG2; } else if( !strcasecmp( optarg, "x264" ) ) { |