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 },
47 enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt)
50 for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
51 if (cv_pix_fmts[i].cv_fmt == cv_fmt)
52 return cv_pix_fmts[i].pix_fmt;
54 return AV_PIX_FMT_NONE;
57 uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt)
60 for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
61 if (cv_pix_fmts[i].pix_fmt == pix_fmt)
62 return cv_pix_fmts[i].cv_fmt;
67 static int vt_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
69 frame->buf[0] = av_buffer_pool_get(ctx->pool);
71 return AVERROR(ENOMEM);
73 frame->data[3] = frame->buf[0]->data;
74 frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
75 frame->width = ctx->width;
76 frame->height = ctx->height;
81 static int vt_transfer_get_formats(AVHWFramesContext *ctx,
82 enum AVHWFrameTransferDirection dir,
83 enum AVPixelFormat **formats)
85 enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts));
87 return AVERROR(ENOMEM);
89 fmts[0] = ctx->sw_format;
90 fmts[1] = AV_PIX_FMT_NONE;
96 static void vt_unmap(AVHWFramesContext *ctx, HWMapDescriptor *hwmap)
98 CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->source->data[3];
100 CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->priv);
103 static int vt_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src,
106 CVPixelBufferRef pixbuf = (CVPixelBufferRef)src->data[3];
107 OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf);
109 uint32_t map_flags = 0;
112 enum AVPixelFormat format;
114 format = av_map_videotoolbox_format_to_pixfmt(pixel_format);
115 if (dst->format != format) {
116 av_log(ctx, AV_LOG_ERROR, "Unsupported or mismatching pixel format: %s\n",
117 av_fourcc2str(pixel_format));
118 return AVERROR_UNKNOWN;
121 if (CVPixelBufferGetWidth(pixbuf) != ctx->width ||
122 CVPixelBufferGetHeight(pixbuf) != ctx->height) {
123 av_log(ctx, AV_LOG_ERROR, "Inconsistent frame dimensions.\n");
124 return AVERROR_UNKNOWN;
127 if (flags == AV_HWFRAME_MAP_READ)
128 map_flags = kCVPixelBufferLock_ReadOnly;
130 err = CVPixelBufferLockBaseAddress(pixbuf, map_flags);
131 if (err != kCVReturnSuccess) {
132 av_log(ctx, AV_LOG_ERROR, "Error locking the pixel buffer.\n");
133 return AVERROR_UNKNOWN;
136 if (CVPixelBufferIsPlanar(pixbuf)) {
137 int planes = CVPixelBufferGetPlaneCount(pixbuf);
138 for (i = 0; i < planes; i++) {
139 dst->data[i] = CVPixelBufferGetBaseAddressOfPlane(pixbuf, i);
140 dst->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf, i);
143 dst->data[0] = CVPixelBufferGetBaseAddress(pixbuf);
144 dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf);
147 ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, vt_unmap,
148 (void *)(uintptr_t)map_flags);
155 CVPixelBufferUnlockBaseAddress(pixbuf, map_flags);
159 static int vt_transfer_data_from(AVHWFramesContext *hwfc,
160 AVFrame *dst, const AVFrame *src)
165 if (dst->width > hwfc->width || dst->height > hwfc->height)
166 return AVERROR(EINVAL);
168 map = av_frame_alloc();
170 return AVERROR(ENOMEM);
171 map->format = dst->format;
173 err = vt_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
177 map->width = dst->width;
178 map->height = dst->height;
180 err = av_frame_copy(dst, map);
190 static int vt_transfer_data_to(AVHWFramesContext *hwfc,
191 AVFrame *dst, const AVFrame *src)
196 if (src->width > hwfc->width || src->height > hwfc->height)
197 return AVERROR(EINVAL);
199 map = av_frame_alloc();
201 return AVERROR(ENOMEM);
202 map->format = src->format;
204 err = vt_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
208 map->width = src->width;
209 map->height = src->height;
211 err = av_frame_copy(map, src);
221 static int vt_device_create(AVHWDeviceContext *ctx, const char *device,
222 AVDictionary *opts, int flags)
224 if (device && device[0]) {
225 av_log(ctx, AV_LOG_ERROR, "Device selection unsupported.\n");
226 return AVERROR_UNKNOWN;
232 const HWContextType ff_hwcontext_type_videotoolbox = {
233 .type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
234 .name = "videotoolbox",
236 .device_create = vt_device_create,
237 .frames_get_buffer = vt_get_buffer,
238 .transfer_get_formats = vt_transfer_get_formats,
239 .transfer_data_to = vt_transfer_data_to,
240 .transfer_data_from = vt_transfer_data_from,
242 .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NONE },