summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2011-03-19 20:58:01 +0000
committerjstebbins <[email protected]>2011-03-19 20:58:01 +0000
commit24d2785ae92568533caed9528944167ca27e8905 (patch)
tree6449a94ad4a22c66bfaddc8df9bce45080ceceb8 /libhb
parentde22836e9e399616ea04051eabd8b8cbedb4e8cb (diff)
Add mpeg-2 encoding support to libhb, cli, and lingui
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3853 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.h9
-rw-r--r--libhb/encavcodec.c99
-rw-r--r--libhb/muxmkv.c11
-rw-r--r--libhb/muxmp4.c42
-rw-r--r--libhb/scan.c2
-rw-r--r--libhb/work.c19
6 files changed, 136 insertions, 46 deletions
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 )
{