Skip to content

Commit

Permalink
libhb: enable FFV1 encoder.
Browse files Browse the repository at this point in the history
Co-authored-by: Damiano Galassi <[email protected]>
  • Loading branch information
sr55 and galad87 committed Apr 8, 2024
1 parent 56e0a0d commit cd04bab
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 15 deletions.
1 change: 1 addition & 0 deletions contrib/ffmpeg/module.defs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ FFMPEG.CONFIGURE.extra = \
--disable-decoder=libvpx_* \
--enable-encoder=libvpx_vp8 \
--enable-encoder=libvpx_vp9 \
--enable-encoder=ffv1 \
--disable-decoder=*_crystalhd \
--enable-demuxer=av1 \
--enable-demuxer=obu \
Expand Down
24 changes: 16 additions & 8 deletions libhb/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ enum
HB_GID_VCODEC_AV1_QSV,
HB_GID_VCODEC_AV1_NVENC,
HB_GID_VCODEC_AV1_VCE,
HB_GID_VCODEC_FFV1,
HB_GID_ACODEC_AAC,
HB_GID_ACODEC_AAC_HE,
HB_GID_ACODEC_AAC_PASS,
Expand Down Expand Up @@ -282,6 +283,7 @@ hb_encoder_internal_t hb_video_encoders[] =
{ { "AV1 (NVEnc)", "nvenc_av1", "AV1 (NVEnc)", HB_VCODEC_FFMPEG_NVENC_AV1, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_AV1_NVENC, },
{ { "AV1 10-bit (NVEnc)", "nvenc_av1_10bit", "AV1 10-bit (NVEnc)", HB_VCODEC_FFMPEG_NVENC_AV1_10BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_AV1_NVENC, },
{ { "AV1 (AMD VCE)", "vce_av1", "AV1 (AMD VCE)", HB_VCODEC_FFMPEG_VCE_AV1, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_AV1_VCE, },
{ { "FFV1", "ffv1", "FFV1 (libavcodec)", HB_VCODEC_FFMPEG_FFV1, HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_FFV1, },
{ { "H.264 (x264)", "x264", "H.264 (libx264)", HB_VCODEC_X264_8BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_X264, },
{ { "H.264 10-bit (x264)", "x264_10bit", "H.264 10-bit (libx264)", HB_VCODEC_X264_10BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_X264, },
{ { "H.264 (Intel QSV)", "qsv_h264", "H.264 (Intel Media SDK)", HB_VCODEC_QSV_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_QSV, },
Expand Down Expand Up @@ -375,6 +377,7 @@ static int hb_video_encoder_is_enabled(int encoder, int disable_hardware)
case HB_VCODEC_FFMPEG_VP9_10BIT:
case HB_VCODEC_SVT_AV1:
case HB_VCODEC_SVT_AV1_10BIT:
case HB_VCODEC_FFMPEG_FFV1:
return 1;

#if HB_PROJECT_FEATURE_X265
Expand Down Expand Up @@ -1532,6 +1535,13 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high,
*high = 100;
break;

case HB_VCODEC_FFMPEG_FFV1:
*direction = 0;
*granularity = 1;
*low = 0;
*high = 0;
break;

case HB_VCODEC_FFMPEG_MPEG2:
case HB_VCODEC_FFMPEG_MPEG4:
default:
Expand Down Expand Up @@ -1637,6 +1647,7 @@ int hb_video_multipass_is_supported(uint32_t codec, int constant_quality)

case HB_VCODEC_FFMPEG_VP9:
case HB_VCODEC_FFMPEG_VP9_10BIT:
case HB_VCODEC_FFMPEG_FFV1:
return 1;

default:
Expand Down Expand Up @@ -1847,12 +1858,15 @@ const char* const* hb_video_encoder_get_levels(int encoder)
}
#endif

if (encoder & HB_VCODEC_FFMPEG_MASK)
{
return hb_av_level_get_names(encoder);
}

switch (encoder)
{
case HB_VCODEC_X264_8BIT:
case HB_VCODEC_X264_10BIT:
case HB_VCODEC_FFMPEG_NVENC_H264:
case HB_VCODEC_FFMPEG_MF_H264:
return hb_h264_level_names;

#if HB_PROJECT_FEATURE_VCE
Expand All @@ -1864,11 +1878,6 @@ const char* const* hb_video_encoder_get_levels(int encoder)
case HB_VCODEC_X265_10BIT:
case HB_VCODEC_X265_12BIT:
case HB_VCODEC_X265_16BIT:
case HB_VCODEC_FFMPEG_NVENC_H265:
case HB_VCODEC_FFMPEG_NVENC_H265_10BIT:
case HB_VCODEC_FFMPEG_VCE_H265:
case HB_VCODEC_FFMPEG_VCE_H265_10BIT:
case HB_VCODEC_FFMPEG_MF_H265:
return hb_h265_level_names;

#ifdef __APPLE__
Expand All @@ -1880,7 +1889,6 @@ const char* const* hb_video_encoder_get_levels(int encoder)

case HB_VCODEC_SVT_AV1:
case HB_VCODEC_SVT_AV1_10BIT:
case HB_VCODEC_FFMPEG_VCE_AV1:
return hb_av1_level_names;

default:
Expand Down
131 changes: 125 additions & 6 deletions libhb/encavcodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "handbrake/av1_common.h"
#include "handbrake/nal_units.h"
#include "handbrake/nvenc_common.h"
#include "handbrake/vce_common.h"
#include "handbrake/extradata.h"

/*
Expand Down Expand Up @@ -57,10 +58,12 @@ int encavcodecInit( hb_work_object_t *, hb_job_t * );
int encavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
void encavcodecClose( hb_work_object_t * );

static int apply_encoder_preset(int vcodec, AVDictionary ** av_opts,
const char * preset);
static int apply_encoder_preset(int vcodec, AVCodecContext *context,
AVDictionary **av_opts,
const char *preset);
static int apply_encoder_tune(int vcodec, AVDictionary ** av_opts,
const char * tune);

static int apply_encoder_options(hb_job_t *job, AVCodecContext *context,
AVDictionary **av_opts);

Expand Down Expand Up @@ -88,6 +91,11 @@ static const char * const h26x_nvenc_preset_names[] =
"fastest", "faster", "fast", "medium", "slow", "slower", "slowest", NULL
};

static const char * const ffv1_preset_names[] =
{
"default", "preservation", NULL
};

static const char * const h264_nvenc_profile_names[] =
{
"auto", "baseline", "main", "high", NULL // "high444p" not supported.
Expand Down Expand Up @@ -118,6 +126,21 @@ static const char * const h265_mf_profile_name[] =
"auto", "main", NULL
};

static const char * const ffv1_profile_names[] =
{
"auto", NULL
};

static const char * const hb_ffv1_level_names[] =
{
"auto", "1", "3", NULL
};

static const int hb_ffv1_level_values[] =
{
-1, 1, 3, 0
};

static const enum AVPixelFormat standard_pix_fmts[] =
{
AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE
Expand Down Expand Up @@ -148,6 +171,13 @@ static const enum AVPixelFormat vce_pix_formats_10bit[] =
AV_PIX_FMT_P010, AV_PIX_FMT_NONE
};

static const enum AVPixelFormat ffv1_pix_formats[] =
{
AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P,
AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P,
AV_PIX_FMT_NONE
};

int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
{
Expand All @@ -157,6 +187,7 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
const AVCodec * codec = NULL;
AVCodecContext * context;
AVRational fps;
AVDictionary *av_opts = NULL;

hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
w->private_data = pv;
Expand Down Expand Up @@ -248,6 +279,15 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
break;
}
}break;
case AV_CODEC_ID_FFV1:
{
switch (job->vcodec) {
case HB_VCODEC_FFMPEG_FFV1:
hb_log("encavcodecInit: FFV1 (libavcodec)");
codec_name = "ffv1";
break;
}
}break;
}

if (codec_name == NULL)
Expand Down Expand Up @@ -347,8 +387,7 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
}
}

AVDictionary * av_opts = NULL;
if (apply_encoder_preset(job->vcodec, &av_opts, job->encoder_preset))
if (apply_encoder_preset(job->vcodec, context, &av_opts, job->encoder_preset))
{
av_free( context );
ret = 1;
Expand Down Expand Up @@ -726,6 +765,32 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
av_dict_set(&av_opts, "a53cc", "0", 0);
av_dict_set(&av_opts, "s12m_tc", "0", 0);
}
if (job->vcodec == HB_VCODEC_FFMPEG_FFV1)
{
if (job->encoder_level != NULL && *job->encoder_level)
{
int i = 1;
while (hb_ffv1_level_names[i] != NULL)
{
if (!strcasecmp(job->encoder_level, hb_ffv1_level_names[i]))
context->level = hb_ffv1_level_values[i];
++i;
}
}

int slices[] = {4, 6, 9, 12, 16, 24, 30};
context->slices = hb_get_cpu_count();

int slice_index = 0;
for (int i = 0; i < sizeof(slices) / sizeof(int); i++)
{
if (context->slices >= slices[i])
{
slice_index = i;
}
}
context->slices = slices[slice_index];
}

// Make VCE h.265 encoder emit an IDR for every GOP
if (job->vcodec == HB_VCODEC_FFMPEG_VCE_H265 || job->vcodec == HB_VCODEC_FFMPEG_VCE_H265_10BIT)
Expand Down Expand Up @@ -1289,8 +1354,22 @@ static int apply_vp9_10bit_preset(AVDictionary ** av_opts, const char * preset)
return apply_vpx_preset(av_opts, preset);
}

static int apply_encoder_preset(int vcodec, AVDictionary ** av_opts,
const char * preset)
static int apply_ffv1_preset(AVCodecContext *context, AVDictionary **av_opts, const char *preset)
{
if (!strcasecmp(preset, "preservation"))
{
context->gop_size = 1;
context->level = 3;
av_dict_set(av_opts, "coder", "1", 0);
av_dict_set(av_opts, "context", "1", 0);
av_dict_set(av_opts, "slicecrc", "1", 0);
}
return 0;
}

static int apply_encoder_preset(int vcodec, AVCodecContext *context,
AVDictionary **av_opts,
const char *preset)
{
switch (vcodec)
{
Expand All @@ -1311,6 +1390,9 @@ static int apply_encoder_preset(int vcodec, AVDictionary ** av_opts,
av_dict_set( av_opts, "preset", preset, 0);
break;
#endif

case HB_VCODEC_FFMPEG_FFV1:
return apply_ffv1_preset(context, av_opts, preset);
default:
break;
}
Expand Down Expand Up @@ -1365,6 +1447,9 @@ const char* const* hb_av_preset_get_names(int encoder)
case HB_VCODEC_FFMPEG_MF_H265:
return h26x_mf_preset_name;

case HB_VCODEC_FFMPEG_FFV1:
return ffv1_preset_names;

default:
return NULL;
}
Expand Down Expand Up @@ -1396,6 +1481,37 @@ const char* const* hb_av_profile_get_names(int encoder)
return h264_mf_profile_name;
case HB_VCODEC_FFMPEG_MF_H265:
return h265_mf_profile_name;
case HB_VCODEC_FFMPEG_FFV1:
return ffv1_profile_names;

default:
return NULL;
}
}

const char* const* hb_av_level_get_names(int encoder)
{
switch (encoder)
{
case HB_VCODEC_FFMPEG_NVENC_H264:
case HB_VCODEC_FFMPEG_MF_H264:
return hb_h264_level_names;

case HB_VCODEC_FFMPEG_VCE_H264:
return hb_vce_h264_level_names; // Not quite the same as x264

case HB_VCODEC_FFMPEG_NVENC_H265:
case HB_VCODEC_FFMPEG_NVENC_H265_10BIT:
case HB_VCODEC_FFMPEG_VCE_H265:
case HB_VCODEC_FFMPEG_VCE_H265_10BIT:
case HB_VCODEC_FFMPEG_MF_H265:
return hb_h265_level_names;

case HB_VCODEC_FFMPEG_VCE_AV1:
return hb_av1_level_names;

case HB_VCODEC_FFMPEG_FFV1:
return hb_ffv1_level_names;

default:
return NULL;
Expand Down Expand Up @@ -1425,6 +1541,9 @@ const int* hb_av_get_pix_fmts(int encoder)
case HB_VCODEC_FFMPEG_VP9_10BIT:
return standard_10bit_pix_fmts;

case HB_VCODEC_FFMPEG_FFV1:
return ffv1_pix_formats;

default:
return standard_pix_fmts;
}
Expand Down
2 changes: 2 additions & 0 deletions libhb/handbrake/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,8 @@ struct hb_job_s
#define HB_VCODEC_FFMPEG_NVENC_AV1 (0x00000033 | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_AV1_MASK)
#define HB_VCODEC_FFMPEG_NVENC_AV1_10BIT (0x00000034 | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_AV1_MASK)

#define HB_VCODEC_FFMPEG_FFV1 (0x00000040 | HB_VCODEC_FFMPEG_MASK)

#define HB_VCODEC_SVT_AV1_8BIT (0x00000041 | HB_VCODEC_SVT_AV1_MASK | HB_VCODEC_AV1_MASK)
#define HB_VCODEC_SVT_AV1 HB_VCODEC_SVT_AV1_8BIT
#define HB_VCODEC_SVT_AV1_10BIT (0x00000042 | HB_VCODEC_SVT_AV1_MASK | HB_VCODEC_AV1_MASK)
Expand Down
5 changes: 5 additions & 0 deletions libhb/muxavformat.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ static int avformatInit( hb_mux_object_t * m )
}
}
} break;

case HB_VCODEC_THEORA:
track->st->codecpar->codec_id = AV_CODEC_ID_THEORA;
break;
Expand Down Expand Up @@ -383,6 +384,10 @@ static int avformatInit( hb_mux_object_t * m )
}
break;

case HB_VCODEC_FFMPEG_FFV1:
track->st->codecpar->codec_id = AV_CODEC_ID_FFV1;
break;

default:
hb_error("muxavformat: Unknown video codec: %x", job->vcodec);
goto error;
Expand Down
17 changes: 16 additions & 1 deletion libhb/work.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ static void SetWorkStateInfo(hb_job_t *job)
hb_set_state( job->h, &state );
}

static void sanitize_multipass(hb_job_t *job)
{
int bit_depth = hb_get_bit_depth(job->title->pix_fmt);
if (job->vcodec == HB_VCODEC_FFMPEG_FFV1 && bit_depth > 8)
{
hb_log("FFV1 2-pass is not supported for bit depth higher than 8, disabling");
job->multipass = 0;
}
}

/**
* Iterates through job list and calls do_job for each job.
* @param _work Handle work object.
Expand Down Expand Up @@ -155,6 +165,8 @@ static void work_func( void * _work )
}
#endif


sanitize_multipass(job);
hb_job_setup_passes(job->h, job, passes);
hb_job_close(&job);

Expand Down Expand Up @@ -337,7 +349,10 @@ hb_work_object_t* hb_video_encoder(hb_handle_t *h, int vcodec)
case HB_VCODEC_SVT_AV1_10BIT:
w = hb_get_work(h, WORK_ENCSVTAV1);
break;

case HB_VCODEC_FFMPEG_FFV1:
w = hb_get_work(h, WORK_ENCAVCODEC);
w->codec_param = AV_CODEC_ID_FFV1;
break;
default:
hb_error("Unknown video codec (0x%x)", vcodec );
}
Expand Down

0 comments on commit cd04bab

Please sign in to comment.