diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml
index ec4c1b9b715f..30149e6a3547 100644
--- a/.github/workflows/flatpak.yml
+++ b/.github/workflows/flatpak.yml
@@ -32,7 +32,7 @@ jobs:
nice make pkg.create.plugins.flatpak
- name: Upload Package
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrake
path: ./build/pkg/flatpak/*.flatpak
diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index 698e1c63a12b..641ab711d400 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -57,7 +57,7 @@ jobs:
make ub && make pkg.create
- name: Upload Assets
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrake-macos
path: ./build/pkg
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 1230a50263c7..5181886d7c63 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -51,13 +51,13 @@ jobs:
- name: Upload HandBrakeCLI
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrakeCLI_ARM64
path: ./build/HandBrakeCLI.exe
- name: Upload LibHB
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: LibHandBrake_ARM64
path: ./build/libhb/hb_a64.dll
@@ -116,13 +116,13 @@ jobs:
make pkg.create.zip
- name: Upload HandBrakeCLI
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrakeCLI
path: ./build/HandBrakeCLI.exe
- name: Upload LibHB
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: LibHandBrake
path: ./build/libhb/hb.dll
@@ -187,13 +187,13 @@ jobs:
shell: cmd
- name: Upload HandBrake Installer
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrake-Win_GUI-x64
path: win/CS/HandBrakeWPF/bin/HandBrake/HandBrake*.exe
- name: Upload HandBrake ZIP
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrake-Win_GUI-x64-Zip
path: win/CS/HandBrakeWPF/bin/*-Win_GUI.zip
@@ -258,13 +258,13 @@ jobs:
shell: cmd
- name: Upload HandBrake Installer
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrake-Win_GUI-ARM64
path: win/CS/HandBrakeWPF/bin/HandBrake/HandBrake*.exe
- name: Upload HandBrake ZIP
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: HandBrake-Win_GUI-ARM64-Zip
path: win/CS/HandBrakeWPF/bin/*-Win_GUI.zip
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