Merge pull request #741 from nyanmisaka/mft-hw-enc

Add misc enhancements to mfenc hw encoder
This commit is contained in:
Nyanmisaka
2026-07-20 16:26:33 +08:00
committed by GitHub
15 changed files with 1392 additions and 258 deletions
+3 -7
View File
@@ -18,7 +18,7 @@ ENV DPKG_INSTALL_LIST=${SOURCE_DIR}/debian/jellyfin-ffmpeg8.install
ENV PATH=${TARGET_DIR}/bin:${PATH}
ENV PKG_CONFIG_PATH=${TARGET_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH}
ENV LD_LIBRARY_PATH=${TARGET_DIR}/lib:${TARGET_DIR}/lib/mfx:${TARGET_DIR}/lib/xorg:${LD_LIBRARY_PATH}
ENV LDFLAGS="-Wl,-rpath=${TARGET_DIR}/lib -L${TARGET_DIR}/lib -lm"
ENV LDFLAGS="-Wl,--disable-new-dtags -Wl,-rpath=${TARGET_DIR}/lib -L${TARGET_DIR}/lib -lm"
ENV CXXFLAGS="-I${TARGET_DIR}/include $CXXFLAGS"
ENV CPPFLAGS="-I${TARGET_DIR}/include $CPPFLAGS"
ENV CFLAGS="-I${TARGET_DIR}/include $CFLAGS"
@@ -28,13 +28,9 @@ ENV CMAKE_POLICY_VERSION_MINIMUM="3.5"
RUN apt-get update \
&& apt-get dist-upgrade -y \
&& apt-get install -y apt-transport-https \
curl ninja-build debhelper gnupg wget devscripts \
curl ninja-build debhelper gnupg wget devscripts \
mmv equivs git nasm pkg-config subversion dh-autoreconf \
libpciaccess-dev libwayland-dev libx11-dev libx11-xcb-dev \
libxcb-dri2-0-dev libxcb-dri3-dev libxcb-present-dev \
libxcb-shm0-dev libxcb-sync-dev libxshmfence-dev libxext-dev \
libxfixes-dev libxcb1-dev libxrandr-dev libzstd-dev \
libelf-dev python3-pip zip unzip tar flex bison gperf ragel \
python3-pip zip unzip tar flex bison gperf ragel \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y
+1 -1
View File
@@ -1,7 +1,7 @@
---
# We just wrap `build` so this is really it
name: "jellyfin-ffmpeg"
version: "8.1.2-1"
version: "8.1.2-2"
packages:
- bullseye-amd64
- bullseye-arm64
+2 -2
View File
@@ -15,9 +15,9 @@ ffbuild_dockerbuild() {
}
ffbuild_configure() {
[[ $TARGET == win* ]] && echo --enable-dxva2 --enable-d3d11va --enable-d3d12va
[[ $TARGET == win* ]] && echo --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-mediafoundation
}
ffbuild_unconfigure() {
[[ $TARGET == win* ]] && echo --disable-dxva2 --disable-d3d11va --disable-d3d12va
[[ $TARGET == win* ]] && echo --disable-dxva2 --disable-d3d11va --disable-d3d12va --disable-mediafoundation
}
+10
View File
@@ -1,3 +1,13 @@
jellyfin-ffmpeg (8.1.2-2) unstable; urgency=medium
* Add d3d11 overlay/tonemap/transpose/deint/scale filters
* Fix full range input for mjpeg_vaapi encoder
* Fix a typo in HDR10 metadata mapping in hevc_nvenc encoder
* Add misc enhancements to mfenc hw encoder
* Enable Mesa AMD drivers for arm64 builds
-- nyanmisaka <nst799610810@gmail.com> Sat, 18 Jul 2026 18:39:50 +0800
jellyfin-ffmpeg (8.1.2-1) unstable; urgency=medium
* New upstream version 8.1.2
-4
View File
@@ -17,16 +17,12 @@ Build-Depends:
libgnutls28-dev | libgnutls-dev,
# --enable-libbluray
libbluray-dev,
# --enable-libdrm
libdrm-dev [!amd64],
# --enable-gmp
libgmp-dev,
# --enable-libmp3lame
libmp3lame-dev,
# --enable-libopus
libopus-dev,
# --enable-libtheora
libtheora-dev,
# --enable-libvorbis
libvorbis-dev,
# --enable-libopenmpt
@@ -1,3 +1,140 @@
Index: FFmpeg/libavcodec/av1dec.c
===================================================================
--- FFmpeg.orig/libavcodec/av1dec.c
+++ FFmpeg/libavcodec/av1dec.c
@@ -688,8 +688,8 @@ static int get_pixel_format(AVCodecConte
static void av1_frame_unref(AV1Frame *f)
{
- ff_progress_frame_unref(&f->pf);
av_refstruct_unref(&f->hwaccel_picture_private);
+ ff_progress_frame_unref(&f->pf);
av_refstruct_unref(&f->header_ref);
f->raw_frame_header = NULL;
f->spatial_id = f->temporal_id = 0;
Index: FFmpeg/libavcodec/h264_picture.c
===================================================================
--- FFmpeg.orig/libavcodec/h264_picture.c
+++ FFmpeg/libavcodec/h264_picture.c
@@ -44,9 +44,9 @@ void ff_h264_unref_picture(H264Picture *
if (!pic->f || !pic->f->buf[0])
return;
+ av_refstruct_unref(&pic->hwaccel_picture_private);
ff_thread_release_ext_buffer(&pic->tf);
av_frame_unref(pic->f_grain);
- av_refstruct_unref(&pic->hwaccel_picture_private);
av_refstruct_unref(&pic->qscale_table_base);
av_refstruct_unref(&pic->mb_type_base);
Index: FFmpeg/libavcodec/hevc/refs.c
===================================================================
--- FFmpeg.orig/libavcodec/hevc/refs.c
+++ FFmpeg/libavcodec/hevc/refs.c
@@ -38,6 +38,8 @@ void ff_hevc_unref_frame(HEVCFrame *fram
if (!(frame->flags & ~HEVC_FRAME_FLAG_CORRUPT))
frame->flags = 0;
if (!frame->flags) {
+ av_refstruct_unref(&frame->hwaccel_picture_private);
+
ff_progress_frame_unref(&frame->tf);
av_frame_unref(frame->frame_grain);
frame->needs_fg = 0;
@@ -49,8 +51,6 @@ void ff_hevc_unref_frame(HEVCFrame *fram
frame->nb_rpl_elems = 0;
av_refstruct_unref(&frame->rpl_tab);
frame->refPicList = NULL;
-
- av_refstruct_unref(&frame->hwaccel_picture_private);
}
}
Index: FFmpeg/libavcodec/vp9.c
===================================================================
--- FFmpeg.orig/libavcodec/vp9.c
+++ FFmpeg/libavcodec/vp9.c
@@ -97,10 +97,10 @@ static void vp9_tile_data_free(VP9TileDa
static void vp9_frame_unref(VP9Frame *f)
{
+ av_refstruct_unref(&f->hwaccel_picture_private);
ff_progress_frame_unref(&f->tf);
av_refstruct_unref(&f->header_ref);
av_refstruct_unref(&f->extradata);
- av_refstruct_unref(&f->hwaccel_picture_private);
f->segmentation_map = NULL;
}
Index: FFmpeg/libavcodec/vulkan_encode.c
===================================================================
--- FFmpeg.orig/libavcodec/vulkan_encode.c
+++ FFmpeg/libavcodec/vulkan_encode.c
@@ -28,6 +28,27 @@ const AVCodecHWConfigInternal *const ff_
NULL,
};
+static void vulkan_encode_free_pic(FFVulkanEncodeContext *ctx,
+ FFHWBaseEncodePicture *pic)
+{
+ FFVulkanFunctions *vk = &ctx->s.vkfn;
+
+ FFVulkanEncodePicture *vp = pic->priv;
+
+ if (vp->in.view)
+ vk->DestroyImageView(ctx->s.hwctx->act_dev, vp->in.view,
+ ctx->s.hwctx->alloc);
+
+ if (!ctx->common.layered_dpb && vp->dpb.view)
+ vk->DestroyImageView(ctx->s.hwctx->act_dev, vp->dpb.view,
+ ctx->s.hwctx->alloc);
+
+ vp->in.view = VK_NULL_HANDLE;
+ vp->dpb.view = VK_NULL_HANDLE;
+
+ ctx->slots[vp->dpb_slot.slotIndex] = NULL;
+}
+
av_cold void ff_vulkan_encode_uninit(FFVulkanEncodeContext *ctx)
{
FFVulkanContext *s = &ctx->s;
@@ -42,10 +63,15 @@ av_cold void ff_vulkan_encode_uninit(FFV
ctx->session_params,
s->hwctx->alloc);
- ff_hw_base_encode_close(&ctx->base);
+ /* Destroy the image views of any pictures still in the queue,
+ * as ff_hw_base_encode_close() only frees the picture structs */
+ for (FFHWBaseEncodePicture *pic = ctx->base.pic_start; pic; pic = pic->next)
+ vulkan_encode_free_pic(ctx, pic);
av_buffer_pool_uninit(&ctx->buf_pool);
+ ff_hw_base_encode_close(&ctx->base);
+
ff_vk_video_common_uninit(s, &ctx->common);
ff_vk_uninit(s);
@@ -95,19 +121,8 @@ static int vulkan_encode_init(AVCodecCon
static int vulkan_encode_free(AVCodecContext *avctx, FFHWBaseEncodePicture *pic)
{
FFVulkanEncodeContext *ctx = avctx->priv_data;
- FFVulkanFunctions *vk = &ctx->s.vkfn;
-
- FFVulkanEncodePicture *vp = pic->priv;
-
- if (vp->in.view)
- vk->DestroyImageView(ctx->s.hwctx->act_dev, vp->in.view,
- ctx->s.hwctx->alloc);
-
- if (!ctx->common.layered_dpb && vp->dpb.view)
- vk->DestroyImageView(ctx->s.hwctx->act_dev, vp->dpb.view,
- ctx->s.hwctx->alloc);
- ctx->slots[vp->dpb_slot.slotIndex] = NULL;
+ vulkan_encode_free_pic(ctx, pic);
return 0;
}
Index: FFmpeg/libavcodec/vulkan_encode.h
===================================================================
--- FFmpeg.orig/libavcodec/vulkan_encode.h
@@ -11,11 +148,186 @@ Index: FFmpeg/libavcodec/vulkan_encode.h
{ "rc_mode", "Select rate control type", OFFSET(common.opts.rc_mode), AV_OPT_TYPE_INT, { .i64 = FF_VK_RC_MODE_AUTO }, 0, FF_VK_RC_MODE_AUTO, FLAGS, "rc_mode" }, \
{ "auto", "Choose mode automatically based on parameters", 0, AV_OPT_TYPE_CONST, { .i64 = FF_VK_RC_MODE_AUTO }, INT_MIN, INT_MAX, FLAGS, "rc_mode" }, \
{ "driver", "Driver-specific rate control", 0, AV_OPT_TYPE_CONST, { .i64 = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR }, INT_MIN, INT_MAX, FLAGS, "rc_mode" }, \
Index: FFmpeg/libavcodec/vulkan_encode_av1.c
===================================================================
--- FFmpeg.orig/libavcodec/vulkan_encode_av1.c
+++ FFmpeg/libavcodec/vulkan_encode_av1.c
@@ -1023,7 +1023,7 @@ static int init_base_units(AVCodecContex
} else {
av_log(avctx, AV_LOG_ERROR, "Unable to get feedback for AV1 sequence header = %zu\n",
data_size);
- return err;
+ return AVERROR_EXTERNAL;
}
ret = vk->GetEncodedVideoSessionParametersKHR(s->hwctx->act_dev, &params_info,
@@ -1031,7 +1031,8 @@ static int init_base_units(AVCodecContex
&data_size, data);
if (ret != VK_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Error writing feedback units\n");
- return err;
+ err = AVERROR_EXTERNAL;
+ goto end;
}
av_log(avctx, AV_LOG_VERBOSE, "Feedback units written, overrides: %i\n",
@@ -1040,20 +1041,21 @@ static int init_base_units(AVCodecContex
params_feedback.hasOverrides = 1;
/* No need to sync any overrides */
+ err = 0;
if (!params_feedback.hasOverrides)
- return 0;
+ goto end;
/* Parse back tne units and override */
err = parse_feedback_units(avctx, data, data_size);
if (err < 0)
- return err;
+ goto end;
/* Create final session parameters */
err = create_session_params(avctx);
- if (err < 0)
- return err;
- return 0;
+end:
+ av_free(data);
+ return err;
}
static int vulkan_encode_av1_add_obu(AVCodecContext *avctx,
@@ -1359,6 +1361,10 @@ static av_cold int vulkan_encode_av1_ini
static av_cold int vulkan_encode_av1_close(AVCodecContext *avctx)
{
VulkanEncodeAV1Context *enc = avctx->priv_data;
+
+ ff_cbs_fragment_free(&enc->current_access_unit);
+ ff_cbs_close(&enc->cbs);
+
av_free(enc->padding_payload);
ff_vulkan_encode_uninit(&enc->common);
return 0;
Index: FFmpeg/libavcodec/vulkan_encode_h264.c
===================================================================
--- FFmpeg.orig/libavcodec/vulkan_encode_h264.c
+++ FFmpeg/libavcodec/vulkan_encode_h264.c
@@ -1148,7 +1148,7 @@ static int init_base_units(AVCodecContex
return AVERROR(ENOMEM);
} else {
av_log(avctx, AV_LOG_ERROR, "Unable to get feedback for H.264 units = %zu\n", data_size);
- return err;
+ return AVERROR_EXTERNAL;
}
ret = vk->GetEncodedVideoSessionParametersKHR(s->hwctx->act_dev, &params_info,
@@ -1156,7 +1156,8 @@ static int init_base_units(AVCodecContex
&data_size, data);
if (ret != VK_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Error writing feedback units\n");
- return err;
+ err = AVERROR_EXTERNAL;
+ goto end;
}
av_log(avctx, AV_LOG_VERBOSE, "Feedback units written, overrides: %i (SPS: %i PPS: %i)\n",
@@ -1168,22 +1169,23 @@ static int init_base_units(AVCodecContex
h264_params_feedback.hasStdPPSOverrides = 1;
/* No need to sync any overrides */
+ err = 0;
if (!params_feedback.hasOverrides)
- return 0;
+ goto end;
/* Parse back tne units and override */
err = parse_feedback_units(avctx, data, data_size,
h264_params_feedback.hasStdSPSOverrides,
h264_params_feedback.hasStdPPSOverrides);
if (err < 0)
- return err;
+ goto end;
/* Create final session parameters */
err = create_session_params(avctx);
- if (err < 0)
- return err;
- return 0;
+end:
+ av_free(data);
+ return err;
}
static int vulkan_encode_h264_add_nal(AVCodecContext *avctx,
@@ -1561,6 +1563,13 @@ static av_cold int vulkan_encode_h264_in
static av_cold int vulkan_encode_h264_close(AVCodecContext *avctx)
{
VulkanEncodeH264Context *enc = avctx->priv_data;
+
+ ff_cbs_fragment_free(&enc->current_access_unit);
+ ff_cbs_close(&enc->cbs);
+
+ av_freep(&enc->sei_a53cc_data);
+ av_freep(&enc->sei_identifier_string);
+
ff_vulkan_encode_uninit(&enc->common);
return 0;
}
Index: FFmpeg/libavcodec/vulkan_encode_h265.c
===================================================================
--- FFmpeg.orig/libavcodec/vulkan_encode_h265.c
+++ FFmpeg/libavcodec/vulkan_encode_h265.c
@@ -1595,23 +1595,21 @@ static av_cold int vulkan_encode_h265_in
@@ -1317,7 +1317,7 @@ static int init_base_units(AVCodecContex
return AVERROR(ENOMEM);
} else {
av_log(avctx, AV_LOG_ERROR, "Unable to get feedback for H.265 units = %zu\n", data_size);
- return err;
+ return AVERROR_EXTERNAL;
}
ret = vk->GetEncodedVideoSessionParametersKHR(s->hwctx->act_dev, &params_info,
@@ -1325,7 +1325,8 @@ static int init_base_units(AVCodecContex
&data_size, data);
if (ret != VK_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Error writing feedback units\n");
- return err;
+ err = AVERROR_EXTERNAL;
+ goto end;
}
av_log(avctx, AV_LOG_VERBOSE, "Feedback units written, overrides: %i (SPS: %i PPS: %i VPS: %i)\n",
@@ -1339,22 +1340,23 @@ static int init_base_units(AVCodecContex
h265_params_feedback.hasStdPPSOverrides = 1;
/* No need to sync any overrides */
+ err = 0;
if (!params_feedback.hasOverrides)
- return 0;
+ goto end;
/* Parse back tne units and override */
err = parse_feedback_units(avctx, data, data_size,
h265_params_feedback.hasStdSPSOverrides,
h265_params_feedback.hasStdPPSOverrides);
if (err < 0)
- return err;
+ goto end;
/* Create final session parameters */
err = create_session_params(avctx);
- if (err < 0)
- return err;
- return 0;
+end:
+ av_free(data);
+ return err;
}
static int vulkan_encode_h265_add_nal(AVCodecContext *avctx,
@@ -1595,23 +1597,21 @@ static av_cold int vulkan_encode_h265_in
av_log(avctx, AV_LOG_VERBOSE, " Capability flags:\n");
av_log(avctx, AV_LOG_VERBOSE, " hdr_compliance: %i\n",
@@ -47,6 +359,19 @@ Index: FFmpeg/libavcodec/vulkan_encode_h265.c
av_log(avctx, AV_LOG_VERBOSE, " Capabilities:\n");
av_log(avctx, AV_LOG_VERBOSE, " maxLevelIdc: %i\n",
@@ -1698,6 +1698,12 @@ static av_cold int vulkan_encode_h265_in
static av_cold int vulkan_encode_h265_close(AVCodecContext *avctx)
{
VulkanEncodeH265Context *enc = avctx->priv_data;
+
+ ff_cbs_fragment_free(&enc->current_access_unit);
+ ff_cbs_close(&enc->cbs);
+
+ av_freep(&enc->sei_a53cc_data);
+
ff_vulkan_encode_uninit(&enc->common);
return 0;
}
Index: FFmpeg/libavutil/hwcontext_vulkan.c
===================================================================
--- FFmpeg.orig/libavutil/hwcontext_vulkan.c
@@ -119,7 +444,12 @@ Index: FFmpeg/libavutil/hwcontext_vulkan.c
if (use_ded_mem)
ded_alloc.image = f->img[img_cnt];
@@ -2854,7 +2871,8 @@ static void try_export_flags(AVHWFramesC
@@ -2850,11 +2867,12 @@ static void try_export_flags(AVHWFramesC
VkPhysicalDeviceImageFormatInfo2 pinfo = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
.pNext = !exp ? NULL : &enext,
- .format = vk_find_format_entry(hwfc->sw_format)->vkf,
+ .format = hwctx->format[0],
.type = VK_IMAGE_TYPE_2D,
.tiling = hwctx->tiling,
.usage = hwctx->usage,
@@ -2,35 +2,77 @@ Index: FFmpeg/fftools/ffmpeg_demux.c
===================================================================
--- FFmpeg.orig/fftools/ffmpeg_demux.c
+++ FFmpeg/fftools/ffmpeg_demux.c
@@ -1757,10 +1757,26 @@ static int is_windows_reserved_device_na
@@ -1770,23 +1770,54 @@ static int is_windows_reserved_device_na
{
#if HAVE_DOS_PATHS
for (const char *p = f; p && *p; ) {
- char stem[6], *s;
+ char stem[16], *s;
- av_strlcpy(stem, p, sizeof(stem));
- if ((s = strchr(stem, '.')))
- *s = 0;
- if ((s = strpbrk(stem, "123456789")))
- *s = '1';
-
- if( !av_strcasecmp(stem, "AUX") ||
- !av_strcasecmp(stem, "CON") ||
- !av_strcasecmp(stem, "NUL") ||
- !av_strcasecmp(stem, "PRN") ||
- !av_strcasecmp(stem, "COM1") ||
- !av_strcasecmp(stem, "LPT1")
- )
+ const char *next_slash;
+ const char *seg_end;
+ const char *dot;
+ size_t len;
av_strlcpy(stem, p, sizeof(stem));
+
+ if ((s = strchr(stem, '/')))
+ *s = 0;
+ next_slash = strchr(p, '/');
+ seg_end = next_slash ? next_slash : p + strlen(p);
+
+ /* Trim trailing spaces and dots */
+ len = strlen(stem);
+ while (len > 0 && (stem[len - 1] == ' ' || stem[len - 1] == '.'))
+ stem[--len] = 0;
+ /* Trim trailing spaces and dots of the current component */
+ while (seg_end > p && (*(seg_end - 1) == ' ' || *(seg_end - 1) == '.'))
+ seg_end--;
+
if ((s = strchr(stem, '.')))
*s = 0;
+ /* Discard extension if present (Windows stops checking at first dot) */
+ dot = p;
+ while (dot < seg_end && *dot != '.')
+ dot++;
+ if (dot < seg_end)
+ seg_end = dot;
+
+ /* Trim again after stripping extension */
+ len = strlen(stem);
+ while (len > 0 && (stem[len - 1] == ' ' || stem[len - 1] == '.'))
+ stem[--len] = 0;
+ while (seg_end > p && (*(seg_end - 1) == ' ' || *(seg_end - 1) == '.'))
+ seg_end--;
+
if ((s = strpbrk(stem, "123456789")))
*s = '1';
+ len = seg_end - p;
+
+ /* Match 3-byte legacy device names: AUX, CON, NUL, PRN */
+ if (len == 3 && (!av_strncasecmp(p, "AUX", 3) ||
+ !av_strncasecmp(p, "CON", 3) ||
+ !av_strncasecmp(p, "NUL", 3) ||
+ !av_strncasecmp(p, "PRN", 3)))
return 1;
@@ -1789,18 +1805,35 @@ static int safe_filename(const char *f,
- p = strchr(p, '/');
+ /* Match COM1-9 / LPT1-9 and their UTF-8 superscript aliases */
+ if ((len == 4 || len == 5) && (!av_strncasecmp(p, "COM", 3) ||
+ !av_strncasecmp(p, "LPT", 3))) {
+ /* Standard ASCII digits 1-9 */
+ if (len == 4 && *(seg_end - 1) >= '1' && *(seg_end - 1) <= '9')
+ return 1;
+
+ /* UTF-8 superscripts (¹, ², ³) */
+ if (len == 5 && (unsigned char)*(seg_end - 2) == 0xC2 &&
+ ((unsigned char)*(seg_end - 1) == 0xB9 ||
+ (unsigned char)*(seg_end - 1) == 0xB2 ||
+ (unsigned char)*(seg_end - 1) == 0xB3))
+ return 1;
+ }
+
+ p = next_slash;
if (p)
p++;
}
@@ -1802,18 +1833,35 @@ static int safe_filename(const char *f,
return 0;
for (; *f; f++) {
@@ -74,7 +116,7 @@ Index: FFmpeg/fftools/ffmpeg_demux.c
}
static int dump_attachment(InputStream *ist, const char *filename)
@@ -1817,8 +1850,8 @@ static int dump_attachment(InputStream *
@@ -1830,8 +1878,8 @@ static int dump_attachment(InputStream *
if (!*filename && (e = av_dict_get(st->metadata, "filename", NULL, 0))) {
filename = e->value;
if (!safe_filename(filename, 0)) {
@@ -0,0 +1,651 @@
Index: FFmpeg/libavcodec/mf_utils.c
===================================================================
--- FFmpeg.orig/libavcodec/mf_utils.c
+++ FFmpeg/libavcodec/mf_utils.c
@@ -555,13 +555,14 @@ int ff_instantiate_mf(void *log,
MFT_REGISTER_TYPE_INFO *in_type,
MFT_REGISTER_TYPE_INFO *out_type,
int use_hw,
+ const LUID *hw_luid,
IMFTransform **res)
{
HRESULT hr;
int n;
int ret;
- IMFActivate **activate;
- UINT32 num_activate;
+ IMFActivate **activate = NULL;
+ UINT32 num_activate = 0;
IMFActivate *winner = 0;
UINT32 flags;
@@ -577,10 +578,26 @@ int ff_instantiate_mf(void *log,
flags |= MFT_ENUM_FLAG_SYNCMFT;
}
- hr = f->MFTEnumEx(category, flags, in_type, out_type, &activate,
- &num_activate);
- if (FAILED(hr))
- goto error_uninit_mf;
+ // MFTEnum2 is loaded optionally
+ if (use_hw && hw_luid && f->MFTEnum2) {
+ IMFAttributes *attrs = NULL;
+ hr = f->MFCreateAttributes(&attrs, 1);
+ if (SUCCEEDED(hr)) {
+ hr = IMFAttributes_SetBlob(attrs, &ff_MFT_ENUM_ADAPTER_LUID,
+ (const UINT8*)hw_luid, sizeof(*hw_luid));
+ if (SUCCEEDED(hr))
+ hr = f->MFTEnum2(category, flags, in_type, out_type, attrs,
+ &activate, &num_activate);
+ IMFAttributes_Release(attrs);
+ }
+ if (FAILED(hr))
+ goto error_uninit_mf;
+ } else {
+ hr = f->MFTEnumEx(category, flags, in_type, out_type,
+ &activate, &num_activate);
+ if (FAILED(hr))
+ goto error_uninit_mf;
+ }
if (log) {
if (!num_activate)
Index: FFmpeg/libavcodec/mf_utils.h
===================================================================
--- FFmpeg.orig/libavcodec/mf_utils.h
+++ FFmpeg/libavcodec/mf_utils.h
@@ -51,6 +51,8 @@ typedef struct MFFunctions {
HRESULT (WINAPI *MFCreateAlignedMemoryBuffer) (DWORD cbMaxLength,
DWORD cbAligment,
IMFMediaBuffer **ppBuffer);
+ HRESULT (WINAPI *MFCreateAttributes)(IMFAttributes **ppMFAttributes,
+ UINT32 cInitialSize);
HRESULT (WINAPI *MFCreateSample) (IMFSample **ppIMFSample);
HRESULT (WINAPI *MFCreateMediaType) (IMFMediaType **ppMFType);
HRESULT (WINAPI *MFCreateDXGISurfaceBuffer) (REFIID riid,
@@ -66,6 +68,14 @@ typedef struct MFFunctions {
const MFT_REGISTER_TYPE_INFO *pOutputType,
IMFActivate ***pppMFTActivate,
UINT32 *pnumMFTActivate);
+ // MFTEnum2 is missing in pre-Windows 10, version 1703's mfplat.dll.
+ // Therefore we load it optionally.
+ HRESULT (WINAPI *MFTEnum2)(GUID guidCategory, UINT32 Flags,
+ const MFT_REGISTER_TYPE_INFO *pInputType,
+ const MFT_REGISTER_TYPE_INFO *pOutputType,
+ IMFAttributes *pAttributes,
+ IMFActivate ***pppMFTActivate,
+ UINT32 *pnumMFTActivate);
} MFFunctions;
// These functions do exist in mfapi.h, but are only available within
@@ -122,6 +132,8 @@ DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_H
DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_HEVC_ES, 0x53564548); // FCC('HEVS')
DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_AV1, 0x31305641); // FCC('AV01')
+// This enum is missing from mingw-w64's headers
+DEFINE_GUID(ff_MFT_ENUM_ADAPTER_LUID, 0x1d39518c, 0xe220, 0x4da8, 0xa0, 0x7f, 0xba, 0x17, 0x25, 0x52, 0xd6, 0xb1);
// This enum is missing from mingw-w64's codecapi.h by v7.0.0.
enum ff_eAVEncCommonRateControlMode {
@@ -160,9 +172,19 @@ enum {
// header when targeting UWP (where including it with MSVC seems to work,
// but fails when built with clang in MSVC mode).
enum ff_eAVEncH264VProfile {
- ff_eAVEncH264VProfile_Base = 66,
- ff_eAVEncH264VProfile_Main = 77,
- ff_eAVEncH264VProfile_High = 100,
+ ff_eAVEncH264VProfile_Base = 66,
+ ff_eAVEncH264VProfile_Main = 77,
+ ff_eAVEncH264VProfile_High = 100,
+};
+
+enum ff_eAVEncH265VProfile {
+ ff_eAVEncH265VProfile_Main_420_8 = 1,
+ ff_eAVEncH265VProfile_Main_420_10 = 2,
+};
+
+enum ff_eAVEncAV1VProfile {
+ ff_eAVEncAV1VProfile_Main_420_8 = 1,
+ ff_eAVEncAV1VProfile_Main_420_10 = 2,
};
char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr);
@@ -188,7 +210,8 @@ const CLSID *ff_codec_to_mf_subtype(enum
int ff_instantiate_mf(void *log, MFFunctions *f, GUID category,
MFT_REGISTER_TYPE_INFO *in_type,
MFT_REGISTER_TYPE_INFO *out_type,
- int use_hw, IMFTransform **res);
+ int use_hw, const LUID *hw_luid,
+ IMFTransform **res);
void ff_free_mf(MFFunctions *f, IMFTransform **mft);
#endif
Index: FFmpeg/libavcodec/mfenc.c
===================================================================
--- FFmpeg.orig/libavcodec/mfenc.c
+++ FFmpeg/libavcodec/mfenc.c
@@ -23,6 +23,7 @@
#endif
#include "encode.h"
+#include "hwconfig.h"
#include "mf_utils.h"
#include "libavutil/imgutils.h"
#include "libavutil/mem.h"
@@ -38,10 +39,12 @@
typedef struct MFContext {
AVClass *av_class;
HMODULE library;
+#if CONFIG_D3D11VA
HMODULE d3d_dll;
ID3D11DeviceContext* d3d_context;
IMFDXGIDeviceManager *dxgiManager;
int resetToken;
+#endif
MFFunctions functions;
AVFrame *frame;
@@ -64,7 +67,9 @@ typedef struct MFContext {
int opt_enc_quality;
int opt_enc_scenario;
int opt_enc_hw;
+#if CONFIG_D3D11VA
AVD3D11VADeviceContext* device_hwctx;
+#endif
} MFContext;
static int mf_choose_output_type(AVCodecContext *avctx);
@@ -327,6 +332,7 @@ static int mf_a_avframe_to_sample(AVCode
return 0;
}
+#if CONFIG_D3D11VA
static int initialize_dxgi_manager(AVCodecContext *avctx)
{
MFContext *c = avctx->priv_data;
@@ -354,27 +360,65 @@ static int initialize_dxgi_manager(AVCod
return 0;
}
+typedef struct MFD3DFrameHolder {
+ const IUnknownVtbl *lpVtbl;
+ long ref_count;
+ AVFrame *frame;
+} MFD3DFrameHolder;
+
+static HRESULT STDMETHODCALLTYPE
+MFD3DFrameHolder_QueryInterface(IUnknown *This, REFIID riid, void **ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (IsEqualIID(riid, &IID_IUnknown)) {
+ *ppvObject = This;
+ This->lpVtbl->AddRef(This);
+ return S_OK;
+ }
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE MFD3DFrameHolder_AddRef(IUnknown *This)
+{
+ MFD3DFrameHolder *holder = (MFD3DFrameHolder*)This;
+ return InterlockedIncrement(&holder->ref_count);
+}
+
+static ULONG STDMETHODCALLTYPE MFD3DFrameHolder_Release(IUnknown *This)
+{
+ MFD3DFrameHolder *holder = (MFD3DFrameHolder*)This;
+ ULONG count = InterlockedDecrement(&holder->ref_count);
+ if (!count) {
+ av_frame_free(&holder->frame);
+ av_free(holder);
+ }
+ return count;
+}
+
+static const IUnknownVtbl MFD3DFrameHolder_Vtbl = {
+ .QueryInterface = MFD3DFrameHolder_QueryInterface,
+ .AddRef = MFD3DFrameHolder_AddRef,
+ .Release = MFD3DFrameHolder_Release
+};
+
+static const GUID GUID_MF_D3D_FRAME_HOLDER = {
+ 0xd29f874b, 0x370f, 0x402f,
+ { 0xa3, 0xaf, 0xb6, 0xd1, 0xed, 0xee, 0xd5, 0xe2 }
+};
+
static int process_d3d11_frame(AVCodecContext *avctx, const AVFrame *frame, IMFSample **out_sample)
{
MFContext *c = avctx->priv_data;
MFFunctions *func = &c->functions;
- AVHWFramesContext *frames_ctx = NULL;
ID3D11Texture2D *d3d11_texture = NULL;
IMFSample *sample = NULL;
IMFMediaBuffer *buffer = NULL;
+ MFD3DFrameHolder *holder = NULL;
int subIdx = 0;
HRESULT hr;
- frames_ctx = (AVHWFramesContext*)frame->hw_frames_ctx->data;
- c->device_hwctx = (AVD3D11VADeviceContext*)frames_ctx->device_ctx->hwctx;
-
- if (!c->dxgiManager) {
- hr = initialize_dxgi_manager(avctx);
- if (FAILED(hr)) {
- return AVERROR_EXTERNAL;
- }
- }
-
d3d11_texture = (ID3D11Texture2D*)frame->data[0];
subIdx = (int)(intptr_t)frame->data[1];
@@ -397,18 +441,41 @@ static int process_d3d11_frame(AVCodecCo
}
hr = IMFSample_AddBuffer(sample, buffer);
+ IMFMediaBuffer_Release(buffer);
if (FAILED(hr)) {
av_log(avctx, AV_LOG_ERROR, "Failed to add buffer to sample: %s\n", ff_hr_str(hr));
- IMFMediaBuffer_Release(buffer);
IMFSample_Release(sample);
return AVERROR_EXTERNAL;
}
- IMFMediaBuffer_Release(buffer);
+ // Bind the D3D11 frame to the sample to prevent in-flight async
+ // frames from being prematurely released back to the frame pool.
+ holder = av_mallocz(sizeof(*holder));
+ if (!holder) {
+ IMFSample_Release(sample);
+ return AVERROR(ENOMEM);
+ }
+ holder->lpVtbl = &MFD3DFrameHolder_Vtbl;
+ holder->ref_count = 1;
+ holder->frame = av_frame_clone(frame);
+ if (!holder->frame) {
+ av_free(holder);
+ IMFSample_Release(sample);
+ return AVERROR(ENOMEM);
+ }
+
+ hr = IMFAttributes_SetUnknown((IMFAttributes*)sample, &GUID_MF_D3D_FRAME_HOLDER, (IUnknown*)holder);
+ holder->lpVtbl->Release((IUnknown*)holder);
+ if (FAILED(hr)) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to bind D3D frame holder to sample: %s\n", ff_hr_str(hr));
+ IMFSample_Release(sample);
+ return AVERROR_EXTERNAL;
+ }
*out_sample = sample;
return 0;
}
+#endif
static int process_software_frame(AVCodecContext *avctx, const AVFrame *frame, IMFSample **out_sample)
{
@@ -464,8 +531,12 @@ static int mf_v_avframe_to_sample(AVCode
int ret;
if (frame->format == AV_PIX_FMT_D3D11) {
+#if CONFIG_D3D11VA
// Handle D3D11 hardware frames
ret = process_d3d11_frame(avctx, frame, &sample);
+#else
+ ret = AVERROR(ENOSYS);
+#endif
if (ret < 0) {
return ret;
}
@@ -809,6 +880,8 @@ static int mf_encv_output_adjust(AVCodec
{
MFContext *c = avctx->priv_data;
AVRational framerate;
+ enum AVPixelFormat sw_pix_fmt = (avctx->pix_fmt == AV_PIX_FMT_D3D11)
+ ? avctx->sw_pix_fmt : avctx->pix_fmt;
ff_MFSetAttributeSize((IMFAttributes *)type, &MF_MT_FRAME_SIZE, avctx->width, avctx->height);
IMFAttributes_SetUINT32(type, &MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
@@ -821,7 +894,6 @@ static int mf_encv_output_adjust(AVCodec
ff_MFSetAttributeRatio((IMFAttributes *)type, &MF_MT_FRAME_RATE, framerate.num, framerate.den);
- // (MS HEVC supports eAVEncH265VProfile_Main_420_8 only.)
if (avctx->codec_id == AV_CODEC_ID_H264) {
UINT32 profile = ff_eAVEncH264VProfile_Base;
switch (avctx->profile) {
@@ -833,8 +905,27 @@ static int mf_encv_output_adjust(AVCodec
break;
}
IMFAttributes_SetUINT32(type, &MF_MT_MPEG2_PROFILE, profile);
+ } else if (avctx->codec_id == AV_CODEC_ID_HEVC) {
+ UINT32 profile = ff_eAVEncH265VProfile_Main_420_8;
+ switch (sw_pix_fmt) {
+ case AV_PIX_FMT_P010:
+ profile = ff_eAVEncH265VProfile_Main_420_10;
+ break;
+ }
+ IMFAttributes_SetUINT32(type, &MF_MT_MPEG2_PROFILE, profile);
+ } else if (avctx->codec_id == AV_CODEC_ID_AV1) {
+ UINT32 profile = ff_eAVEncAV1VProfile_Main_420_8;
+ switch (sw_pix_fmt) {
+ case AV_PIX_FMT_P010:
+ profile = ff_eAVEncAV1VProfile_Main_420_10;
+ break;
+ }
+ IMFAttributes_SetUINT32(type, &MF_MT_MPEG2_PROFILE, profile);
}
+ if (avctx->level > 0)
+ IMFAttributes_SetUINT32(type, &MF_MT_MPEG2_LEVEL, (UINT32)avctx->level);
+
IMFAttributes_SetUINT32(type, &MF_MT_AVG_BITRATE, avctx->bit_rate);
// Note that some of the ICodecAPI options must be set before SetOutputType.
@@ -889,11 +980,12 @@ static int64_t mf_encv_input_score(AVCod
enum AVPixelFormat pix_fmt = ff_media_type_to_pix_fmt((IMFAttributes *)type);
if (avctx->pix_fmt == AV_PIX_FMT_D3D11) {
- if (pix_fmt != AV_PIX_FMT_NV12) {
+ if (pix_fmt != avctx->sw_pix_fmt ||
+ (pix_fmt != AV_PIX_FMT_NV12 &&
+ pix_fmt != AV_PIX_FMT_P010)) {
return -1; // can not use
}
- }
- else {
+ } else {
if (pix_fmt != avctx->pix_fmt)
return -1; // can not use
}
@@ -905,7 +997,9 @@ static int mf_encv_input_adjust(AVCodecC
{
enum AVPixelFormat pix_fmt = ff_media_type_to_pix_fmt((IMFAttributes *)type);
if (avctx->pix_fmt == AV_PIX_FMT_D3D11) {
- if (pix_fmt != AV_PIX_FMT_NV12 && pix_fmt != AV_PIX_FMT_D3D11) {
+ if (pix_fmt != avctx->sw_pix_fmt ||
+ (pix_fmt != AV_PIX_FMT_NV12 &&
+ pix_fmt != AV_PIX_FMT_P010)) {
av_log(avctx, AV_LOG_ERROR, "unsupported input pixel format set\n");
return AVERROR(EINVAL);
}
@@ -1202,13 +1296,62 @@ err:
return res;
}
-static int mf_create(void *log, MFFunctions *f, IMFTransform **mft,
+static int get_luid_from_dxgi_adapter(AVCodecContext *avctx, LUID *luid)
+{
+ int ret = AVERROR_UNKNOWN;
+#if CONFIG_D3D11VA
+ AVHWFramesContext *frames_ctx = NULL;
+ AVHWDeviceContext *dev_ctx = NULL;
+ AVD3D11VADeviceContext *hwctx = NULL;
+ IDXGIDevice *dxgi_dev = NULL;
+ IDXGIAdapter *adapter = NULL;
+ HRESULT hr;
+
+ if (!luid)
+ return AVERROR(EINVAL);
+
+ if (avctx->hw_frames_ctx &&
+ (frames_ctx = (AVHWFramesContext *)avctx->hw_frames_ctx->data))
+ dev_ctx = frames_ctx->device_ctx;
+
+ if (!dev_ctx && avctx->hw_device_ctx)
+ dev_ctx = (AVHWDeviceContext *)avctx->hw_device_ctx->data;
+
+ if (!dev_ctx || dev_ctx->type != AV_HWDEVICE_TYPE_D3D11VA)
+ return AVERROR(EINVAL);
+
+ hwctx = (AVD3D11VADeviceContext *)dev_ctx->hwctx;
+
+ hr = ID3D11Device_QueryInterface(hwctx->device,
+ &IID_IDXGIDevice, (void**)&dxgi_dev);
+ if (SUCCEEDED(hr)) {
+ hr = IDXGIDevice_GetAdapter(dxgi_dev, &adapter);
+ if (SUCCEEDED(hr)) {
+ DXGI_ADAPTER_DESC adapter_desc;
+ hr = IDXGIAdapter_GetDesc(adapter, &adapter_desc);
+ if (SUCCEEDED(hr)) {
+ *luid = adapter_desc.AdapterLuid;
+ ret = 0;
+ }
+ }
+ }
+ if (dxgi_dev)
+ IDXGIDevice_Release(dxgi_dev);
+ if (adapter)
+ IDXGIAdapter_Release(adapter);
+#endif
+ return ret;
+}
+
+static int mf_create(AVCodecContext *avctx, MFFunctions *f, IMFTransform **mft,
const AVCodec *codec, int use_hw)
{
int is_audio = codec->type == AVMEDIA_TYPE_AUDIO;
const CLSID *subtype = ff_codec_to_mf_subtype(codec->id);
MFT_REGISTER_TYPE_INFO reg = {0};
GUID category;
+ const LUID *p_hw_luid = NULL;
+ LUID hw_luid = {0};
int ret;
*mft = NULL;
@@ -1224,9 +1367,15 @@ static int mf_create(void *log, MFFuncti
} else {
reg.guidMajorType = MFMediaType_Video;
category = MFT_CATEGORY_VIDEO_ENCODER;
+
+ if (use_hw && avctx->pix_fmt == AV_PIX_FMT_D3D11) {
+ ret = get_luid_from_dxgi_adapter(avctx, &hw_luid);
+ if (!ret)
+ p_hw_luid = &hw_luid;
+ }
}
- if ((ret = ff_instantiate_mf(log, f, category, NULL, &reg, use_hw, mft)) < 0)
+ if ((ret = ff_instantiate_mf(avctx, f, category, NULL, &reg, use_hw, p_hw_luid, mft)) < 0)
return ret;
return 0;
@@ -1262,6 +1411,28 @@ static int mf_init_encoder(AVCodecContex
if ((ret = mf_unlock_async(avctx)) < 0)
return ret;
+#if CONFIG_D3D11VA
+ if (c->is_video && use_hw && avctx->pix_fmt == AV_PIX_FMT_D3D11) {
+ AVHWFramesContext *frames_ctx = NULL;
+ AVHWDeviceContext *dev_ctx = NULL;
+
+ if (avctx->hw_frames_ctx &&
+ (frames_ctx = (AVHWFramesContext *)avctx->hw_frames_ctx->data))
+ dev_ctx = frames_ctx->device_ctx;
+
+ if (!dev_ctx && avctx->hw_device_ctx)
+ dev_ctx = (AVHWDeviceContext *)avctx->hw_device_ctx->data;
+
+ if (!dev_ctx || dev_ctx->type != AV_HWDEVICE_TYPE_D3D11VA)
+ return AVERROR(EINVAL);
+
+ c->device_hwctx = (AVD3D11VADeviceContext *)dev_ctx->hwctx;
+
+ if ((ret = initialize_dxgi_manager(avctx)) < 0)
+ return ret;
+ }
+#endif
+
hr = IMFTransform_QueryInterface(c->mft, &IID_ICodecAPI, (void **)&c->codec_api);
if (!FAILED(hr))
av_log(avctx, AV_LOG_VERBOSE, "MFT supports ICodecAPI.\n");
@@ -1309,23 +1480,25 @@ static int mf_init_encoder(AVCodecContex
}
#if !HAVE_UWP
-#define LOAD_MF_FUNCTION(context, func_name) \
+#define LOAD_MF_FUNCTION(context, func_name, optional) \
context->functions.func_name = (void *)dlsym(context->library, #func_name); \
if (!context->functions.func_name) { \
av_log(context, AV_LOG_ERROR, "DLL mfplat.dll failed to find function "\
#func_name "\n"); \
- return AVERROR_UNKNOWN; \
+ if (!(optional)) \
+ return AVERROR_UNKNOWN; \
}
#else
// In UWP (which lacks LoadLibrary), just link directly against
// the functions - this requires building with new/complete enough
// import libraries.
-#define LOAD_MF_FUNCTION(context, func_name) \
+#define LOAD_MF_FUNCTION(context, func_name, optional) \
context->functions.func_name = func_name; \
if (!context->functions.func_name) { \
av_log(context, AV_LOG_ERROR, "Failed to find function " #func_name \
"\n"); \
- return AVERROR_UNKNOWN; \
+ if (!(optional)) \
+ return AVERROR_UNKNOWN; \
}
#endif
@@ -1338,7 +1511,9 @@ static int mf_load_library(AVCodecContex
#if !HAVE_UWP
c->library = dlopen("mfplat.dll", 0);
+#if CONFIG_D3D11VA
c->d3d_dll = dlopen("D3D11.dll", 0);
+#endif
if (!c->library) {
av_log(c, AV_LOG_ERROR, "DLL mfplat.dll failed to open\n");
@@ -1346,15 +1521,23 @@ static int mf_load_library(AVCodecContex
}
#endif
- LOAD_MF_FUNCTION(c, MFStartup);
- LOAD_MF_FUNCTION(c, MFShutdown);
- LOAD_MF_FUNCTION(c, MFCreateAlignedMemoryBuffer);
- LOAD_MF_FUNCTION(c, MFCreateSample);
- LOAD_MF_FUNCTION(c, MFCreateMediaType);
- LOAD_MF_FUNCTION(c, MFCreateDXGISurfaceBuffer);
- LOAD_MF_FUNCTION(c, MFCreateDXGIDeviceManager);
+ LOAD_MF_FUNCTION(c, MFStartup, 0);
+ LOAD_MF_FUNCTION(c, MFShutdown, 0);
+ LOAD_MF_FUNCTION(c, MFCreateAlignedMemoryBuffer, 0);
+ LOAD_MF_FUNCTION(c, MFCreateAttributes, 0);
+ LOAD_MF_FUNCTION(c, MFCreateSample, 0);
+ LOAD_MF_FUNCTION(c, MFCreateMediaType, 0);
+#if CONFIG_D3D11VA
+ LOAD_MF_FUNCTION(c, MFCreateDXGISurfaceBuffer, 0);
+ LOAD_MF_FUNCTION(c, MFCreateDXGIDeviceManager, 0);
+#endif
// MFTEnumEx is missing in Windows Vista's mfplat.dll.
- LOAD_MF_FUNCTION(c, MFTEnumEx);
+ LOAD_MF_FUNCTION(c, MFTEnumEx, 0);
+#if CONFIG_D3D11VA
+ // MFTEnum2 is missing in pre-Windows 10, version 1703's mfplat.dll.
+ // Therefore we load it optionally.
+ LOAD_MF_FUNCTION(c, MFTEnum2, 1);
+#endif
return 0;
}
@@ -1369,15 +1552,19 @@ static int mf_close(AVCodecContext *avct
if (c->async_events)
IMFMediaEventGenerator_Release(c->async_events);
+#if CONFIG_D3D11VA
if (c->dxgiManager)
IMFDXGIDeviceManager_Release(c->dxgiManager);
+#endif
#if !HAVE_UWP
if (c->library)
ff_free_mf(&c->functions, &c->mft);
dlclose(c->library);
+#if CONFIG_D3D11VA
dlclose(c->d3d_dll);
+#endif
c->library = NULL;
#else
ff_free_mf(&c->functions, &c->mft);
@@ -1404,7 +1591,7 @@ static av_cold int mf_init(AVCodecContex
#define OFFSET(x) offsetof(MFContext, x)
-#define MF_ENCODER(MEDIATYPE, NAME, ID, OPTS, FMTS, CAPS, DEFAULTS) \
+#define MF_ENCODER(MEDIATYPE, NAME, ID, OPTS, FMTS, CAPS, DEFAULTS, HWCONFIGS) \
static const AVClass ff_ ## NAME ## _mf_encoder_class = { \
.class_name = #NAME "_mf", \
.item_name = av_default_item_name, \
@@ -1425,6 +1612,7 @@ static av_cold int mf_init(AVCodecContex
CAPS \
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP, \
.defaults = DEFAULTS, \
+ .hw_configs = HWCONFIGS, \
};
#define AFMTS \
@@ -1433,9 +1621,9 @@ static av_cold int mf_init(AVCodecContex
.p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HYBRID | \
AV_CODEC_CAP_DR1 | AV_CODEC_CAP_VARIABLE_FRAME_SIZE,
-MF_ENCODER(AUDIO, aac, AAC, NULL, AFMTS, ACAPS, NULL);
-MF_ENCODER(AUDIO, ac3, AC3, NULL, AFMTS, ACAPS, NULL);
-MF_ENCODER(AUDIO, mp3, MP3, NULL, AFMTS, ACAPS, NULL);
+MF_ENCODER(AUDIO, aac, AAC, NULL, AFMTS, ACAPS, NULL, NULL);
+MF_ENCODER(AUDIO, ac3, AC3, NULL, AFMTS, ACAPS, NULL, NULL);
+MF_ENCODER(AUDIO, mp3, MP3, NULL, AFMTS, ACAPS, NULL, NULL);
#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
static const AVOption venc_opts[] = {
@@ -1469,12 +1657,42 @@ static const FFCodecDefault defaults[] =
{ NULL },
};
-#define VFMTS \
- CODEC_PIXFMTS(AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P, AV_PIX_FMT_D3D11),
+static const AVCodecHWConfigInternal *const hw_configs[] = {
+#if CONFIG_D3D11VA
+ HW_CONFIG_ENCODER_FRAMES(D3D11, D3D11VA),
+ HW_CONFIG_ENCODER_DEVICE(NONE, D3D11VA),
+#endif
+ NULL,
+};
+
+static const enum AVPixelFormat pix_fmts_10b_8b[] = {
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_YUV420P,
+ AV_PIX_FMT_P010,
+#if CONFIG_D3D11VA
+ AV_PIX_FMT_D3D11,
+#endif
+ AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat pix_fmts_8b[] = {
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_YUV420P,
+#if CONFIG_D3D11VA
+ AV_PIX_FMT_D3D11,
+#endif
+ AV_PIX_FMT_NONE
+};
+
+#define VFMTS8 \
+ CODEC_PIXFMTS_ARRAY(pix_fmts_8b),
+#define VFMTS10 \
+ CODEC_PIXFMTS_ARRAY(pix_fmts_10b_8b),
#define VCAPS \
+ .color_ranges = AVCOL_RANGE_MPEG | AVCOL_RANGE_JPEG, \
.p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HYBRID | \
AV_CODEC_CAP_DR1,
-MF_ENCODER(VIDEO, h264, H264, venc_opts, VFMTS, VCAPS, defaults);
-MF_ENCODER(VIDEO, hevc, HEVC, venc_opts, VFMTS, VCAPS, defaults);
-MF_ENCODER(VIDEO, av1, AV1, venc_opts, VFMTS, VCAPS, defaults);
+MF_ENCODER(VIDEO, h264, H264, venc_opts, VFMTS8, VCAPS, defaults, hw_configs);
+MF_ENCODER(VIDEO, hevc, HEVC, venc_opts, VFMTS10, VCAPS, defaults, hw_configs);
+MF_ENCODER(VIDEO, av1, AV1, venc_opts, VFMTS10, VCAPS, defaults, hw_configs);
+1
View File
@@ -94,3 +94,4 @@
0094-add-d3d11-tonemap-filter.patch
0095-add-scale-d3d11-p010-shader-fallback.patch
0096-fix-full-range-input-for-mjpeg-vaapi-encoder.patch
0097-add-misc-enhancements-to-mfenc-hw-encoder.patch
+8 -5
View File
@@ -14,8 +14,11 @@ CONFIG := --prefix=${TARGET_DIR} \
--disable-ffplay \
--disable-static \
--disable-libxcb \
--disable-sdl2 \
--disable-libxcb-shm \
--disable-libxcb-xfixes \
--disable-libxcb-shape \
--disable-xlib \
--disable-sdl2 \
--enable-lto=auto \
--enable-gpl \
--enable-version3 \
@@ -46,6 +49,10 @@ CONFIG := --prefix=${TARGET_DIR} \
--enable-libzvbi \
--enable-libzimg \
--enable-libfdk-aac \
--enable-libshaderc \
--enable-libplacebo \
--enable-vulkan \
--enable-vaapi \
--enable-ffnvcodec \
--enable-cuda \
--enable-cuda-llvm \
@@ -54,10 +61,6 @@ CONFIG := --prefix=${TARGET_DIR} \
--enable-nvenc \
CONFIG_x86 := --arch=amd64 \
--enable-libshaderc \
--enable-libplacebo \
--enable-vulkan \
--enable-vaapi \
--enable-libvpl \
--enable-amf \
+1
View File
@@ -623,6 +623,7 @@ fi
--enable-opencl \
--enable-dxva2 \
--enable-d3d11va \
--enable-mediafoundation \
--enable-amf \
--enable-libvpl \
--enable-ffnvcodec \
+278 -217
View File
@@ -177,6 +177,40 @@ prepare_extra_common() {
popd
popd
# OGG
pushd ${SOURCE_DIR}
git clone -b v1.3.6 --depth=1 https://github.com/xiph/ogg.git
pushd ogg
./autogen.sh
./configure \
${CROSS_OPT} \
--prefix=${TARGET_DIR} \
--disable-static \
--enable-shared \
--with-pic
make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/ogg
echo "ogg${TARGET_DIR}/lib/libogg.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
# THEORA
pushd ${SOURCE_DIR}
git clone -b v1.2.0 --depth=1 https://github.com/xiph/theora.git
pushd theora
# autotools: relax autoconf requirement to 2.69
wget -q -O - https://github.com/xiph/theora/commit/3ae2669.patch | git apply
./autogen.sh
./configure \
${CROSS_OPT} \
--prefix=${TARGET_DIR} \
--disable-{static,examples,extra-programs,oggtest,vorbistest,spec,doc} \
--enable-shared \
--with-pic
make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/theora
echo "theora${TARGET_DIR}/lib/libtheora{enc,dec}.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
# FFTW3
pushd ${SOURCE_DIR}
mkdir fftw3
@@ -208,7 +242,7 @@ prepare_extra_common() {
# CHROMAPRINT
pushd ${SOURCE_DIR}
git clone --depth=1 https://github.com/acoustid/chromaprint.git
git clone -b v1.6.0 --depth=1 https://github.com/acoustid/chromaprint.git
pushd chromaprint
echo "Libs.private: -lfftw3f -lstdc++" >> libchromaprint.pc.cmake
echo "Cflags.private: -DCHROMAPRINT_NODLL" >> libchromaprint.pc.cmake
@@ -230,7 +264,7 @@ prepare_extra_common() {
# ZIMG
pushd ${SOURCE_DIR}
git clone --recursive --depth=1 https://github.com/sekrit-twc/zimg.git
git clone -b release-3.0.6 --recursive --depth=1 https://github.com/sekrit-twc/zimg.git
pushd zimg
./autogen.sh
./configure --prefix=${TARGET_DIR} ${CROSS_OPT}
@@ -300,6 +334,246 @@ prepare_extra_common() {
make PREFIX=${TARGET_DIR} install
popd
popd
# Install crossbuild dependencies
apt-get install -y lib{udev,pciaccess,zstd,elf,expat1}-dev:${ARCH}
# LIBDRM
pushd ${SOURCE_DIR}
mkdir libdrm
pushd libdrm
libdrm_ver="libdrm-2.4.131"
libdrm_link="https://gitlab.freedesktop.org/mesa/libdrm/-/archive/${libdrm_ver}/libdrm-${libdrm_ver}.tar.gz"
wget ${libdrm_link} -O libdrm.tar.gz
tar xaf libdrm.tar.gz
meson setup libdrm-${libdrm_ver} drm_build \
${MESON_CROSS_OPT} \
--prefix=${TARGET_DIR} \
--libdir=lib \
--buildtype=release \
-D{udev,tests,install-test-programs}=false \
-D{amdgpu,radeon,intel}=enabled \
-D{etnaviv,valgrind,freedreno,vc4,vmwgfx,nouveau,man-pages}=disabled
meson configure drm_build
ninja -j$(nproc) -C drm_build install
cp -a ${TARGET_DIR}/lib/libdrm*.so* ${SOURCE_DIR}/libdrm
cp ${TARGET_DIR}/share/libdrm/*.ids ${SOURCE_DIR}/libdrm
echo "libdrm/libdrm*.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
echo "libdrm/*.ids usr/lib/jellyfin-ffmpeg/share/libdrm" >> ${DPKG_INSTALL_LIST}
popd
popd
# LIBVA
pushd ${SOURCE_DIR}
git clone -b 2.24.1 --depth=1 https://github.com/intel/libva.git
pushd libva
if [ "${ARCH}" = "arm64" ]; then
libva_drv_arch_path="/usr/lib/aarch64-linux-gnu/dri"
else
libva_drv_arch_path="/usr/lib/x86_64-linux-gnu/dri"
fi
sed -i "s#secure_getenv(\"LIBVA_DRIVERS_PATH\")#\"/usr/lib/jellyfin-ffmpeg/lib/dri:${libva_drv_arch_path}:/usr/lib/dri:/usr/local/lib/dri\"#g" va/va.c
sed -i "s#secure_getenv(\"LIBVA_DRIVER_NAME\")#secure_getenv(\"LIBVA_DRIVER_NAME_JELLYFIN\")#g" va/va.c
./autogen.sh
./configure \
${CROSS_OPT} \
--prefix=${TARGET_DIR} \
--enable-drm \
--disable-{glx,x11,wayland,docs}
make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel
echo "intel${TARGET_DIR}/lib/libva.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
echo "intel${TARGET_DIR}/lib/libva-drm.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
# LIBVA-UTILS
pushd ${SOURCE_DIR}
git clone -b 2.24.0 --depth=1 https://github.com/intel/libva-utils.git
pushd libva-utils
./autogen.sh
./configure \
${CROSS_OPT} \
--prefix=${TARGET_DIR}
make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel
echo "intel${TARGET_DIR}/bin/vainfo usr/lib/jellyfin-ffmpeg" >> ${DPKG_INSTALL_LIST}
popd
popd
# Vulkan Headers
pushd ${SOURCE_DIR}
git clone -b v1.4.355 --depth=1 https://github.com/KhronosGroup/Vulkan-Headers.git
pushd Vulkan-Headers
mkdir build && pushd build
cmake \
${CMAKE_TOOLCHAIN_OPT} \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=${TARGET_DIR} ..
make -j$(nproc) && make install
popd
popd
popd
# Vulkan ICD Loader
pushd ${SOURCE_DIR}
git clone -b v1.4.355 --depth=1 https://github.com/KhronosGroup/Vulkan-Loader.git
pushd Vulkan-Loader
sed -i 's/memset(disable_struct, 0, sizeof.*);/& disable_struct->disable_all_implicit = 1;/' loader/loader_environment.c
mkdir build && pushd build
cmake \
${CMAKE_TOOLCHAIN_OPT} \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=${TARGET_DIR} \
-DVULKAN_HEADERS_INSTALL_DIR="${TARGET_DIR}" \
-DCMAKE_INSTALL_SYSCONFDIR=${TARGET_DIR}/share \
-DCMAKE_INSTALL_DATADIR=${TARGET_DIR}/share \
-DCMAKE_INSTALL_LIBDIR=lib \
-DBUILD_TESTS=OFF \
-DBUILD_WSI_{XCB,XLIB,XLIB_XRANDR,WAYLAND}_SUPPORT=OFF ..
make -j$(nproc) && make install
cp -a ${TARGET_DIR}/lib/libvulkan.so* ${SOURCE_DIR}/Vulkan-Loader
echo "Vulkan-Loader/libvulkan.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
popd
# SHADERC
shaderc_ver="v2026.2"
pushd ${SOURCE_DIR}
git clone -b ${shaderc_ver} --depth=1 https://github.com/google/shaderc.git
pushd shaderc
./utils/git-sync-deps
shaderc_conf=$(echo -GNinja \
-DCMAKE_BUILD_TYPE=Release \
-DSHADERC_SKIP_{TESTS,EXAMPLES,COPYRIGHT_CHECK}=ON \
-DENABLE_EXCEPTIONS=ON \
-DSPIRV_SKIP_EXECUTABLES=ON \
-DSPIRV_TOOLS_BUILD_STATIC=ON \
-DBUILD_SHARED_LIBS=OFF)
# Build native glslangValidator for crossbuild
if [ "${ARCH}" != "amd64" ]; then
mkdir glslang_build && pushd glslang_build
cmake $shaderc_conf \
-DENABLE_GLSLANG_BINARIES=ON ..
ninja -j$(nproc) third_party/glslang/StandAlone/glslang
cp third_party/glslang/StandAlone/glslangValidator ${TARGET_DIR}/bin
popd
fi
# Build target shaderc
mkdir shaderc_build && pushd shaderc_build
cmake $shaderc_conf \
${CMAKE_TOOLCHAIN_OPT} \
-DENABLE_GLSLANG_BINARIES=$([ "${ARCH}" = "amd64" ] && echo "ON" || echo "OFF") \
-DCMAKE_INSTALL_PREFIX=${TARGET_DIR} ..
ninja -j$(nproc)
ninja install
cp -a ${TARGET_DIR}/lib/libshaderc_shared.so* ${SOURCE_DIR}/shaderc
echo "shaderc/libshaderc_shared* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
popd
# MESA
# Minimal libs for AMD VAAPI, AMD RADV and Intel ANV
if [[ ${LLVM_VER} -ge 15 ]]; then
if [[ "${ARCH}" = "amd64" && ${LLVMSPIRVLIB_VER} -ge 15 && ${LLVMSPIRVLIB_VER} -le 21 ]]; then
# Intel ANV requires llvmspirvlib >= 15 (and <= 21 in mesa 26.0)
mesa_vk_drv="amd,intel"
mesa_llvm_clc="enabled"
apt-get install -y {llvm-,libllvmspirvlib-,libclc-,libclang-,libclang-cpp}${LLVMSPIRVLIB_VER}-dev
else
mesa_vk_drv="amd"
mesa_llvm_clc="disabled"
fi
pushd ${SOURCE_DIR}
mkdir mesa
pushd mesa
mesa_ver="mesa-26.0.8"
mesa_link="https://gitlab.freedesktop.org/mesa/mesa/-/archive/${mesa_ver}/mesa-${mesa_ver}.tar.gz"
wget ${mesa_link} -O mesa.tar.gz
tar xaf mesa.tar.gz
# Enable VAAPI VPP alpha blending support
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41090.patch | \
patch -p1 -d mesa-${mesa_ver}
# Fix misc CSC issues in VAAPI VPP
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/42181.patch | \
patch -p1 -d mesa-${mesa_ver}
# Fix setting VPE rotation with horizontal flip enabled
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/42408.patch | \
sed 's#/mm/#/#g' | patch -p1 -d mesa-${mesa_ver}
# Fix setting chroma swizzle mode in VK Video on GFX9
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/42763.patch | \
patch -p1 -d mesa-${mesa_ver}
meson setup mesa-${mesa_ver} mesa_build \
${MESON_CROSS_OPT} \
--prefix=${TARGET_DIR} \
--libdir=lib \
--buildtype=release \
--wrap-mode=nofallback \
-Db_ndebug=true \
-Db_lto=false \
-Dplatforms=[] \
-Dgallium-drivers=radeonsi \
-Dvulkan-drivers=${mesa_vk_drv} \
-Dvulkan-layers=[] \
-Dvulkan-manifest-per-architecture=true \
-Degl=disabled \
-Dgallium-{extra-hud,rusticl}=false \
-Dgallium-mediafoundation=disabled \
-Dgallium-va=enabled \
-Dvideo-codecs=all \
-Dgbm=disabled \
-Dgles1=disabled \
-Dgles2=disabled \
-Dopengl=false \
-Dglvnd=disabled \
-Dglx=disabled \
-Dlibunwind=disabled \
-Dllvm=${mesa_llvm_clc} \
-Damd-use-llvm=false \
-Dlmsensors=disabled \
-Dvalgrind=disabled \
-Dtools=[] \
-Dzstd=enabled \
-Dmicrosoft-clc=disabled \
-Dintel-elk=false
meson configure mesa_build
ninja -j$(nproc) -C mesa_build install
cp -a ${TARGET_DIR}/lib/libvulkan_*.so ${SOURCE_DIR}/mesa
# radeonsi_drv_video.so -> libgallium_drv_video.so is soft link
cp ${TARGET_DIR}/lib/dri/radeonsi_drv_video.so ${SOURCE_DIR}/mesa
echo "mesa/lib*.so usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
echo "mesa/radeonsi_drv_video.so usr/lib/jellyfin-ffmpeg/lib/dri" >> ${DPKG_INSTALL_LIST}
cp ${TARGET_DIR}/share/drirc.d/*.conf ${SOURCE_DIR}/mesa
echo "mesa/*defaults.conf usr/lib/jellyfin-ffmpeg/share/drirc.d" >> ${DPKG_INSTALL_LIST}
cp ${TARGET_DIR}/share/vulkan/icd.d/*.json ${SOURCE_DIR}/mesa
echo "mesa/*icd.*.json usr/lib/jellyfin-ffmpeg/share/vulkan/icd.d" >> ${DPKG_INSTALL_LIST}
popd
popd
fi
# LIBPLACEBO
pushd ${SOURCE_DIR}
git clone -b v7.360.1 --recursive --depth=1 https://github.com/haasn/libplacebo.git
# Fix bit shift when importing P01x non-multiplane image
git -C libplacebo apply ${SOURCE_DIR}/builder/patches/libplacebo/*.patch
sed -i 's/env: python_env,//g' libplacebo/src/vulkan/meson.build
meson setup libplacebo placebo_build \
${MESON_CROSS_OPT} \
--prefix=${TARGET_DIR} \
--libdir=lib \
--buildtype=release \
--default-library=shared \
-Dvulkan=enabled \
-Dvk-proc-addr=enabled \
-Dvulkan-registry=${TARGET_DIR}/share/vulkan/registry/vk.xml \
-Dshaderc=enabled \
-Dglslang=disabled \
-D{demos,tests,bench,fuzz}=false
meson configure placebo_build
ninja -j$(nproc) -C placebo_build install
cp -a ${TARGET_DIR}/lib/libplacebo.so* ${SOURCE_DIR}/libplacebo
echo "libplacebo/libplacebo* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
}
# Prepare extra headers, libs and drivers for x86_64-linux-gnu
@@ -320,58 +594,6 @@ prepare_extra_amd64() {
popd
popd
# LIBDRM
pushd ${SOURCE_DIR}
mkdir libdrm
pushd libdrm
libdrm_ver="libdrm-2.4.131"
libdrm_link="https://gitlab.freedesktop.org/mesa/libdrm/-/archive/${libdrm_ver}/libdrm-${libdrm_ver}.tar.gz"
wget ${libdrm_link} -O libdrm.tar.gz
tar xaf libdrm.tar.gz
meson setup libdrm-${libdrm_ver} drm_build \
--prefix=${TARGET_DIR} \
--libdir=lib \
--buildtype=release \
-D{udev,tests,install-test-programs}=false \
-D{amdgpu,radeon,intel}=enabled \
-D{valgrind,freedreno,vc4,vmwgfx,nouveau,man-pages}=disabled
meson configure drm_build
ninja -j$(nproc) -C drm_build install
cp -a ${TARGET_DIR}/lib/libdrm*.so* ${SOURCE_DIR}/libdrm
cp ${TARGET_DIR}/share/libdrm/*.ids ${SOURCE_DIR}/libdrm
echo "libdrm/libdrm*.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
echo "libdrm/*.ids usr/lib/jellyfin-ffmpeg/share/libdrm" >> ${DPKG_INSTALL_LIST}
popd
popd
# LIBVA
pushd ${SOURCE_DIR}
git clone -b 2.24.1 --depth=1 https://github.com/intel/libva.git
pushd libva
sed -i 's|secure_getenv("LIBVA_DRIVERS_PATH")|"/usr/lib/jellyfin-ffmpeg/lib/dri:/usr/lib/x86_64-linux-gnu/dri:/usr/lib/dri:/usr/local/lib/dri"|g' va/va.c
sed -i 's|secure_getenv("LIBVA_DRIVER_NAME")|secure_getenv("LIBVA_DRIVER_NAME_JELLYFIN")|g' va/va.c
./autogen.sh
./configure \
--prefix=${TARGET_DIR} \
--enable-drm \
--disable-{glx,x11,wayland,docs}
make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel
echo "intel${TARGET_DIR}/lib/libva.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
echo "intel${TARGET_DIR}/lib/libva-drm.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
# LIBVA-UTILS
pushd ${SOURCE_DIR}
git clone -b 2.24.0 --depth=1 https://github.com/intel/libva-utils.git
pushd libva-utils
./autogen.sh
./configure --prefix=${TARGET_DIR}
make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel
echo "intel${TARGET_DIR}/bin/vainfo usr/lib/jellyfin-ffmpeg" >> ${DPKG_INSTALL_LIST}
popd
popd
# INTEL-VAAPI-DRIVER
pushd ${SOURCE_DIR}
git clone --depth=1 https://github.com/intel/intel-vaapi-driver.git
@@ -492,169 +714,6 @@ prepare_extra_amd64() {
popd
popd
popd
# Vulkan Headers
pushd ${SOURCE_DIR}
git clone -b v1.4.355 --depth=1 https://github.com/KhronosGroup/Vulkan-Headers.git
pushd Vulkan-Headers
mkdir build && pushd build
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=${TARGET_DIR} ..
make -j$(nproc) && make install
popd
popd
popd
# Vulkan ICD Loader
pushd ${SOURCE_DIR}
git clone -b v1.4.355 --depth=1 https://github.com/KhronosGroup/Vulkan-Loader.git
pushd Vulkan-Loader
mkdir build && pushd build
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=${TARGET_DIR} \
-DVULKAN_HEADERS_INSTALL_DIR="${TARGET_DIR}" \
-DCMAKE_INSTALL_SYSCONFDIR=${TARGET_DIR}/share \
-DCMAKE_INSTALL_DATADIR=${TARGET_DIR}/share \
-DCMAKE_INSTALL_LIBDIR=lib \
-DBUILD_TESTS=OFF \
-DBUILD_WSI_{XCB,XLIB,WAYLAND}_SUPPORT=ON ..
make -j$(nproc) && make install
cp -a ${TARGET_DIR}/lib/libvulkan.so* ${SOURCE_DIR}/Vulkan-Loader
echo "Vulkan-Loader/libvulkan.so* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
popd
# SHADERC
shaderc_ver="v2026.2"
pushd ${SOURCE_DIR}
git clone -b ${shaderc_ver} --depth=1 https://github.com/google/shaderc.git
pushd shaderc
./utils/git-sync-deps
mkdir build && pushd build
cmake \
-GNinja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=${TARGET_DIR} \
-DSHADERC_SKIP_{TESTS,EXAMPLES,COPYRIGHT_CHECK}=ON \
-DENABLE_{GLSLANG_BINARIES,EXCEPTIONS}=ON \
-DENABLE_CTEST=OFF \
-DSPIRV_SKIP_EXECUTABLES=ON \
-DSPIRV_TOOLS_BUILD_STATIC=ON \
-DBUILD_SHARED_LIBS=OFF ..
ninja -j$(nproc)
ninja install
cp -a ${TARGET_DIR}/lib/libshaderc_shared.so* ${SOURCE_DIR}/shaderc
echo "shaderc/libshaderc_shared* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
popd
popd
# MESA
# Minimal libs for AMD VAAPI, AMD RADV and Intel ANV
if [[ ${LLVM_VER} -ge 15 ]]; then
if [[ ${LLVMSPIRVLIB_VER} -ge 15 && ${LLVMSPIRVLIB_VER} -le 21 ]]; then
# Intel ANV requires llvmspirvlib >= 15 (and <= 21 in mesa 26.0)
mesa_vk_drv="amd,intel"
mesa_llvm_clc="enabled"
apt-get install -y {llvm-,libllvmspirvlib-,libclc-,libclang-,libclang-cpp}${LLVMSPIRVLIB_VER}-dev libudev-dev
else
mesa_vk_drv="amd"
mesa_llvm_clc="disabled"
apt-get install -y libudev-dev
fi
pushd ${SOURCE_DIR}
mkdir mesa
pushd mesa
mesa_ver="mesa-26.0.8"
mesa_link="https://gitlab.freedesktop.org/mesa/mesa/-/archive/${mesa_ver}/mesa-${mesa_ver}.tar.gz"
wget ${mesa_link} -O mesa.tar.gz
tar xaf mesa.tar.gz
# Enable VAAPI VPP alpha blending support
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41090.patch | \
patch -p1 -d mesa-${mesa_ver}
# Fix misc CSC issues in VAAPI VPP
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/42181.patch | \
patch -p1 -d mesa-${mesa_ver}
# Fix setting VPE rotation with horizontal flip enabled
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/42408.patch | \
sed 's#/mm/#/#g' | patch -p1 -d mesa-${mesa_ver}
# Fix setting chroma swizzle mode in VK Video on GFX9
wget -q -O - https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/42763.patch | \
patch -p1 -d mesa-${mesa_ver}
meson setup mesa-${mesa_ver} mesa_build \
--prefix=${TARGET_DIR} \
--libdir=lib \
--buildtype=release \
--wrap-mode=nofallback \
-Db_ndebug=true \
-Db_lto=false \
-Dplatforms=x11 \
-Dgallium-drivers=radeonsi \
-Dvulkan-drivers=${mesa_vk_drv} \
-Dvulkan-layers=device-select \
-Dvulkan-manifest-per-architecture=true \
-Degl=disabled \
-Dgallium-{extra-hud,rusticl}=false \
-Dgallium-mediafoundation=disabled \
-Dgallium-va=enabled \
-Dvideo-codecs=all \
-Dgbm=disabled \
-Dgles1=disabled \
-Dgles2=disabled \
-Dopengl=false \
-Dglvnd=disabled \
-Dglx=disabled \
-Dlibunwind=disabled \
-Dllvm=${mesa_llvm_clc} \
-Damd-use-llvm=false \
-Dlmsensors=disabled \
-Dvalgrind=disabled \
-Dtools=[] \
-Dzstd=enabled \
-Dmicrosoft-clc=disabled \
-Dintel-elk=false
meson configure mesa_build
ninja -j$(nproc) -C mesa_build install
cp -a ${TARGET_DIR}/lib/libvulkan_*.so ${SOURCE_DIR}/mesa
cp -a ${TARGET_DIR}/lib/libVkLayer_MESA_device_select.so ${SOURCE_DIR}/mesa
# radeonsi_drv_video.so -> libgallium_drv_video.so is soft link
cp ${TARGET_DIR}/lib/dri/radeonsi_drv_video.so ${SOURCE_DIR}/mesa
echo "mesa/lib*.so usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
echo "mesa/radeonsi_drv_video.so usr/lib/jellyfin-ffmpeg/lib/dri" >> ${DPKG_INSTALL_LIST}
cp ${TARGET_DIR}/share/drirc.d/*.conf ${SOURCE_DIR}/mesa
echo "mesa/*defaults.conf usr/lib/jellyfin-ffmpeg/share/drirc.d" >> ${DPKG_INSTALL_LIST}
cp ${TARGET_DIR}/share/vulkan/{icd.d,implicit_layer.d}/*.json ${SOURCE_DIR}/mesa
echo "mesa/*icd.x86_64.json usr/lib/jellyfin-ffmpeg/share/vulkan/icd.d" >> ${DPKG_INSTALL_LIST}
echo "mesa/*device_select.json usr/lib/jellyfin-ffmpeg/share/vulkan/implicit_layer.d" >> ${DPKG_INSTALL_LIST}
popd
popd
fi
# LIBPLACEBO
pushd ${SOURCE_DIR}
git clone -b v7.360.1 --recursive --depth=1 https://github.com/haasn/libplacebo.git
# Fix bit shift when importing P01x non-multiplane image
git -C libplacebo apply ${SOURCE_DIR}/builder/patches/libplacebo/*.patch
sed -i 's/env: python_env,//g' libplacebo/src/vulkan/meson.build
meson setup libplacebo placebo_build \
--prefix=${TARGET_DIR} \
--libdir=lib \
--buildtype=release \
--default-library=shared \
-Dvulkan=enabled \
-Dvk-proc-addr=enabled \
-Dvulkan-registry=${TARGET_DIR}/share/vulkan/registry/vk.xml \
-Dshaderc=enabled \
-Dglslang=disabled \
-D{demos,tests,bench,fuzz}=false
meson configure placebo_build
ninja -j$(nproc) -C placebo_build install
cp -a ${TARGET_DIR}/lib/libplacebo.so* ${SOURCE_DIR}/libplacebo
echo "libplacebo/libplacebo* usr/lib/jellyfin-ffmpeg/lib" >> ${DPKG_INSTALL_LIST}
popd
}
# Prepare extra headers, libs and drivers for {arm,aarch64}-linux-gnu*
@@ -733,6 +792,8 @@ EOF
for tool in {gcc,g++,gcc-ar,gcc-ranlib,gcc-nm}; do
ln -sf "/usr/bin/aarch64-linux-gnu-$tool-${GCC_VER}" "/usr/bin/aarch64-linux-gnu-$tool"
done
# Update pkgconfig search path
export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/lib/aarch64-linux-gnu/pkgconfig
}
# Set the architecture-specific options
@@ -0,0 +1,34 @@
From 15ee32753c92faddbabbff11676779618fc6db7e Mon Sep 17 00:00:00 2001
From: Diego de Souza <ddesouza@nvidia.com>
Date: Fri, 26 Jun 2026 22:59:03 +0200
Subject: [PATCH] Load nvEncodeAPI.dll for native Windows ARM64
On Windows on Arm, nvEncodeAPI.dll is an Arm64X dispatch proxy.
It routes native ARM64 callers to nvEncodeAPIa64.dll and x64 or
ARM64EC callers to nvEncodeAPI64.dll.
Select the proxy for native ARM64 builds while preserving the existing
library name for other 64-bit Windows targets.
Signed-off-by: Diego de Souza <ddesouza@nvidia.com>
---
include/ffnvcodec/dynlink_loader.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/include/ffnvcodec/dynlink_loader.h b/include/ffnvcodec/dynlink_loader.h
index 6d6a0ec..1be0dbf 100644
--- a/include/ffnvcodec/dynlink_loader.h
+++ b/include/ffnvcodec/dynlink_loader.h
@@ -50,7 +50,11 @@
# define CUDA_LIBNAME "nvcuda.dll"
# define NVCUVID_LIBNAME "nvcuvid.dll"
# if defined(_WIN64) || defined(__CYGWIN64__)
-# define NVENC_LIBNAME "nvEncodeAPI64.dll"
+# if defined(_M_ARM64) || defined(__aarch64__)
+# define NVENC_LIBNAME "nvEncodeAPI.dll"
+# else
+# define NVENC_LIBNAME "nvEncodeAPI64.dll"
+# endif
# else
# define NVENC_LIBNAME "nvEncodeAPI.dll"
# endif
@@ -12,8 +12,10 @@ url=https://github.com/FFmpeg/nv-codec-headers.git
license=('spdx:MIT')
makedepends=("git")
_tag=451da99614412a7f9526ef301a5ee0c7a6f9ad76
source=(git+https://github.com/FFmpeg/nv-codec-headers.git#tag=${_tag})
sha256sums=('SKIP')
source=("git+https://github.com/FFmpeg/nv-codec-headers.git#tag=${_tag}"
"01-Load-nvEncodeAPI-dll-for-native-Windows-ARM64.patch")
sha256sums=('SKIP'
'43c248049c16e207635a9e56a26365b26014d2dc2d7c0e0688aa09ad649208f9')
export MINGW_TOOLCHAIN_PREFIX="${MINGW_PREFIX}"
export FF_MINGW_PREFIX="${MINGW_TOOLCHAIN_PREFIX}/ffbuild"
@@ -24,6 +26,12 @@ pkgver() {
git describe --tags | sed 's/^n//'
}
prepare() {
cd nv-codec-headers
patch -p1 -i "${srcdir}/01-Load-nvEncodeAPI-dll-for-native-Windows-ARM64.patch"
}
build() {
make PREFIX=${FF_MINGW_PREFIX} -C nv-codec-headers
sed -n '4,25p' nv-codec-headers/include/ffnvcodec/nvEncodeAPI.h > LICENSE # Extract license
+1
View File
@@ -89,6 +89,7 @@ PKG_CONFIG_PATH=/clang64/ffbuild/lib/pkgconfig ./configure \
--enable-dxva2 \
--enable-d3d11va \
--enable-d3d12va \
--enable-mediafoundation \
--enable-amf \
--enable-libvpl \
--enable-ffnvcodec \