Skip to content

Commit

Permalink
Merge pull request #431 from jellyfin/transpose-vt-autoselect
Browse files Browse the repository at this point in the history
avfilter/transpose_vt: select implementation based on macOS version
  • Loading branch information
gnattu committed Aug 10, 2024
2 parents c9dd753 + 567fc82 commit d177518
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 50 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/_meta_mac_portable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ jobs:
strategy:
fail-fast: true
matrix:
# Currently, macOS 12 is x86 exclusive and macOS 14 is arm exclusive and we have no other way to specify arch
# Currently, macOS 13 is x86 exclusive and macOS 14 is arm exclusive and we have no other way to specify arch
os:
- name: macos-12
- name: macos-13
arch: x86_64
- name: macos-14
arch: arm64
Expand Down
1 change: 1 addition & 0 deletions builder/variants/defaults-mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export PKG_CONFIG_LIBDIR="/usr/lib/pkgconfig:/opt/homebrew/Library/Homebrew/os/m
export CMAKE_PREFIX_PATH=""$FFBUILD_PREFIX""
export PKG_CONFIG_PATH=""$FFBUILD_PREFIX"/lib/pkgconfig"
export RANLIB="/usr/bin/ranlib"
export MACOSX_DEPLOYMENT_TARGET="12.0"
212 changes: 164 additions & 48 deletions debian/patches/0051-add-coreimage-based-vf-transpose-vt-filter.patch
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,9 @@ Index: FFmpeg/libavfilter/vf_transpose_vt.m
===================================================================
--- /dev/null
+++ FFmpeg/libavfilter/vf_transpose_vt.m
@@ -0,0 +1,265 @@
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2023 Zhao Zhili <zhilizhao@tencent.com>
+ * Copyright (c) 2024 Gnattu OC <gnattuoc@me.com>
+ *
+ * This file is part of FFmpeg.
Expand All @@ -327,17 +328,31 @@ Index: FFmpeg/libavfilter/vf_transpose_vt.m
+ */
+
+#include <CoreImage/CoreImage.h>
+#include <VideoToolbox/VideoToolbox.h>
+
+#include "libavutil/hwcontext.h"
+#include "libavutil/opt.h"
+#include "libavutil/objc.h"
+#include "internal.h"
+#include "transpose.h"
+#include "video.h"
+
+// Declaration for SDKs not having VTPixelRotationSession
+#if (TARGET_OS_OSX && (!defined(__MAC_13_0))) || \
+ (TARGET_OS_IOS && (!defined(__IPHONE_16_0))) || \
+ (TARGET_OS_TV && (!defined(__TVOS_16_0)))
+ #define LEGACY_VT_SDK
+#endif
+
+#ifdef LEGACY_VT_SDK
+typedef void* VTPixelRotationSessionRef;
+#endif
+
+typedef struct TransposeVtContext {
+ AVClass *class;
+ CIContext *ci_ctx;
+ CGImagePropertyOrientation orientation;
+ VTPixelRotationSessionRef session;
+
+ int dir;
+ int passthrough;
Expand All @@ -346,10 +361,28 @@ Index: FFmpeg/libavfilter/vf_transpose_vt.m
+static av_cold int transpose_vt_init(AVFilterContext *avctx)
+{
+ TransposeVtContext *s = avctx->priv;
+ s->ci_ctx = CFBridgingRetain([CIContext context]);
+ if (!s->ci_ctx) {
+ av_log(avctx, AV_LOG_ERROR, "CoreImage Context create failed\n");
+ return AVERROR_EXTERNAL;
+#ifndef LEGACY_VT_SDK
+ if (@available(macOS 13.0, iOS 16, *)) {
+ int ret;
+
+ ret = VTPixelRotationSessionCreate(kCFAllocatorDefault, &s->session);
+ if (ret != noErr) {
+ av_log(avctx, AV_LOG_ERROR, "Rotation session create failed, %d\n", ret);
+ return AVERROR_EXTERNAL;
+ } else {
+ av_log(avctx, AV_LOG_DEBUG, "VT Rotation session created\n");
+ }
+ }
+ else
+#endif
+ {
+ s->ci_ctx = CFBridgingRetain([CIContext context]);
+ if (!s->ci_ctx) {
+ av_log(avctx, AV_LOG_ERROR, "CoreImage Context create failed\n");
+ return AVERROR_EXTERNAL;
+ } else {
+ av_log(avctx, AV_LOG_DEBUG, "CoreImage Context created\n");
+ }
+ }
+
+ return 0;
Expand All @@ -362,6 +395,15 @@ Index: FFmpeg/libavfilter/vf_transpose_vt.m
+ CFRelease(s->ci_ctx);
+ s->ci_ctx = NULL;
+ }
+#ifndef LEGACY_VT_SDK
+ if (@available(macOS 13.0, iOS 16, *)) {
+ if (s->session) {
+ VTPixelRotationSessionInvalidate(s->session);
+ CFRelease(s->session);
+ s->session = NULL;
+ }
+ }
+#endif
+}
+
+static int transpose_vt_filter_frame(AVFilterLink *link, AVFrame *in)
Expand All @@ -373,8 +415,6 @@ Index: FFmpeg/libavfilter/vf_transpose_vt.m
+ CVPixelBufferRef src;
+ CVPixelBufferRef dst;
+ AVFrame *out;
+ CIImage *source_image = NULL;
+ CIImage *transposed_image = NULL;
+
+ if (s->passthrough)
+ return ff_filter_frame(outlink, in);
Expand All @@ -391,19 +431,33 @@ Index: FFmpeg/libavfilter/vf_transpose_vt.m
+
+ src = (CVPixelBufferRef)in->data[3];
+ dst = (CVPixelBufferRef)out->data[3];
+
+ source_image = CFBridgingRetain([CIImage imageWithCVPixelBuffer: src]);
+ transposed_image = CFBridgingRetain([source_image imageByApplyingCGOrientation: s->orientation]);
+ if (!transposed_image) {
+ CFRelease(source_image);
+ av_log(ctx, AV_LOG_ERROR, "transpose image failed, %d\n", ret);
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+#ifndef LEGACY_VT_SDK
+ if (@available(macOS 13.0, iOS 16, *)) {
+ ret = VTPixelRotationSessionRotateImage(s->session, src, dst);
+ if (ret != noErr) {
+ av_log(ctx, AV_LOG_ERROR, "transfer image failed, %d\n", ret);
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+ }
+ }
+ else
+#endif
+ {
+ @autoreleasepool {
+ CIImage *source_image = NULL;
+ CIImage *transposed_image = NULL;
+ source_image = [CIImage imageWithCVPixelBuffer:src];
+ transposed_image = [source_image imageByApplyingCGOrientation:s->orientation];
+ if (!transposed_image) {
+ ff_objc_release(&source_image);
+ av_log(ctx, AV_LOG_ERROR, "transpose image failed, %d\n", ret);
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+ }
+ CVBufferPropagateAttachments(src, dst);
+ [(__bridge CIContext *) s->ci_ctx render:transposed_image toCVPixelBuffer:dst];
+ }
+ }
+ [(__bridge CIContext*)s->ci_ctx render: (__bridge CIImage*)transposed_image toCVPixelBuffer: dst];
+ CFRelease(source_image);
+ CFRelease(transposed_image);
+ CVBufferPropagateAttachments(src, dst);
+
+ av_frame_free(&in);
+
Expand Down Expand Up @@ -470,35 +524,97 @@ Index: FFmpeg/libavfilter/vf_transpose_vt.m
+
+ s->passthrough = TRANSPOSE_PT_TYPE_NONE;
+
+ switch (s->dir) {
+ case TRANSPOSE_CCLOCK_FLIP:
+ s->orientation = kCGImagePropertyOrientationLeftMirrored;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CCLOCK:
+ s->orientation = kCGImagePropertyOrientationLeft;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CLOCK:
+ s->orientation = kCGImagePropertyOrientationRight;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CLOCK_FLIP:
+ s->orientation = kCGImagePropertyOrientationRightMirrored;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_REVERSAL:
+ s->orientation = kCGImagePropertyOrientationDown;
+ break;
+ case TRANSPOSE_HFLIP:
+ s->orientation = kCGImagePropertyOrientationUpMirrored;
+ break;
+ case TRANSPOSE_VFLIP:
+ s->orientation = kCGImagePropertyOrientationDownMirrored;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", s->dir);
+ return AVERROR(EINVAL);
+#ifndef LEGACY_VT_SDK
+ if (@available(macOS 13.0, iOS 16, *)) {
+ CFStringRef rotation = kVTRotation_0;
+ CFBooleanRef vflip = kCFBooleanFalse;
+ CFBooleanRef hflip = kCFBooleanFalse;
+
+ switch (s->dir) {
+ case TRANSPOSE_CCLOCK_FLIP:
+ rotation = kVTRotation_CCW90;
+ vflip = kCFBooleanTrue;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CCLOCK:
+ rotation = kVTRotation_CCW90;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CLOCK:
+ rotation = kVTRotation_CW90;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CLOCK_FLIP:
+ rotation = kVTRotation_CW90;
+ vflip = kCFBooleanTrue;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_REVERSAL:
+ rotation = kVTRotation_180;
+ break;
+ case TRANSPOSE_HFLIP:
+ hflip = kCFBooleanTrue;
+ break;
+ case TRANSPOSE_VFLIP:
+ vflip = kCFBooleanTrue;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", s->dir);
+ return AVERROR(EINVAL);
+ }
+
+ err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_Rotation,
+ rotation);
+ if (err != noErr) {
+ av_log(avctx, AV_LOG_ERROR, "Set rotation property failed, %d\n", err);
+ return AVERROR_EXTERNAL;
+ }
+ err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipVerticalOrientation,
+ vflip);
+ if (err != noErr) {
+ av_log(avctx, AV_LOG_ERROR, "Set vertical flip property failed, %d\n", err);
+ return AVERROR_EXTERNAL;
+ }
+ err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipHorizontalOrientation,
+ hflip);
+ if (err != noErr) {
+ av_log(avctx, AV_LOG_ERROR, "Set horizontal flip property failed, %d\n", err);
+ return AVERROR_EXTERNAL;
+ }
+ }
+ else
+#endif
+ {
+ switch (s->dir) {
+ case TRANSPOSE_CCLOCK_FLIP:
+ s->orientation = kCGImagePropertyOrientationLeftMirrored;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CCLOCK:
+ s->orientation = kCGImagePropertyOrientationLeft;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CLOCK:
+ s->orientation = kCGImagePropertyOrientationRight;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_CLOCK_FLIP:
+ s->orientation = kCGImagePropertyOrientationRightMirrored;
+ swap_w_h = 1;
+ break;
+ case TRANSPOSE_REVERSAL:
+ s->orientation = kCGImagePropertyOrientationDown;
+ break;
+ case TRANSPOSE_HFLIP:
+ s->orientation = kCGImagePropertyOrientationUpMirrored;
+ break;
+ case TRANSPOSE_VFLIP:
+ s->orientation = kCGImagePropertyOrientationDownMirrored;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", s->dir);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!swap_w_h)
Expand Down

0 comments on commit d177518

Please sign in to comment.