]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_videotoolbox.c
Merge commit '4ce701b4e640d4723a4005d664f31f8342fac40e'
[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     enum AVPixelFormat pix_fmt;
38 } cv_pix_fmts[] = {
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 },
44 #endif
45 };
46
47 enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt)
48 {
49     int i;
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;
53     }
54     return AV_PIX_FMT_NONE;
55 }
56
57 uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt)
58 {
59     int i;
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;
63     }
64     return 0;
65 }
66
67 static int vt_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
68 {
69     frame->buf[0] = av_buffer_pool_get(ctx->pool);
70     if (!frame->buf[0])
71         return AVERROR(ENOMEM);
72
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;
77
78     return 0;
79 }
80
81 static int vt_transfer_get_formats(AVHWFramesContext *ctx,
82                                    enum AVHWFrameTransferDirection dir,
83                                    enum AVPixelFormat **formats)
84 {
85     enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts));
86     if (!fmts)
87         return AVERROR(ENOMEM);
88
89     fmts[0] = ctx->sw_format;
90     fmts[1] = AV_PIX_FMT_NONE;
91
92     *formats = fmts;
93     return 0;
94 }
95
96 static void vt_unmap(AVHWFramesContext *ctx, HWMapDescriptor *hwmap)
97 {
98     CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->source->data[3];
99
100     CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->priv);
101 }
102
103 static int vt_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src,
104                         int flags)
105 {
106     CVPixelBufferRef pixbuf = (CVPixelBufferRef)src->data[3];
107     OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf);
108     CVReturn err;
109     uint32_t map_flags = 0;
110     int ret;
111     int i;
112     enum AVPixelFormat format;
113
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;
119     }
120
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;
125     }
126
127     if (flags == AV_HWFRAME_MAP_READ)
128         map_flags = kCVPixelBufferLock_ReadOnly;
129
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;
134     }
135
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);
141         }
142     } else {
143         dst->data[0]     = CVPixelBufferGetBaseAddress(pixbuf);
144         dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf);
145     }
146
147     ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, vt_unmap,
148                                 (void *)(uintptr_t)map_flags);
149     if (ret < 0)
150         goto unlock;
151
152     return 0;
153
154 unlock:
155     CVPixelBufferUnlockBaseAddress(pixbuf, map_flags);
156     return ret;
157 }
158
159 static int vt_transfer_data_from(AVHWFramesContext *hwfc,
160                                  AVFrame *dst, const AVFrame *src)
161 {
162     AVFrame *map;
163     int err;
164
165     if (dst->width > hwfc->width || dst->height > hwfc->height)
166         return AVERROR(EINVAL);
167
168     map = av_frame_alloc();
169     if (!map)
170         return AVERROR(ENOMEM);
171     map->format = dst->format;
172
173     err = vt_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
174     if (err)
175         goto fail;
176
177     map->width  = dst->width;
178     map->height = dst->height;
179
180     err = av_frame_copy(dst, map);
181     if (err)
182         goto fail;
183
184     err = 0;
185 fail:
186     av_frame_free(&map);
187     return err;
188 }
189
190 static int vt_transfer_data_to(AVHWFramesContext *hwfc,
191                                AVFrame *dst, const AVFrame *src)
192 {
193     AVFrame *map;
194     int err;
195
196     if (src->width > hwfc->width || src->height > hwfc->height)
197         return AVERROR(EINVAL);
198
199     map = av_frame_alloc();
200     if (!map)
201         return AVERROR(ENOMEM);
202     map->format = src->format;
203
204     err = vt_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
205     if (err)
206         goto fail;
207
208     map->width  = src->width;
209     map->height = src->height;
210
211     err = av_frame_copy(map, src);
212     if (err)
213         goto fail;
214
215     err = 0;
216 fail:
217     av_frame_free(&map);
218     return err;
219 }
220
221 static int vt_device_create(AVHWDeviceContext *ctx, const char *device,
222                             AVDictionary *opts, int flags)
223 {
224     if (device && device[0]) {
225         av_log(ctx, AV_LOG_ERROR, "Device selection unsupported.\n");
226         return AVERROR_UNKNOWN;
227     }
228
229     return 0;
230 }
231
232 const HWContextType ff_hwcontext_type_videotoolbox = {
233     .type                 = AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
234     .name                 = "videotoolbox",
235
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,
241
242     .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NONE },
243 };