The color trc key was assigned a color primaries value which causes the resulting colorspace is always SDR. Fixes #10884. Signed-off-by: Gnattu OC <gnattuoc@me.com> Signed-off-by: Zhao Zhili <zhilizhao@tencent.com>
		
			
				
	
	
		
			846 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			846 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This file is part of FFmpeg.
 | 
						|
 *
 | 
						|
 * FFmpeg is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2.1 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * FFmpeg is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with FFmpeg; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <VideoToolbox/VideoToolbox.h>
 | 
						|
 | 
						|
#include "buffer.h"
 | 
						|
#include "buffer_internal.h"
 | 
						|
#include "common.h"
 | 
						|
#include "hwcontext.h"
 | 
						|
#include "hwcontext_internal.h"
 | 
						|
#include "hwcontext_videotoolbox.h"
 | 
						|
#include "mem.h"
 | 
						|
#include "pixfmt.h"
 | 
						|
#include "pixdesc.h"
 | 
						|
 | 
						|
typedef struct VTFramesContext {
 | 
						|
    /**
 | 
						|
     * The public AVVTFramesContext. See hwcontext_videotoolbox.h for it.
 | 
						|
     */
 | 
						|
    AVVTFramesContext p;
 | 
						|
    CVPixelBufferPoolRef pool;
 | 
						|
} VTFramesContext;
 | 
						|
 | 
						|
static const struct {
 | 
						|
    uint32_t cv_fmt;
 | 
						|
    bool full_range;
 | 
						|
    enum AVPixelFormat pix_fmt;
 | 
						|
} cv_pix_fmts[] = {
 | 
						|
    { kCVPixelFormatType_420YpCbCr8Planar,              false, AV_PIX_FMT_YUV420P },
 | 
						|
    { kCVPixelFormatType_420YpCbCr8PlanarFullRange,     true,  AV_PIX_FMT_YUV420P },
 | 
						|
    { kCVPixelFormatType_422YpCbCr8,                    false, AV_PIX_FMT_UYVY422 },
 | 
						|
    { kCVPixelFormatType_32BGRA,                        true,  AV_PIX_FMT_BGRA },
 | 
						|
#ifdef kCFCoreFoundationVersionNumber10_7
 | 
						|
    { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,  false, AV_PIX_FMT_NV12 },
 | 
						|
    { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,   true,  AV_PIX_FMT_NV12 },
 | 
						|
    { kCVPixelFormatType_4444AYpCbCr16,                 false, AV_PIX_FMT_AYUV64 },
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
 | 
						|
    { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P010 },
 | 
						|
    { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange,  true,  AV_PIX_FMT_P010 },
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE
 | 
						|
    { kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange,  false, AV_PIX_FMT_NV16 },
 | 
						|
    { kCVPixelFormatType_422YpCbCr8BiPlanarFullRange,   true,  AV_PIX_FMT_NV16 },
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE
 | 
						|
    { kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P210 },
 | 
						|
    { kCVPixelFormatType_422YpCbCr10BiPlanarFullRange,  true,  AV_PIX_FMT_P210 },
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE
 | 
						|
    { kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange, false, AV_PIX_FMT_P216 },
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE
 | 
						|
    { kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange,  false, AV_PIX_FMT_NV24 },
 | 
						|
    { kCVPixelFormatType_444YpCbCr8BiPlanarFullRange,   true,  AV_PIX_FMT_NV24 },
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE
 | 
						|
    { kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P410 },
 | 
						|
    { kCVPixelFormatType_444YpCbCr10BiPlanarFullRange,  true,  AV_PIX_FMT_P410 },
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE
 | 
						|
    { kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange, false, AV_PIX_FMT_P416 },
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
static const enum AVPixelFormat supported_formats[] = {
 | 
						|
#ifdef kCFCoreFoundationVersionNumber10_7
 | 
						|
    AV_PIX_FMT_NV12,
 | 
						|
    AV_PIX_FMT_AYUV64,
 | 
						|
#endif
 | 
						|
    AV_PIX_FMT_YUV420P,
 | 
						|
    AV_PIX_FMT_UYVY422,
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
 | 
						|
    AV_PIX_FMT_P010,
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR8BIPLANARVIDEORANGE
 | 
						|
    AV_PIX_FMT_NV16,
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR10BIPLANARVIDEORANGE
 | 
						|
    AV_PIX_FMT_P210,
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_422YPCBCR16BIPLANARVIDEORANGE
 | 
						|
    AV_PIX_FMT_P216,
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR8BIPLANARVIDEORANGE
 | 
						|
    AV_PIX_FMT_NV24,
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR10BIPLANARVIDEORANGE
 | 
						|
    AV_PIX_FMT_P410,
 | 
						|
#endif
 | 
						|
#if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE
 | 
						|
    AV_PIX_FMT_P416,
 | 
						|
#endif
 | 
						|
    AV_PIX_FMT_BGRA,
 | 
						|
};
 | 
						|
 | 
						|
static int vt_frames_get_constraints(AVHWDeviceContext *ctx,
 | 
						|
                                     const void *hwconfig,
 | 
						|
                                     AVHWFramesConstraints *constraints)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1,
 | 
						|
                                                    sizeof(*constraints->valid_sw_formats));
 | 
						|
    if (!constraints->valid_sw_formats)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
 | 
						|
    for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
 | 
						|
        constraints->valid_sw_formats[i] = supported_formats[i];
 | 
						|
    constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_formats)] = AV_PIX_FMT_NONE;
 | 
						|
 | 
						|
    constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats));
 | 
						|
    if (!constraints->valid_hw_formats)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
 | 
						|
    constraints->valid_hw_formats[0] = AV_PIX_FMT_VIDEOTOOLBOX;
 | 
						|
    constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
 | 
						|
        if (cv_pix_fmts[i].cv_fmt == cv_fmt)
 | 
						|
            return cv_pix_fmts[i].pix_fmt;
 | 
						|
    }
 | 
						|
    return AV_PIX_FMT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vt_format_from_pixfmt(enum AVPixelFormat pix_fmt,
 | 
						|
                                      enum AVColorRange range)
 | 
						|
{
 | 
						|
    for (int i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
 | 
						|
        if (cv_pix_fmts[i].pix_fmt == pix_fmt) {
 | 
						|
            int full_range = (range == AVCOL_RANGE_JPEG);
 | 
						|
 | 
						|
            // Don't care if unspecified
 | 
						|
            if (range == AVCOL_RANGE_UNSPECIFIED)
 | 
						|
                return cv_pix_fmts[i].cv_fmt;
 | 
						|
 | 
						|
            if (cv_pix_fmts[i].full_range == full_range)
 | 
						|
                return cv_pix_fmts[i].cv_fmt;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt)
 | 
						|
{
 | 
						|
    return av_map_videotoolbox_format_from_pixfmt2(pix_fmt, false);
 | 
						|
}
 | 
						|
 | 
						|
uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt, bool full_range)
 | 
						|
{
 | 
						|
    return vt_format_from_pixfmt(pix_fmt, full_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG);
 | 
						|
}
 | 
						|
 | 
						|
static int vt_pool_alloc(AVHWFramesContext *ctx)
 | 
						|
{
 | 
						|
    VTFramesContext *fctx = ctx->hwctx;
 | 
						|
    AVVTFramesContext *hw_ctx = &fctx->p;
 | 
						|
    CVReturn err;
 | 
						|
    CFNumberRef w, h, pixfmt;
 | 
						|
    uint32_t cv_pixfmt;
 | 
						|
    CFMutableDictionaryRef attributes, iosurface_properties;
 | 
						|
 | 
						|
    attributes = CFDictionaryCreateMutable(
 | 
						|
        NULL,
 | 
						|
        2,
 | 
						|
        &kCFTypeDictionaryKeyCallBacks,
 | 
						|
        &kCFTypeDictionaryValueCallBacks);
 | 
						|
 | 
						|
    cv_pixfmt = vt_format_from_pixfmt(ctx->sw_format, hw_ctx->color_range);
 | 
						|
    pixfmt = CFNumberCreate(NULL, kCFNumberSInt32Type, &cv_pixfmt);
 | 
						|
    CFDictionarySetValue(
 | 
						|
        attributes,
 | 
						|
        kCVPixelBufferPixelFormatTypeKey,
 | 
						|
        pixfmt);
 | 
						|
    CFRelease(pixfmt);
 | 
						|
 | 
						|
    iosurface_properties = CFDictionaryCreateMutable(
 | 
						|
        NULL,
 | 
						|
        0,
 | 
						|
        &kCFTypeDictionaryKeyCallBacks,
 | 
						|
        &kCFTypeDictionaryValueCallBacks);
 | 
						|
    CFDictionarySetValue(attributes, kCVPixelBufferIOSurfacePropertiesKey, iosurface_properties);
 | 
						|
    CFRelease(iosurface_properties);
 | 
						|
 | 
						|
    w = CFNumberCreate(NULL, kCFNumberSInt32Type, &ctx->width);
 | 
						|
    h = CFNumberCreate(NULL, kCFNumberSInt32Type, &ctx->height);
 | 
						|
    CFDictionarySetValue(attributes, kCVPixelBufferWidthKey, w);
 | 
						|
    CFDictionarySetValue(attributes, kCVPixelBufferHeightKey, h);
 | 
						|
    CFRelease(w);
 | 
						|
    CFRelease(h);
 | 
						|
 | 
						|
    err = CVPixelBufferPoolCreate(
 | 
						|
        NULL,
 | 
						|
        NULL,
 | 
						|
        attributes,
 | 
						|
        &fctx->pool);
 | 
						|
    CFRelease(attributes);
 | 
						|
 | 
						|
    if (err == kCVReturnSuccess)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    av_log(ctx, AV_LOG_ERROR, "Error creating CVPixelBufferPool: %d\n", err);
 | 
						|
    return AVERROR_EXTERNAL;
 | 
						|
}
 | 
						|
 | 
						|
static void videotoolbox_buffer_release(void *opaque, uint8_t *data)
 | 
						|
{
 | 
						|
    CVPixelBufferRelease((CVPixelBufferRef)data);
 | 
						|
}
 | 
						|
 | 
						|
static AVBufferRef *vt_pool_alloc_buffer(void *opaque, size_t size)
 | 
						|
{
 | 
						|
    CVPixelBufferRef pixbuf;
 | 
						|
    AVBufferRef *buf;
 | 
						|
    CVReturn err;
 | 
						|
    AVHWFramesContext *ctx = opaque;
 | 
						|
    VTFramesContext *fctx = ctx->hwctx;
 | 
						|
 | 
						|
    err = CVPixelBufferPoolCreatePixelBuffer(
 | 
						|
        NULL,
 | 
						|
        fctx->pool,
 | 
						|
        &pixbuf
 | 
						|
    );
 | 
						|
    if (err != kCVReturnSuccess) {
 | 
						|
        av_log(ctx, AV_LOG_ERROR, "Failed to create pixel buffer from pool: %d\n", err);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    buf = av_buffer_create((uint8_t *)pixbuf, size,
 | 
						|
                           videotoolbox_buffer_release, NULL, 0);
 | 
						|
    if (!buf) {
 | 
						|
        CVPixelBufferRelease(pixbuf);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return buf;
 | 
						|
}
 | 
						|
 | 
						|
static void vt_frames_uninit(AVHWFramesContext *ctx)
 | 
						|
{
 | 
						|
    VTFramesContext *fctx = ctx->hwctx;
 | 
						|
    if (fctx->pool) {
 | 
						|
        CVPixelBufferPoolRelease(fctx->pool);
 | 
						|
        fctx->pool = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int vt_frames_init(AVHWFramesContext *ctx)
 | 
						|
{
 | 
						|
    int i, ret;
 | 
						|
 | 
						|
    for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
 | 
						|
        if (ctx->sw_format == supported_formats[i])
 | 
						|
            break;
 | 
						|
    }
 | 
						|
    if (i == FF_ARRAY_ELEMS(supported_formats)) {
 | 
						|
        av_log(ctx, AV_LOG_ERROR, "Pixel format '%s' is not supported\n",
 | 
						|
               av_get_pix_fmt_name(ctx->sw_format));
 | 
						|
        return AVERROR(ENOSYS);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!ctx->pool) {
 | 
						|
        ffhwframesctx(ctx)->pool_internal = av_buffer_pool_init2(
 | 
						|
                sizeof(CVPixelBufferRef), ctx, vt_pool_alloc_buffer, NULL);
 | 
						|
        if (!ffhwframesctx(ctx)->pool_internal)
 | 
						|
            return AVERROR(ENOMEM);
 | 
						|
    }
 | 
						|
 | 
						|
    ret = vt_pool_alloc(ctx);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vt_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
 | 
						|
{
 | 
						|
    frame->buf[0] = av_buffer_pool_get(ctx->pool);
 | 
						|
    if (!frame->buf[0])
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
 | 
						|
    frame->data[3] = frame->buf[0]->data;
 | 
						|
    frame->format  = AV_PIX_FMT_VIDEOTOOLBOX;
 | 
						|
    frame->width   = ctx->width;
 | 
						|
    frame->height  = ctx->height;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vt_transfer_get_formats(AVHWFramesContext *ctx,
 | 
						|
                                   enum AVHWFrameTransferDirection dir,
 | 
						|
                                   enum AVPixelFormat **formats)
 | 
						|
{
 | 
						|
    enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts));
 | 
						|
    if (!fmts)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
 | 
						|
    fmts[0] = ctx->sw_format;
 | 
						|
    fmts[1] = AV_PIX_FMT_NONE;
 | 
						|
 | 
						|
    *formats = fmts;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void vt_unmap(AVHWFramesContext *ctx, HWMapDescriptor *hwmap)
 | 
						|
{
 | 
						|
    CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->source->data[3];
 | 
						|
 | 
						|
    CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->priv);
 | 
						|
}
 | 
						|
 | 
						|
static int vt_pixbuf_set_par(void *log_ctx,
 | 
						|
                             CVPixelBufferRef pixbuf, const AVFrame *src)
 | 
						|
{
 | 
						|
    CFMutableDictionaryRef par = NULL;
 | 
						|
    CFNumberRef num = NULL, den = NULL;
 | 
						|
    AVRational avpar = src->sample_aspect_ratio;
 | 
						|
 | 
						|
    if (avpar.num == 0) {
 | 
						|
        CVBufferRemoveAttachment(pixbuf, kCVImageBufferPixelAspectRatioKey);
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    av_reduce(&avpar.num, &avpar.den,
 | 
						|
                avpar.num, avpar.den,
 | 
						|
                0xFFFFFFFF);
 | 
						|
 | 
						|
    num = CFNumberCreate(kCFAllocatorDefault,
 | 
						|
                            kCFNumberIntType,
 | 
						|
                            &avpar.num);
 | 
						|
 | 
						|
    den = CFNumberCreate(kCFAllocatorDefault,
 | 
						|
                            kCFNumberIntType,
 | 
						|
                            &avpar.den);
 | 
						|
 | 
						|
    par = CFDictionaryCreateMutable(kCFAllocatorDefault,
 | 
						|
                                    2,
 | 
						|
                                    &kCFCopyStringDictionaryKeyCallBacks,
 | 
						|
                                    &kCFTypeDictionaryValueCallBacks);
 | 
						|
 | 
						|
    if (!par || !num || !den) {
 | 
						|
        if (par) CFRelease(par);
 | 
						|
        if (num) CFRelease(num);
 | 
						|
        if (den) CFRelease(den);
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
    }
 | 
						|
 | 
						|
    CFDictionarySetValue(
 | 
						|
        par,
 | 
						|
        kCVImageBufferPixelAspectRatioHorizontalSpacingKey,
 | 
						|
        num);
 | 
						|
    CFDictionarySetValue(
 | 
						|
        par,
 | 
						|
        kCVImageBufferPixelAspectRatioVerticalSpacingKey,
 | 
						|
        den);
 | 
						|
 | 
						|
    CVBufferSetAttachment(
 | 
						|
        pixbuf,
 | 
						|
        kCVImageBufferPixelAspectRatioKey,
 | 
						|
        par,
 | 
						|
        kCVAttachmentMode_ShouldPropagate
 | 
						|
    );
 | 
						|
 | 
						|
    CFRelease(par);
 | 
						|
    CFRelease(num);
 | 
						|
    CFRelease(den);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
CFStringRef av_map_videotoolbox_chroma_loc_from_av(enum AVChromaLocation loc)
 | 
						|
{
 | 
						|
    switch (loc) {
 | 
						|
    case AVCHROMA_LOC_LEFT:
 | 
						|
        return kCVImageBufferChromaLocation_Left;
 | 
						|
    case AVCHROMA_LOC_CENTER:
 | 
						|
        return kCVImageBufferChromaLocation_Center;
 | 
						|
    case AVCHROMA_LOC_TOP:
 | 
						|
        return kCVImageBufferChromaLocation_Top;
 | 
						|
    case AVCHROMA_LOC_BOTTOM:
 | 
						|
        return kCVImageBufferChromaLocation_Bottom;
 | 
						|
    case AVCHROMA_LOC_TOPLEFT:
 | 
						|
        return kCVImageBufferChromaLocation_TopLeft;
 | 
						|
    case AVCHROMA_LOC_BOTTOMLEFT:
 | 
						|
        return kCVImageBufferChromaLocation_BottomLeft;
 | 
						|
    default:
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int vt_pixbuf_set_chromaloc(void *log_ctx,
 | 
						|
                                   CVPixelBufferRef pixbuf, const AVFrame *src)
 | 
						|
{
 | 
						|
    CFStringRef loc = av_map_videotoolbox_chroma_loc_from_av(src->chroma_location);
 | 
						|
 | 
						|
    if (loc) {
 | 
						|
        CVBufferSetAttachment(
 | 
						|
            pixbuf,
 | 
						|
            kCVImageBufferChromaLocationTopFieldKey,
 | 
						|
            loc,
 | 
						|
            kCVAttachmentMode_ShouldPropagate);
 | 
						|
    } else
 | 
						|
        CVBufferRemoveAttachment(
 | 
						|
            pixbuf,
 | 
						|
            kCVImageBufferChromaLocationTopFieldKey);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
CFStringRef av_map_videotoolbox_color_matrix_from_av(enum AVColorSpace space)
 | 
						|
{
 | 
						|
    switch (space) {
 | 
						|
    case AVCOL_SPC_BT2020_CL:
 | 
						|
    case AVCOL_SPC_BT2020_NCL:
 | 
						|
#if HAVE_KCVIMAGEBUFFERYCBCRMATRIX_ITU_R_2020
 | 
						|
        if (__builtin_available(macOS 10.11, iOS 9, *))
 | 
						|
            return kCVImageBufferYCbCrMatrix_ITU_R_2020;
 | 
						|
#endif
 | 
						|
        return CFSTR("ITU_R_2020");
 | 
						|
    case AVCOL_SPC_BT470BG:
 | 
						|
    case AVCOL_SPC_SMPTE170M:
 | 
						|
        return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
 | 
						|
    case AVCOL_SPC_BT709:
 | 
						|
        return kCVImageBufferYCbCrMatrix_ITU_R_709_2;
 | 
						|
    case AVCOL_SPC_SMPTE240M:
 | 
						|
        return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
 | 
						|
    default:
 | 
						|
#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
 | 
						|
        if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *))
 | 
						|
            return CVYCbCrMatrixGetStringForIntegerCodePoint(space);
 | 
						|
#endif
 | 
						|
    case AVCOL_SPC_UNSPECIFIED:
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
CFStringRef av_map_videotoolbox_color_primaries_from_av(enum AVColorPrimaries pri)
 | 
						|
{
 | 
						|
    switch (pri) {
 | 
						|
    case AVCOL_PRI_BT2020:
 | 
						|
#if HAVE_KCVIMAGEBUFFERCOLORPRIMARIES_ITU_R_2020
 | 
						|
        if (__builtin_available(macOS 10.11, iOS 9, *))
 | 
						|
            return kCVImageBufferColorPrimaries_ITU_R_2020;
 | 
						|
#endif
 | 
						|
        return CFSTR("ITU_R_2020");
 | 
						|
    case AVCOL_PRI_BT709:
 | 
						|
        return kCVImageBufferColorPrimaries_ITU_R_709_2;
 | 
						|
    case AVCOL_PRI_SMPTE170M:
 | 
						|
        return kCVImageBufferColorPrimaries_SMPTE_C;
 | 
						|
    case AVCOL_PRI_BT470BG:
 | 
						|
        return kCVImageBufferColorPrimaries_EBU_3213;
 | 
						|
    default:
 | 
						|
#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
 | 
						|
        if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *))
 | 
						|
            return CVColorPrimariesGetStringForIntegerCodePoint(pri);
 | 
						|
#endif
 | 
						|
    case AVCOL_PRI_UNSPECIFIED:
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
CFStringRef av_map_videotoolbox_color_trc_from_av(enum AVColorTransferCharacteristic trc)
 | 
						|
{
 | 
						|
 | 
						|
    switch (trc) {
 | 
						|
    case AVCOL_TRC_SMPTE2084:
 | 
						|
#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_2084_PQ
 | 
						|
        if (__builtin_available(macOS 10.13, iOS 11, *))
 | 
						|
            return kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ;
 | 
						|
#endif
 | 
						|
        return CFSTR("SMPTE_ST_2084_PQ");
 | 
						|
    case AVCOL_TRC_BT2020_10:
 | 
						|
    case AVCOL_TRC_BT2020_12:
 | 
						|
#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2020
 | 
						|
        if (__builtin_available(macOS 10.11, iOS 9, *))
 | 
						|
            return kCVImageBufferTransferFunction_ITU_R_2020;
 | 
						|
#endif
 | 
						|
        return CFSTR("ITU_R_2020");
 | 
						|
    case AVCOL_TRC_BT709:
 | 
						|
        return kCVImageBufferTransferFunction_ITU_R_709_2;
 | 
						|
    case AVCOL_TRC_SMPTE240M:
 | 
						|
        return kCVImageBufferTransferFunction_SMPTE_240M_1995;
 | 
						|
    case AVCOL_TRC_SMPTE428:
 | 
						|
#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_428_1
 | 
						|
        if (__builtin_available(macOS 10.12, iOS 10, *))
 | 
						|
            return kCVImageBufferTransferFunction_SMPTE_ST_428_1;
 | 
						|
#endif
 | 
						|
        return CFSTR("SMPTE_ST_428_1");
 | 
						|
    case AVCOL_TRC_ARIB_STD_B67:
 | 
						|
#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
 | 
						|
        if (__builtin_available(macOS 10.13, iOS 11, *))
 | 
						|
            return kCVImageBufferTransferFunction_ITU_R_2100_HLG;
 | 
						|
#endif
 | 
						|
        return CFSTR("ITU_R_2100_HLG");
 | 
						|
    case AVCOL_TRC_GAMMA22:
 | 
						|
        return kCVImageBufferTransferFunction_UseGamma;
 | 
						|
    case AVCOL_TRC_GAMMA28:
 | 
						|
        return kCVImageBufferTransferFunction_UseGamma;
 | 
						|
    default:
 | 
						|
#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG
 | 
						|
        if (__builtin_available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *))
 | 
						|
            return CVTransferFunctionGetStringForIntegerCodePoint(trc);
 | 
						|
#endif
 | 
						|
    case AVCOL_TRC_UNSPECIFIED:
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Copy all attachments for the specified mode from the given buffer.
 | 
						|
 */
 | 
						|
static CFDictionaryRef vt_cv_buffer_copy_attachments(CVBufferRef buffer,
 | 
						|
                                                     CVAttachmentMode attachment_mode)
 | 
						|
{
 | 
						|
    CFDictionaryRef dict;
 | 
						|
 | 
						|
    // Check that our SDK is at least macOS 12 / iOS 15 / tvOS 15
 | 
						|
    #if (TARGET_OS_OSX  && defined(__MAC_12_0)    && __MAC_OS_X_VERSION_MAX_ALLOWED  >= __MAC_12_0)     || \
 | 
						|
        (TARGET_OS_IOS  && defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0)  || \
 | 
						|
        (TARGET_OS_TV   && defined(__TVOS_15_0)   && __TV_OS_VERSION_MAX_ALLOWED     >= __TVOS_15_0)
 | 
						|
        // On recent enough versions, just use the respective API
 | 
						|
        if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, *))
 | 
						|
            return CVBufferCopyAttachments(buffer, attachment_mode);
 | 
						|
    #endif
 | 
						|
 | 
						|
    // Check that the target is lower than macOS 12 / iOS 15 / tvOS 15
 | 
						|
    // else this would generate a deprecation warning and anyway never run because
 | 
						|
    // the runtime availability check above would be always true.
 | 
						|
    #if (TARGET_OS_OSX  && (!defined(__MAC_12_0)    || __MAC_OS_X_VERSION_MIN_REQUIRED  < __MAC_12_0))     || \
 | 
						|
        (TARGET_OS_IOS  && (!defined(__IPHONE_15_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0))  || \
 | 
						|
        (TARGET_OS_TV   && (!defined(__TVOS_15_0)   || __TV_OS_VERSION_MIN_REQUIRED     < __TVOS_15_0))
 | 
						|
        // Fallback on SDKs or runtime versions < macOS 12 / iOS 15 / tvOS 15
 | 
						|
        dict = CVBufferGetAttachments(buffer, attachment_mode);
 | 
						|
        return (dict) ? CFDictionaryCreateCopy(NULL, dict) : NULL;
 | 
						|
    #else
 | 
						|
        return NULL; // Impossible, just make the compiler happy
 | 
						|
    #endif
 | 
						|
}
 | 
						|
 | 
						|
static int vt_pixbuf_set_colorspace(void *log_ctx,
 | 
						|
                                    CVPixelBufferRef pixbuf, const AVFrame *src)
 | 
						|
{
 | 
						|
    CGColorSpaceRef colorspace = NULL;
 | 
						|
    CFStringRef colormatrix = NULL, colorpri = NULL, colortrc = NULL;
 | 
						|
    Float32 gamma = 0;
 | 
						|
 | 
						|
    colormatrix = av_map_videotoolbox_color_matrix_from_av(src->colorspace);
 | 
						|
    if (colormatrix)
 | 
						|
        CVBufferSetAttachment(pixbuf, kCVImageBufferYCbCrMatrixKey,
 | 
						|
            colormatrix, kCVAttachmentMode_ShouldPropagate);
 | 
						|
    else {
 | 
						|
        CVBufferRemoveAttachment(pixbuf, kCVImageBufferYCbCrMatrixKey);
 | 
						|
        if (src->colorspace != AVCOL_SPC_UNSPECIFIED)
 | 
						|
            av_log(log_ctx, AV_LOG_WARNING,
 | 
						|
                "Color space %s is not supported.\n",
 | 
						|
                av_color_space_name(src->colorspace));
 | 
						|
    }
 | 
						|
 | 
						|
    colorpri = av_map_videotoolbox_color_primaries_from_av(src->color_primaries);
 | 
						|
    if (colorpri)
 | 
						|
        CVBufferSetAttachment(pixbuf, kCVImageBufferColorPrimariesKey,
 | 
						|
            colorpri, kCVAttachmentMode_ShouldPropagate);
 | 
						|
    else {
 | 
						|
        CVBufferRemoveAttachment(pixbuf, kCVImageBufferColorPrimariesKey);
 | 
						|
        if (src->color_primaries != AVCOL_SPC_UNSPECIFIED)
 | 
						|
            av_log(log_ctx, AV_LOG_WARNING,
 | 
						|
                "Color primaries %s is not supported.\n",
 | 
						|
                av_color_primaries_name(src->color_primaries));
 | 
						|
    }
 | 
						|
 | 
						|
    colortrc = av_map_videotoolbox_color_trc_from_av(src->color_trc);
 | 
						|
    if (colortrc)
 | 
						|
        CVBufferSetAttachment(pixbuf, kCVImageBufferTransferFunctionKey,
 | 
						|
            colortrc, kCVAttachmentMode_ShouldPropagate);
 | 
						|
    else {
 | 
						|
        CVBufferRemoveAttachment(pixbuf, kCVImageBufferTransferFunctionKey);
 | 
						|
        if (src->color_trc != AVCOL_TRC_UNSPECIFIED)
 | 
						|
            av_log(log_ctx, AV_LOG_WARNING,
 | 
						|
                "Color transfer function %s is not supported.\n",
 | 
						|
                av_color_transfer_name(src->color_trc));
 | 
						|
    }
 | 
						|
 | 
						|
    if (src->color_trc == AVCOL_TRC_GAMMA22)
 | 
						|
        gamma = 2.2;
 | 
						|
    else if (src->color_trc == AVCOL_TRC_GAMMA28)
 | 
						|
        gamma = 2.8;
 | 
						|
 | 
						|
    if (gamma != 0) {
 | 
						|
        CFNumberRef gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma);
 | 
						|
        CVBufferSetAttachment(pixbuf, kCVImageBufferGammaLevelKey,
 | 
						|
            gamma_level, kCVAttachmentMode_ShouldPropagate);
 | 
						|
        CFRelease(gamma_level);
 | 
						|
    } else
 | 
						|
        CVBufferRemoveAttachment(pixbuf, kCVImageBufferGammaLevelKey);
 | 
						|
 | 
						|
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED >= 100800) || \
 | 
						|
    (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000)
 | 
						|
    if (__builtin_available(macOS 10.8, iOS 10, *)) {
 | 
						|
        CFDictionaryRef attachments =
 | 
						|
            vt_cv_buffer_copy_attachments(pixbuf, kCVAttachmentMode_ShouldPropagate);
 | 
						|
 | 
						|
        if (attachments) {
 | 
						|
            colorspace =
 | 
						|
                CVImageBufferCreateColorSpaceFromAttachments(attachments);
 | 
						|
            CFRelease(attachments);
 | 
						|
        }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    // Done outside the above preprocessor code and if's so that
 | 
						|
    // in any case a wrong kCVImageBufferCGColorSpaceKey is removed
 | 
						|
    // if the above code is not used or fails.
 | 
						|
    if (colorspace) {
 | 
						|
        CVBufferSetAttachment(pixbuf, kCVImageBufferCGColorSpaceKey,
 | 
						|
            colorspace, kCVAttachmentMode_ShouldPropagate);
 | 
						|
        CFRelease(colorspace);
 | 
						|
    } else
 | 
						|
        CVBufferRemoveAttachment(pixbuf, kCVImageBufferCGColorSpaceKey);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vt_pixbuf_set_attachments(void *log_ctx,
 | 
						|
                                     CVPixelBufferRef pixbuf, const AVFrame *src)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
    ret = vt_pixbuf_set_par(log_ctx, pixbuf, src);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
    ret = vt_pixbuf_set_colorspace(log_ctx, pixbuf, src);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
    ret = vt_pixbuf_set_chromaloc(log_ctx, pixbuf, src);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int av_vt_pixbuf_set_attachments(void *log_ctx,
 | 
						|
                                 CVPixelBufferRef pixbuf, const AVFrame *src)
 | 
						|
{
 | 
						|
    return vt_pixbuf_set_attachments(log_ctx, pixbuf, src);
 | 
						|
}
 | 
						|
 | 
						|
static int vt_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src,
 | 
						|
                        int flags)
 | 
						|
{
 | 
						|
    CVPixelBufferRef pixbuf = (CVPixelBufferRef)src->data[3];
 | 
						|
    OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf);
 | 
						|
    CVReturn err;
 | 
						|
    uint32_t map_flags = 0;
 | 
						|
    int ret;
 | 
						|
    int i;
 | 
						|
    enum AVPixelFormat format;
 | 
						|
 | 
						|
    format = av_map_videotoolbox_format_to_pixfmt(pixel_format);
 | 
						|
    if (dst->format != format) {
 | 
						|
        av_log(ctx, AV_LOG_ERROR, "Unsupported or mismatching pixel format: %s\n",
 | 
						|
               av_fourcc2str(pixel_format));
 | 
						|
        return AVERROR_UNKNOWN;
 | 
						|
    }
 | 
						|
 | 
						|
    if (CVPixelBufferGetWidth(pixbuf) != ctx->width ||
 | 
						|
        CVPixelBufferGetHeight(pixbuf) != ctx->height) {
 | 
						|
        av_log(ctx, AV_LOG_ERROR, "Inconsistent frame dimensions.\n");
 | 
						|
        return AVERROR_UNKNOWN;
 | 
						|
    }
 | 
						|
 | 
						|
    if (flags == AV_HWFRAME_MAP_READ)
 | 
						|
        map_flags = kCVPixelBufferLock_ReadOnly;
 | 
						|
 | 
						|
    err = CVPixelBufferLockBaseAddress(pixbuf, map_flags);
 | 
						|
    if (err != kCVReturnSuccess) {
 | 
						|
        av_log(ctx, AV_LOG_ERROR, "Error locking the pixel buffer.\n");
 | 
						|
        return AVERROR_UNKNOWN;
 | 
						|
    }
 | 
						|
 | 
						|
    if (CVPixelBufferIsPlanar(pixbuf)) {
 | 
						|
        int planes = CVPixelBufferGetPlaneCount(pixbuf);
 | 
						|
        for (i = 0; i < planes; i++) {
 | 
						|
            dst->data[i]     = CVPixelBufferGetBaseAddressOfPlane(pixbuf, i);
 | 
						|
            dst->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf, i);
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        dst->data[0]     = CVPixelBufferGetBaseAddress(pixbuf);
 | 
						|
        dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf);
 | 
						|
    }
 | 
						|
 | 
						|
    ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, vt_unmap,
 | 
						|
                                (void *)(uintptr_t)map_flags);
 | 
						|
    if (ret < 0)
 | 
						|
        goto unlock;
 | 
						|
 | 
						|
    return 0;
 | 
						|
 | 
						|
unlock:
 | 
						|
    CVPixelBufferUnlockBaseAddress(pixbuf, map_flags);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int vt_transfer_data_from(AVHWFramesContext *hwfc,
 | 
						|
                                 AVFrame *dst, const AVFrame *src)
 | 
						|
{
 | 
						|
    AVFrame *map;
 | 
						|
    int err;
 | 
						|
 | 
						|
    if (dst->width > hwfc->width || dst->height > hwfc->height)
 | 
						|
        return AVERROR(EINVAL);
 | 
						|
 | 
						|
    map = av_frame_alloc();
 | 
						|
    if (!map)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
    map->format = dst->format;
 | 
						|
 | 
						|
    err = vt_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
 | 
						|
    if (err)
 | 
						|
        goto fail;
 | 
						|
 | 
						|
    map->width  = dst->width;
 | 
						|
    map->height = dst->height;
 | 
						|
 | 
						|
    err = av_frame_copy(dst, map);
 | 
						|
    if (err)
 | 
						|
        goto fail;
 | 
						|
 | 
						|
    err = 0;
 | 
						|
fail:
 | 
						|
    av_frame_free(&map);
 | 
						|
    return err;
 | 
						|
}
 | 
						|
 | 
						|
static int vt_transfer_data_to(AVHWFramesContext *hwfc,
 | 
						|
                               AVFrame *dst, const AVFrame *src)
 | 
						|
{
 | 
						|
    AVFrame *map;
 | 
						|
    int err;
 | 
						|
 | 
						|
    if (src->width > hwfc->width || src->height > hwfc->height)
 | 
						|
        return AVERROR(EINVAL);
 | 
						|
 | 
						|
    map = av_frame_alloc();
 | 
						|
    if (!map)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
    map->format = src->format;
 | 
						|
 | 
						|
    err = vt_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
 | 
						|
    if (err)
 | 
						|
        goto fail;
 | 
						|
 | 
						|
    map->width  = src->width;
 | 
						|
    map->height = src->height;
 | 
						|
 | 
						|
    err = av_frame_copy(map, src);
 | 
						|
    if (err)
 | 
						|
        goto fail;
 | 
						|
 | 
						|
    err = vt_pixbuf_set_attachments(hwfc, (CVPixelBufferRef)dst->data[3], src);
 | 
						|
    if (err)
 | 
						|
        goto fail;
 | 
						|
 | 
						|
    err = 0;
 | 
						|
fail:
 | 
						|
    av_frame_free(&map);
 | 
						|
    return err;
 | 
						|
}
 | 
						|
 | 
						|
static int vt_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
 | 
						|
                       const AVFrame *src, int flags)
 | 
						|
{
 | 
						|
    int err;
 | 
						|
 | 
						|
    if (dst->format == AV_PIX_FMT_NONE)
 | 
						|
        dst->format = hwfc->sw_format;
 | 
						|
    else if (dst->format != hwfc->sw_format)
 | 
						|
        return AVERROR(ENOSYS);
 | 
						|
 | 
						|
    err = vt_map_frame(hwfc, dst, src, flags);
 | 
						|
    if (err)
 | 
						|
        return err;
 | 
						|
 | 
						|
    dst->width  = src->width;
 | 
						|
    dst->height = src->height;
 | 
						|
 | 
						|
    err = av_frame_copy_props(dst, src);
 | 
						|
    if (err)
 | 
						|
        return err;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vt_device_create(AVHWDeviceContext *ctx, const char *device,
 | 
						|
                            AVDictionary *opts, int flags)
 | 
						|
{
 | 
						|
    if (device && device[0]) {
 | 
						|
        av_log(ctx, AV_LOG_ERROR, "Device selection unsupported.\n");
 | 
						|
        return AVERROR_UNKNOWN;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
const HWContextType ff_hwcontext_type_videotoolbox = {
 | 
						|
    .type                 = AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
 | 
						|
    .name                 = "videotoolbox",
 | 
						|
 | 
						|
    .frames_hwctx_size    = sizeof(VTFramesContext),
 | 
						|
 | 
						|
    .device_create        = vt_device_create,
 | 
						|
    .frames_init          = vt_frames_init,
 | 
						|
    .frames_get_buffer    = vt_get_buffer,
 | 
						|
    .frames_get_constraints = vt_frames_get_constraints,
 | 
						|
    .frames_uninit        = vt_frames_uninit,
 | 
						|
    .transfer_get_formats = vt_transfer_get_formats,
 | 
						|
    .transfer_data_to     = vt_transfer_data_to,
 | 
						|
    .transfer_data_from   = vt_transfer_data_from,
 | 
						|
    .map_from             = vt_map_from,
 | 
						|
 | 
						|
    .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NONE },
 | 
						|
};
 |