summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2014-04-15 18:42:17 +0000
committerjstebbins <[email protected]>2014-04-15 18:42:17 +0000
commitc6c85528383812a0d11c8ce035b1584f35624f7c (patch)
tree30409e8e361022168d608cda17bc1abe1b2c515e /libhb
parentc973315226c1009f2cadc8a14e5219c1ef5d69c2 (diff)
Add VP8 support
Thanks to Matthew Harvey for this patch git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6165 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.c13
-rw-r--r--libhb/common.h1
-rw-r--r--libhb/encavcodec.c260
-rw-r--r--libhb/module.defs4
-rw-r--r--libhb/muxavformat.c6
-rw-r--r--libhb/muxmkv.c5
-rw-r--r--libhb/work.c4
7 files changed, 191 insertions, 102 deletions
diff --git a/libhb/common.c b/libhb/common.c
index 8e9c02f22..25bc74f69 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -43,6 +43,7 @@ enum
HB_GID_VCODEC_MPEG2,
HB_GID_VCODEC_MPEG4,
HB_GID_VCODEC_THEORA,
+ HB_GID_VCODEC_VP8,
HB_GID_ACODEC_AAC,
HB_GID_ACODEC_AAC_HE,
HB_GID_ACODEC_AAC_PASS,
@@ -209,6 +210,7 @@ hb_encoder_internal_t hb_video_encoders[] =
{ { "H.265 (x265)", "x265", "H.265 (libx265)", HB_VCODEC_X265, HB_MUX_AV_MP4|HB_MUX_AV_MKV, }, NULL, 1, HB_GID_VCODEC_H265, },
{ { "MPEG-4", "mpeg4", "MPEG-4 (libavcodec)", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4, },
{ { "MPEG-2", "mpeg2", "MPEG-2 (libavcodec)", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2, },
+ { { "VP8", "VP8", "VP8 (libvpx)", HB_VCODEC_FFMPEG_VP8, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP8, },
{ { "Theora", "theora", "Theora (libtheora)", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_THEORA, },
};
int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_video_encoders[0]);
@@ -227,6 +229,7 @@ static int hb_video_encoder_is_enabled(int encoder)
case HB_VCODEC_THEORA:
case HB_VCODEC_FFMPEG_MPEG4:
case HB_VCODEC_FFMPEG_MPEG2:
+ case HB_VCODEC_FFMPEG_VP8:
#ifdef USE_X265
case HB_VCODEC_X265:
#endif
@@ -1119,6 +1122,13 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high,
*high = 63.;
break;
+ case HB_VCODEC_FFMPEG_VP8:
+ *direction = 1;
+ *granularity = 1.;
+ *low = 0.;
+ *high = 63.;
+ break;
+
case HB_VCODEC_FFMPEG_MPEG2:
case HB_VCODEC_FFMPEG_MPEG4:
default:
@@ -1147,6 +1157,9 @@ const char* hb_video_quality_get_name(uint32_t codec)
#endif
return "RF";
+ case HB_VCODEC_FFMPEG_VP8:
+ return "CQ";
+
default:
return "QP";
}
diff --git a/libhb/common.h b/libhb/common.h
index 97ac820df..d85c7224b 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -449,6 +449,7 @@ struct hb_job_s
#define HB_VCODEC_X265 0x0000004
#define HB_VCODEC_FFMPEG_MPEG4 0x0000010
#define HB_VCODEC_FFMPEG_MPEG2 0x0000020
+#define HB_VCODEC_FFMPEG_VP8 0x0000040
#define HB_VCODEC_FFMPEG_MASK 0x00000F0
#define HB_VCODEC_QSV_H264 0x0000100
#define HB_VCODEC_QSV_MASK 0x0000F00
diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c
index 844c62777..127e1249d 100644
--- a/libhb/encavcodec.c
+++ b/libhb/encavcodec.c
@@ -77,6 +77,10 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
{
hb_log("encavcodecInit: MPEG-2 encoder");
} break;
+ case AV_CODEC_ID_VP8:
+ {
+ hb_log("encavcodecInit: VP8 encoder");
+ } break;
default:
{
hb_error("encavcodecInit: unsupported encoder!");
@@ -89,6 +93,7 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
{
hb_log( "encavcodecInit: avcodec_find_encoder "
"failed" );
+ return 1;
}
context = avcodec_alloc_context3( codec );
@@ -192,8 +197,28 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
// what was previously used
context->flags |= CODEC_FLAG_QSCALE;
context->global_quality = FF_QP2LAMBDA * job->vquality + 0.5;
- hb_log( "encavcodec: encoding at constant quantizer %d",
- context->global_quality );
+ //Set constant quality for libvpx
+ if ( w->codec_param == AV_CODEC_ID_VP8 )
+ {
+ char quality[7];
+ snprintf(quality, 7, "%.2f", job->vquality);
+ av_dict_set( &av_opts, "crf", quality, 0 );
+ //Setting the deadline to good and cpu-used to 0
+ //causes the encoder to balance video quality and
+ //encode time, with a bias to video quality.
+ av_dict_set( &av_opts, "deadline", "good", 0);
+ av_dict_set( &av_opts, "cpu-used", "0", 0);
+ //This value was chosen to make the bitrate high enough
+ //for libvpx to "turn off" the maximum bitrate feature
+ //that is normally applied to constant quality.
+ context->bit_rate = job->width*job->height*( (double)fps.num / (double)fps.den );
+ hb_log( "encavcodec: encoding at CQ %.2f", job->vquality );
+ }
+ else
+ {
+ hb_log( "encavcodec: encoding at constant quantizer %d",
+ context->global_quality );
+ }
}
context->width = job->width;
context->height = job->height;
@@ -336,6 +361,63 @@ static void compute_dts_offset( hb_work_private_t * pv, hb_buffer_t * buf )
}
}
+static uint8_t convert_pict_type( int pict_type, char pkt_flag_key, uint16_t* sflags )
+{
+ uint8_t retval = 0;
+ switch ( pict_type )
+ {
+ case AV_PICTURE_TYPE_P:
+ {
+ retval = HB_FRAME_P;
+ } break;
+
+ case AV_PICTURE_TYPE_B:
+ {
+ retval = HB_FRAME_B;
+ } break;
+
+ case AV_PICTURE_TYPE_S:
+ {
+ retval = HB_FRAME_P;
+ } break;
+
+ case AV_PICTURE_TYPE_SP:
+ {
+ retval = HB_FRAME_P;
+ } break;
+
+ case AV_PICTURE_TYPE_BI:
+ case AV_PICTURE_TYPE_SI:
+ case AV_PICTURE_TYPE_I:
+ {
+ *sflags |= HB_FRAME_REF;
+ if ( pkt_flag_key )
+ {
+ retval = HB_FRAME_IDR;
+ }
+ else
+ {
+ retval = HB_FRAME_I;
+ }
+ } break;
+
+ default:
+ {
+ if ( pkt_flag_key )
+ {
+ //buf->s.flags |= HB_FRAME_REF;
+ *sflags |= HB_FRAME_REF;
+ retval = HB_FRAME_KEY;
+ }
+ else
+ {
+ retval = HB_FRAME_REF;
+ }
+ } break;
+ }
+ return retval;
+}
+
// Generate DTS by rearranging PTS in this sequence:
// pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
//
@@ -433,120 +515,98 @@ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_job_t * job = pv->job;
AVFrame * frame;
hb_buffer_t * in = *buf_in, * buf;
-
- if ( in->size <= 0 )
+ char final_flushing_call = (in->size <= 0);
+ if ( final_flushing_call )
{
+ //make a flushing call to encode for codecs that can encode out of order
/* EOF on input - send it downstream & say we're done */
- *buf_out = in;
*buf_in = NULL;
- return HB_WORK_DONE;
+ frame = NULL;
+ }
+ else
+ {
+ frame = av_frame_alloc();
+ frame->data[0] = in->plane[0].data;
+ frame->data[1] = in->plane[1].data;
+ frame->data[2] = in->plane[2].data;
+ frame->linesize[0] = in->plane[0].stride;
+ frame->linesize[1] = in->plane[1].stride;
+ frame->linesize[2] = in->plane[2].stride;
+
+ // For constant quality, setting the quality in AVCodecContext
+ // doesn't do the trick. It must be set in the AVFrame.
+ frame->quality = pv->context->global_quality;
+
+ // Remember info about this frame that we need to pass across
+ // the avcodec_encode_video call (since it reorders frames).
+ save_frame_info( pv, in );
+ compute_dts_offset( pv, in );
+
+ // Bizarro ffmpeg appears to require the input AVFrame.pts to be
+ // set to a frame number. Setting it to an actual pts causes
+ // jerky video.
+ // frame->pts = in->s.start;
+ frame->pts = pv->frameno_in++;
}
-
- frame = av_frame_alloc();
- frame->data[0] = in->plane[0].data;
- frame->data[1] = in->plane[1].data;
- frame->data[2] = in->plane[2].data;
- frame->linesize[0] = in->plane[0].stride;
- frame->linesize[1] = in->plane[1].stride;
- frame->linesize[2] = in->plane[2].stride;
-
- // For constant quality, setting the quality in AVCodecContext
- // doesn't do the trick. It must be set in the AVFrame.
- frame->quality = pv->context->global_quality;
-
- // Remember info about this frame that we need to pass across
- // the avcodec_encode_video call (since it reorders frames).
- save_frame_info( pv, in );
- compute_dts_offset( pv, in );
-
- // Bizarro ffmpeg appears to require the input AVFrame.pts to be
- // set to a frame number. Setting it to an actual pts causes
- // jerky video.
- // frame->pts = in->s.start;
- frame->pts = pv->frameno_in++;
if ( pv->context->codec )
{
int ret;
AVPacket pkt;
int got_packet;
+ char still_flushing = final_flushing_call;
+ hb_buffer_t* buf_head = NULL;
+ hb_buffer_t* buf_last = NULL;
- av_init_packet(&pkt);
- /* Should be way too large */
- buf = hb_video_buffer_init( job->width, job->height );
- pkt.data = buf->data;
- pkt.size = buf->alloc;
-
- ret = avcodec_encode_video2( pv->context, &pkt, frame, &got_packet );
- if ( ret < 0 || pkt.size <= 0 || !got_packet )
- {
- hb_buffer_close( &buf );
- }
- else
+ do
{
- int64_t frameno = pkt.pts;
- buf->size = pkt.size;
- buf->s.start = get_frame_start( pv, frameno );
- buf->s.duration = get_frame_duration( pv, frameno );
- buf->s.stop = buf->s.stop + buf->s.duration;
- buf->s.flags &= ~HB_FRAME_REF;
- switch ( pv->context->coded_frame->pict_type )
+ av_init_packet(&pkt);
+ /* Should be way too large */
+ buf = hb_video_buffer_init( job->width, job->height );
+ pkt.data = buf->data;
+ pkt.size = buf->alloc;
+
+ ret = avcodec_encode_video2( pv->context, &pkt, frame, &got_packet );
+ if ( ret < 0 || pkt.size <= 0 || !got_packet )
{
- case AV_PICTURE_TYPE_P:
- {
- buf->s.frametype = HB_FRAME_P;
- } break;
-
- case AV_PICTURE_TYPE_B:
- {
- buf->s.frametype = HB_FRAME_B;
- } break;
-
- case AV_PICTURE_TYPE_S:
- {
- buf->s.frametype = HB_FRAME_P;
- } break;
-
- case AV_PICTURE_TYPE_SP:
- {
- buf->s.frametype = HB_FRAME_P;
- } break;
-
- case AV_PICTURE_TYPE_BI:
- case AV_PICTURE_TYPE_SI:
- case AV_PICTURE_TYPE_I:
+ hb_buffer_close( &buf );
+ still_flushing = 0;
+ }
+ else
+ {
+ int64_t frameno = pkt.pts;
+ buf->size = pkt.size;
+ buf->s.start = get_frame_start( pv, frameno );
+ buf->s.duration = get_frame_duration( pv, frameno );
+ buf->s.stop = buf->s.stop + buf->s.duration;
+ buf->s.flags &= ~HB_FRAME_REF;
+ buf->s.frametype = convert_pict_type( pv->context->coded_frame->pict_type, pkt.flags & AV_PKT_FLAG_KEY, &buf->s.flags );
+ buf = process_delay_list( pv, buf );
+
+ if (buf_head == NULL)
{
- buf->s.flags |= HB_FRAME_REF;
- if ( pkt.flags & AV_PKT_FLAG_KEY )
- {
- buf->s.frametype = HB_FRAME_IDR;
- }
- else
- {
- buf->s.frametype = HB_FRAME_I;
- }
- } break;
-
- default:
+ buf_head = buf;
+ }
+ else
{
- if ( pkt.flags & AV_PKT_FLAG_KEY )
- {
- buf->s.flags |= HB_FRAME_REF;
- buf->s.frametype = HB_FRAME_KEY;
- }
- else
- {
- buf->s.frametype = HB_FRAME_REF;
- }
- } break;
+ buf_last->next = buf;
+ }
+ buf_last = buf;
+ }
+ /* Write stats */
+ if (job->pass == 1 && pv->context->stats_out != NULL)
+ {
+ fprintf( pv->file, "%s", pv->context->stats_out );
}
- buf = process_delay_list( pv, buf );
+ } while (still_flushing);
+ if (buf_last != NULL && final_flushing_call)
+ {
+ buf_last->next = in;
+ buf = buf_head;
}
-
- if( job->pass == 1 )
+ else if (final_flushing_call)
{
- /* Write stats */
- fprintf( pv->file, "%s", pv->context->stats_out );
+ buf = in;
}
}
else
@@ -556,11 +616,11 @@ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_error( "encavcodec: codec context has uninitialized codec; skipping frame" );
}
- av_frame_free(&frame);
+ av_frame_free( &frame );
*buf_out = buf;
- return HB_WORK_OK;
+ return final_flushing_call? HB_WORK_DONE : HB_WORK_OK;
}
diff --git a/libhb/module.defs b/libhb/module.defs
index 73a3b6999..d0a2da131 100644
--- a/libhb/module.defs
+++ b/libhb/module.defs
@@ -1,4 +1,4 @@
-__deps__ := A52DEC BZIP2 FFMPEG FONTCONFIG FREETYPE LAME LIBASS LIBDCA \
+__deps__ := A52DEC BZIP2 LIBVPX FFMPEG FONTCONFIG FREETYPE LAME LIBASS LIBDCA \
LIBDVDREAD LIBDVDNAV LIBICONV LIBMKV LIBOGG LIBSAMPLERATE LIBTHEORA LIBVORBIS LIBXML2 \
MP4V2 PTHREADW32 X264 X265 ZLIB LIBBLURAY FDKAAC LIBMFX
@@ -116,7 +116,7 @@ LIBHB.lib = $(LIBHB.build/)hb.lib
LIBHB.dll.libs = $(foreach n, \
ass avcodec avformat avutil avresample dvdnav dvdread \
fontconfig freetype mp3lame \
- ogg samplerate swscale theora vorbis vorbisenc x264 xml2 bluray, \
+ ogg samplerate swscale vpx theora vorbis vorbisenc x264 xml2 bluray, \
$(CONTRIB.build/)lib/lib$(n).a )
ifeq (1,$(FEATURE.fdk_aac))
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index de8680e17..9037dc074 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -280,6 +280,12 @@ static int avformatInit( hb_mux_object_t * m )
}
break;
+ case HB_VCODEC_FFMPEG_VP8:
+ track->st->codec->codec_id = AV_CODEC_ID_VP8;
+ priv_data = NULL;
+ priv_size = 0;
+ break;
+
case HB_VCODEC_THEORA:
{
track->st->codec->codec_id = AV_CODEC_ID_THEORA;
diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c
index 1250a681e..4a8de8a37 100644
--- a/libhb/muxmkv.c
+++ b/libhb/muxmkv.c
@@ -180,6 +180,11 @@ static int MKVInit( hb_mux_object_t * m )
if (job->areBframes)
track->minCache = 1;
break;
+ case HB_VCODEC_FFMPEG_VP8:
+ track->codecID = "V_VP8";
+ track->codecPrivate = NULL;
+ track->codecPrivateSize = 0;
+ break;
case HB_VCODEC_THEORA:
{
int i;
diff --git a/libhb/work.c b/libhb/work.c
index 92e8692d0..5c8c38bed 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -1248,6 +1248,10 @@ static void do_job(hb_job_t *job)
w = hb_get_work( WORK_ENCAVCODEC );
w->codec_param = AV_CODEC_ID_MPEG2VIDEO;
break;
+ case HB_VCODEC_FFMPEG_VP8:
+ w = hb_get_work( WORK_ENCAVCODEC );
+ w->codec_param = AV_CODEC_ID_VP8;
+ break;
case HB_VCODEC_X264:
w = hb_get_work( WORK_ENCX264 );
break;