PR #20975 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20975
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20975.patch


>From e74cabe39e131684a1139cbc128bbfec2241c738 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Sat, 15 Nov 2025 23:58:14 +0800
Subject: [PATCH 1/3] doc/filters: add section for VideoToolbox filter

Move scale_vt and transpose_vt to this section. transpose_vt was
incorrectly placed in the Vulkan section previously.

(cherry picked from commit 925282fafcf844eb9ea9095a80eba81009d85b3f)
---
 doc/filters.texi | 169 ++++++++++++++++++++++++++++-------------------
 1 file changed, 100 insertions(+), 69 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 0b0663cd9d..f0962755df 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -21294,27 +21294,6 @@ If the specified expression is not valid, it is kept 
at its current
 value.
 @end table
 
-@section scale_vt
-
-Scale and convert the color parameters using VTPixelTransferSession.
-
-The filter accepts the following options:
-@table @option
-@item w
-@item h
-Set the output video dimension expression. Default value is the input 
dimension.
-
-@item color_matrix
-Set the output colorspace matrix.
-
-@item color_primaries
-Set the output color primaries.
-
-@item color_transfer
-Set the output transfer characteristics.
-
-@end table
-
 @section scharr
 Apply scharr operator to input video stream.
 
@@ -28730,6 +28709,106 @@ 
drawbox=x=-t:y=0.5*(ih-iw/2.4)-t:w=iw+t*2:h=iw/2.4+t*2:t=2:c=red
 
 @c man end VAAPI VIDEO FILTERS
 
+@chapter VideoToolbox Video Filters
+@c man begin VIDEOTOOLBOX VIDEO FILTERS
+
+Below is a description of the currently available VideoToolbox video filters.
+
+VideoToolbox filter depends on VideoToolbox framework, and is auto detected
+when building ffmpeg for an Apple platform such as macOS. Add
+@code{--enable-videotoolbox} to configure if autodetect is disabled.
+
+@section scale_vt
+
+Scale and convert the color parameters using VTPixelTransferSession.
+
+The filter accepts the following options:
+@table @option
+@item w
+@item h
+Set the output video dimension expression. Default value is the input 
dimension.
+
+@item color_matrix
+Set the output colorspace matrix.
+
+@item color_primaries
+Set the output color primaries.
+
+@item color_transfer
+Set the output transfer characteristics.
+
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Perform HDR to SDR conversion, and scale to half size of input
+@example
+ffmpeg -hwaccel videotoolbox \
+        -hwaccel_output_format videotoolbox_vld \
+        -i hdr.mov \
+        -c:v hevc_videotoolbox \
+        -profile:v main \
+        -b:v 3M \
+        -vf 
scale_vt=w=iw/2:h=ih/2:color_matrix=bt709:color_primaries=bt709:color_transfer=bt709
 \
+        -c:a copy \
+        -tag:v hvc1 \
+        sdr.mp4
+@end example
+
+@end itemize
+
+@section transpose_vt
+
+Transpose rows with columns in the input video and optionally flip it.
+For more in depth examples see the @ref{transpose} video filter, which shares 
mostly the same options.
+
+It accepts the following parameters:
+
+@table @option
+
+@item dir
+Specify the transposition direction.
+
+Can assume the following values:
+@table @samp
+@item cclock_flip
+Rotate by 90 degrees counterclockwise and vertically flip. (default)
+
+@item clock
+Rotate by 90 degrees clockwise.
+
+@item cclock
+Rotate by 90 degrees counterclockwise.
+
+@item clock_flip
+Rotate by 90 degrees clockwise and vertically flip.
+
+@item hflip
+Flip the input video horizontally.
+
+@item vflip
+Flip the input video vertically.
+
+@end table
+
+@item passthrough
+Do not apply the transposition if the input geometry matches the one
+specified by the specified value. It accepts the following values:
+@table @samp
+@item none
+Always apply transposition. (default)
+@item portrait
+Preserve portrait geometry (when @var{height} >= @var{width}).
+@item landscape
+Preserve landscape geometry (when @var{width} >= @var{height}).
+@end table
+
+@end table
+
+@c man end VIDEOTOOLBOX VIDEO FILTERS
+
 @chapter Vulkan Video Filters
 @c man begin VULKAN VIDEO FILTERS
 
@@ -29031,54 +29110,6 @@ Default value is @code{0}.
 
 @end table
 
-@section transpose_vt
-
-Transpose rows with columns in the input video and optionally flip it.
-For more in depth examples see the @ref{transpose} video filter, which shares 
mostly the same options.
-
-It accepts the following parameters:
-
-@table @option
-
-@item dir
-Specify the transposition direction.
-
-Can assume the following values:
-@table @samp
-@item cclock_flip
-Rotate by 90 degrees counterclockwise and vertically flip. (default)
-
-@item clock
-Rotate by 90 degrees clockwise.
-
-@item cclock
-Rotate by 90 degrees counterclockwise.
-
-@item clock_flip
-Rotate by 90 degrees clockwise and vertically flip.
-
-@item hflip
-Flip the input video horizontally.
-
-@item vflip
-Flip the input video vertically.
-
-@end table
-
-@item passthrough
-Do not apply the transposition if the input geometry matches the one
-specified by the specified value. It accepts the following values:
-@table @samp
-@item none
-Always apply transposition. (default)
-@item portrait
-Preserve portrait geometry (when @var{height} >= @var{width}).
-@item landscape
-Preserve landscape geometry (when @var{width} >= @var{height}).
-@end table
-
-@end table
-
 @section transpose_vulkan
 
 Transpose rows with columns in the input video and optionally flip it.
-- 
2.49.1


>From 5d72d77d730ec9451edb6487aeb344e8f3c793b9 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 18 Nov 2025 12:46:13 +0800
Subject: [PATCH 2/3] avcodec/videotoolboxenc: improve Lock/Unlock BaseAddress
 error handling

1. Fix continue after CVPixelBufferLockBaseAddress.
2. Remove redundant "Error: " in error message.

(cherry picked from commit 0da15c93c8411f435b37ab7d504a650ee881cb0f)
---
 libavcodec/videotoolboxenc.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c
index 729072c0b9..c9c4014a6b 100644
--- a/libavcodec/videotoolboxenc.c
+++ b/libavcodec/videotoolboxenc.c
@@ -2414,12 +2414,8 @@ static int copy_avframe_to_pixel_buffer(AVCodecContext   
*avctx,
 
     status = CVPixelBufferLockBaseAddress(cv_img, 0);
     if (status) {
-        av_log(
-            avctx,
-            AV_LOG_ERROR,
-            "Error: Could not lock base address of CVPixelBuffer: %d.\n",
-            status
-        );
+        av_log(avctx, AV_LOG_ERROR, "Could not lock base address of 
CVPixelBuffer: %d.\n", status);
+        return AVERROR_EXTERNAL;
     }
 
     if (CVPixelBufferIsPlanar(cv_img)) {
@@ -2481,7 +2477,7 @@ static int copy_avframe_to_pixel_buffer(AVCodecContext   
*avctx,
 
     status = CVPixelBufferUnlockBaseAddress(cv_img, 0);
     if (status) {
-        av_log(avctx, AV_LOG_ERROR, "Error: Could not unlock CVPixelBuffer 
base address: %d.\n", status);
+        av_log(avctx, AV_LOG_ERROR, "Could not unlock CVPixelBuffer base 
address: %d.\n", status);
         return AVERROR_EXTERNAL;
     }
 
-- 
2.49.1


>From 84dedda9246a064e97ccacbc82d553c1ca4558ce Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 18 Nov 2025 11:02:59 +0800
Subject: [PATCH 3/3] avcodec/videotoolboxenc: fix crash with negative linesize

(cherry picked from commit 7049df14c83a89e2d32eb138feeb692170d24dc5)
---
 libavcodec/videotoolboxenc.c | 196 ++++++-----------------------------
 1 file changed, 31 insertions(+), 165 deletions(-)

diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c
index c9c4014a6b..ef32c81278 100644
--- a/libavcodec/videotoolboxenc.c
+++ b/libavcodec/videotoolboxenc.c
@@ -28,6 +28,7 @@
 #include "libavutil/opt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
+#include "libavutil/imgutils.h"
 #include "libavcodec/avcodec.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/hwcontext_videotoolbox.h"
@@ -2328,89 +2329,20 @@ static int vtenc_cm_to_avpacket(
     return 0;
 }
 
-/*
- * contiguous_buf_size is 0 if not contiguous, and the size of the buffer
- * containing all planes if so.
- */
-static int get_cv_pixel_info(
-    AVCodecContext *avctx,
-    const AVFrame  *frame,
-    int            *color,
-    int            *plane_count,
-    size_t         *widths,
-    size_t         *heights,
-    size_t         *strides,
-    size_t         *contiguous_buf_size)
-{
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt);
-    VTEncContext *vtctx = avctx->priv_data;
-    int av_format       = frame->format;
-    int av_color_range  = avctx->color_range;
-    int i;
-    int range_guessed;
-    int status;
-
-    if (!desc)
-        return AVERROR(EINVAL);
-
-    status = get_cv_pixel_format(avctx, av_format, av_color_range, color, 
&range_guessed);
-    if (status)
-        return status;
-
-    if (range_guessed) {
-        if (!vtctx->warned_color_range) {
-            vtctx->warned_color_range = true;
-            av_log(avctx,
-                   AV_LOG_WARNING,
-                   "Color range not set for %s. Using MPEG range.\n",
-                   av_get_pix_fmt_name(av_format));
-        }
-    }
-
-    *plane_count = av_pix_fmt_count_planes(avctx->pix_fmt);
-
-    for (i = 0; i < desc->nb_components; i++) {
-        int p = desc->comp[i].plane;
-        bool hasAlpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA);
-        bool isAlpha = hasAlpha && (p + 1 == *plane_count);
-        bool isChroma = (p != 0) && !isAlpha;
-        int shiftw = isChroma ? desc->log2_chroma_w : 0;
-        int shifth = isChroma ? desc->log2_chroma_h : 0;
-        widths[p]  = (avctx->width  + ((1 << shiftw) >> 1)) >> shiftw;
-        heights[p] = (avctx->height + ((1 << shifth) >> 1)) >> shifth;
-        strides[p] = frame->linesize[p];
-    }
-
-    *contiguous_buf_size = 0;
-    for (i = 0; i < *plane_count; i++) {
-        if (i < *plane_count - 1 &&
-            frame->data[i] + strides[i] * heights[i] != frame->data[i + 1]) {
-            *contiguous_buf_size = 0;
-            break;
-        }
-
-        *contiguous_buf_size += strides[i] * heights[i];
-    }
-
-    return 0;
-}
-
-//Not used on OSX - frame is never copied.
 static int copy_avframe_to_pixel_buffer(AVCodecContext   *avctx,
                                         const AVFrame    *frame,
-                                        CVPixelBufferRef cv_img,
-                                        const size_t     *plane_strides,
-                                        const size_t     *plane_rows)
+                                        CVPixelBufferRef cv_img)
 {
-    int i, j;
-    size_t plane_count;
     int status;
-    int rows;
-    int src_stride;
-    int dst_stride;
-    uint8_t *src_addr;
-    uint8_t *dst_addr;
-    size_t copy_bytes;
+
+    int num_planes = av_pix_fmt_count_planes(frame->format);
+    size_t num_cv_plane = CVPixelBufferIsPlanar(cv_img) ?
+                          CVPixelBufferGetPlaneCount(cv_img) : 1;
+    if (num_planes != num_cv_plane) {
+        av_log(avctx, AV_LOG_ERROR,
+               "Different number of planes in AVFrame and CVPixelBuffer.\n");
+        return AVERROR_BUG;
+    }
 
     status = CVPixelBufferLockBaseAddress(cv_img, 0);
     if (status) {
@@ -2418,62 +2350,14 @@ static int copy_avframe_to_pixel_buffer(AVCodecContext  
 *avctx,
         return AVERROR_EXTERNAL;
     }
 
-    if (CVPixelBufferIsPlanar(cv_img)) {
-        plane_count = CVPixelBufferGetPlaneCount(cv_img);
-        for (i = 0; frame->data[i]; i++) {
-            if (i == plane_count) {
-                CVPixelBufferUnlockBaseAddress(cv_img, 0);
-                av_log(avctx,
-                    AV_LOG_ERROR,
-                    "Error: different number of planes in AVFrame and 
CVPixelBuffer.\n"
-                );
-
-                return AVERROR_EXTERNAL;
-            }
-
-            dst_addr = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(cv_img, i);
-            src_addr = (uint8_t*)frame->data[i];
-            dst_stride = CVPixelBufferGetBytesPerRowOfPlane(cv_img, i);
-            src_stride = plane_strides[i];
-            rows = plane_rows[i];
-
-            if (dst_stride == src_stride) {
-                memcpy(dst_addr, src_addr, src_stride * rows);
-            } else {
-                copy_bytes = dst_stride < src_stride ? dst_stride : src_stride;
-
-                for (j = 0; j < rows; j++) {
-                    memcpy(dst_addr + j * dst_stride, src_addr + j * 
src_stride, copy_bytes);
-                }
-            }
-        }
-    } else {
-        if (frame->data[1]) {
-            CVPixelBufferUnlockBaseAddress(cv_img, 0);
-            av_log(avctx,
-                AV_LOG_ERROR,
-                "Error: different number of planes in AVFrame and non-planar 
CVPixelBuffer.\n"
-            );
-
-            return AVERROR_EXTERNAL;
-        }
-
-        dst_addr = (uint8_t*)CVPixelBufferGetBaseAddress(cv_img);
-        src_addr = (uint8_t*)frame->data[0];
-        dst_stride = CVPixelBufferGetBytesPerRow(cv_img);
-        src_stride = plane_strides[0];
-        rows = plane_rows[0];
-
-        if (dst_stride == src_stride) {
-            memcpy(dst_addr, src_addr, src_stride * rows);
-        } else {
-            copy_bytes = dst_stride < src_stride ? dst_stride : src_stride;
-
-            for (j = 0; j < rows; j++) {
-                memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, 
copy_bytes);
-            }
-        }
+    int dst_stride[4] = {0};
+    uint8_t *dst_addr[4] = {0};
+    for (int i = 0; i < num_planes; i++) {
+        dst_addr[i] = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(cv_img, i);
+        dst_stride[i] = CVPixelBufferGetBytesPerRowOfPlane(cv_img, i);
     }
+    av_image_copy2(dst_addr, dst_stride, frame->data, frame->linesize,
+                   frame->format, frame->width, frame->height);
 
     status = CVPixelBufferUnlockBaseAddress(cv_img, 0);
     if (status) {
@@ -2489,13 +2373,7 @@ static int create_cv_pixel_buffer(AVCodecContext   
*avctx,
                                   CVPixelBufferRef *cv_img,
                                   BufNode          *node)
 {
-    int plane_count;
-    int color;
-    size_t widths [AV_NUM_DATA_POINTERS];
-    size_t heights[AV_NUM_DATA_POINTERS];
-    size_t strides[AV_NUM_DATA_POINTERS];
     int status;
-    size_t contiguous_buf_size;
     CVPixelBufferPoolRef pix_buf_pool;
     VTEncContext* vtctx = avctx->priv_data;
 
@@ -2515,33 +2393,21 @@ static int create_cv_pixel_buffer(AVCodecContext   
*avctx,
         return 0;
     }
 
-    memset(widths,  0, sizeof(widths));
-    memset(heights, 0, sizeof(heights));
-    memset(strides, 0, sizeof(strides));
-
-    status = get_cv_pixel_info(
-        avctx,
-        frame,
-        &color,
-        &plane_count,
-        widths,
-        heights,
-        strides,
-        &contiguous_buf_size
-    );
-
+    int range_guessed;
+    status = get_cv_pixel_format(avctx, frame->format, avctx->color_range,
+                                 &(int) {0}, &range_guessed);
     if (status) {
-        av_log(
-            avctx,
-            AV_LOG_ERROR,
-            "Error: Cannot convert format %d color_range %d: %d\n",
-            frame->format,
-            frame->color_range,
-            status
-        );
-
+        av_log(avctx, AV_LOG_ERROR, "Cannot convert format %d color_range %d: 
%d\n",
+            frame->format, frame->color_range, status);
         return status;
     }
+    if (range_guessed) {
+        if (!vtctx->warned_color_range) {
+            vtctx->warned_color_range = true;
+            av_log(avctx, AV_LOG_WARNING, "Color range not set for %s. Using 
MPEG range.\n",
+                   av_get_pix_fmt_name(frame->format));
+        }
+    }
 
     pix_buf_pool = VTCompressionSessionGetPixelBufferPool(vtctx->session);
     if (!pix_buf_pool) {
@@ -2578,7 +2444,7 @@ static int create_cv_pixel_buffer(AVCodecContext   *avctx,
         return AVERROR_EXTERNAL;
     }
 
-    status = copy_avframe_to_pixel_buffer(avctx, frame, *cv_img, strides, 
heights);
+    status = copy_avframe_to_pixel_buffer(avctx, frame, *cv_img);
     if (status) {
         CFRelease(*cv_img);
         *cv_img = NULL;
-- 
2.49.1

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to