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>
28 #include "hwcontext.h"
29 #include "hwcontext_internal.h"
30 #include "hwcontext_videotoolbox.h"
37 enum AVPixelFormat pix_fmt;
39 { kCVPixelFormatType_420YpCbCr8Planar, AV_PIX_FMT_YUV420P },
40 { kCVPixelFormatType_422YpCbCr8, AV_PIX_FMT_UYVY422 },
41 { kCVPixelFormatType_32BGRA, AV_PIX_FMT_BGRA },
42 #ifdef kCFCoreFoundationVersionNumber10_7
43 { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, AV_PIX_FMT_NV12 },
45 #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
46 { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, AV_PIX_FMT_P010 },
50 enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt)
53 for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
54 if (cv_pix_fmts[i].cv_fmt == cv_fmt)
55 return cv_pix_fmts[i].pix_fmt;
57 return AV_PIX_FMT_NONE;
60 uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt)
63 for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
64 if (cv_pix_fmts[i].pix_fmt == pix_fmt)
65 return cv_pix_fmts[i].cv_fmt;
70 static int vt_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
72 frame->buf[0] = av_buffer_pool_get(ctx->pool);
74 return AVERROR(ENOMEM);
76 frame->data[3] = frame->buf[0]->data;
77 frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
78 frame->width = ctx->width;
79 frame->height = ctx->height;
84 static int vt_transfer_get_formats(AVHWFramesContext *ctx,
85 enum AVHWFrameTransferDirection dir,
86 enum AVPixelFormat **formats)
88 enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts));
90 return AVERROR(ENOMEM);
92 fmts[0] = ctx->sw_format;
93 fmts[1] = AV_PIX_FMT_NONE;
99 static void vt_unmap(AVHWFramesContext *ctx, HWMapDescriptor *hwmap)
101 CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->source->data[3];
103 CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->priv);
106 static int vt_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src,
109 CVPixelBufferRef pixbuf = (CVPixelBufferRef)src->data[3];
110 OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf);
112 uint32_t map_flags = 0;
115 enum AVPixelFormat format;
117 format = av_map_videotoolbox_format_to_pixfmt(pixel_format);
118 if (dst->format != format) {
119 av_log(ctx, AV_LOG_ERROR, "Unsupported or mismatching pixel format: %s\n",
120 av_fourcc2str(pixel_format));
121 return AVERROR_UNKNOWN;
124 if (CVPixelBufferGetWidth(pixbuf) != ctx->width ||
125 CVPixelBufferGetHeight(pixbuf) != ctx->height) {
126 av_log(ctx, AV_LOG_ERROR, "Inconsistent frame dimensions.\n");
127 return AVERROR_UNKNOWN;
130 if (flags == AV_HWFRAME_MAP_READ)
131 map_flags = kCVPixelBufferLock_ReadOnly;
133 err = CVPixelBufferLockBaseAddress(pixbuf, map_flags);
134 if (err != kCVReturnSuccess) {
135 av_log(ctx, AV_LOG_ERROR, "Error locking the pixel buffer.\n");
136 return AVERROR_UNKNOWN;
139 if (CVPixelBufferIsPlanar(pixbuf)) {
140 int planes = CVPixelBufferGetPlaneCount(pixbuf);
141 for (i = 0; i < planes; i++) {
142 dst->data[i] = CVPixelBufferGetBaseAddressOfPlane(pixbuf, i);
143 dst->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf, i);
146 dst->data[0] = CVPixelBufferGetBaseAddress(pixbuf);
147 dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf);
150 ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, vt_unmap,
151 (void *)(uintptr_t)map_flags);
158 CVPixelBufferUnlockBaseAddress(pixbuf, map_flags);
162 static int vt_transfer_data_from(AVHWFramesContext *hwfc,
163 AVFrame *dst, const AVFrame *src)
168 if (dst->width > hwfc->width || dst->height > hwfc->height)
169 return AVERROR(EINVAL);
171 map = av_frame_alloc();
173 return AVERROR(ENOMEM);
174 map->format = dst->format;
176 err = vt_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
180 map->width = dst->width;
181 map->height = dst->height;
183 err = av_frame_copy(dst, map);
193 static int vt_transfer_data_to(AVHWFramesContext *hwfc,
194 AVFrame *dst, const AVFrame *src)
199 if (src->width > hwfc->width || src->height > hwfc->height)
200 return AVERROR(EINVAL);
202 map = av_frame_alloc();
204 return AVERROR(ENOMEM);
205 map->format = src->format;
207 err = vt_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
211 map->width = src->width;
212 map->height = src->height;
214 err = av_frame_copy(map, src);
224 static int vt_device_create(AVHWDeviceContext *ctx, const char *device,
225 AVDictionary *opts, int flags)
227 if (device && device[0]) {
228 av_log(ctx, AV_LOG_ERROR, "Device selection unsupported.\n");
229 return AVERROR_UNKNOWN;
235 const HWContextType ff_hwcontext_type_videotoolbox = {
236 .type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
237 .name = "videotoolbox",
239 .device_create = vt_device_create,
240 .frames_get_buffer = vt_get_buffer,
241 .transfer_get_formats = vt_transfer_get_formats,
242 .transfer_data_to = vt_transfer_data_to,
243 .transfer_data_from = vt_transfer_data_from,
245 .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NONE },