Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Copyright (c) 2012, Xidorn Quan | |
3 | * | |
4 | * This file is part of FFmpeg. | |
5 | * | |
6 | * FFmpeg is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * FFmpeg is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with FFmpeg; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * @file | |
23 | * H.264 decoder via VDA | |
24 | * @author Xidorn Quan <quanxunzhen@gmail.com> | |
25 | */ | |
26 | ||
27 | #include <string.h> | |
28 | #include <CoreFoundation/CoreFoundation.h> | |
29 | ||
30 | #include "vda.h" | |
31 | #include "h264.h" | |
32 | #include "avcodec.h" | |
33 | ||
34 | #ifndef kCFCoreFoundationVersionNumber10_7 | |
35 | #define kCFCoreFoundationVersionNumber10_7 635.00 | |
36 | #endif | |
37 | ||
38 | extern AVCodec ff_h264_decoder, ff_h264_vda_decoder; | |
39 | ||
40 | static const enum AVPixelFormat vda_pixfmts_prior_10_7[] = { | |
41 | AV_PIX_FMT_UYVY422, | |
42 | AV_PIX_FMT_YUV420P, | |
43 | AV_PIX_FMT_NONE | |
44 | }; | |
45 | ||
46 | static const enum AVPixelFormat vda_pixfmts[] = { | |
47 | AV_PIX_FMT_UYVY422, | |
48 | AV_PIX_FMT_YUYV422, | |
49 | AV_PIX_FMT_NV12, | |
50 | AV_PIX_FMT_YUV420P, | |
51 | AV_PIX_FMT_NONE | |
52 | }; | |
53 | ||
54 | typedef struct { | |
55 | H264Context h264ctx; | |
56 | int h264_initialized; | |
57 | struct vda_context vda_ctx; | |
58 | enum AVPixelFormat pix_fmt; | |
59 | ||
60 | /* for backing-up fields set by user. | |
61 | * we have to gain full control of such fields here */ | |
62 | void *hwaccel_context; | |
63 | enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt); | |
64 | int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags); | |
65 | #if FF_API_GET_BUFFER | |
66 | int (*get_buffer)(struct AVCodecContext *c, AVFrame *pic); | |
67 | #endif | |
68 | } VDADecoderContext; | |
69 | ||
70 | static enum AVPixelFormat get_format(struct AVCodecContext *avctx, | |
71 | const enum AVPixelFormat *fmt) | |
72 | { | |
73 | return AV_PIX_FMT_VDA_VLD; | |
74 | } | |
75 | ||
76 | typedef struct { | |
77 | CVPixelBufferRef cv_buffer; | |
78 | } VDABufferContext; | |
79 | ||
80 | static void release_buffer(void *opaque, uint8_t *data) | |
81 | { | |
82 | VDABufferContext *context = opaque; | |
83 | CVPixelBufferUnlockBaseAddress(context->cv_buffer, 0); | |
84 | CVPixelBufferRelease(context->cv_buffer); | |
85 | av_free(context); | |
86 | } | |
87 | ||
88 | static int get_buffer2(AVCodecContext *avctx, AVFrame *pic, int flag) | |
89 | { | |
90 | VDABufferContext *context = av_mallocz(sizeof(VDABufferContext)); | |
91 | AVBufferRef *buffer = av_buffer_create(NULL, 0, release_buffer, context, 0); | |
92 | if (!context || !buffer) { | |
93 | av_free(context); | |
94 | return AVERROR(ENOMEM); | |
95 | } | |
96 | ||
97 | pic->buf[0] = buffer; | |
98 | pic->data[0] = (void *)1; | |
99 | return 0; | |
100 | } | |
101 | ||
102 | static inline void set_context(AVCodecContext *avctx) | |
103 | { | |
104 | VDADecoderContext *ctx = avctx->priv_data; | |
105 | ctx->hwaccel_context = avctx->hwaccel_context; | |
106 | avctx->hwaccel_context = &ctx->vda_ctx; | |
107 | ctx->get_format = avctx->get_format; | |
108 | avctx->get_format = get_format; | |
109 | ctx->get_buffer2 = avctx->get_buffer2; | |
110 | avctx->get_buffer2 = get_buffer2; | |
111 | #if FF_API_GET_BUFFER | |
112 | ctx->get_buffer = avctx->get_buffer; | |
113 | avctx->get_buffer = NULL; | |
114 | #endif | |
115 | } | |
116 | ||
117 | static inline void restore_context(AVCodecContext *avctx) | |
118 | { | |
119 | VDADecoderContext *ctx = avctx->priv_data; | |
120 | avctx->hwaccel_context = ctx->hwaccel_context; | |
121 | avctx->get_format = ctx->get_format; | |
122 | avctx->get_buffer2 = ctx->get_buffer2; | |
123 | #if FF_API_GET_BUFFER | |
124 | avctx->get_buffer = ctx->get_buffer; | |
125 | #endif | |
126 | } | |
127 | ||
128 | static int vdadec_decode(AVCodecContext *avctx, | |
129 | void *data, int *got_frame, AVPacket *avpkt) | |
130 | { | |
131 | VDADecoderContext *ctx = avctx->priv_data; | |
132 | AVFrame *pic = data; | |
133 | int ret; | |
134 | ||
135 | set_context(avctx); | |
136 | ret = ff_h264_decoder.decode(avctx, data, got_frame, avpkt); | |
137 | restore_context(avctx); | |
138 | if (*got_frame) { | |
139 | AVBufferRef *buffer = pic->buf[0]; | |
140 | VDABufferContext *context = av_buffer_get_opaque(buffer); | |
141 | CVPixelBufferRef cv_buffer = (CVPixelBufferRef)pic->data[3]; | |
142 | ||
143 | CVPixelBufferRetain(cv_buffer); | |
144 | CVPixelBufferLockBaseAddress(cv_buffer, 0); | |
145 | context->cv_buffer = cv_buffer; | |
146 | pic->format = ctx->pix_fmt; | |
147 | if (CVPixelBufferIsPlanar(cv_buffer)) { | |
148 | int i, count = CVPixelBufferGetPlaneCount(cv_buffer); | |
149 | av_assert0(count < 4); | |
150 | for (i = 0; i < count; i++) { | |
151 | pic->data[i] = CVPixelBufferGetBaseAddressOfPlane(cv_buffer, i); | |
152 | pic->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(cv_buffer, i); | |
153 | } | |
154 | } else { | |
155 | pic->data[0] = CVPixelBufferGetBaseAddress(cv_buffer); | |
156 | pic->linesize[0] = CVPixelBufferGetBytesPerRow(cv_buffer); | |
157 | } | |
158 | } | |
159 | avctx->pix_fmt = ctx->pix_fmt; | |
160 | ||
161 | return ret; | |
162 | } | |
163 | ||
164 | static av_cold int vdadec_close(AVCodecContext *avctx) | |
165 | { | |
166 | VDADecoderContext *ctx = avctx->priv_data; | |
167 | /* release buffers and decoder */ | |
168 | ff_vda_destroy_decoder(&ctx->vda_ctx); | |
169 | /* close H.264 decoder */ | |
170 | if (ctx->h264_initialized) { | |
171 | set_context(avctx); | |
172 | ff_h264_decoder.close(avctx); | |
173 | restore_context(avctx); | |
174 | } | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static av_cold int vdadec_init(AVCodecContext *avctx) | |
179 | { | |
180 | VDADecoderContext *ctx = avctx->priv_data; | |
181 | struct vda_context *vda_ctx = &ctx->vda_ctx; | |
182 | OSStatus status; | |
183 | int ret, i; | |
184 | ||
185 | ctx->h264_initialized = 0; | |
186 | ||
187 | /* init pix_fmts of codec */ | |
188 | if (!ff_h264_vda_decoder.pix_fmts) { | |
189 | if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7) | |
190 | ff_h264_vda_decoder.pix_fmts = vda_pixfmts_prior_10_7; | |
191 | else | |
192 | ff_h264_vda_decoder.pix_fmts = vda_pixfmts; | |
193 | } | |
194 | ||
195 | /* init vda */ | |
196 | memset(vda_ctx, 0, sizeof(struct vda_context)); | |
197 | vda_ctx->width = avctx->width; | |
198 | vda_ctx->height = avctx->height; | |
199 | vda_ctx->format = 'avc1'; | |
200 | vda_ctx->use_sync_decoding = 1; | |
201 | vda_ctx->use_ref_buffer = 1; | |
202 | ctx->pix_fmt = avctx->get_format(avctx, avctx->codec->pix_fmts); | |
203 | switch (ctx->pix_fmt) { | |
204 | case AV_PIX_FMT_UYVY422: | |
205 | vda_ctx->cv_pix_fmt_type = '2vuy'; | |
206 | break; | |
207 | case AV_PIX_FMT_YUYV422: | |
208 | vda_ctx->cv_pix_fmt_type = 'yuvs'; | |
209 | break; | |
210 | case AV_PIX_FMT_NV12: | |
211 | vda_ctx->cv_pix_fmt_type = '420v'; | |
212 | break; | |
213 | case AV_PIX_FMT_YUV420P: | |
214 | vda_ctx->cv_pix_fmt_type = 'y420'; | |
215 | break; | |
216 | default: | |
217 | av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format: %d\n", avctx->pix_fmt); | |
218 | goto failed; | |
219 | } | |
220 | status = ff_vda_create_decoder(vda_ctx, | |
221 | avctx->extradata, avctx->extradata_size); | |
222 | if (status != kVDADecoderNoErr) { | |
223 | av_log(avctx, AV_LOG_ERROR, | |
224 | "Failed to init VDA decoder: %d.\n", status); | |
225 | goto failed; | |
226 | } | |
227 | ||
228 | /* init H.264 decoder */ | |
229 | set_context(avctx); | |
230 | ret = ff_h264_decoder.init(avctx); | |
231 | restore_context(avctx); | |
232 | if (ret < 0) { | |
233 | av_log(avctx, AV_LOG_ERROR, "Failed to open H.264 decoder.\n"); | |
234 | goto failed; | |
235 | } | |
236 | ctx->h264_initialized = 1; | |
237 | ||
238 | for (i = 0; i < MAX_SPS_COUNT; i++) { | |
239 | SPS *sps = ctx->h264ctx.sps_buffers[i]; | |
240 | if (sps && (sps->bit_depth_luma != 8 || | |
241 | sps->chroma_format_idc == 2 || | |
242 | sps->chroma_format_idc == 3)) { | |
243 | av_log(avctx, AV_LOG_ERROR, "Format is not supported.\n"); | |
244 | goto failed; | |
245 | } | |
246 | } | |
247 | ||
248 | return 0; | |
249 | ||
250 | failed: | |
251 | vdadec_close(avctx); | |
252 | return -1; | |
253 | } | |
254 | ||
255 | static void vdadec_flush(AVCodecContext *avctx) | |
256 | { | |
257 | set_context(avctx); | |
258 | ff_h264_decoder.flush(avctx); | |
259 | restore_context(avctx); | |
260 | } | |
261 | ||
262 | AVCodec ff_h264_vda_decoder = { | |
263 | .name = "h264_vda", | |
264 | .type = AVMEDIA_TYPE_VIDEO, | |
265 | .id = AV_CODEC_ID_H264, | |
266 | .priv_data_size = sizeof(VDADecoderContext), | |
267 | .init = vdadec_init, | |
268 | .close = vdadec_close, | |
269 | .decode = vdadec_decode, | |
270 | .capabilities = CODEC_CAP_DELAY, | |
271 | .flush = vdadec_flush, | |
272 | .long_name = NULL_IF_CONFIG_SMALL("H.264 (VDA acceleration)"), | |
273 | }; |