]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_videotoolbox.c
avformat/avio: Add Metacube support
[ffmpeg] / libavutil / hwcontext_videotoolbox.c
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 "common.h"
28 #include "hwcontext.h"
29 #include "hwcontext_internal.h"
30 #include "hwcontext_videotoolbox.h"
31 #include "mem.h"
32 #include "pixfmt.h"
33 #include "pixdesc.h"
34
35 static const struct {
36     uint32_t cv_fmt;
37     bool full_range;
38     enum AVPixelFormat pix_fmt;
39 } cv_pix_fmts[] = {
40     { kCVPixelFormatType_420YpCbCr8Planar,              false, AV_PIX_FMT_YUV420P },
41     { kCVPixelFormatType_422YpCbCr8,                    false, AV_PIX_FMT_UYVY422 },
42     { kCVPixelFormatType_32BGRA,                        false, AV_PIX_FMT_BGRA },
43 #ifdef kCFCoreFoundationVersionNumber10_7
44     { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,  false, AV_PIX_FMT_NV12 },
45     { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,   true,  AV_PIX_FMT_NV12 },
46 #endif
47 #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
48     { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P010 },
49     { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange,  true,  AV_PIX_FMT_P010 },
50 #endif
51 };
52
53 enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt)
54 {
55     int i;
56     for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
57         if (cv_pix_fmts[i].cv_fmt == cv_fmt)
58             return cv_pix_fmts[i].pix_fmt;
59     }
60     return AV_PIX_FMT_NONE;
61 }
62
63 uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt)
64 {
65     return av_map_videotoolbox_format_from_pixfmt2(pix_fmt, false);
66 }
67
68 uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt, bool full_range)
69 {
70     int i;
71     for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
72         if (cv_pix_fmts[i].pix_fmt == pix_fmt && cv_pix_fmts[i].full_range == full_range)
73             return cv_pix_fmts[i].cv_fmt;
74     }
75     return 0;
76 }
77
78 static int vt_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
79 {
80     frame->buf[0] = av_buffer_pool_get(ctx->pool);
81     if (!frame->buf[0])
82         return AVERROR(ENOMEM);
83
84     frame->data[3] = frame->buf[0]->data;
85     frame->format  = AV_PIX_FMT_VIDEOTOOLBOX;
86     frame->width   = ctx->width;
87     frame->height  = ctx->height;
88
89     return 0;
90 }
91
92 static int vt_transfer_get_formats(AVHWFramesContext *ctx,
93                                    enum AVHWFrameTransferDirection dir,
94                                    enum AVPixelFormat **formats)
95 {
96     enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts));
97     if (!fmts)
98         return AVERROR(ENOMEM);
99
100     fmts[0] = ctx->sw_format;
101     fmts[1] = AV_PIX_FMT_NONE;
102
103     *formats = fmts;
104     return 0;
105 }
106
107 static void vt_unmap(AVHWFramesContext *ctx, HWMapDescriptor *hwmap)
108 {
109     CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->source->data[3];
110
111     CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->priv);
112 }
113
114 static int vt_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src,
115                         int flags)
116 {
117     CVPixelBufferRef pixbuf = (CVPixelBufferRef)src->data[3];
118     OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf);
119     CVReturn err;
120     uint32_t map_flags = 0;
121     int ret;
122     int i;
123     enum AVPixelFormat format;
124
125     format = av_map_videotoolbox_format_to_pixfmt(pixel_format);
126     if (dst->format != format) {
127         av_log(ctx, AV_LOG_ERROR, "Unsupported or mismatching pixel format: %s\n",
128                av_fourcc2str(pixel_format));
129         return AVERROR_UNKNOWN;
130     }
131
132     if (CVPixelBufferGetWidth(pixbuf) != ctx->width ||
133         CVPixelBufferGetHeight(pixbuf) != ctx->height) {
134         av_log(ctx, AV_LOG_ERROR, "Inconsistent frame dimensions.\n");
135         return AVERROR_UNKNOWN;
136     }
137
138     if (flags == AV_HWFRAME_MAP_READ)
139         map_flags = kCVPixelBufferLock_ReadOnly;
140
141     err = CVPixelBufferLockBaseAddress(pixbuf, map_flags);
142     if (err != kCVReturnSuccess) {
143         av_log(ctx, AV_LOG_ERROR, "Error locking the pixel buffer.\n");
144         return AVERROR_UNKNOWN;
145     }
146
147     if (CVPixelBufferIsPlanar(pixbuf)) {
148         int planes = CVPixelBufferGetPlaneCount(pixbuf);
149         for (i = 0; i < planes; i++) {
150             dst->data[i]     = CVPixelBufferGetBaseAddressOfPlane(pixbuf, i);
151             dst->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf, i);
152         }
153     } else {
154         dst->data[0]     = CVPixelBufferGetBaseAddress(pixbuf);
155         dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf);
156     }
157
158     ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, vt_unmap,
159                                 (void *)(uintptr_t)map_flags);
160     if (ret < 0)
161         goto unlock;
162
163     return 0;
164
165 unlock:
166     CVPixelBufferUnlockBaseAddress(pixbuf, map_flags);
167     return ret;
168 }
169
170 static int vt_transfer_data_from(AVHWFramesContext *hwfc,
171                                  AVFrame *dst, const AVFrame *src)
172 {
173     AVFrame *map;
174     int err;
175
176     if (dst->width > hwfc->width || dst->height > hwfc->height)
177         return AVERROR(EINVAL);
178
179     map = av_frame_alloc();
180     if (!map)
181         return AVERROR(ENOMEM);
182     map->format = dst->format;
183
184     err = vt_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
185     if (err)
186         goto fail;
187
188     map->width  = dst->width;
189     map->height = dst->height;
190
191     err = av_frame_copy(dst, map);
192     if (err)
193         goto fail;
194
195     err = 0;
196 fail:
197     av_frame_free(&map);
198     return err;
199 }
200
201 static int vt_transfer_data_to(AVHWFramesContext *hwfc,
202                                AVFrame *dst, const AVFrame *src)
203 {
204     AVFrame *map;
205     int err;
206
207     if (src->width > hwfc->width || src->height > hwfc->height)
208         return AVERROR(EINVAL);
209
210     map = av_frame_alloc();
211     if (!map)
212         return AVERROR(ENOMEM);
213     map->format = src->format;
214
215     err = vt_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
216     if (err)
217         goto fail;
218
219     map->width  = src->width;
220     map->height = src->height;
221
222     err = av_frame_copy(map, src);
223     if (err)
224         goto fail;
225
226     err = 0;
227 fail:
228     av_frame_free(&map);
229     return err;
230 }
231
232 static int vt_device_create(AVHWDeviceContext *ctx, const char *device,
233                             AVDictionary *opts, int flags)
234 {
235     if (device && device[0]) {
236         av_log(ctx, AV_LOG_ERROR, "Device selection unsupported.\n");
237         return AVERROR_UNKNOWN;
238     }
239
240     return 0;
241 }
242
243 const HWContextType ff_hwcontext_type_videotoolbox = {
244     .type                 = AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
245     .name                 = "videotoolbox",
246
247     .device_create        = vt_device_create,
248     .frames_get_buffer    = vt_get_buffer,
249     .transfer_get_formats = vt_transfer_get_formats,
250     .transfer_data_to     = vt_transfer_data_to,
251     .transfer_data_from   = vt_transfer_data_from,
252
253     .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NONE },
254 };