2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #include <VideoToolbox/VideoToolbox.h>
27 #include "buffer_internal.h"
29 #include "hwcontext.h"
30 #include "hwcontext_internal.h"
31 #include "hwcontext_videotoolbox.h"
36 typedef struct VTFramesContext
{
38 * The public AVVTFramesContext. See hwcontext_videotoolbox.h for it.
41 CVPixelBufferPoolRef pool
;
47 enum AVPixelFormat pix_fmt
;
49 { kCVPixelFormatType_420YpCbCr8Planar
, false, AV_PIX_FMT_YUV420P
},
50 { kCVPixelFormatType_420YpCbCr8PlanarFullRange
, true, AV_PIX_FMT_YUV420P
},
51 { kCVPixelFormatType_422YpCbCr8
, false, AV_PIX_FMT_UYVY422
},
52 { kCVPixelFormatType_32BGRA
, true, AV_PIX_FMT_BGRA
},
53 #ifdef kCFCoreFoundationVersionNumber10_7
54 { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
, false, AV_PIX_FMT_NV12
},
55 { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
, true, AV_PIX_FMT_NV12
},
56 { kCVPixelFormatType_4444AYpCbCr8
, false, AV_PIX_FMT_AYUV
},
57 { kCVPixelFormatType_4444AYpCbCr16
, false, AV_PIX_FMT_AYUV64
},
59 #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
60 { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange
, false, AV_PIX_FMT_P010
},
61 { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange
, true, AV_PIX_FMT_P010
},
63 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE
64 { kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange
, false, AV_PIX_FMT_NV16
},
65 { kCVPixelFormatType_422YpCbCr8BiPlanarFullRange
, true, AV_PIX_FMT_NV16
},
67 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE
68 { kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange
, false, AV_PIX_FMT_P210
},
69 { kCVPixelFormatType_422YpCbCr10BiPlanarFullRange
, true, AV_PIX_FMT_P210
},
71 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE
72 { kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange
, false, AV_PIX_FMT_P216
},
74 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE
75 { kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange
, false, AV_PIX_FMT_NV24
},
76 { kCVPixelFormatType_444YpCbCr8BiPlanarFullRange
, true, AV_PIX_FMT_NV24
},
78 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE
79 { kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange
, false, AV_PIX_FMT_P410
},
80 { kCVPixelFormatType_444YpCbCr10BiPlanarFullRange
, true, AV_PIX_FMT_P410
},
82 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE
83 { kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange
, false, AV_PIX_FMT_P416
},
87 static const enum AVPixelFormat supported_formats
[] = {
88 #ifdef kCFCoreFoundationVersionNumber10_7
95 #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
98 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE
101 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE
104 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE
107 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE
110 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE
113 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE
119 static int vt_frames_get_constraints(AVHWDeviceContext
*ctx
,
120 const void *hwconfig
,
121 AVHWFramesConstraints
*constraints
)
125 constraints
->valid_sw_formats
= av_malloc_array(FF_ARRAY_ELEMS(supported_formats
) + 1,
126 sizeof(*constraints
->valid_sw_formats
));
127 if (!constraints
->valid_sw_formats
)
128 return AVERROR(ENOMEM
);
130 for (i
= 0; i
< FF_ARRAY_ELEMS(supported_formats
); i
++)
131 constraints
->valid_sw_formats
[i
] = supported_formats
[i
];
132 constraints
->valid_sw_formats
[FF_ARRAY_ELEMS(supported_formats
)] = AV_PIX_FMT_NONE
;
134 constraints
->valid_hw_formats
= av_malloc_array(2, sizeof(*constraints
->valid_hw_formats
));
135 if (!constraints
->valid_hw_formats
)
136 return AVERROR(ENOMEM
);
138 constraints
->valid_hw_formats
[0] = AV_PIX_FMT_VIDEOTOOLBOX
;
139 constraints
->valid_hw_formats
[1] = AV_PIX_FMT_NONE
;
144 enum AVPixelFormat
av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt
)
147 for (i
= 0; i
< FF_ARRAY_ELEMS(cv_pix_fmts
); i
++) {
148 if (cv_pix_fmts
[i
].cv_fmt
== cv_fmt
)
149 return cv_pix_fmts
[i
].pix_fmt
;
151 return AV_PIX_FMT_NONE
;
154 static uint32_t vt_format_from_pixfmt(enum AVPixelFormat pix_fmt
,
155 enum AVColorRange range
)
157 for (int i
= 0; i
< FF_ARRAY_ELEMS(cv_pix_fmts
); i
++) {
158 if (cv_pix_fmts
[i
].pix_fmt
== pix_fmt
) {
159 int full_range
= (range
== AVCOL_RANGE_JPEG
);
161 // Don't care if unspecified
162 if (range
== AVCOL_RANGE_UNSPECIFIED
)
163 return cv_pix_fmts
[i
].cv_fmt
;
165 if (cv_pix_fmts
[i
].full_range
== full_range
)
166 return cv_pix_fmts
[i
].cv_fmt
;
173 uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt
)
175 return av_map_videotoolbox_format_from_pixfmt2(pix_fmt
, false);
178 uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt
, bool full_range
)
180 return vt_format_from_pixfmt(pix_fmt
, full_range
? AVCOL_RANGE_JPEG
: AVCOL_RANGE_MPEG
);
183 static int vt_pool_alloc(AVHWFramesContext
*ctx
)
185 VTFramesContext
*fctx
= ctx
->hwctx
;
186 AVVTFramesContext
*hw_ctx
= &fctx
->p
;
188 CFNumberRef w
, h
, pixfmt
;
190 CFMutableDictionaryRef attributes
, iosurface_properties
;
192 attributes
= CFDictionaryCreateMutable(
195 &kCFTypeDictionaryKeyCallBacks
,
196 &kCFTypeDictionaryValueCallBacks
);
198 cv_pixfmt
= vt_format_from_pixfmt(ctx
->sw_format
, hw_ctx
->color_range
);
199 pixfmt
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &cv_pixfmt
);
200 CFDictionarySetValue(
202 kCVPixelBufferPixelFormatTypeKey
,
206 iosurface_properties
= CFDictionaryCreateMutable(
209 &kCFTypeDictionaryKeyCallBacks
,
210 &kCFTypeDictionaryValueCallBacks
);
211 CFDictionarySetValue(attributes
, kCVPixelBufferIOSurfacePropertiesKey
, iosurface_properties
);
212 CFRelease(iosurface_properties
);
214 w
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ctx
->width
);
215 h
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ctx
->height
);
216 CFDictionarySetValue(attributes
, kCVPixelBufferWidthKey
, w
);
217 CFDictionarySetValue(attributes
, kCVPixelBufferHeightKey
, h
);
221 err
= CVPixelBufferPoolCreate(
226 CFRelease(attributes
);
228 if (err
== kCVReturnSuccess
)
231 av_log(ctx
, AV_LOG_ERROR
, "Error creating CVPixelBufferPool: %d\n", err
);
232 return AVERROR_EXTERNAL
;
235 static void videotoolbox_buffer_release(void *opaque
, uint8_t *data
)
237 CVPixelBufferRelease((CVPixelBufferRef
)data
);
240 static AVBufferRef
*vt_pool_alloc_buffer(void *opaque
, size_t size
)
242 CVPixelBufferRef pixbuf
;
245 AVHWFramesContext
*ctx
= opaque
;
246 VTFramesContext
*fctx
= ctx
->hwctx
;
248 err
= CVPixelBufferPoolCreatePixelBuffer(
253 if (err
!= kCVReturnSuccess
) {
254 av_log(ctx
, AV_LOG_ERROR
, "Failed to create pixel buffer from pool: %d\n", err
);
258 buf
= av_buffer_create((uint8_t *)pixbuf
, size
,
259 videotoolbox_buffer_release
, NULL
, 0);
261 CVPixelBufferRelease(pixbuf
);
267 static void vt_frames_uninit(AVHWFramesContext
*ctx
)
269 VTFramesContext
*fctx
= ctx
->hwctx
;
271 CVPixelBufferPoolRelease(fctx
->pool
);
276 static int vt_frames_init(AVHWFramesContext
*ctx
)
280 for (i
= 0; i
< FF_ARRAY_ELEMS(supported_formats
); i
++) {
281 if (ctx
->sw_format
== supported_formats
[i
])
284 if (i
== FF_ARRAY_ELEMS(supported_formats
)) {
285 av_log(ctx
, AV_LOG_ERROR
, "Pixel format '%s' is not supported\n",
286 av_get_pix_fmt_name(ctx
->sw_format
));
287 return AVERROR(ENOSYS
);
291 ffhwframesctx(ctx
)->pool_internal
= av_buffer_pool_init2(
292 sizeof(CVPixelBufferRef
), ctx
, vt_pool_alloc_buffer
, NULL
);
293 if (!ffhwframesctx(ctx
)->pool_internal
)
294 return AVERROR(ENOMEM
);
297 ret
= vt_pool_alloc(ctx
);
304 static int vt_get_buffer(AVHWFramesContext
*ctx
, AVFrame
*frame
)
306 frame
->buf
[0] = av_buffer_pool_get(ctx
->pool
);
308 return AVERROR(ENOMEM
);
310 frame
->data
[3] = frame
->buf
[0]->data
;
311 frame
->format
= AV_PIX_FMT_VIDEOTOOLBOX
;
312 frame
->width
= ctx
->width
;
313 frame
->height
= ctx
->height
;
318 static int vt_transfer_get_formats(AVHWFramesContext
*ctx
,
319 enum AVHWFrameTransferDirection dir
,
320 enum AVPixelFormat
**formats
)
322 enum AVPixelFormat
*fmts
= av_malloc_array(2, sizeof(*fmts
));
324 return AVERROR(ENOMEM
);
326 fmts
[0] = ctx
->sw_format
;
327 fmts
[1] = AV_PIX_FMT_NONE
;
333 static void vt_unmap(AVHWFramesContext
*ctx
, HWMapDescriptor
*hwmap
)
335 CVPixelBufferRef pixbuf
= (CVPixelBufferRef
)hwmap
->source
->data
[3];
337 CVPixelBufferUnlockBaseAddress(pixbuf
, (uintptr_t)hwmap
->priv
);
340 static int vt_pixbuf_set_par(void *log_ctx
,
341 CVPixelBufferRef pixbuf
, const AVFrame
*src
)
343 CFMutableDictionaryRef par
= NULL
;
344 CFNumberRef num
= NULL
, den
= NULL
;
345 AVRational avpar
= src
->sample_aspect_ratio
;
347 if (avpar
.num
== 0) {
348 CVBufferRemoveAttachment(pixbuf
, kCVImageBufferPixelAspectRatioKey
);
352 av_reduce(&avpar
.num
, &avpar
.den
,
353 avpar
.num
, avpar
.den
,
356 num
= CFNumberCreate(kCFAllocatorDefault
,
360 den
= CFNumberCreate(kCFAllocatorDefault
,
364 par
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
366 &kCFCopyStringDictionaryKeyCallBacks
,
367 &kCFTypeDictionaryValueCallBacks
);
369 if (!par
|| !num
|| !den
) {
370 if (par
) CFRelease(par
);
371 if (num
) CFRelease(num
);
372 if (den
) CFRelease(den
);
373 return AVERROR(ENOMEM
);
376 CFDictionarySetValue(
378 kCVImageBufferPixelAspectRatioHorizontalSpacingKey
,
380 CFDictionarySetValue(
382 kCVImageBufferPixelAspectRatioVerticalSpacingKey
,
385 CVBufferSetAttachment(
387 kCVImageBufferPixelAspectRatioKey
,
389 kCVAttachmentMode_ShouldPropagate
399 CFStringRef
av_map_videotoolbox_chroma_loc_from_av(enum AVChromaLocation loc
)
402 case AVCHROMA_LOC_LEFT
:
403 return kCVImageBufferChromaLocation_Left
;
404 case AVCHROMA_LOC_CENTER
:
405 return kCVImageBufferChromaLocation_Center
;
406 case AVCHROMA_LOC_TOP
:
407 return kCVImageBufferChromaLocation_Top
;
408 case AVCHROMA_LOC_BOTTOM
:
409 return kCVImageBufferChromaLocation_Bottom
;
410 case AVCHROMA_LOC_TOPLEFT
:
411 return kCVImageBufferChromaLocation_TopLeft
;
412 case AVCHROMA_LOC_BOTTOMLEFT
:
413 return kCVImageBufferChromaLocation_BottomLeft
;
419 static int vt_pixbuf_set_chromaloc(void *log_ctx
,
420 CVPixelBufferRef pixbuf
, const AVFrame
*src
)
422 CFStringRef loc
= av_map_videotoolbox_chroma_loc_from_av(src
->chroma_location
);
425 CVBufferSetAttachment(
427 kCVImageBufferChromaLocationTopFieldKey
,
429 kCVAttachmentMode_ShouldPropagate
);
431 CVBufferRemoveAttachment(
433 kCVImageBufferChromaLocationTopFieldKey
);
438 CFStringRef
av_map_videotoolbox_color_matrix_from_av(enum AVColorSpace space
)
441 case AVCOL_SPC_BT2020_CL
:
442 case AVCOL_SPC_BT2020_NCL
:
443 #if HAVE_KCVIMAGEBUFFERYCBCRMATRIX_ITU_R_2020
444 if (__builtin_available(macOS
10.11, iOS
9, *))
445 return kCVImageBufferYCbCrMatrix_ITU_R_2020
;
447 return CFSTR("ITU_R_2020");
448 case AVCOL_SPC_BT470BG
:
449 case AVCOL_SPC_SMPTE170M
:
450 return kCVImageBufferYCbCrMatrix_ITU_R_601_4
;
451 case AVCOL_SPC_BT709
:
452 return kCVImageBufferYCbCrMatrix_ITU_R_709_2
;
453 case AVCOL_SPC_SMPTE240M
:
454 return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995
;
456 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
457 if (__builtin_available(macOS
10.13, iOS
11, tvOS
11, watchOS
4, *))
458 return CVYCbCrMatrixGetStringForIntegerCodePoint(space
);
460 case AVCOL_SPC_UNSPECIFIED
:
465 CFStringRef
av_map_videotoolbox_color_primaries_from_av(enum AVColorPrimaries pri
)
468 case AVCOL_PRI_BT2020
:
469 #if HAVE_KCVIMAGEBUFFERCOLORPRIMARIES_ITU_R_2020
470 if (__builtin_available(macOS
10.11, iOS
9, *))
471 return kCVImageBufferColorPrimaries_ITU_R_2020
;
473 return CFSTR("ITU_R_2020");
474 case AVCOL_PRI_BT709
:
475 return kCVImageBufferColorPrimaries_ITU_R_709_2
;
476 case AVCOL_PRI_SMPTE170M
:
477 return kCVImageBufferColorPrimaries_SMPTE_C
;
478 case AVCOL_PRI_BT470BG
:
479 return kCVImageBufferColorPrimaries_EBU_3213
;
481 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
482 if (__builtin_available(macOS
10.13, iOS
11, tvOS
11, watchOS
4, *))
483 return CVColorPrimariesGetStringForIntegerCodePoint(pri
);
485 case AVCOL_PRI_UNSPECIFIED
:
490 CFStringRef
av_map_videotoolbox_color_trc_from_av(enum AVColorTransferCharacteristic trc
)
494 case AVCOL_TRC_SMPTE2084
:
495 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_2084_PQ
496 if (__builtin_available(macOS
10.13, iOS
11, *))
497 return kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ
;
499 return CFSTR("SMPTE_ST_2084_PQ");
500 case AVCOL_TRC_BT2020_10
:
501 case AVCOL_TRC_BT2020_12
:
502 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2020
503 if (__builtin_available(macOS
10.11, iOS
9, *))
504 return kCVImageBufferTransferFunction_ITU_R_2020
;
506 return CFSTR("ITU_R_2020");
507 case AVCOL_TRC_BT709
:
508 return kCVImageBufferTransferFunction_ITU_R_709_2
;
509 case AVCOL_TRC_SMPTE240M
:
510 return kCVImageBufferTransferFunction_SMPTE_240M_1995
;
511 case AVCOL_TRC_SMPTE428
:
512 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_428_1
513 if (__builtin_available(macOS
10.12, iOS
10, *))
514 return kCVImageBufferTransferFunction_SMPTE_ST_428_1
;
516 return CFSTR("SMPTE_ST_428_1");
517 case AVCOL_TRC_ARIB_STD_B67
:
518 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
519 if (__builtin_available(macOS
10.13, iOS
11, *))
520 return kCVImageBufferTransferFunction_ITU_R_2100_HLG
;
522 return CFSTR("ITU_R_2100_HLG");
523 case AVCOL_TRC_GAMMA22
:
524 return kCVImageBufferTransferFunction_UseGamma
;
525 case AVCOL_TRC_GAMMA28
:
526 return kCVImageBufferTransferFunction_UseGamma
;
528 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
529 if (__builtin_available(macOS
10.13, iOS
11, tvOS
11, watchOS
4, *))
530 return CVTransferFunctionGetStringForIntegerCodePoint(trc
);
532 case AVCOL_TRC_UNSPECIFIED
:
538 * Copy all attachments for the specified mode from the given buffer.
540 static CFDictionaryRef
vt_cv_buffer_copy_attachments(CVBufferRef buffer
,
541 CVAttachmentMode attachment_mode
)
543 // Check that our SDK is at least macOS 12 / iOS 15 / tvOS 15
544 #if (TARGET_OS_OSX && defined(__MAC_12_0) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_12_0) || \
545 (TARGET_OS_IOS && defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) || \
546 (TARGET_OS_TV && defined(__TVOS_15_0) && __TV_OS_VERSION_MAX_ALLOWED >= __TVOS_15_0)
547 // On recent enough versions, just use the respective API
548 if (__builtin_available(macOS
12.0, iOS
15.0, tvOS
15.0, *))
549 return CVBufferCopyAttachments(buffer
, attachment_mode
);
552 // Check that the target is lower than macOS 12 / iOS 15 / tvOS 15
553 // else this would generate a deprecation warning and anyway never run because
554 // the runtime availability check above would be always true.
555 #if (TARGET_OS_OSX && (!defined(__MAC_12_0) || __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_12_0)) || \
556 (TARGET_OS_IOS && (!defined(__IPHONE_15_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0)) || \
557 (TARGET_OS_TV && (!defined(__TVOS_15_0) || __TV_OS_VERSION_MIN_REQUIRED < __TVOS_15_0))
558 // Fallback on SDKs or runtime versions < macOS 12 / iOS 15 / tvOS 15
559 CFDictionaryRef dict
= CVBufferGetAttachments(buffer
, attachment_mode
);
560 return (dict
) ? CFDictionaryCreateCopy(NULL
, dict
) : NULL
;
562 return NULL
; // Impossible, just make the compiler happy
566 static int vt_pixbuf_set_colorspace(void *log_ctx
,
567 CVPixelBufferRef pixbuf
, const AVFrame
*src
)
569 CGColorSpaceRef colorspace
= NULL
;
570 CFStringRef colormatrix
= NULL
, colorpri
= NULL
, colortrc
= NULL
;
573 colormatrix
= av_map_videotoolbox_color_matrix_from_av(src
->colorspace
);
575 CVBufferSetAttachment(pixbuf
, kCVImageBufferYCbCrMatrixKey
,
576 colormatrix
, kCVAttachmentMode_ShouldPropagate
);
578 CVBufferRemoveAttachment(pixbuf
, kCVImageBufferYCbCrMatrixKey
);
579 if (src
->colorspace
!= AVCOL_SPC_UNSPECIFIED
&& src
->colorspace
!= AVCOL_SPC_RGB
)
580 av_log(log_ctx
, AV_LOG_WARNING
,
581 "Color space %s is not supported.\n",
582 av_color_space_name(src
->colorspace
));
585 colorpri
= av_map_videotoolbox_color_primaries_from_av(src
->color_primaries
);
587 CVBufferSetAttachment(pixbuf
, kCVImageBufferColorPrimariesKey
,
588 colorpri
, kCVAttachmentMode_ShouldPropagate
);
590 CVBufferRemoveAttachment(pixbuf
, kCVImageBufferColorPrimariesKey
);
591 if (src
->color_primaries
!= AVCOL_PRI_UNSPECIFIED
)
592 av_log(log_ctx
, AV_LOG_WARNING
,
593 "Color primaries %s is not supported.\n",
594 av_color_primaries_name(src
->color_primaries
));
597 colortrc
= av_map_videotoolbox_color_trc_from_av(src
->color_trc
);
599 CVBufferSetAttachment(pixbuf
, kCVImageBufferTransferFunctionKey
,
600 colortrc
, kCVAttachmentMode_ShouldPropagate
);
602 CVBufferRemoveAttachment(pixbuf
, kCVImageBufferTransferFunctionKey
);
603 if (src
->color_trc
!= AVCOL_TRC_UNSPECIFIED
)
604 av_log(log_ctx
, AV_LOG_WARNING
,
605 "Color transfer function %s is not supported.\n",
606 av_color_transfer_name(src
->color_trc
));
609 if (src
->color_trc
== AVCOL_TRC_GAMMA22
)
611 else if (src
->color_trc
== AVCOL_TRC_GAMMA28
)
615 CFNumberRef gamma_level
= CFNumberCreate(NULL
, kCFNumberFloat32Type
, &gamma
);
616 CVBufferSetAttachment(pixbuf
, kCVImageBufferGammaLevelKey
,
617 gamma_level
, kCVAttachmentMode_ShouldPropagate
);
618 CFRelease(gamma_level
);
620 CVBufferRemoveAttachment(pixbuf
, kCVImageBufferGammaLevelKey
);
622 #if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED >= 100800) || \
623 (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000)
624 if (__builtin_available(macOS
10.8, iOS
10, *)) {
625 CFDictionaryRef attachments
=
626 vt_cv_buffer_copy_attachments(pixbuf
, kCVAttachmentMode_ShouldPropagate
);
630 CVImageBufferCreateColorSpaceFromAttachments(attachments
);
631 CFRelease(attachments
);
636 // Done outside the above preprocessor code and if's so that
637 // in any case a wrong kCVImageBufferCGColorSpaceKey is removed
638 // if the above code is not used or fails.
640 CVBufferSetAttachment(pixbuf
, kCVImageBufferCGColorSpaceKey
,
641 colorspace
, kCVAttachmentMode_ShouldPropagate
);
642 CFRelease(colorspace
);
644 CVBufferRemoveAttachment(pixbuf
, kCVImageBufferCGColorSpaceKey
);
649 static int vt_pixbuf_set_attachments(void *log_ctx
,
650 CVPixelBufferRef pixbuf
, const AVFrame
*src
)
653 ret
= vt_pixbuf_set_par(log_ctx
, pixbuf
, src
);
656 ret
= vt_pixbuf_set_colorspace(log_ctx
, pixbuf
, src
);
659 ret
= vt_pixbuf_set_chromaloc(log_ctx
, pixbuf
, src
);
665 int av_vt_pixbuf_set_attachments(void *log_ctx
,
666 CVPixelBufferRef pixbuf
, const AVFrame
*src
)
668 return vt_pixbuf_set_attachments(log_ctx
, pixbuf
, src
);
671 static int vt_map_frame(AVHWFramesContext
*ctx
, AVFrame
*dst
, const AVFrame
*src
,
674 CVPixelBufferRef pixbuf
= (CVPixelBufferRef
)src
->data
[3];
675 OSType pixel_format
= CVPixelBufferGetPixelFormatType(pixbuf
);
677 uint32_t map_flags
= 0;
680 enum AVPixelFormat format
;
682 format
= av_map_videotoolbox_format_to_pixfmt(pixel_format
);
683 if (dst
->format
!= format
) {
684 av_log(ctx
, AV_LOG_ERROR
, "Unsupported or mismatching pixel format: %s\n",
685 av_fourcc2str(pixel_format
));
686 return AVERROR_UNKNOWN
;
689 if (CVPixelBufferGetWidth(pixbuf
) != ctx
->width
||
690 CVPixelBufferGetHeight(pixbuf
) != ctx
->height
) {
691 av_log(ctx
, AV_LOG_ERROR
, "Inconsistent frame dimensions.\n");
692 return AVERROR_UNKNOWN
;
695 if (flags
== AV_HWFRAME_MAP_READ
)
696 map_flags
= kCVPixelBufferLock_ReadOnly
;
698 err
= CVPixelBufferLockBaseAddress(pixbuf
, map_flags
);
699 if (err
!= kCVReturnSuccess
) {
700 av_log(ctx
, AV_LOG_ERROR
, "Error locking the pixel buffer.\n");
701 return AVERROR_UNKNOWN
;
704 if (CVPixelBufferIsPlanar(pixbuf
)) {
705 int planes
= CVPixelBufferGetPlaneCount(pixbuf
);
706 for (i
= 0; i
< planes
; i
++) {
707 dst
->data
[i
] = CVPixelBufferGetBaseAddressOfPlane(pixbuf
, i
);
708 dst
->linesize
[i
] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf
, i
);
711 dst
->data
[0] = CVPixelBufferGetBaseAddress(pixbuf
);
712 dst
->linesize
[0] = CVPixelBufferGetBytesPerRow(pixbuf
);
715 ret
= ff_hwframe_map_create(src
->hw_frames_ctx
, dst
, src
, vt_unmap
,
716 (void *)(uintptr_t)map_flags
);
723 CVPixelBufferUnlockBaseAddress(pixbuf
, map_flags
);
727 static int vt_transfer_data_from(AVHWFramesContext
*hwfc
,
728 AVFrame
*dst
, const AVFrame
*src
)
733 if (dst
->width
> hwfc
->width
|| dst
->height
> hwfc
->height
)
734 return AVERROR(EINVAL
);
736 map
= av_frame_alloc();
738 return AVERROR(ENOMEM
);
739 map
->format
= dst
->format
;
741 err
= vt_map_frame(hwfc
, map
, src
, AV_HWFRAME_MAP_READ
);
745 map
->width
= dst
->width
;
746 map
->height
= dst
->height
;
748 err
= av_frame_copy(dst
, map
);
758 static int vt_transfer_data_to(AVHWFramesContext
*hwfc
,
759 AVFrame
*dst
, const AVFrame
*src
)
764 if (src
->width
> hwfc
->width
|| src
->height
> hwfc
->height
)
765 return AVERROR(EINVAL
);
767 map
= av_frame_alloc();
769 return AVERROR(ENOMEM
);
770 map
->format
= src
->format
;
772 err
= vt_map_frame(hwfc
, map
, dst
, AV_HWFRAME_MAP_WRITE
| AV_HWFRAME_MAP_OVERWRITE
);
776 map
->width
= src
->width
;
777 map
->height
= src
->height
;
779 err
= av_frame_copy(map
, src
);
783 err
= vt_pixbuf_set_attachments(hwfc
, (CVPixelBufferRef
)dst
->data
[3], src
);
793 static int vt_map_from(AVHWFramesContext
*hwfc
, AVFrame
*dst
,
794 const AVFrame
*src
, int flags
)
798 if (dst
->format
== AV_PIX_FMT_NONE
)
799 dst
->format
= hwfc
->sw_format
;
800 else if (dst
->format
!= hwfc
->sw_format
)
801 return AVERROR(ENOSYS
);
803 err
= vt_map_frame(hwfc
, dst
, src
, flags
);
807 dst
->width
= src
->width
;
808 dst
->height
= src
->height
;
810 err
= av_frame_copy_props(dst
, src
);
817 static int vt_device_create(AVHWDeviceContext
*ctx
, const char *device
,
818 AVDictionary
*opts
, int flags
)
820 if (device
&& device
[0]) {
821 av_log(ctx
, AV_LOG_ERROR
, "Device selection unsupported.\n");
822 return AVERROR_UNKNOWN
;
828 const HWContextType ff_hwcontext_type_videotoolbox
= {
829 .type
= AV_HWDEVICE_TYPE_VIDEOTOOLBOX
,
830 .name
= "videotoolbox",
832 .frames_hwctx_size
= sizeof(VTFramesContext
),
834 .device_create
= vt_device_create
,
835 .frames_init
= vt_frames_init
,
836 .frames_get_buffer
= vt_get_buffer
,
837 .frames_get_constraints
= vt_frames_get_constraints
,
838 .frames_uninit
= vt_frames_uninit
,
839 .transfer_get_formats
= vt_transfer_get_formats
,
840 .transfer_data_to
= vt_transfer_data_to
,
841 .transfer_data_from
= vt_transfer_data_from
,
842 .map_from
= vt_map_from
,
844 .pix_fmts
= (const enum AVPixelFormat
[]){ AV_PIX_FMT_VIDEOTOOLBOX
, AV_PIX_FMT_NONE
},