diff --git a/Dockerfile.in b/Dockerfile.in index 79ed9142c..781fa2753 100644 --- a/Dockerfile.in +++ b/Dockerfile.in @@ -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 diff --git a/build.yaml b/build.yaml index b31ce6e7c..9f691c761 100644 --- a/build.yaml +++ b/build.yaml @@ -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 diff --git a/builder/scripts.d/50-dxva.sh b/builder/scripts.d/50-dxva.sh index 2eddfdc53..b9f1c88ba 100755 --- a/builder/scripts.d/50-dxva.sh +++ b/builder/scripts.d/50-dxva.sh @@ -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 } diff --git a/debian/changelog b/debian/changelog index 8c8442ac0..6fead11e5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 Sat, 18 Jul 2026 18:39:50 +0800 + jellyfin-ffmpeg (8.1.2-1) unstable; urgency=medium * New upstream version 8.1.2 diff --git a/debian/control b/debian/control index d927ea585..2a036c863 100644 --- a/debian/control +++ b/debian/control @@ -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 diff --git a/debian/patches/0078-backport-fixes-for-vulkan-from-upstream.patch b/debian/patches/0078-backport-fixes-for-vulkan-from-upstream.patch index 755fe9843..d1c172cab 100644 --- a/debian/patches/0078-backport-fixes-for-vulkan-from-upstream.patch +++ b/debian/patches/0078-backport-fixes-for-vulkan-from-upstream.patch @@ -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, ¶ms_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, ¶ms_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, ¶ms_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, diff --git a/debian/patches/0089-relax-to-allow-safe-filenames-in-mkv-attachments.patch b/debian/patches/0089-relax-to-allow-safe-filenames-in-mkv-attachments.patch index 5f709ccee..ee108c89a 100644 --- a/debian/patches/0089-relax-to-allow-safe-filenames-in-mkv-attachments.patch +++ b/debian/patches/0089-relax-to-allow-safe-filenames-in-mkv-attachments.patch @@ -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)) { diff --git a/debian/patches/0097-add-misc-enhancements-to-mfenc-hw-encoder.patch b/debian/patches/0097-add-misc-enhancements-to-mfenc-hw-encoder.patch new file mode 100644 index 000000000..67ae861b0 --- /dev/null +++ b/debian/patches/0097-add-misc-enhancements-to-mfenc-hw-encoder.patch @@ -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, ®, use_hw, mft)) < 0) ++ if ((ret = ff_instantiate_mf(avctx, f, category, NULL, ®, 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); diff --git a/debian/patches/series b/debian/patches/series index 92c44f207..dbdd5eb87 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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 diff --git a/debian/rules b/debian/rules index bbef52dfc..22c847034 100755 --- a/debian/rules +++ b/debian/rules @@ -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 \ diff --git a/docker-build-win64.sh b/docker-build-win64.sh index 65bceb4bd..209eeb236 100755 --- a/docker-build-win64.sh +++ b/docker-build-win64.sh @@ -623,6 +623,7 @@ fi --enable-opencl \ --enable-dxva2 \ --enable-d3d11va \ + --enable-mediafoundation \ --enable-amf \ --enable-libvpl \ --enable-ffnvcodec \ diff --git a/docker-build.sh b/docker-build.sh index 50d269a71..5f713ad19 100755 --- a/docker-build.sh +++ b/docker-build.sh @@ -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 diff --git a/msys2/PKGBUILD/50-mingw-w64-ffnvcodec-headers/01-Load-nvEncodeAPI-dll-for-native-Windows-ARM64.patch b/msys2/PKGBUILD/50-mingw-w64-ffnvcodec-headers/01-Load-nvEncodeAPI-dll-for-native-Windows-ARM64.patch new file mode 100644 index 000000000..61a0cb002 --- /dev/null +++ b/msys2/PKGBUILD/50-mingw-w64-ffnvcodec-headers/01-Load-nvEncodeAPI-dll-for-native-Windows-ARM64.patch @@ -0,0 +1,34 @@ +From 15ee32753c92faddbabbff11676779618fc6db7e Mon Sep 17 00:00:00 2001 +From: Diego de Souza +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 +--- + 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 diff --git a/msys2/PKGBUILD/50-mingw-w64-ffnvcodec-headers/PKGBUILD b/msys2/PKGBUILD/50-mingw-w64-ffnvcodec-headers/PKGBUILD index a1841a1b1..8042bfcb0 100644 --- a/msys2/PKGBUILD/50-mingw-w64-ffnvcodec-headers/PKGBUILD +++ b/msys2/PKGBUILD/50-mingw-w64-ffnvcodec-headers/PKGBUILD @@ -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 diff --git a/msys2/build.sh b/msys2/build.sh index f1e72cbb6..13f5018e0 100755 --- a/msys2/build.sh +++ b/msys2/build.sh @@ -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 \