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 26#include "buffer.h" 27#include "buffer_internal.h" 28#include "common.h" 29#include "hwcontext.h" 30#include "hwcontext_internal.h" 31#include "hwcontext_videotoolbox.h" 32#include "mem.h" 33#include "pixfmt.h" 34#include "pixdesc.h" 35 36typedef struct VTFramesContext { 37 CVPixelBufferPoolRef pool; 38} VTFramesContext; 39 40static const struct { 41 uint32_t cv_fmt; 42 bool full_range; 43 enum AVPixelFormat pix_fmt; 44} cv_pix_fmts[] = { 45 { kCVPixelFormatType_420YpCbCr8Planar, false, AV_PIX_FMT_YUV420P }, 46 { kCVPixelFormatType_422YpCbCr8, false, AV_PIX_FMT_UYVY422 }, 47 { kCVPixelFormatType_32BGRA, false, AV_PIX_FMT_BGRA }, 48#ifdef kCFCoreFoundationVersionNumber10_7 49 { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, false, AV_PIX_FMT_NV12 }, 50 { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, true, AV_PIX_FMT_NV12 }, 51 { kCVPixelFormatType_4444AYpCbCr16, false, AV_PIX_FMT_AYUV64 }, 52#endif 53#if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE 54 { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P010 }, 55 { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange, true, AV_PIX_FMT_P010 }, 56#endif 57#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE 58 { kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange, false, AV_PIX_FMT_NV16 }, 59 { kCVPixelFormatType_422YpCbCr8BiPlanarFullRange, true, AV_PIX_FMT_NV16 }, 60#endif 61#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE 62 { kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P210 }, 63 { kCVPixelFormatType_422YpCbCr10BiPlanarFullRange, true, AV_PIX_FMT_P210 }, 64#endif 65#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE 66 { kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange, false, AV_PIX_FMT_P216 }, 67#endif 68#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE 69 { kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange, false, AV_PIX_FMT_NV24 }, 70 { kCVPixelFormatType_444YpCbCr8BiPlanarFullRange, true, AV_PIX_FMT_NV24 }, 71#endif 72#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE 73 { kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P410 }, 74 { kCVPixelFormatType_444YpCbCr10BiPlanarFullRange, true, AV_PIX_FMT_P410 }, 75#endif 76#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE 77 { kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange, false, AV_PIX_FMT_P416 }, 78#endif 79}; 80 81static const enum AVPixelFormat supported_formats[] = { 82#ifdef kCFCoreFoundationVersionNumber10_7 83 AV_PIX_FMT_NV12, 84 AV_PIX_FMT_AYUV64, 85#endif 86 AV_PIX_FMT_YUV420P, 87 AV_PIX_FMT_UYVY422, 88#if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE 89 AV_PIX_FMT_P010, 90#endif 91#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE 92 AV_PIX_FMT_NV16, 93#endif 94#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE 95 AV_PIX_FMT_P210, 96#endif 97#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE 98 AV_PIX_FMT_P216, 99#endif 100#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE 101 AV_PIX_FMT_NV24, 102#endif 103#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE 104 AV_PIX_FMT_P410, 105#endif 106#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE 107 AV_PIX_FMT_P416, 108#endif 109 AV_PIX_FMT_BGRA, 110}; 111 112static int vt_frames_get_constraints(AVHWDeviceContext *ctx, 113 const void *hwconfig, 114 AVHWFramesConstraints *constraints) 115{ 116 int i; 117 118 constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1, 119 sizeof(*constraints->valid_sw_formats)); 120 if (!constraints->valid_sw_formats) 121 return AVERROR(ENOMEM); 122 123 for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) 124 constraints->valid_sw_formats[i] = supported_formats[i]; 125 constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_formats)] = AV_PIX_FMT_NONE; 126 127 constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); 128 if (!constraints->valid_hw_formats) 129 return AVERROR(ENOMEM); 130 131 constraints->valid_hw_formats[0] = AV_PIX_FMT_VIDEOTOOLBOX; 132 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; 133 134 return 0; 135} 136 137enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt) 138{ 139 int i; 140 for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) { 141 if (cv_pix_fmts[i].cv_fmt == cv_fmt) 142 return cv_pix_fmts[i].pix_fmt; 143 } 144 return AV_PIX_FMT_NONE; 145} 146 147uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt) 148{ 149 return av_map_videotoolbox_format_from_pixfmt2(pix_fmt, false); 150} 151 152uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt, bool full_range) 153{ 154 int i; 155 for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) { 156 if (cv_pix_fmts[i].pix_fmt == pix_fmt && cv_pix_fmts[i].full_range == full_range) 157 return cv_pix_fmts[i].cv_fmt; 158 } 159 return 0; 160} 161 162static int vt_pool_alloc(AVHWFramesContext *ctx) 163{ 164 VTFramesContext *fctx = ctx->internal->priv; 165 CVReturn err; 166 CFNumberRef w, h, pixfmt; 167 uint32_t cv_pixfmt; 168 CFMutableDictionaryRef attributes, iosurface_properties; 169 170 attributes = CFDictionaryCreateMutable( 171 NULL, 172 2, 173 &kCFTypeDictionaryKeyCallBacks, 174 &kCFTypeDictionaryValueCallBacks); 175 176 cv_pixfmt = av_map_videotoolbox_format_from_pixfmt(ctx->sw_format); 177 pixfmt = CFNumberCreate(NULL, kCFNumberSInt32Type, &cv_pixfmt); 178 CFDictionarySetValue( 179 attributes, 180 kCVPixelBufferPixelFormatTypeKey, 181 pixfmt); 182 CFRelease(pixfmt); 183 184 iosurface_properties = CFDictionaryCreateMutable( 185 NULL, 186 0, 187 &kCFTypeDictionaryKeyCallBacks, 188 &kCFTypeDictionaryValueCallBacks); 189 CFDictionarySetValue(attributes, kCVPixelBufferIOSurfacePropertiesKey, iosurface_properties); 190 CFRelease(iosurface_properties); 191 192 w = CFNumberCreate(NULL, kCFNumberSInt32Type, &ctx->width); 193 h = CFNumberCreate(NULL, kCFNumberSInt32Type, &ctx->height); 194 CFDictionarySetValue(attributes, kCVPixelBufferWidthKey, w); 195 CFDictionarySetValue(attributes, kCVPixelBufferHeightKey, h); 196 CFRelease(w); 197 CFRelease(h); 198 199 err = CVPixelBufferPoolCreate( 200 NULL, 201 NULL, 202 attributes, 203 &fctx->pool); 204 CFRelease(attributes); 205 206 if (err == kCVReturnSuccess) 207 return 0; 208 209 av_log(ctx, AV_LOG_ERROR, "Error creating CVPixelBufferPool: %d\n", err); 210 return AVERROR_EXTERNAL; 211} 212 213static void videotoolbox_buffer_release(void *opaque, uint8_t *data) 214{ 215 CVPixelBufferRelease((CVPixelBufferRef)data); 216} 217 218static AVBufferRef *vt_pool_alloc_buffer(void *opaque, size_t size) 219{ 220 CVPixelBufferRef pixbuf; 221 AVBufferRef *buf; 222 CVReturn err; 223 AVHWFramesContext *ctx = opaque; 224 VTFramesContext *fctx = ctx->internal->priv; 225 226 err = CVPixelBufferPoolCreatePixelBuffer( 227 NULL, 228 fctx->pool, 229 &pixbuf 230 ); 231 if (err != kCVReturnSuccess) { 232 av_log(ctx, AV_LOG_ERROR, "Failed to create pixel buffer from pool: %d\n", err); 233 return NULL; 234 } 235 236 buf = av_buffer_create((uint8_t *)pixbuf, size, 237 videotoolbox_buffer_release, NULL, 0); 238 if (!buf) { 239 CVPixelBufferRelease(pixbuf); 240 return NULL; 241 } 242 return buf; 243} 244 245static void vt_frames_uninit(AVHWFramesContext *ctx) 246{ 247 VTFramesContext *fctx = ctx->internal->priv; 248 if (fctx->pool) { 249 CVPixelBufferPoolRelease(fctx->pool); 250 fctx->pool = NULL; 251 } 252} 253 254static int vt_frames_init(AVHWFramesContext *ctx) 255{ 256 int i, ret; 257 258 for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { 259 if (ctx->sw_format == supported_formats[i]) 260 break; 261 } 262 if (i == FF_ARRAY_ELEMS(supported_formats)) { 263 av_log(ctx, AV_LOG_ERROR, "Pixel format '%s' is not supported\n", 264 av_get_pix_fmt_name(ctx->sw_format)); 265 return AVERROR(ENOSYS); 266 } 267 268 if (!ctx->pool) { 269 ctx->internal->pool_internal = av_buffer_pool_init2( 270 sizeof(CVPixelBufferRef), ctx, vt_pool_alloc_buffer, NULL); 271 if (!ctx->internal->pool_internal) 272 return AVERROR(ENOMEM); 273 } 274 275 ret = vt_pool_alloc(ctx); 276 if (ret < 0) 277 return ret; 278 279 return 0; 280} 281 282static int vt_get_buffer(AVHWFramesContext *ctx, AVFrame *frame) 283{ 284 frame->buf[0] = av_buffer_pool_get(ctx->pool); 285 if (!frame->buf[0]) 286 return AVERROR(ENOMEM); 287 288 frame->data[3] = frame->buf[0]->data; 289 frame->format = AV_PIX_FMT_VIDEOTOOLBOX; 290 frame->width = ctx->width; 291 frame->height = ctx->height; 292 293 return 0; 294} 295 296static int vt_transfer_get_formats(AVHWFramesContext *ctx, 297 enum AVHWFrameTransferDirection dir, 298 enum AVPixelFormat **formats) 299{ 300 enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts)); 301 if (!fmts) 302 return AVERROR(ENOMEM); 303 304 fmts[0] = ctx->sw_format; 305 fmts[1] = AV_PIX_FMT_NONE; 306 307 *formats = fmts; 308 return 0; 309} 310 311static void vt_unmap(AVHWFramesContext *ctx, HWMapDescriptor *hwmap) 312{ 313 CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->source->data[3]; 314 315 CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->priv); 316} 317 318static int vt_pixbuf_set_par(void *log_ctx, 319 CVPixelBufferRef pixbuf, const AVFrame *src) 320{ 321 CFMutableDictionaryRef par = NULL; 322 CFNumberRef num = NULL, den = NULL; 323 AVRational avpar = src->sample_aspect_ratio; 324 325 if (avpar.num == 0) 326 return 0; 327 328 av_reduce(&avpar.num, &avpar.den, 329 avpar.num, avpar.den, 330 0xFFFFFFFF); 331 332 num = CFNumberCreate(kCFAllocatorDefault, 333 kCFNumberIntType, 334 &avpar.num); 335 336 den = CFNumberCreate(kCFAllocatorDefault, 337 kCFNumberIntType, 338 &avpar.den); 339 340 par = CFDictionaryCreateMutable(kCFAllocatorDefault, 341 2, 342 &kCFCopyStringDictionaryKeyCallBacks, 343 &kCFTypeDictionaryValueCallBacks); 344 345 if (!par || !num || !den) { 346 if (par) CFRelease(par); 347 if (num) CFRelease(num); 348 if (den) CFRelease(den); 349 return AVERROR(ENOMEM); 350 } 351 352 CFDictionarySetValue( 353 par, 354 kCVImageBufferPixelAspectRatioHorizontalSpacingKey, 355 num); 356 CFDictionarySetValue( 357 par, 358 kCVImageBufferPixelAspectRatioVerticalSpacingKey, 359 den); 360 361 CVBufferSetAttachment( 362 pixbuf, 363 kCVImageBufferPixelAspectRatioKey, 364 par, 365 kCVAttachmentMode_ShouldPropagate 366 ); 367 368 CFRelease(par); 369 CFRelease(num); 370 CFRelease(den); 371 372 return 0; 373} 374 375CFStringRef av_map_videotoolbox_chroma_loc_from_av(enum AVChromaLocation loc) 376{ 377 switch (loc) { 378 case AVCHROMA_LOC_LEFT: 379 return kCVImageBufferChromaLocation_Left; 380 case AVCHROMA_LOC_CENTER: 381 return kCVImageBufferChromaLocation_Center; 382 case AVCHROMA_LOC_TOP: 383 return kCVImageBufferChromaLocation_Top; 384 case AVCHROMA_LOC_BOTTOM: 385 return kCVImageBufferChromaLocation_Bottom; 386 case AVCHROMA_LOC_TOPLEFT: 387 return kCVImageBufferChromaLocation_TopLeft; 388 case AVCHROMA_LOC_BOTTOMLEFT: 389 return kCVImageBufferChromaLocation_BottomLeft; 390 default: 391 return NULL; 392 } 393} 394 395static int vt_pixbuf_set_chromaloc(void *log_ctx, 396 CVPixelBufferRef pixbuf, const AVFrame *src) 397{ 398 CFStringRef loc = av_map_videotoolbox_chroma_loc_from_av(src->chroma_location); 399 400 if (loc) { 401 CVBufferSetAttachment( 402 pixbuf, 403 kCVImageBufferChromaLocationTopFieldKey, 404 loc, 405 kCVAttachmentMode_ShouldPropagate); 406 } 407 408 return 0; 409} 410 411CFStringRef av_map_videotoolbox_color_matrix_from_av(enum AVColorSpace space) 412{ 413 switch (space) { 414 case AVCOL_SPC_BT2020_CL: 415 case AVCOL_SPC_BT2020_NCL: 416#if HAVE_KCVIMAGEBUFFERYCBCRMATRIX_ITU_R_2020 417 if (__builtin_available(macOS 10.11, iOS 9, *)) 418 return kCVImageBufferYCbCrMatrix_ITU_R_2020; 419#endif 420 return CFSTR("ITU_R_2020"); 421 case AVCOL_SPC_BT470BG: 422 case AVCOL_SPC_SMPTE170M: 423 return kCVImageBufferYCbCrMatrix_ITU_R_601_4; 424 case AVCOL_SPC_BT709: 425 return kCVImageBufferYCbCrMatrix_ITU_R_709_2; 426 case AVCOL_SPC_SMPTE240M: 427 return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995; 428 default: 429#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG 430 if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *)) 431 return CVYCbCrMatrixGetStringForIntegerCodePoint(space); 432#endif 433 case AVCOL_SPC_UNSPECIFIED: 434 return NULL; 435 } 436} 437 438CFStringRef av_map_videotoolbox_color_primaries_from_av(enum AVColorPrimaries pri) 439{ 440 switch (pri) { 441 case AVCOL_PRI_BT2020: 442#if HAVE_KCVIMAGEBUFFERCOLORPRIMARIES_ITU_R_2020 443 if (__builtin_available(macOS 10.11, iOS 9, *)) 444 return kCVImageBufferColorPrimaries_ITU_R_2020; 445#endif 446 return CFSTR("ITU_R_2020"); 447 case AVCOL_PRI_BT709: 448 return kCVImageBufferColorPrimaries_ITU_R_709_2; 449 case AVCOL_PRI_SMPTE170M: 450 return kCVImageBufferColorPrimaries_SMPTE_C; 451 case AVCOL_PRI_BT470BG: 452 return kCVImageBufferColorPrimaries_EBU_3213; 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 CVColorPrimariesGetStringForIntegerCodePoint(pri); 457#endif 458 case AVCOL_PRI_UNSPECIFIED: 459 return NULL; 460 } 461} 462 463CFStringRef av_map_videotoolbox_color_trc_from_av(enum AVColorTransferCharacteristic trc) 464{ 465 466 switch (trc) { 467 case AVCOL_TRC_SMPTE2084: 468#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_2084_PQ 469 if (__builtin_available(macOS 10.13, iOS 11, *)) 470 return kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ; 471#endif 472 return CFSTR("SMPTE_ST_2084_PQ"); 473 case AVCOL_TRC_BT2020_10: 474 case AVCOL_TRC_BT2020_12: 475#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2020 476 if (__builtin_available(macOS 10.11, iOS 9, *)) 477 return kCVImageBufferTransferFunction_ITU_R_2020; 478#endif 479 return CFSTR("ITU_R_2020"); 480 case AVCOL_TRC_BT709: 481 return kCVImageBufferTransferFunction_ITU_R_709_2; 482 case AVCOL_TRC_SMPTE240M: 483 return kCVImageBufferTransferFunction_SMPTE_240M_1995; 484 case AVCOL_TRC_SMPTE428: 485#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_428_1 486 if (__builtin_available(macOS 10.12, iOS 10, *)) 487 return kCVImageBufferTransferFunction_SMPTE_ST_428_1; 488#endif 489 return CFSTR("SMPTE_ST_428_1"); 490 case AVCOL_TRC_ARIB_STD_B67: 491#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG 492 if (__builtin_available(macOS 10.13, iOS 11, *)) 493 return kCVImageBufferTransferFunction_ITU_R_2100_HLG; 494#endif 495 return CFSTR("ITU_R_2100_HLG"); 496 case AVCOL_TRC_GAMMA22: 497 return kCVImageBufferTransferFunction_UseGamma; 498 case AVCOL_TRC_GAMMA28: 499 return kCVImageBufferTransferFunction_UseGamma; 500 default: 501#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG 502 if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *)) 503 return CVTransferFunctionGetStringForIntegerCodePoint(trc); 504#endif 505 case AVCOL_TRC_UNSPECIFIED: 506 return NULL; 507 } 508} 509 510static int vt_pixbuf_set_colorspace(void *log_ctx, 511 CVPixelBufferRef pixbuf, const AVFrame *src) 512{ 513 CFStringRef colormatrix = NULL, colorpri = NULL, colortrc = NULL; 514 Float32 gamma = 0; 515 516 colormatrix = av_map_videotoolbox_color_matrix_from_av(src->colorspace); 517 if (!colormatrix && src->colorspace != AVCOL_SPC_UNSPECIFIED) 518 av_log(log_ctx, AV_LOG_WARNING, "Color space %s is not supported.\n", av_color_space_name(src->colorspace)); 519 520 colorpri = av_map_videotoolbox_color_primaries_from_av(src->color_primaries); 521 if (!colorpri && src->color_primaries != AVCOL_PRI_UNSPECIFIED) 522 av_log(log_ctx, AV_LOG_WARNING, "Color primaries %s is not supported.\n", av_color_primaries_name(src->color_primaries)); 523 524 colortrc = av_map_videotoolbox_color_trc_from_av(src->color_trc); 525 if (!colortrc && src->color_trc != AVCOL_TRC_UNSPECIFIED) 526 av_log(log_ctx, AV_LOG_WARNING, "Color transfer function %s is not supported.\n", av_color_transfer_name(src->color_trc)); 527 528 if (src->color_trc == AVCOL_TRC_GAMMA22) 529 gamma = 2.2; 530 else if (src->color_trc == AVCOL_TRC_GAMMA28) 531 gamma = 2.8; 532 533 if (colormatrix) { 534 CVBufferSetAttachment( 535 pixbuf, 536 kCVImageBufferYCbCrMatrixKey, 537 colormatrix, 538 kCVAttachmentMode_ShouldPropagate); 539 } 540 if (colorpri) { 541 CVBufferSetAttachment( 542 pixbuf, 543 kCVImageBufferColorPrimariesKey, 544 colorpri, 545 kCVAttachmentMode_ShouldPropagate); 546 } 547 if (colortrc) { 548 CVBufferSetAttachment( 549 pixbuf, 550 kCVImageBufferTransferFunctionKey, 551 colortrc, 552 kCVAttachmentMode_ShouldPropagate); 553 } 554 if (gamma != 0) { 555 CFNumberRef gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma); 556 CVBufferSetAttachment( 557 pixbuf, 558 kCVImageBufferGammaLevelKey, 559 gamma_level, 560 kCVAttachmentMode_ShouldPropagate); 561 CFRelease(gamma_level); 562 } 563 564 return 0; 565} 566 567static int vt_pixbuf_set_attachments(void *log_ctx, 568 CVPixelBufferRef pixbuf, const AVFrame *src) 569{ 570 int ret; 571 ret = vt_pixbuf_set_par(log_ctx, pixbuf, src); 572 if (ret < 0) 573 return ret; 574 ret = vt_pixbuf_set_colorspace(log_ctx, pixbuf, src); 575 if (ret < 0) 576 return ret; 577 ret = vt_pixbuf_set_chromaloc(log_ctx, pixbuf, src); 578 if (ret < 0) 579 return ret; 580 return 0; 581} 582 583int av_vt_pixbuf_set_attachments(void *log_ctx, 584 CVPixelBufferRef pixbuf, const AVFrame *src) 585{ 586 return vt_pixbuf_set_attachments(log_ctx, pixbuf, src); 587} 588 589static int vt_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src, 590 int flags) 591{ 592 CVPixelBufferRef pixbuf = (CVPixelBufferRef)src->data[3]; 593 OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf); 594 CVReturn err; 595 uint32_t map_flags = 0; 596 int ret; 597 int i; 598 enum AVPixelFormat format; 599 600 format = av_map_videotoolbox_format_to_pixfmt(pixel_format); 601 if (dst->format != format) { 602 av_log(ctx, AV_LOG_ERROR, "Unsupported or mismatching pixel format: %s\n", 603 av_fourcc2str(pixel_format)); 604 return AVERROR_UNKNOWN; 605 } 606 607 if (CVPixelBufferGetWidth(pixbuf) != ctx->width || 608 CVPixelBufferGetHeight(pixbuf) != ctx->height) { 609 av_log(ctx, AV_LOG_ERROR, "Inconsistent frame dimensions.\n"); 610 return AVERROR_UNKNOWN; 611 } 612 613 if (flags == AV_HWFRAME_MAP_READ) 614 map_flags = kCVPixelBufferLock_ReadOnly; 615 616 err = CVPixelBufferLockBaseAddress(pixbuf, map_flags); 617 if (err != kCVReturnSuccess) { 618 av_log(ctx, AV_LOG_ERROR, "Error locking the pixel buffer.\n"); 619 return AVERROR_UNKNOWN; 620 } 621 622 if (CVPixelBufferIsPlanar(pixbuf)) { 623 int planes = CVPixelBufferGetPlaneCount(pixbuf); 624 for (i = 0; i < planes; i++) { 625 dst->data[i] = CVPixelBufferGetBaseAddressOfPlane(pixbuf, i); 626 dst->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf, i); 627 } 628 } else { 629 dst->data[0] = CVPixelBufferGetBaseAddress(pixbuf); 630 dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf); 631 } 632 633 ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, vt_unmap, 634 (void *)(uintptr_t)map_flags); 635 if (ret < 0) 636 goto unlock; 637 638 return 0; 639 640unlock: 641 CVPixelBufferUnlockBaseAddress(pixbuf, map_flags); 642 return ret; 643} 644 645static int vt_transfer_data_from(AVHWFramesContext *hwfc, 646 AVFrame *dst, const AVFrame *src) 647{ 648 AVFrame *map; 649 int err; 650 651 if (dst->width > hwfc->width || dst->height > hwfc->height) 652 return AVERROR(EINVAL); 653 654 map = av_frame_alloc(); 655 if (!map) 656 return AVERROR(ENOMEM); 657 map->format = dst->format; 658 659 err = vt_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ); 660 if (err) 661 goto fail; 662 663 map->width = dst->width; 664 map->height = dst->height; 665 666 err = av_frame_copy(dst, map); 667 if (err) 668 goto fail; 669 670 err = 0; 671fail: 672 av_frame_free(&map); 673 return err; 674} 675 676static int vt_transfer_data_to(AVHWFramesContext *hwfc, 677 AVFrame *dst, const AVFrame *src) 678{ 679 AVFrame *map; 680 int err; 681 682 if (src->width > hwfc->width || src->height > hwfc->height) 683 return AVERROR(EINVAL); 684 685 map = av_frame_alloc(); 686 if (!map) 687 return AVERROR(ENOMEM); 688 map->format = src->format; 689 690 err = vt_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE); 691 if (err) 692 goto fail; 693 694 map->width = src->width; 695 map->height = src->height; 696 697 err = av_frame_copy(map, src); 698 if (err) 699 goto fail; 700 701 err = vt_pixbuf_set_attachments(hwfc, (CVPixelBufferRef)dst->data[3], src); 702 if (err) 703 goto fail; 704 705 err = 0; 706fail: 707 av_frame_free(&map); 708 return err; 709} 710 711static int vt_map_from(AVHWFramesContext *hwfc, AVFrame *dst, 712 const AVFrame *src, int flags) 713{ 714 int err; 715 716 if (dst->format == AV_PIX_FMT_NONE) 717 dst->format = hwfc->sw_format; 718 else if (dst->format != hwfc->sw_format) 719 return AVERROR(ENOSYS); 720 721 err = vt_map_frame(hwfc, dst, src, flags); 722 if (err) 723 return err; 724 725 dst->width = src->width; 726 dst->height = src->height; 727 728 err = av_frame_copy_props(dst, src); 729 if (err) 730 return err; 731 732 return 0; 733} 734 735static int vt_device_create(AVHWDeviceContext *ctx, const char *device, 736 AVDictionary *opts, int flags) 737{ 738 if (device && device[0]) { 739 av_log(ctx, AV_LOG_ERROR, "Device selection unsupported.\n"); 740 return AVERROR_UNKNOWN; 741 } 742 743 return 0; 744} 745 746const HWContextType ff_hwcontext_type_videotoolbox = { 747 .type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX, 748 .name = "videotoolbox", 749 750 .frames_priv_size = sizeof(VTFramesContext), 751 752 .device_create = vt_device_create, 753 .frames_init = vt_frames_init, 754 .frames_get_buffer = vt_get_buffer, 755 .frames_get_constraints = vt_frames_get_constraints, 756 .frames_uninit = vt_frames_uninit, 757 .transfer_get_formats = vt_transfer_get_formats, 758 .transfer_data_to = vt_transfer_data_to, 759 .transfer_data_from = vt_transfer_data_from, 760 .map_from = vt_map_from, 761 762 .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NONE }, 763}; 764