diff options
Diffstat (limited to 'libhb/encavcodec.c')
-rw-r--r-- | libhb/encavcodec.c | 260 |
1 files changed, 160 insertions, 100 deletions
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; } |