diff options
author | jstebbins <[email protected]> | 2014-04-15 18:42:17 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2014-04-15 18:42:17 +0000 |
commit | c6c85528383812a0d11c8ce035b1584f35624f7c (patch) | |
tree | 30409e8e361022168d608cda17bc1abe1b2c515e /libhb | |
parent | c973315226c1009f2cadc8a14e5219c1ef5d69c2 (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.c | 13 | ||||
-rw-r--r-- | libhb/common.h | 1 | ||||
-rw-r--r-- | libhb/encavcodec.c | 260 | ||||
-rw-r--r-- | libhb/module.defs | 4 | ||||
-rw-r--r-- | libhb/muxavformat.c | 6 | ||||
-rw-r--r-- | libhb/muxmkv.c | 5 | ||||
-rw-r--r-- | libhb/work.c | 4 |
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; |