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]
