1 /*
2 * This file is part of FFmpeg.
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #include "config.h"
20
21 #include <stdint.h>
22 #include <string.h>
23
24 #include <VideoToolbox/VideoToolbox.h>
25
35
37 /**
38 * The public AVVTFramesContext. See hwcontext_videotoolbox.h for it.
39 */
43
44 static const struct {
53 #ifdef kCFCoreFoundationVersionNumber10_7
54 { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
false,
AV_PIX_FMT_NV12 },
55 { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
true,
AV_PIX_FMT_NV12 },
57 #endif
58 #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
61 #endif
62 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE
63 { kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange,
false,
AV_PIX_FMT_NV16 },
64 { kCVPixelFormatType_422YpCbCr8BiPlanarFullRange,
true,
AV_PIX_FMT_NV16 },
65 #endif
66 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE
67 { kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange,
false,
AV_PIX_FMT_P210 },
68 { kCVPixelFormatType_422YpCbCr10BiPlanarFullRange,
true,
AV_PIX_FMT_P210 },
69 #endif
70 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE
71 { kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange,
false,
AV_PIX_FMT_P216 },
72 #endif
73 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE
74 { kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange,
false,
AV_PIX_FMT_NV24 },
75 { kCVPixelFormatType_444YpCbCr8BiPlanarFullRange,
true,
AV_PIX_FMT_NV24 },
76 #endif
77 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE
78 { kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange,
false,
AV_PIX_FMT_P410 },
79 { kCVPixelFormatType_444YpCbCr10BiPlanarFullRange,
true,
AV_PIX_FMT_P410 },
80 #endif
81 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE
82 { kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange,
false,
AV_PIX_FMT_P416 },
83 #endif
84 };
85
87 #ifdef kCFCoreFoundationVersionNumber10_7
90 #endif
93 #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
95 #endif
96 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE
98 #endif
99 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE
101 #endif
102 #if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE
104 #endif
105 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE
107 #endif
108 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE
110 #endif
111 #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE
113 #endif
115 };
116
118 const void *hwconfig,
120 {
122
127
131
135
138
139 return 0;
140 }
141
143 {
148 }
150 }
151
154 {
158
159 // Don't care if unspecified
162
165 }
166 }
167
168 return 0;
169 }
170
172 {
174 }
175
177 {
179 }
180
182 {
185 CVReturn err;
187 uint32_t cv_pixfmt;
188 CFMutableDictionaryRef attributes, iosurface_properties;
189
190 attributes = CFDictionaryCreateMutable(
192 2,
193 &kCFTypeDictionaryKeyCallBacks,
194 &kCFTypeDictionaryValueCallBacks);
195
197 pixfmt = CFNumberCreate(
NULL, kCFNumberSInt32Type, &cv_pixfmt);
198 CFDictionarySetValue(
199 attributes,
200 kCVPixelBufferPixelFormatTypeKey,
203
204 iosurface_properties = CFDictionaryCreateMutable(
206 0,
207 &kCFTypeDictionaryKeyCallBacks,
208 &kCFTypeDictionaryValueCallBacks);
209 CFDictionarySetValue(attributes, kCVPixelBufferIOSurfacePropertiesKey, iosurface_properties);
210 CFRelease(iosurface_properties);
211
212 w = CFNumberCreate(
NULL, kCFNumberSInt32Type, &
ctx->width);
213 h = CFNumberCreate(
NULL, kCFNumberSInt32Type, &
ctx->height);
214 CFDictionarySetValue(attributes, kCVPixelBufferWidthKey,
w);
215 CFDictionarySetValue(attributes, kCVPixelBufferHeightKey,
h);
218
219 err = CVPixelBufferPoolCreate(
222 attributes,
224 CFRelease(attributes);
225
226 if (err == kCVReturnSuccess)
227 return 0;
228
231 }
232
234 {
235 CVPixelBufferRelease((CVPixelBufferRef)
data);
236 }
237
239 {
240 CVPixelBufferRef pixbuf;
242 CVReturn err;
245
246 err = CVPixelBufferPoolCreatePixelBuffer(
249 &pixbuf
250 );
251 if (err != kCVReturnSuccess) {
254 }
255
258 if (!buf) {
259 CVPixelBufferRelease(pixbuf);
261 }
262 return buf;
263 }
264
266 {
269 CVPixelBufferPoolRelease(fctx->
pool);
271 }
272 }
273
275 {
277
280 break;
281 }
286 }
287
293 }
294
298
299 return 0;
300 }
301
303 {
307
312
313 return 0;
314 }
315
319 {
321 if (!fmts)
323
324 fmts[0] =
ctx->sw_format;
326
328 return 0;
329 }
330
332 {
333 CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->
source->
data[3];
334
335 CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->
priv);
336 }
337
340 {
341 CFMutableDictionaryRef par =
NULL;
344
345 if (avpar.
num == 0) {
346 CVBufferRemoveAttachment(pixbuf, kCVImageBufferPixelAspectRatioKey);
347 return 0;
348 }
349
352 0xFFFFFFFF);
353
354 num = CFNumberCreate(kCFAllocatorDefault,
355 kCFNumberIntType,
357
358 den = CFNumberCreate(kCFAllocatorDefault,
359 kCFNumberIntType,
361
362 par = CFDictionaryCreateMutable(kCFAllocatorDefault,
363 2,
364 &kCFCopyStringDictionaryKeyCallBacks,
365 &kCFTypeDictionaryValueCallBacks);
366
367 if (!par || !num || !den) {
368 if (par) CFRelease(par);
369 if (num) CFRelease(num);
370 if (den) CFRelease(den);
372 }
373
374 CFDictionarySetValue(
375 par,
376 kCVImageBufferPixelAspectRatioHorizontalSpacingKey,
377 num);
378 CFDictionarySetValue(
379 par,
380 kCVImageBufferPixelAspectRatioVerticalSpacingKey,
381 den);
382
383 CVBufferSetAttachment(
384 pixbuf,
385 kCVImageBufferPixelAspectRatioKey,
386 par,
387 kCVAttachmentMode_ShouldPropagate
388 );
389
390 CFRelease(par);
391 CFRelease(num);
392 CFRelease(den);
393
394 return 0;
395 }
396
398 {
399 switch (loc) {
401 return kCVImageBufferChromaLocation_Left;
403 return kCVImageBufferChromaLocation_Center;
405 return kCVImageBufferChromaLocation_Top;
407 return kCVImageBufferChromaLocation_Bottom;
409 return kCVImageBufferChromaLocation_TopLeft;
411 return kCVImageBufferChromaLocation_BottomLeft;
412 default:
414 }
415 }
416
419 {
421
422 if (loc) {
423 CVBufferSetAttachment(
424 pixbuf,
425 kCVImageBufferChromaLocationTopFieldKey,
426 loc,
427 kCVAttachmentMode_ShouldPropagate);
428 } else
429 CVBufferRemoveAttachment(
430 pixbuf,
431 kCVImageBufferChromaLocationTopFieldKey);
432
433 return 0;
434 }
435
437 {
441 #if HAVE_KCVIMAGEBUFFERYCBCRMATRIX_ITU_R_2020
442 if (__builtin_available(macOS 10.11, iOS 9, *))
444 #endif
445 return CFSTR("ITU_R_2020");
448 return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
450 return kCVImageBufferYCbCrMatrix_ITU_R_709_2;
452 return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
453 default:
454 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
455 if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *))
456 return CVYCbCrMatrixGetStringForIntegerCodePoint(
space);
457 #endif
460 }
461 }
462
464 {
465 switch (pri) {
467 #if HAVE_KCVIMAGEBUFFERCOLORPRIMARIES_ITU_R_2020
468 if (__builtin_available(macOS 10.11, iOS 9, *))
470 #endif
471 return CFSTR("ITU_R_2020");
473 return kCVImageBufferColorPrimaries_ITU_R_709_2;
475 return kCVImageBufferColorPrimaries_SMPTE_C;
477 return kCVImageBufferColorPrimaries_EBU_3213;
478 default:
479 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
480 if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *))
481 return CVColorPrimariesGetStringForIntegerCodePoint(pri);
482 #endif
485 }
486 }
487
489 {
490
491 switch (trc) {
493 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_2084_PQ
494 if (__builtin_available(macOS 10.13, iOS 11, *))
495 return kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ;
496 #endif
497 return CFSTR("SMPTE_ST_2084_PQ");
500 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2020
501 if (__builtin_available(macOS 10.11, iOS 9, *))
503 #endif
504 return CFSTR("ITU_R_2020");
506 return kCVImageBufferTransferFunction_ITU_R_709_2;
508 return kCVImageBufferTransferFunction_SMPTE_240M_1995;
510 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_428_1
511 if (__builtin_available(macOS 10.12, iOS 10, *))
512 return kCVImageBufferTransferFunction_SMPTE_ST_428_1;
513 #endif
514 return CFSTR("SMPTE_ST_428_1");
516 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
517 if (__builtin_available(macOS 10.13, iOS 11, *))
518 return kCVImageBufferTransferFunction_ITU_R_2100_HLG;
519 #endif
520 return CFSTR("ITU_R_2100_HLG");
522 return kCVImageBufferTransferFunction_UseGamma;
524 return kCVImageBufferTransferFunction_UseGamma;
525 default:
526 #if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
527 if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *))
528 return CVTransferFunctionGetStringForIntegerCodePoint(trc);
529 #endif
532 }
533 }
534
535 /**
536 * Copy all attachments for the specified mode from the given buffer.
537 */
539 CVAttachmentMode attachment_mode)
540 {
541 CFDictionaryRef dict;
542
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);
550 #endif
551
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 dict = CVBufferGetAttachments(
buffer, attachment_mode);
560 return (dict) ? CFDictionaryCreateCopy(
NULL, dict) :
NULL;
561 #else
562 return NULL;
// Impossible, just make the compiler happy
563 #endif
564 }
565
568 {
569 CGColorSpaceRef colorspace =
NULL;
570 CFStringRef colormatrix =
NULL, colorpri =
NULL, colortrc =
NULL;
571 Float32 gamma = 0;
572
574 if (colormatrix)
575 CVBufferSetAttachment(pixbuf, kCVImageBufferYCbCrMatrixKey,
576 colormatrix, kCVAttachmentMode_ShouldPropagate);
577 else {
578 CVBufferRemoveAttachment(pixbuf, kCVImageBufferYCbCrMatrixKey);
581 "Color space %s is not supported.\n",
583 }
584
586 if (colorpri)
587 CVBufferSetAttachment(pixbuf, kCVImageBufferColorPrimariesKey,
588 colorpri, kCVAttachmentMode_ShouldPropagate);
589 else {
590 CVBufferRemoveAttachment(pixbuf, kCVImageBufferColorPrimariesKey);
593 "Color primaries %s is not supported.\n",
595 }
596
598 if (colortrc)
599 CVBufferSetAttachment(pixbuf, kCVImageBufferTransferFunctionKey,
600 colortrc, kCVAttachmentMode_ShouldPropagate);
601 else {
602 CVBufferRemoveAttachment(pixbuf, kCVImageBufferTransferFunctionKey);
605 "Color transfer function %s is not supported.\n",
607 }
608
610 gamma = 2.2;
612 gamma = 2.8;
613
614 if (gamma != 0) {
615 CFNumberRef gamma_level = CFNumberCreate(
NULL, kCFNumberFloat32Type, &gamma);
616 CVBufferSetAttachment(pixbuf, kCVImageBufferGammaLevelKey,
617 gamma_level, kCVAttachmentMode_ShouldPropagate);
618 CFRelease(gamma_level);
619 } else
620 CVBufferRemoveAttachment(pixbuf, kCVImageBufferGammaLevelKey);
621
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 =
627
628 if (attachments) {
629 colorspace =
630 CVImageBufferCreateColorSpaceFromAttachments(attachments);
631 CFRelease(attachments);
632 }
633 }
634 #endif
635
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.
639 if (colorspace) {
640 CVBufferSetAttachment(pixbuf, kCVImageBufferCGColorSpaceKey,
641 colorspace, kCVAttachmentMode_ShouldPropagate);
642 CFRelease(colorspace);
643 } else
644 CVBufferRemoveAttachment(pixbuf, kCVImageBufferCGColorSpaceKey);
645
646 return 0;
647 }
648
651 {
662 return 0;
663 }
664
667 {
669 }
670
673 {
674 CVPixelBufferRef pixbuf = (CVPixelBufferRef)
src->data[3];
675 OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf);
676 CVReturn err;
677 uint32_t map_flags = 0;
681
687 }
688
689 if (CVPixelBufferGetWidth(pixbuf) !=
ctx->width ||
690 CVPixelBufferGetHeight(pixbuf) !=
ctx->height) {
693 }
694
696 map_flags = kCVPixelBufferLock_ReadOnly;
697
698 err = CVPixelBufferLockBaseAddress(pixbuf, map_flags);
699 if (err != kCVReturnSuccess) {
702 }
703
704 if (CVPixelBufferIsPlanar(pixbuf)) {
705 int planes = CVPixelBufferGetPlaneCount(pixbuf);
707 dst->data[
i] = CVPixelBufferGetBaseAddressOfPlane(pixbuf,
i);
708 dst->linesize[
i] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf,
i);
709 }
710 } else {
711 dst->data[0] = CVPixelBufferGetBaseAddress(pixbuf);
712 dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf);
713 }
714
716 (void *)(uintptr_t)map_flags);
718 goto unlock;
719
720 return 0;
721
722 unlock:
723 CVPixelBufferUnlockBaseAddress(pixbuf, map_flags);
725 }
726
729 {
731 int err;
732
735
739 map->format =
dst->format;
740
742 if (err)
744
746 map->height =
dst->height;
747
749 if (err)
751
752 err = 0;
755 return err;
756 }
757
760 {
762 int err;
763
766
770 map->format =
src->format;
771
773 if (err)
775
777 map->height =
src->height;
778
780 if (err)
782
784 if (err)
786
787 err = 0;
790 return err;
791 }
792
795 {
796 int err;
797
802
804 if (err)
805 return err;
806
808 dst->height =
src->height;
809
811 if (err)
812 return err;
813
814 return 0;
815 }
816
819 {
820 if (device && device[0]) {
823 }
824
825 return 0;
826 }
827
830 .name = "videotoolbox",
831
833
843
845 };