diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1230a50263c7..6d2383e89a69 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -138,7 +138,7 @@ jobs: - uses: actions/checkout@v5 - name: Download LibHandBrake - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: LibHandBrake path: win/CS/HandBrakeWPF/bin/publish @@ -209,7 +209,7 @@ jobs: - uses: actions/checkout@v5 - name: Download LibHandBrake_ARM64 - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: LibHandBrake_ARM64 path: win/CS/HandBrakeWPF/bin/publish diff --git a/contrib/ffmpeg/module.defs b/contrib/ffmpeg/module.defs index 0ef5e79cff7a..ecb2a9fc376d 100644 --- a/contrib/ffmpeg/module.defs +++ b/contrib/ffmpeg/module.defs @@ -198,6 +198,17 @@ FFMPEG.CONFIGURE.extra += \ --enable-filter=scale_cuda endif +ifeq (1,$(FEATURE.vaapi)) +FFMPEG.CONFIGURE.extra += \ + --enable-hwaccels \ + --enable-vaapi \ + --enable-encoder=h264_vaapi \ + --enable-encoder=hevc_vaapi \ + --enable-encoder=av1_vaapi \ + --enable-encoder=vp8_vaapi \ + --enable-encoder=vp9_vaapi +endif + ifeq (1,$(FEATURE.mf)) FFMPEG.CONFIGURE.extra += \ --enable-hwaccel=h264_d3d11va \ diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index e1227fbe5697..e6f197b46efd 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -423,8 +423,8 @@ static GhbBinding widget_bindings[] = {"PictureDetelecine", "active-id", "custom10", "PictureDetelecineCustom", "visible"}, {"PictureColorspacePreset", "active-id", "custom11", "PictureColorspaceCustom", "visible"}, {"VideoEncoder", "active-id", "svt_av1|svt_av1_10bit|x264|x264_10bit", "x264FastDecode", "visible"}, - {"VideoEncoder", "active-id", "svt_av1|svt_av1_10bit|x264|x264_10bit|x265|x265_10bit|x265_12bit|x265_16bit|mpeg4|mpeg2|VP8|VP9|VP9_10bit|qsv_av1|qsv_av1_10bit|qsv_h264|qsv_h265|qsv_h265_10bit", "VideoOptionExtraWindow", "visible"}, - {"VideoEncoder", "active-id", "svt_av1|svt_av1_10bit|x264|x264_10bit|x265|x265_10bit|x265_12bit|x265_16bit|mpeg4|mpeg2|VP8|VP9|VP9_10bit|qsv_av1|qsv_av1_10bit|qsv_h264|qsv_h265|qsv_h265_10bit", "VideoOptionExtraLabel", "visible"}, + {"VideoEncoder", "active-id", "svt_av1|svt_av1_10bit|x264|x264_10bit|x265|x265_10bit|x265_12bit|x265_16bit|mpeg4|mpeg2|VP8|VP9|VP9_10bit|qsv_av1|qsv_av1_10bit|qsv_h264|qsv_h265|qsv_h265_10bit|vaapi_h264|vaapi_hevc|vaapi_vp8|vaapi_vp9", "VideoOptionExtraWindow", "visible"}, + {"VideoEncoder", "active-id", "svt_av1|svt_av1_10bit|x264|x264_10bit|x265|x265_10bit|x265_12bit|x265_16bit|mpeg4|mpeg2|VP8|VP9|VP9_10bit|qsv_av1|qsv_av1_10bit|qsv_h264|qsv_h265|qsv_h265_10bit|vaapi_h264|vaapi_hevc|vaapi_vp8|vaapi_vp9", "VideoOptionExtraLabel", "visible"}, {"auto_name", "active", NULL, "autoname_box", "sensitive"}, {"CustomTmpEnable", "active", NULL, "CustomTmpDir", "sensitive"}, {"PresetCategory", "active-id", "new", "PresetCategoryName", "visible"}, diff --git a/libhb/common.c b/libhb/common.c index afebc52df26a..167253a43d52 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -42,6 +42,9 @@ #if HB_PROJECT_FEATURE_NVENC #include "handbrake/nvenc_common.h" #endif +#if HB_PROJECT_FEATURE_VAAPI +#include "handbrake/vaapi_common.h" +#endif #if HB_PROJECT_FEATURE_VCE #include "handbrake/vce_common.h" #endif @@ -63,12 +66,14 @@ enum HB_GID_NONE = -1, // encoders must NEVER use it HB_GID_VCODEC_H264_MF, HB_GID_VCODEC_H264_NVENC, + HB_GID_VCODEC_H264_VAAPI, HB_GID_VCODEC_H264_QSV, HB_GID_VCODEC_H264_VCE, HB_GID_VCODEC_H264_VT, HB_GID_VCODEC_H264_X264, HB_GID_VCODEC_H265_MF, HB_GID_VCODEC_H265_NVENC, + HB_GID_VCODEC_H265_VAAPI, HB_GID_VCODEC_H265_QSV, HB_GID_VCODEC_H265_VCE, HB_GID_VCODEC_H265_VT, @@ -77,12 +82,15 @@ enum HB_GID_VCODEC_MPEG4, HB_GID_VCODEC_THEORA, HB_GID_VCODEC_VP8, + HB_GID_VCODEC_VP8_VAAPI, HB_GID_VCODEC_VP9, + HB_GID_VCODEC_VP9_VAAPI, HB_GID_VCODEC_AV1_SVT, HB_GID_VCODEC_AV1_QSV, HB_GID_VCODEC_AV1_NVENC, HB_GID_VCODEC_AV1_VCE, HB_GID_VCODEC_AV1_MF, + HB_GID_VCODEC_AV1_VAAPI, HB_GID_VCODEC_FFV1, HB_GID_ACODEC_ALAC, HB_GID_ACODEC_ALAC_PASS, @@ -291,12 +299,14 @@ hb_encoder_internal_t hb_video_encoders[] = { { "AV1 10-bit (NVEnc)", "nvenc_av1_10bit", "AV1 10-bit (NVEnc)", HB_VCODEC_FFMPEG_NVENC_AV1_10BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_WEBM|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_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_AV1_VCE, }, { { "AV1 (MediaFoundation)", "mf_av1", "AV1 (MediaFoundation)", HB_VCODEC_FFMPEG_MF_AV1, HB_MUX_MASK_MP4|HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_AV1_MF, }, + { { "AV1 (vaapi)", "vaapi_av1", "AV1 (vaapi)", HB_VCODEC_FFMPEG_VAAPI_AV1, HB_MUX_MASK_MP4|HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_AV1_VAAPI, }, { { "FFV1", "ffv1", "FFV1 (libavcodec)", HB_VCODEC_FFMPEG_FFV1, HB_MUX_MASK_MP4|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 QSV)", HB_VCODEC_FFMPEG_QSV_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_QSV, }, { { "H.264 (AMD VCE)", "vce_h264", "H.264 (AMD VCE)", HB_VCODEC_FFMPEG_VCE_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_VCE, }, { { "H.264 (NVEnc)", "nvenc_h264", "H.264 (NVEnc)", HB_VCODEC_FFMPEG_NVENC_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_NVENC, }, + { { "H.264 (vaapi)", "vaapi_h264", "H.264 (vaapi)", HB_VCODEC_FFMPEG_VAAPI_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_VAAPI, }, { { "H.264 (MediaFoundation)", "mf_h264", "H.264 (MediaFoundation)", HB_VCODEC_FFMPEG_MF_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_MF, }, { { "H.264 (VideoToolbox)", "vt_h264", "H.264 (VideoToolbox)", HB_VCODEC_VT_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H264_VT, }, { { "H.265 (x265)", "x265", "H.265 (libx265)", HB_VCODEC_X265_8BIT, HB_MUX_AV_MP4|HB_MUX_AV_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_X265, }, @@ -308,6 +318,7 @@ hb_encoder_internal_t hb_video_encoders[] = { { "H.265 (AMD VCE)", "vce_h265", "H.265 (AMD VCE)", HB_VCODEC_FFMPEG_VCE_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_VCE, }, { { "H.265 10-bit (AMD VCE)", "vce_h265_10bit", "H.265 10-bit (AMD VCE)", HB_VCODEC_FFMPEG_VCE_H265_10BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_VCE, }, { { "H.265 (NVEnc)", "nvenc_h265", "H.265 (NVEnc)", HB_VCODEC_FFMPEG_NVENC_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_NVENC, }, + { { "H.265 (vaapi)", "vaapi_hevc", "H.265 (vaapi)", HB_VCODEC_FFMPEG_VAAPI_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_VAAPI, }, { { "H.265 10-bit (NVEnc)", "nvenc_h265_10bit", "H.265 10-bit (NVEnc)", HB_VCODEC_FFMPEG_NVENC_H265_10BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_NVENC, }, { { "H.265 (MediaFoundation)", "mf_h265", "H.265 (MediaFoundation)", HB_VCODEC_FFMPEG_MF_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_MF, }, { { "H.265 (VideoToolbox)", "vt_h265", "H.265 (VideoToolbox)", HB_VCODEC_VT_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_H265_VT, }, @@ -315,7 +326,9 @@ hb_encoder_internal_t hb_video_encoders[] = { { "MPEG-4", "mpeg4", "MPEG-4 (libavcodec)", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_MPEG4, }, { { "MPEG-2", "mpeg2", "MPEG-2 (libavcodec)", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_MPEG2, }, { { "VP8", "VP8", "VP8 (libvpx)", HB_VCODEC_FFMPEG_VP8, HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_VP8, }, + { { "VP8 (vaapi)", "vaapi_VP8", "VP8 (vaapi)", HB_VCODEC_FFMPEG_VAAPI_VP8, HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_VP8_VAAPI, }, { { "VP9", "VP9", "VP9 (libvpx)", HB_VCODEC_FFMPEG_VP9, HB_MUX_MASK_MP4|HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_VP9, }, + { { "VP9 (vaapi)", "vaapi_VP9", "VP9 (vaapi)", HB_VCODEC_FFMPEG_VAAPI_VP9, HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_VP9_VAAPI, }, { { "VP9 10-bit", "VP9_10bit", "VP9 10-bit (libvpx)", HB_VCODEC_FFMPEG_VP9_10BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_VP9, }, { { "Theora", "theora", "Theora (libtheora)", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 0, 1, HB_GID_VCODEC_THEORA, }, }; @@ -355,6 +368,15 @@ static int hb_video_encoder_is_enabled(int encoder, int disable_hardware) return hb_nvenc_av1_available(); #endif +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_H264: + case HB_VCODEC_FFMPEG_VAAPI_H265: + case HB_VCODEC_FFMPEG_VAAPI_AV1: + case HB_VCODEC_FFMPEG_VAAPI_VP8: + case HB_VCODEC_FFMPEG_VAAPI_VP9: + return hb_vaapi_encoder_available(encoder); +#endif + #ifdef __APPLE__ case HB_VCODEC_VT_H264: case HB_VCODEC_VT_H265: @@ -586,10 +608,20 @@ static void hb_common_global_hw_init() hb_qsv_available(); hb_register_hwaccel(&hb_hwaccel_qsv); #endif +#if HB_PROJECT_FEATURE_VAAPI + hb_vaapi_init(); +#endif hb_hwaccel_common_hwaccel_init(); } +static void hb_common_global_hw_close() +{ +#if HB_PROJECT_FEATURE_VAAPI + hb_vaapi_free(); +#endif +} + void hb_common_global_init(int disable_hardware) { static int common_init_done = 0; @@ -853,6 +885,14 @@ void hb_common_global_init(int disable_hardware) common_init_done = 1; } +void hb_common_global_close(int disable_hardware) +{ + if (!disable_hardware) + { + hb_common_global_hw_close(); + } +} + int hb_video_framerate_get_from_name(const char *name) { if (name == NULL || *name == '\0') @@ -1619,6 +1659,8 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high, case HB_VCODEC_FFMPEG_NVENC_H264: case HB_VCODEC_FFMPEG_NVENC_H265: case HB_VCODEC_FFMPEG_NVENC_H265_10BIT: + case HB_VCODEC_FFMPEG_VAAPI_H264: + case HB_VCODEC_FFMPEG_VAAPI_H265: *direction = 1; *granularity = 0.1; *low = 1.; @@ -1626,6 +1668,7 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high, break; case HB_VCODEC_FFMPEG_NVENC_AV1: case HB_VCODEC_FFMPEG_NVENC_AV1_10BIT: + case HB_VCODEC_FFMPEG_VAAPI_AV1: *direction = 1; *granularity = 0.1; *low = 1.; @@ -1667,6 +1710,8 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high, case HB_VCODEC_FFMPEG_VP8: case HB_VCODEC_FFMPEG_VP9: case HB_VCODEC_FFMPEG_VP9_10BIT: + case HB_VCODEC_FFMPEG_VAAPI_VP8: + case HB_VCODEC_FFMPEG_VAAPI_VP9: case HB_VCODEC_SVT_AV1: case HB_VCODEC_SVT_AV1_10BIT: *direction = 1; @@ -1754,6 +1799,11 @@ const char* hb_video_quality_get_name(uint32_t codec) case HB_VCODEC_FFMPEG_MF_AV1: return "Quality"; + case HB_VCODEC_FFMPEG_VAAPI_VP8: + case HB_VCODEC_FFMPEG_VAAPI_VP9: + case HB_VCODEC_FFMPEG_VAAPI_H264: + case HB_VCODEC_FFMPEG_VAAPI_H265: + case HB_VCODEC_FFMPEG_VAAPI_AV1: default: return "QP"; } @@ -1875,6 +1925,11 @@ int hb_video_encoder_is_supported(int encoder) return 0; } +int hb_video_encoder_is_vaapi(int encoder) +{ + return HB_VCODEC_VAAPI_MASK == (encoder & HB_VCODEC_VAAPI_MASK); +} + int hb_video_encoder_get_count_of_analysis_passes(int encoder) { switch (encoder) @@ -2050,6 +2105,9 @@ const char* const* hb_video_encoder_get_profiles(int encoder) case HB_VCODEC_FFMPEG_MF_H264: case HB_VCODEC_FFMPEG_MF_H265: case HB_VCODEC_FFMPEG_MF_AV1: + case HB_VCODEC_FFMPEG_VAAPI_H264: + case HB_VCODEC_FFMPEG_VAAPI_H265: + case HB_VCODEC_FFMPEG_VAAPI_AV1: return hb_av_profile_get_names(encoder); case HB_VCODEC_SVT_AV1: @@ -2079,6 +2137,7 @@ const char* const* hb_video_encoder_get_levels(int encoder) { case HB_VCODEC_X264_8BIT: case HB_VCODEC_X264_10BIT: + case HB_VCODEC_FFMPEG_VAAPI_H264: return hb_h264_level_names; #if HB_PROJECT_FEATURE_VCE @@ -2090,6 +2149,7 @@ 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_VAAPI_H265: return hb_h265_level_names; #ifdef __APPLE__ diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c index 0ab62286d86d..e0b8b8de7e7f 100644 --- a/libhb/encavcodec.c +++ b/libhb/encavcodec.c @@ -21,6 +21,10 @@ #include "handbrake/extradata.h" #include "handbrake/qsv_common.h" +#if HB_PROJECT_FEATURE_VAAPI +#include "handbrake/vaapi_common.h" +#endif + /* * The frame info struct remembers information about each frame across calls * to avcodec_encode_video. Since frames are uniquely identified by their @@ -99,7 +103,7 @@ static const char * const vpx_preset_names[] = "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", NULL }; -static const char * const vp9_tune_names[] = +static const char * const vp9_tune_names[] = { "none", "screen", "film", NULL }; @@ -159,6 +163,61 @@ static const char * const av1_mf_profile_name[] = "auto", "main", NULL }; +/// ffmpeg -h encoder=h264_vaapi +static const char * const h264_vaapi_profile_names[] = +{ + "auto", "baseline", "main", "high", NULL // default -99, constrained_baseline 578, main 77, high 100 +}; + +/// ffmpeg -h encoder=hevc_vaapi +static const char * const h265_vaapi_profile_names[] = +{ + "auto", "main", "main10", "rext", NULL // default -99, main 1, main10 2, rext 4 +}; + +/// ffmpeg -h encoder=av1_vaapi +static const char * const av1_vaapi_profile_names[] = +{ + "auto", "main", "high", "professional", NULL // default -99, main 0, high 1, professional 2 +}; + +/** + * synchronized with 'hb_h264_level_names' definition + * ffmpeg -h encoder=h264_vaapi + */ +static const char * const h264_vaapi_level_values[] = +{ + // "auto", "1.0", "1b", "1.1", "1.2", "1.3", "2.0", "2.1", "2.2", "3.0", + "-99", "10", "10", "11", "12", "13", "20", "21", "22", "30", + // "3.1", "3.2", "4.0", "4.1", "4.2", "5.0", "5.1", "5.2", "6.0", "6.1", "6.2", NULL, }; + "31", "32", "40", "41", "42", "50", "51", "52", "60", "61", "62", NULL +}; + +/** + * synchronized with 'hb_h265_level_names' definition + * ffmpeg -h encoder=hevc_vaapi + */ +static const char * const h265_vaapi_level_values[] = +{ + // "auto", "1.0", "2.0", "2.1", "3.0", "3.1", "4.0", "4.1", + "-99", "30", "60", "63", "90", "93", "120", "123", + // "5.0", "5.1", "5.2", "6.0", "6.1", "6.2", NULL + "150", "153", "156", "180", "183", "186", NULL +}; + +/** + * synchronized with 'hb_av1_level_names' definition + * ffmpeg -h encoder=av1_vaapi + */ +static const char * const av1_vaapi_level_values[] = +{ + // "auto", "2.0", "2.1", "2.2", "2.3", "3.0", "3.1", "3.2", + "-99", "0", "1", "1", "1", "4", "5", "5", + // "3.3", "4.0", "4.1", "4.2", "4.3", "5.0", "5.1", "5.2", + "5", "8", "9", "9", "9", "12", "13", "14" + // "5.3", "6.0", "6.1", "6.2", "6.3", NULL, + "15", "16", "17", "18", "19", NULL +}; static const char * const hb_ffv1_level_names[] = { @@ -261,13 +320,33 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) } break; case AV_CODEC_ID_VP8: { - hb_log("encavcodecInit: VP8 encoder"); - codec_name = "libvpx"; + switch (job->vcodec) { +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_VP8: + hb_log("encavcodecInit: VP8 (vaapi)"); + codec_name = "vp8_vaapi"; + break; +#endif + default: + hb_log("encavcodecInit: VP8 (libvpx)"); + codec_name = "libvpx"; + break; + } } break; case AV_CODEC_ID_VP9: { - hb_log("encavcodecInit: VP9 encoder"); - codec_name = "libvpx-vp9"; + switch (job->vcodec) { +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_VP9: + hb_log("encavcodecInit: VP9 (vaapi)"); + codec_name = "vp9_vaapi"; + break; +#endif + default: + hb_log("encavcodecInit: VP9 (libvpx)"); + codec_name = "libvpx-vp9"; + break; + } } break; case AV_CODEC_ID_H264: { @@ -276,6 +355,12 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) hb_log("encavcodecInit: H.264 (Nvidia NVENC)"); codec_name = "h264_nvenc"; break; +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_H264: + hb_log("encavcodecInit: H.264 (vaapi)"); + codec_name = "h264_vaapi"; + break; +#endif case HB_VCODEC_FFMPEG_VCE_H264: hb_log("encavcodecInit: H.264 (AMD VCE)"); codec_name = "h264_amf"; @@ -298,6 +383,12 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) hb_log("encavcodecInit: H.265 (Nvidia NVENC)"); codec_name = "hevc_nvenc"; break; +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_H265: + hb_log("encavcodecInit: H.265 (vaapi)"); + codec_name = "hevc_vaapi"; + break; +#endif case HB_VCODEC_FFMPEG_VCE_H265: case HB_VCODEC_FFMPEG_VCE_H265_10BIT: hb_log("encavcodecInit: H.265 (AMD VCE)"); @@ -335,6 +426,12 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) hb_log("encavcodecInit: AV1 (MediaFoundation)"); codec_name = "av1_mf"; break; +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_AV1: + hb_log("encavcodecInit: AV1 (vaapi)"); + codec_name = "av1_vaapi"; + break; +#endif } }break; case AV_CODEC_ID_FFV1: @@ -479,6 +576,15 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) av_dict_set( &av_opts, "rc", "vbr", 0 ); hb_log( "encavcodec: encoding at rc=vbr, Bitrate %d", job->vbitrate ); } +#if HB_PROJECT_FEATURE_VAAPI + else if (hb_video_encoder_is_vaapi(job->vcodec)) + { + char quality[7]; + snprintf(quality, 7, "%.0f", job->vquality); + av_dict_set( &av_opts, "rc_mode", "VBR", 0 ); // Enable constant-quality rate control mode + hb_log( "encavcodec: vaapi: encoding at rc_mode=VBR, Bitrate %d", job->vbitrate ); + } +#endif #if HB_PROJECT_FEATURE_QSV if (hb_qsv_is_ffmpeg_supported_codec(job->vcodec)) @@ -607,6 +713,26 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) hb_log("encavcodec: encoding with brc ICQ %s", global_quality); } } +#endif +#if HB_PROJECT_FEATURE_VAAPI + // Set constant quality for vaapi + else if (hb_video_encoder_is_vaapi(job->vcodec)) + { + char quality[7]; + snprintf(quality, 7, "%.0f", job->vquality); + av_dict_set( &av_opts, "rc_mode", "CQP", 0 ); // Enable constant-quality rate control mode + if ( job->vcodec == HB_VCODEC_FFMPEG_VAAPI_H264 || + job->vcodec == HB_VCODEC_FFMPEG_VAAPI_H265 ) + { + // h264 and hevc + av_dict_set( &av_opts, "qp", quality, 0 ); + hb_log( "encavcodec: vaapi: encoding at rc_mode=CQP, qp=%.0f", job->vquality ); + } else { + // VP8, VP9 and AV1 + av_dict_set( &av_opts, "q", quality, 0 ); + hb_log( "encavcodec: vaapi: encoding at rc_mode=CQP, q=%.0f", job->vquality ); + } + } #endif else if ( job->vcodec == HB_VCODEC_FFMPEG_VCE_H264 || job->vcodec == HB_VCODEC_FFMPEG_VCE_H265 || @@ -726,7 +852,21 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) } } #endif - context->pix_fmt = job->output_pix_fmt; +#if HB_PROJECT_FEATURE_VAAPI + if (hb_video_encoder_is_vaapi(job->vcodec)) + { + int err; + context->pix_fmt = AV_PIX_FMT_VAAPI; + if((err = hb_vaapi_avcodec_set_hwframe_ctx(context, 0, 20))<0) { + hb_log("Failed to set the VAAPI hwframe_ctx. Error code: %s", av_err2str(err)); + ret = 1; + goto done; + } + } else +#endif + { + context->pix_fmt = job->output_pix_fmt; + } } context->sample_aspect_ratio.num = job->par.num; @@ -876,6 +1016,97 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) av_dict_set(&av_opts, "scenario", "archive", 0); } } +#if HB_PROJECT_FEATURE_VAAPI + else if (hb_video_encoder_is_vaapi(job->vcodec)) + { + // FIXME: Validate whether VAAPI 'B frames are supported (works on AMD NAVI) + av_dict_set(&av_opts, "b_depth", "2", 0); + + // Set profile and level + if (job->encoder_profile != NULL && *job->encoder_profile) + { + if ( job->vcodec == HB_VCODEC_FFMPEG_VAAPI_H264 ) { + // ffmpeg -h encoder=h264_vaapi + // "auto", "baseline", "main", "high", NULL // default -99, constrained_baseline 578, main 77, high 100 + if (!strcasecmp(job->encoder_profile, "auto")) { + av_dict_set(&av_opts, "profile", "-99", 0); + } else if (!strcasecmp(job->encoder_profile, "baseline")) { + av_dict_set(&av_opts, "profile", "578", 0); + } else if (!strcasecmp(job->encoder_profile, "main")) { + av_dict_set(&av_opts, "profile", "77", 0); + } else if (!strcasecmp(job->encoder_profile, "high")) { + av_dict_set(&av_opts, "profile", "100", 0); + } + } else if ( job->vcodec == HB_VCODEC_FFMPEG_VAAPI_H265 ) { + // ffmpeg -h encoder=hevc_vaapi + // "auto", "main", "main10", "rext", NULL // default -99, main 1, main10 2, rext 4 + if (!strcasecmp(job->encoder_profile, "auto")) { + av_dict_set(&av_opts, "profile", "-99", 0); + } else if (!strcasecmp(job->encoder_profile, "main")) { + av_dict_set(&av_opts, "profile", "1", 0); + } else if (!strcasecmp(job->encoder_profile, "main10")) { + av_dict_set(&av_opts, "profile", "2", 0); + } else if (!strcasecmp(job->encoder_profile, "rext")) { + av_dict_set(&av_opts, "profile", "4", 0); + } + } else if ( job->vcodec == HB_VCODEC_FFMPEG_VAAPI_AV1 ) { + // ffmpeg -h encoder=av1_vaapi + // "auto", "main", "high", "professional", NULL // default -99, main 0, high 1, professional 2 + if (!strcasecmp(job->encoder_profile, "auto")) { + av_dict_set(&av_opts, "profile", "-99", 0); + } else if (!strcasecmp(job->encoder_profile, "main")) { + av_dict_set(&av_opts, "profile", "0", 0); + } else if (!strcasecmp(job->encoder_profile, "high")) { + av_dict_set(&av_opts, "profile", "1", 0); + } else if (!strcasecmp(job->encoder_profile, "professional")) { + av_dict_set(&av_opts, "profile", "2", 0); + } + } + } + + if (job->encoder_level != NULL && *job->encoder_level) + { + int set = 0; + if ( job->vcodec == HB_VCODEC_FFMPEG_VAAPI_H264 ) { + // ffmpeg -h encoder=h264_vaapi + for (int i=0; hb_h264_level_names[i] != NULL; i++) { + if (!strcasecmp(job->encoder_level, hb_h264_level_names[i])) { + av_dict_set(&av_opts, "level", h264_vaapi_level_values[i], 0); + set = 1; + break; + } + } + if( 0 == set ) { + av_dict_set(&av_opts, "level", "40", 0); // default 4.0 + } + } else if ( job->vcodec == HB_VCODEC_FFMPEG_VAAPI_H265 ) { + // ffmpeg -h encoder=h265_vaapi + for (int i=0; hb_h265_level_names[i] != NULL; i++) { + if (!strcasecmp(job->encoder_level, hb_h265_level_names[i])) { + av_dict_set(&av_opts, "level", h265_vaapi_level_values[i], 0); + set = 1; + break; + } + } + if( 0 == set ) { + av_dict_set(&av_opts, "level", "120", 0); // default 4.0 + } + } else if ( job->vcodec == HB_VCODEC_FFMPEG_VAAPI_AV1 ) { + // ffmpeg -h encoder=av1_vaapi + for (int i=0; hb_av1_level_names[i] != NULL; i++) { + if (!strcasecmp(job->encoder_level, hb_av1_level_names[i])) { + av_dict_set(&av_opts, "level", av1_vaapi_level_values[i], 0); + set = 1; + break; + } + } + if( 0 == set ) { + av_dict_set(&av_opts, "level", "8", 0); // default 4.0 + } + } + } + } +#endif if( job->pass_id == HB_PASS_ENCODE_ANALYSIS || job->pass_id == HB_PASS_ENCODE_FINAL ) @@ -954,6 +1185,22 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) free(filename); } + if( 3 <= global_verbosity_level ) { + // Allow user to see av_opts in log file and see ffmpeg verbosity + int av_log_level = av_log_get_level(); + char * av_opts_string = NULL; + hb_log( "encavcodec: verbosity enabled: %d", global_verbosity_level); + if( 0 <= av_dict_get_string(av_opts, &av_opts_string, '=', ',') && NULL != av_opts_string ) { + hb_log( "encavcodec: avcodec_open options: %s", av_opts_string); + free(av_opts_string); + } else { + hb_log( "encavcodec: Error gathering avcodec_open options"); + } + if( AV_LOG_VERBOSE > av_log_level ) { + av_log_set_level(AV_LOG_VERBOSE); + } + } + if (hb_avcodec_open(context, codec, &av_opts, HB_FFMPEG_THREADS_AUTO)) { hb_log( "encavcodecInit: avcodec_open failed" ); @@ -1212,6 +1459,9 @@ static void Encode( hb_work_object_t *w, hb_buffer_t **buf_in, hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; AVFrame frame = {{0}}; +#if HB_PROJECT_FEATURE_VAAPI + AVFrame *hw_frame = NULL; +#endif int key_frame = 0; int ret; @@ -1264,14 +1514,25 @@ static void Encode( hb_work_object_t *w, hb_buffer_t **buf_in, frame.flags = 0; } - // Encode - ret = avcodec_send_frame(pv->context, &frame); - av_frame_unref(&frame); +#if HB_PROJECT_FEATURE_VAAPI + if (NULL != pv->context->hw_frames_ctx && + frame.format != pv->context->pix_fmt && + AV_PIX_FMT_VAAPI == pv->context->pix_fmt) + { + if ((ret = hb_vaapi_avcodec_send_frame(pv->context, &frame, &hw_frame)) < 0) + goto close; + } + else +#endif + { + // Encode + ret = avcodec_send_frame(pv->context, &frame); + } if (ret < 0) { - hb_log("encavcodec: avcodec_send_frame failed"); - return; + hb_log("encavcodec: avcodec_send_frame failed, error code: %s",av_err2str(ret)); + goto close; } // Write stats @@ -1282,6 +1543,14 @@ static void Encode( hb_work_object_t *w, hb_buffer_t **buf_in, } get_packets(w, list); + +close: + av_frame_unref(&frame); +#if HB_PROJECT_FEATURE_VAAPI + if( NULL != hw_frame ) { + av_frame_free(&hw_frame); + } +#endif } static void Flush( hb_work_object_t * w, hb_buffer_list_t * list ) @@ -1564,6 +1833,7 @@ static int apply_encoder_level(AVCodecContext *context, AVDictionary **av_opts, case HB_VCODEC_FFMPEG_VCE_H264: case HB_VCODEC_FFMPEG_NVENC_H264: case HB_VCODEC_FFMPEG_MF_H264: + case HB_VCODEC_FFMPEG_VAAPI_H264: level_names = hb_h264_level_names; level_values = hb_h264_level_values; break; @@ -1580,6 +1850,7 @@ static int apply_encoder_level(AVCodecContext *context, AVDictionary **av_opts, case HB_VCODEC_FFMPEG_NVENC_H265: case HB_VCODEC_FFMPEG_NVENC_H265_10BIT: case HB_VCODEC_FFMPEG_MF_H265: + case HB_VCODEC_FFMPEG_VAAPI_H265: level_names = hb_h265_level_names; level_values = hb_h265_level_values; break; @@ -1596,6 +1867,7 @@ static int apply_encoder_level(AVCodecContext *context, AVDictionary **av_opts, case HB_VCODEC_FFMPEG_NVENC_AV1: case HB_VCODEC_FFMPEG_NVENC_AV1_10BIT: case HB_VCODEC_FFMPEG_MF_AV1: + case HB_VCODEC_FFMPEG_VAAPI_AV1: level_names = hb_av1_level_names; level_values = hb_av1_level_values; break; @@ -1716,16 +1988,22 @@ const char* const* hb_av_profile_get_names(int encoder) { case HB_VCODEC_FFMPEG_NVENC_H264: return h264_nvenc_profile_names; + case HB_VCODEC_FFMPEG_VAAPI_H264: + return h264_vaapi_profile_names; case HB_VCODEC_FFMPEG_NVENC_H265: return h265_nvenc_profile_names; case HB_VCODEC_FFMPEG_NVENC_H265_10BIT: return h265_nvenc_10bit_profile_names; + case HB_VCODEC_FFMPEG_VAAPI_H265: + return h265_vaapi_profile_names; case HB_VCODEC_FFMPEG_MF_H264: return h264_mf_profile_name; case HB_VCODEC_FFMPEG_MF_H265: return h265_mf_profile_name; case HB_VCODEC_FFMPEG_MF_AV1: return av1_mf_profile_name; + case HB_VCODEC_FFMPEG_VAAPI_AV1: + return av1_vaapi_profile_names; case HB_VCODEC_FFMPEG_QSV_H264: return h264_qsv_profile_name; case HB_VCODEC_FFMPEG_QSV_H265: @@ -1742,6 +2020,7 @@ const char* const* hb_av_level_get_names(int encoder) { case HB_VCODEC_FFMPEG_NVENC_H264: case HB_VCODEC_FFMPEG_MF_H264: + case HB_VCODEC_FFMPEG_VAAPI_H264: return hb_h264_level_names; case HB_VCODEC_FFMPEG_VCE_H264: @@ -1754,6 +2033,7 @@ const char* const* hb_av_level_get_names(int encoder) case HB_VCODEC_FFMPEG_QSV_H265: case HB_VCODEC_FFMPEG_QSV_H265_10BIT: case HB_VCODEC_FFMPEG_MF_H265: + case HB_VCODEC_FFMPEG_VAAPI_H265: return hb_h265_level_names; case HB_VCODEC_FFMPEG_VCE_AV1: @@ -1762,6 +2042,7 @@ const char* const* hb_av_level_get_names(int encoder) case HB_VCODEC_FFMPEG_QSV_AV1: case HB_VCODEC_FFMPEG_QSV_AV1_10BIT: case HB_VCODEC_FFMPEG_MF_AV1: + case HB_VCODEC_FFMPEG_VAAPI_AV1: return hb_av1_level_names; case HB_VCODEC_FFMPEG_FFV1: @@ -1812,3 +2093,4 @@ const int* hb_av_get_pix_fmts(int encoder) return standard_pix_fmts; } } + diff --git a/libhb/handbrake/common.h b/libhb/handbrake/common.h index d3445e36b194..1fe542040e45 100644 --- a/libhb/handbrake/common.h +++ b/libhb/handbrake/common.h @@ -467,6 +467,7 @@ int hb_str_ends_with(const char *base, const char *str); */ void hb_common_global_init(int); +void hb_common_global_close(int); int hb_video_framerate_get_from_name(const char *name); const char* hb_video_framerate_get_name(int framerate); @@ -533,6 +534,8 @@ const char* const* hb_video_encoder_get_tunes (int encoder); const char* const* hb_video_encoder_get_profiles(int encoder); const char* const* hb_video_encoder_get_levels (int encoder); const int* hb_video_encoder_get_pix_fmts(int encoder, const char *profile); +int hb_video_encoder_is_vaapi(int encoder); + void hb_audio_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); float hb_audio_quality_get_best(uint32_t codec, float quality); @@ -688,6 +691,9 @@ struct hb_job_s #define HB_VCODEC_QSV_MASK 0x00040000 #define HB_VCODEC_FFMPEG_MASK 0x00010000 +#define HB_VCODEC_QSV_AV1_MASK (HB_VCODEC_QSV_MASK | HB_VCODEC_AV1_MASK) +#define HB_VCODEC_VAAPI_MASK 0x00008000 + #define HB_VCODEC_THEORA 0x00000001 #define HB_VCODEC_X264_8BIT (0x00000002 | HB_VCODEC_X264_MASK | HB_VCODEC_H264_MASK) @@ -741,6 +747,12 @@ struct hb_job_s #define HB_VCODEC_FFMPEG_QSV_AV1_10BIT (0x00000071 | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_QSV_MASK | HB_VCODEC_AV1_MASK) #define HB_VCODEC_FFMPEG_QSV_AV1 HB_VCODEC_FFMPEG_QSV_AV1_8BIT +#define HB_VCODEC_FFMPEG_VAAPI_H264 (0x00000080 | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_VAAPI_MASK | HB_VCODEC_H264_MASK) +#define HB_VCODEC_FFMPEG_VAAPI_H265 (0x00000081 | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_VAAPI_MASK | HB_VCODEC_H265_MASK) +#define HB_VCODEC_FFMPEG_VAAPI_AV1 (0x00000082 | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_VAAPI_MASK | HB_VCODEC_AV1_MASK) +#define HB_VCODEC_FFMPEG_VAAPI_VP8 (0x0000008a | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_VAAPI_MASK) +#define HB_VCODEC_FFMPEG_VAAPI_VP9 (0x0000008b | HB_VCODEC_FFMPEG_MASK | HB_VCODEC_VAAPI_MASK) + /* define an invalid CQ value compatible with all CQ-capable codecs */ #define HB_INVALID_VIDEO_QUALITY (-1000.) diff --git a/libhb/handbrake/hbffmpeg.h b/libhb/handbrake/hbffmpeg.h index 1d95aefa0bb8..861e27602b1e 100644 --- a/libhb/handbrake/hbffmpeg.h +++ b/libhb/handbrake/hbffmpeg.h @@ -22,6 +22,7 @@ #include "libavutil/mastering_display_metadata.h" #include "libavutil/ambient_viewing_environment.h" #include "libavutil/dovi_meta.h" +#include "libavutil/hwcontext.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" #include "handbrake/common.h" @@ -75,4 +76,7 @@ int hb_av_can_use_zscale(enum AVPixelFormat pix_fmt, int in_width, int in_height, int out_width, int out_height); +int hb_avcodec_test_encoder_available(int encoder); +int hb_avcodec_test_encoder(const AVCodec *codec, enum AVPixelFormat fmt); + #endif // HANDBRAKE_FFMPEG_H diff --git a/libhb/handbrake/project.h.m4 b/libhb/handbrake/project.h.m4 index f8b2fcab7cc2..5026e2917b16 100644 --- a/libhb/handbrake/project.h.m4 +++ b/libhb/handbrake/project.h.m4 @@ -46,6 +46,7 @@ dnl <<#>>define HB_PROJECT_FEATURE_GTK __FEATURE_gtk <<#>>define HB_PROJECT_FEATURE_MF __FEATURE_mf <<#>>define HB_PROJECT_FEATURE_NVENC __FEATURE_nvenc +<<#>>define HB_PROJECT_FEATURE_VAAPI __FEATURE_vaapi <<#>>define HB_PROJECT_FEATURE_NVDEC __FEATURE_nvdec <<#>>define HB_PROJECT_FEATURE_QSV __FEATURE_qsv <<#>>define HB_PROJECT_FEATURE_VCE __FEATURE_vce diff --git a/libhb/handbrake/vaapi_common.h b/libhb/handbrake/vaapi_common.h new file mode 100644 index 000000000000..bff7d5046369 --- /dev/null +++ b/libhb/handbrake/vaapi_common.h @@ -0,0 +1,42 @@ +/* vaapi_common.h + * + * Copyright (c) 2003-2020 HandBrake Team + * This file is part of the HandBrake source code. + * Homepage: . + * It may be used under the terms of the GNU General Public License v2. + * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ + +#ifndef HANDBRAKE_VAAPI_COMMON_H +#define HANDBRAKE_VAAPI_COMMON_H + +#include "handbrake/hbffmpeg.h" + +/** + * Initializes global vaapi encoder device. + * + * For encoding options, see + * - https://www.ffmpeg.org/ffmpeg-codecs.html#VAAPI-encoders + * - `ffmpeg -h encoder=h264_vaapi` + * - `ffmpeg -h encoder=hevc_vaapi` + * - `ffmpeg -h encoder=av1_vaapi` + */ +void hb_vaapi_init(); +void hb_vaapi_free(); +int hb_vaapi_available(); +int hb_vaapi_encoder_available(int encoder); + +int hb_vaapi_avcodec_set_hwframe_ctx(AVCodecContext *ctx, int hw_device_ctxidx, int init_pool_sz); + +/** + * Sends the frame to encode via `avcodec_send_frame` + * and converts source frame (e.g. AV_PIX_FMT_YUV420P) to AV_PIX_FMT_VAAPI/NV12. + * + * @param context the codec context + * @param frame the source AVFrame + * @param hw_frame may hold hardware AVFrame pointer, must be released via `av_frame_free` if not NULL + * @return + */ +int hb_vaapi_avcodec_send_frame(AVCodecContext *context, AVFrame *frame, AVFrame **hw_frame); + +#endif // HANDBRAKE_VAAPI_COMMON_H diff --git a/libhb/hb.c b/libhb/hb.c index 0f8f4a657f6b..b8f6374cd9c2 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -11,6 +11,7 @@ #include "handbrake/hbffmpeg.h" #include "handbrake/hbavfilter.h" #include "handbrake/encx264.h" +#include "handbrake/vaapi_common.h" #include "libavfilter/avfilter.h" #include #include @@ -387,7 +388,7 @@ void hb_scan( hb_handle_t * h, hb_list_t * paths, int title_index, { single_path = hb_list_item(paths, 0); } - + // Check if scanning is necessary. Only works on Single Path. if (single_path != NULL && h->title_set.path != NULL && !strcmp(h->title_set.path, single_path)) { @@ -500,12 +501,12 @@ hb_title_set_t * hb_get_title_set( hb_handle_t * h ) hb_list_t * hb_get_title_coverarts( hb_handle_t * h, int title ) { hb_title_t * sourceTitle = hb_list_item(h->title_set.list_title, title); - if (sourceTitle) + if (sourceTitle) { hb_list_t * coverart = sourceTitle->metadata->list_coverart; return coverart; } - + hb_list_t * emptyList = hb_list_init(); return emptyList; } @@ -2239,6 +2240,8 @@ void hb_global_close() closedir( dir ); rmdir( dirname ); } + + hb_common_global_close(disable_hardware); } /** diff --git a/libhb/hbffmpeg.c b/libhb/hbffmpeg.c index 9742101d8edc..1e99312c70d8 100644 --- a/libhb/hbffmpeg.c +++ b/libhb/hbffmpeg.c @@ -9,6 +9,7 @@ #include "handbrake/handbrake.h" #include "handbrake/hbffmpeg.h" +#include "handbrake/vaapi_common.h" #include "libavutil/cpu.h" static int get_frame_type(int type) @@ -838,3 +839,106 @@ int hb_av_can_use_zscale(enum AVPixelFormat pix_fmt, return 0; } + +int hb_avcodec_test_encoder_available(int encoder) +{ + int err; + enum AVPixelFormat fmt = AV_PIX_FMT_YUV420P; + const char *codec_name; + const AVCodec *codec; + switch (encoder) + { +#if HB_PROJECT_FEATURE_NVENC + case HB_VCODEC_FFMPEG_NVENC_H264: + codec_name = "h264_nvenc"; + fmt = AV_PIX_FMT_YUV420P; + break; + case HB_VCODEC_FFMPEG_NVENC_H265: + codec_name = "hevc_nvenc"; + fmt = AV_PIX_FMT_YUV420P; + break; +#endif +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_H264: + codec_name = "h264_vaapi"; + fmt = AV_PIX_FMT_VAAPI; + break; + case HB_VCODEC_FFMPEG_VAAPI_H265: + codec_name = "hevc_vaapi"; + fmt = AV_PIX_FMT_VAAPI; + break; + case HB_VCODEC_FFMPEG_VAAPI_AV1: + codec_name = "av1_vaapi"; + fmt = AV_PIX_FMT_VAAPI; + break; + case HB_VCODEC_FFMPEG_VAAPI_VP8: + codec_name = "vp8_vaapi"; + fmt = AV_PIX_FMT_VAAPI; + break; + case HB_VCODEC_FFMPEG_VAAPI_VP9: + codec_name = "vp9_vaapi"; + fmt = AV_PIX_FMT_VAAPI; + break; +#endif + default: + // FIXME: Add other supported avcodec encoder! + hb_log("hb_avcodec_test_encoder_available encoder=0x%X: Not supported yet", encoder); + return 0; + } + codec = avcodec_find_encoder_by_name(codec_name); + if( NULL == codec ) { + hb_log("hb_avcodec_test_encoder_available encoder=0x%X, codec=%s: Not available", encoder, codec_name); + return 0; + } + err = hb_avcodec_test_encoder(codec, fmt); + hb_log("hb_avcodec_test_encoder_available encoder=0x%X, codec=%s: err %d", encoder, codec_name, err); + return 0 == err; +} + +int hb_avcodec_test_encoder(const AVCodec *codec, enum AVPixelFormat fmt) +{ + int err, res=0; + AVDictionary * av_opts = NULL; + AVCodecContext * context = NULL; + if( NULL == codec ) { + return -1; + } + context = avcodec_alloc_context3(codec); + if( NULL == context ) { + res = -2; + goto close; + } + // setting all fields marked: 'encoding: MUST be set by user' + context->time_base.num = 1; + context->time_base.den = 25; + context->width = 640; + context->height = 480; + // deprecated: context->me_method = 1; + // setting other fields as required via testing + context->pix_fmt = fmt; + +#if HB_PROJECT_FEATURE_VAAPI + if( AV_PIX_FMT_VAAPI == fmt ) { + if((err = hb_vaapi_avcodec_set_hwframe_ctx(context, 0, 20))<0) { + hb_log("Failed to set the VAAPI hwframe_ctx. Error code: %s", av_err2str(err)); + res = -4; + goto close; + } + } +#endif + + av_dict_set(&av_opts, "b", "2M", 0); + if (avcodec_open2(context, codec, &av_opts) < 0) { + res = -3; + goto close; + } +close: + if(NULL!=av_opts) { + av_dict_free( &av_opts ); + } + if(NULL!=context) { + avcodec_free_context(&context); + } + return res; +} + diff --git a/libhb/module.defs b/libhb/module.defs index 5fbb4750d76a..4a7df465ff01 100644 --- a/libhb/module.defs +++ b/libhb/module.defs @@ -138,6 +138,12 @@ ifeq (1,$(FEATURE.libdovi)) LIBHB.dll.libs += $(CONTRIB.build/)lib/libdovi.a endif +ifeq (1,$(FEATURE.vaapi)) +LIBHB.dll.libs += $(foreach n, \ + X11 va va-drm va-x11, \ + $(CONTRIB.build/)lib/lib$(n).a ) +endif + ifneq ($(HAS.iconv),1) LIBHB.dll.libs += $(CONTRIB.build/)lib/libiconv.a else diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c index 3b50ae28b651..2875acfb65c6 100644 --- a/libhb/muxavformat.c +++ b/libhb/muxavformat.c @@ -374,6 +374,7 @@ static int avformatInit( hb_mux_object_t * m ) case HB_VCODEC_VT_H264: case HB_VCODEC_FFMPEG_VCE_H264: case HB_VCODEC_FFMPEG_NVENC_H264: + case HB_VCODEC_FFMPEG_VAAPI_H264: case HB_VCODEC_FFMPEG_QSV_H264: case HB_VCODEC_FFMPEG_MF_H264: track->st->codecpar->codec_id = AV_CODEC_ID_H264; @@ -396,11 +397,13 @@ static int avformatInit( hb_mux_object_t * m ) break; case HB_VCODEC_FFMPEG_VP8: + case HB_VCODEC_FFMPEG_VAAPI_VP8: track->st->codecpar->codec_id = AV_CODEC_ID_VP8; break; case HB_VCODEC_FFMPEG_VP9: case HB_VCODEC_FFMPEG_VP9_10BIT: + case HB_VCODEC_FFMPEG_VAAPI_VP9: track->st->codecpar->codec_id = AV_CODEC_ID_VP9; break; @@ -410,6 +413,7 @@ static int avformatInit( hb_mux_object_t * m ) case HB_VCODEC_FFMPEG_NVENC_AV1_10BIT: case HB_VCODEC_FFMPEG_VCE_AV1: case HB_VCODEC_FFMPEG_MF_AV1: + case HB_VCODEC_FFMPEG_VAAPI_AV1: track->st->codecpar->codec_id = AV_CODEC_ID_AV1; break; @@ -461,6 +465,7 @@ static int avformatInit( hb_mux_object_t * m ) case HB_VCODEC_FFMPEG_QSV_H265: case HB_VCODEC_FFMPEG_QSV_H265_10BIT: case HB_VCODEC_FFMPEG_MF_H265: + case HB_VCODEC_FFMPEG_VAAPI_H265: track->st->codecpar->codec_id = AV_CODEC_ID_HEVC; if (job->mux == HB_MUX_AV_MP4 && job->inline_parameter_sets) { diff --git a/libhb/vaapi_common.c b/libhb/vaapi_common.c new file mode 100644 index 000000000000..737dd1f81065 --- /dev/null +++ b/libhb/vaapi_common.c @@ -0,0 +1,138 @@ +/* vaapi_common.c + * + * Copyright (c) 2003-2020 HandBrake Team + * This file is part of the HandBrake source code. + * Homepage: . + * It may be used under the terms of the GNU General Public License v2. + * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ + +#include "handbrake/vaapi_common.h" +#include "handbrake/hbffmpeg.h" +#include "handbrake/handbrake.h" + +#if HB_PROJECT_FEATURE_VAAPI +static AVBufferRef *vaapi_device_ctx0 = NULL; +#endif + +void hb_vaapi_init() +{ +#if HB_PROJECT_FEATURE_VAAPI + { + int err; + if( (err = av_hwdevice_ctx_create(&vaapi_device_ctx0, AV_HWDEVICE_TYPE_VAAPI, + NULL, NULL, 0)) < 0 ) { + hb_log("Failed to create a VAAPI device. Error code: %s %p\n", av_err2str(err), vaapi_device_ctx0); + vaapi_device_ctx0 = NULL; + } + } +#endif +} + +void hb_vaapi_free() +{ +#if HB_PROJECT_FEATURE_VAAPI + if( NULL != vaapi_device_ctx0 ) { + av_buffer_unref(&vaapi_device_ctx0); + vaapi_device_ctx0 = NULL; + } +#endif +} + +int hb_vaapi_available() +{ + #if HB_PROJECT_FEATURE_VAAPI + if (hb_is_hardware_disabled()) + { + hb_log("hb_vaapi_available: hardware disabled"); + return 0; + } + if( NULL == vaapi_device_ctx0 ) { + hb_log("hb_vaapi_available: device ctx null"); + return 0; + } + return 1; + #else + return 0; + #endif +} + +int hb_vaapi_encoder_available(int encoder) +{ + #if HB_PROJECT_FEATURE_VAAPI + return hb_vaapi_available() && + hb_avcodec_test_encoder_available(encoder); + #else + return 0; + #endif +} + +int hb_vaapi_avcodec_set_hwframe_ctx(AVCodecContext *ctx, int hw_device_ctxidx, int init_pool_sz) +{ +#if HB_PROJECT_FEATURE_VAAPI + // AV_PIX_FMT_NV12 planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) + // NV12 0x3231564E 12 8-bit Y plane followed by an interleaved U/V plane with 2x2 subsampling + // NV21 0x3132564E 12 As NV12 with U and V reversed in the interleaved plane + // AV_PIX_FMT_YUV420P planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + AVBufferRef *hw_device_ctx = vaapi_device_ctx0; + AVBufferRef *hw_frames_ref; + AVHWFramesContext *frames_ctx = NULL; + int err = 0; + if( NULL == hw_device_ctx ) + { + hb_log("vaapi: Device is null."); + return -1; + } + if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) + { + hb_log("vaapi: Failed to create frame context."); + return -2; + } + frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data); + frames_ctx->format = AV_PIX_FMT_VAAPI; + frames_ctx->sw_format = AV_PIX_FMT_NV12; // AV_PIX_FMT_NV12; // AV_PIX_FMT_YUV420P + frames_ctx->width = ctx->width; + frames_ctx->height = ctx->height; + frames_ctx->initial_pool_size = init_pool_sz; + if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) + { + hb_log("vaapi: Failed to initialize frame context." + "Error code: %s",av_err2str(err)); + av_buffer_unref(&hw_frames_ref); + return err; + } + ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref); + if (!ctx->hw_frames_ctx) + { + err = AVERROR(ENOMEM); + } + av_buffer_unref(&hw_frames_ref); + return err; +#else + return -1; +#endif +} + +int hb_vaapi_avcodec_send_frame(AVCodecContext *context, AVFrame *frame, AVFrame **hw_frame) +{ + int ret = -1; + + // Convert incompatible source frame (e.g. AV_PIX_FMT_YUV420P) to AV_PIX_FMT_VAAPI/NV12 + if (!(*hw_frame = av_frame_alloc())) + { + hb_error("vaapi: Encode: Couldn't allocate hw_frame"); + return ret; + } + if ((ret = av_hwframe_get_buffer(context->hw_frames_ctx, *hw_frame, 0)) < 0) + { + hb_error("vaapi: Encode: Error while allocating hw_frame data buffers: %s", av_err2str(ret)); + return ret; + } + (*hw_frame)->pts = frame->pts; + if ((ret = av_hwframe_transfer_data(*hw_frame, frame, 0)) < 0) + { + hb_error("vaapi: Encode: Error while transferring frame data to hw_frame: %s.", av_err2str(ret)); + return ret; + } + return avcodec_send_frame(context, *hw_frame); +} diff --git a/libhb/work.c b/libhb/work.c index 4309d902e557..449bb61ae0f5 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -321,6 +321,28 @@ hb_work_object_t* hb_video_encoder(hb_handle_t *h, int vcodec) w->codec_param = AV_CODEC_ID_AV1; break; #endif +#if HB_PROJECT_FEATURE_VAAPI + case HB_VCODEC_FFMPEG_VAAPI_H264: + w = hb_get_work(h, WORK_ENCAVCODEC); + w->codec_param = AV_CODEC_ID_H264; + break; + case HB_VCODEC_FFMPEG_VAAPI_H265: + w = hb_get_work(h, WORK_ENCAVCODEC); + w->codec_param = AV_CODEC_ID_HEVC; + break; + case HB_VCODEC_FFMPEG_VAAPI_AV1: + w = hb_get_work(h, WORK_ENCAVCODEC); + w->codec_param = AV_CODEC_ID_AV1; + break; + case HB_VCODEC_FFMPEG_VAAPI_VP8: + w = hb_get_work(h, WORK_ENCAVCODEC); + w->codec_param = AV_CODEC_ID_VP8; + break; + case HB_VCODEC_FFMPEG_VAAPI_VP9: + w = hb_get_work(h, WORK_ENCAVCODEC); + w->codec_param = AV_CODEC_ID_VP9; + break; +#endif #ifdef __APPLE__ case HB_VCODEC_VT_H264: case HB_VCODEC_VT_H265: diff --git a/make/configure.py b/make/configure.py index 25640d9930b7..f6f0bea582ad 100644 --- a/make/configure.py +++ b/make/configure.py @@ -1440,6 +1440,10 @@ def createCLI( cross = None ): grp.add_argument( '--enable-nvdec', dest="enable_nvdec", default=False, action='store_true', help=(( 'enable %s' %h ) if h != argparse.SUPPRESS else h) ) grp.add_argument( '--disable-nvdec', dest="enable_nvdec", action='store_false', help=(( 'disable %s' %h ) if h != argparse.SUPPRESS else h) ) + h = 'VAAPI video encoder/decoder' if vaapi_supported else argparse.SUPPRESS + grp.add_argument( '--enable-vaapi', dest="enable_vaapi", default=True, action='store_true', help=(( 'enable %s' %h ) if h != argparse.SUPPRESS else h) ) + grp.add_argument( '--disable-vaapi', dest="enable_vaapi", action='store_false', help=(( 'disable %s' %h ) if h != argparse.SUPPRESS else h) ) + h = 'Intel QSV video encoder/decoder' if qsv_supported else argparse.SUPPRESS grp.add_argument( '--enable-qsv', dest="enable_qsv", default=IfHost(True, "x86_64-w64-mingw32*", none=False).value, action='store_true', help=(( 'enable %s' %h ) if h != argparse.SUPPRESS else h) ) grp.add_argument( '--disable-qsv', dest="enable_qsv", action='store_false', help=(( 'disable %s' %h ) if h != argparse.SUPPRESS else h) ) @@ -1737,6 +1741,7 @@ class Tools: nvenc_supported = host_tuple.match( '*-*-linux*', 'x86_64-w64-mingw32*' ) vce_supported = host_tuple.match( '*-*-linux*', 'x86_64-w64-mingw32*' ) mf_supported = host_tuple.match( 'aarch64-w64-mingw32*' ) + vaapi_supported = host_tuple.match( '*-*-linux*', '*-*-freebsd*' ) # create CLI and parse cli = createCLI( cross ) @@ -1781,6 +1786,7 @@ class Tools: options.enable_qsv = options.enable_qsv if qsv_supported else False options.enable_vce = options.enable_vce if vce_supported else False options.enable_gtk = options.enable_gtk if gtk_supported else False + options.enable_vaapi = options.enable_vaapi if vaapi_supported else False ##################################### ## Additional library and tool checks @@ -2091,6 +2097,7 @@ class Tools: doc.add( 'FEATURE.mf', int( options.enable_mf )) doc.add( 'FEATURE.nvenc', int( options.enable_nvenc )) doc.add( 'FEATURE.nvdec', int( options.enable_nvdec )) + doc.add( 'FEATURE.vaapi', int( options.enable_vaapi )) doc.add( 'FEATURE.qsv', int( options.enable_qsv )) doc.add( 'FEATURE.vce', int( options.enable_vce )) doc.add( 'FEATURE.x265', int( options.enable_x265 )) @@ -2218,6 +2225,7 @@ class Tools: print(f'Enable MediaFound.: {options.enable_mf}' + ('' if mf_supported else note_unsupported)) print(f'Enable NVENC: {options.enable_nvenc}' + ('' if nvenc_supported else note_unsupported)) print(f'Enable NVDEC: {options.enable_nvdec}' + ('' if nvenc_supported else note_unsupported)) + print(f'Enable VAAPI: {options.enable_vaapi}' + ('' if vaapi_supported else note_unsupported)) print(f'Enable QSV: {options.enable_qsv}' + ('' if qsv_supported else note_unsupported)) print(f'Enable VCE: {options.enable_vce}' + ('' if vce_supported else note_unsupported)) print(f'Enable libdovi: {options.enable_libdovi}') diff --git a/test/module.defs b/test/module.defs index 91c762e51612..0642a60dcc6f 100644 --- a/test/module.defs +++ b/test/module.defs @@ -22,6 +22,10 @@ TEST.pkgconfig_libs = libass libavformat libavfilter libavcodec libavutil libswr TEST.pkgconfig_libs += $(foreach m,$(MODULES.NAMES),$($m.OSL.libs)) +ifeq (1,$(FEATURE.vaapi)) + TEST.GCC.l += X11 va va-drm va-x11 +endif + ifeq (1,$(FEATURE.flatpak)) TEST.pkgconfig_libs += glib-2.0 endif