Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * VDA H264 HW acceleration. |
||
3 | * |
||
4 | * copyright (c) 2011 Sebastien Zwickert |
||
5 | * |
||
6 | * This file is part of FFmpeg. |
||
7 | * |
||
8 | * FFmpeg is free software; you can redistribute it and/or |
||
9 | * modify it under the terms of the GNU Lesser General Public |
||
10 | * License as published by the Free Software Foundation; either |
||
11 | * version 2.1 of the License, or (at your option) any later version. |
||
12 | * |
||
13 | * FFmpeg is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
16 | * Lesser General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU Lesser General Public |
||
19 | * License along with FFmpeg; if not, write to the Free Software |
||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||
21 | */ |
||
22 | |||
23 | #include |
||
24 | #include |
||
25 | #include |
||
26 | |||
27 | #include "vda.h" |
||
28 | #include "libavutil/avutil.h" |
||
29 | #include "h264.h" |
||
30 | |||
31 | struct vda_buffer { |
||
32 | CVPixelBufferRef cv_buffer; |
||
33 | }; |
||
34 | |||
35 | /* Decoder callback that adds the vda frame to the queue in display order. */ |
||
36 | static void vda_decoder_callback (void *vda_hw_ctx, |
||
37 | CFDictionaryRef user_info, |
||
38 | OSStatus status, |
||
39 | uint32_t infoFlags, |
||
40 | CVImageBufferRef image_buffer) |
||
41 | { |
||
42 | struct vda_context *vda_ctx = vda_hw_ctx; |
||
43 | |||
44 | if (!image_buffer) |
||
45 | return; |
||
46 | |||
47 | if (vda_ctx->cv_pix_fmt_type != CVPixelBufferGetPixelFormatType(image_buffer)) |
||
48 | return; |
||
49 | |||
50 | vda_ctx->cv_buffer = CVPixelBufferRetain(image_buffer); |
||
51 | } |
||
52 | |||
53 | static int vda_sync_decode(struct vda_context *vda_ctx) |
||
54 | { |
||
55 | OSStatus status; |
||
56 | CFDataRef coded_frame; |
||
57 | uint32_t flush_flags = 1 << 0; ///< kVDADecoderFlush_emitFrames |
||
58 | |||
59 | coded_frame = CFDataCreate(kCFAllocatorDefault, |
||
60 | vda_ctx->priv_bitstream, |
||
61 | vda_ctx->priv_bitstream_size); |
||
62 | |||
63 | status = VDADecoderDecode(vda_ctx->decoder, 0, coded_frame, NULL); |
||
64 | |||
65 | if (kVDADecoderNoErr == status) |
||
66 | status = VDADecoderFlush(vda_ctx->decoder, flush_flags); |
||
67 | |||
68 | CFRelease(coded_frame); |
||
69 | |||
70 | return status; |
||
71 | } |
||
72 | |||
73 | |||
74 | static int vda_h264_start_frame(AVCodecContext *avctx, |
||
75 | av_unused const uint8_t *buffer, |
||
76 | av_unused uint32_t size) |
||
77 | { |
||
78 | struct vda_context *vda_ctx = avctx->hwaccel_context; |
||
79 | |||
80 | if (!vda_ctx->decoder) |
||
81 | return -1; |
||
82 | |||
83 | vda_ctx->priv_bitstream_size = 0; |
||
84 | |||
85 | return 0; |
||
86 | } |
||
87 | |||
88 | static int vda_h264_decode_slice(AVCodecContext *avctx, |
||
89 | const uint8_t *buffer, |
||
90 | uint32_t size) |
||
91 | { |
||
92 | struct vda_context *vda_ctx = avctx->hwaccel_context; |
||
93 | void *tmp; |
||
94 | |||
95 | if (!vda_ctx->decoder) |
||
96 | return -1; |
||
97 | |||
98 | tmp = av_fast_realloc(vda_ctx->priv_bitstream, |
||
99 | &vda_ctx->priv_allocated_size, |
||
100 | vda_ctx->priv_bitstream_size + size + 4); |
||
101 | if (!tmp) |
||
102 | return AVERROR(ENOMEM); |
||
103 | |||
104 | vda_ctx->priv_bitstream = tmp; |
||
105 | |||
106 | AV_WB32(vda_ctx->priv_bitstream + vda_ctx->priv_bitstream_size, size); |
||
107 | memcpy(vda_ctx->priv_bitstream + vda_ctx->priv_bitstream_size + 4, buffer, size); |
||
108 | |||
109 | vda_ctx->priv_bitstream_size += size + 4; |
||
110 | |||
111 | return 0; |
||
112 | } |
||
113 | |||
114 | static void vda_h264_release_buffer(void *opaque, uint8_t *data) |
||
115 | { |
||
116 | struct vda_buffer *context = opaque; |
||
117 | CVPixelBufferRelease(context->cv_buffer); |
||
118 | av_free(context); |
||
119 | } |
||
120 | |||
121 | static int vda_h264_end_frame(AVCodecContext *avctx) |
||
122 | { |
||
123 | H264Context *h = avctx->priv_data; |
||
124 | struct vda_context *vda_ctx = avctx->hwaccel_context; |
||
125 | AVFrame *frame = &h->cur_pic_ptr->f; |
||
126 | struct vda_buffer *context; |
||
127 | AVBufferRef *buffer; |
||
128 | int status; |
||
129 | |||
130 | if (!vda_ctx->decoder || !vda_ctx->priv_bitstream) |
||
131 | return -1; |
||
132 | |||
133 | status = vda_sync_decode(vda_ctx); |
||
134 | frame->data[3] = (void*)vda_ctx->cv_buffer; |
||
135 | |||
136 | if (status) |
||
137 | av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%d)\n", status); |
||
138 | |||
139 | if (!vda_ctx->use_ref_buffer || status) |
||
140 | return status; |
||
141 | |||
142 | context = av_mallocz(sizeof(*context)); |
||
143 | buffer = av_buffer_create(NULL, 0, vda_h264_release_buffer, context, 0); |
||
144 | if (!context || !buffer) { |
||
145 | CVPixelBufferRelease(vda_ctx->cv_buffer); |
||
146 | av_free(context); |
||
147 | return -1; |
||
148 | } |
||
149 | |||
150 | context->cv_buffer = vda_ctx->cv_buffer; |
||
151 | frame->buf[3] = buffer; |
||
152 | |||
153 | return status; |
||
154 | } |
||
155 | |||
156 | int ff_vda_create_decoder(struct vda_context *vda_ctx, |
||
157 | uint8_t *extradata, |
||
158 | int extradata_size) |
||
159 | { |
||
160 | OSStatus status; |
||
161 | CFNumberRef height; |
||
162 | CFNumberRef width; |
||
163 | CFNumberRef format; |
||
164 | CFDataRef avc_data; |
||
165 | CFMutableDictionaryRef config_info; |
||
166 | CFMutableDictionaryRef buffer_attributes; |
||
167 | CFMutableDictionaryRef io_surface_properties; |
||
168 | CFNumberRef cv_pix_fmt; |
||
169 | |||
170 | vda_ctx->priv_bitstream = NULL; |
||
171 | vda_ctx->priv_allocated_size = 0; |
||
172 | |||
173 | /* Each VCL NAL in the bitstream sent to the decoder |
||
174 | * is preceded by a 4 bytes length header. |
||
175 | * Change the avcC atom header if needed, to signal headers of 4 bytes. */ |
||
176 | if (extradata_size >= 4 && (extradata[4] & 0x03) != 0x03) { |
||
177 | uint8_t *rw_extradata; |
||
178 | |||
179 | if (!(rw_extradata = av_malloc(extradata_size))) |
||
180 | return AVERROR(ENOMEM); |
||
181 | |||
182 | memcpy(rw_extradata, extradata, extradata_size); |
||
183 | |||
184 | rw_extradata[4] |= 0x03; |
||
185 | |||
186 | avc_data = CFDataCreate(kCFAllocatorDefault, rw_extradata, extradata_size); |
||
187 | |||
188 | av_freep(&rw_extradata); |
||
189 | } else { |
||
190 | avc_data = CFDataCreate(kCFAllocatorDefault, extradata, extradata_size); |
||
191 | } |
||
192 | |||
193 | config_info = CFDictionaryCreateMutable(kCFAllocatorDefault, |
||
194 | 4, |
||
195 | &kCFTypeDictionaryKeyCallBacks, |
||
196 | &kCFTypeDictionaryValueCallBacks); |
||
197 | |||
198 | height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->height); |
||
199 | width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->width); |
||
200 | format = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->format); |
||
201 | |||
202 | CFDictionarySetValue(config_info, kVDADecoderConfiguration_Height, height); |
||
203 | CFDictionarySetValue(config_info, kVDADecoderConfiguration_Width, width); |
||
204 | CFDictionarySetValue(config_info, kVDADecoderConfiguration_SourceFormat, format); |
||
205 | CFDictionarySetValue(config_info, kVDADecoderConfiguration_avcCData, avc_data); |
||
206 | |||
207 | buffer_attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, |
||
208 | 2, |
||
209 | &kCFTypeDictionaryKeyCallBacks, |
||
210 | &kCFTypeDictionaryValueCallBacks); |
||
211 | io_surface_properties = CFDictionaryCreateMutable(kCFAllocatorDefault, |
||
212 | 0, |
||
213 | &kCFTypeDictionaryKeyCallBacks, |
||
214 | &kCFTypeDictionaryValueCallBacks); |
||
215 | cv_pix_fmt = CFNumberCreate(kCFAllocatorDefault, |
||
216 | kCFNumberSInt32Type, |
||
217 | &vda_ctx->cv_pix_fmt_type); |
||
218 | CFDictionarySetValue(buffer_attributes, |
||
219 | kCVPixelBufferPixelFormatTypeKey, |
||
220 | cv_pix_fmt); |
||
221 | CFDictionarySetValue(buffer_attributes, |
||
222 | kCVPixelBufferIOSurfacePropertiesKey, |
||
223 | io_surface_properties); |
||
224 | |||
225 | status = VDADecoderCreate(config_info, |
||
226 | buffer_attributes, |
||
227 | vda_decoder_callback, |
||
228 | vda_ctx, |
||
229 | &vda_ctx->decoder); |
||
230 | |||
231 | CFRelease(height); |
||
232 | CFRelease(width); |
||
233 | CFRelease(format); |
||
234 | CFRelease(avc_data); |
||
235 | CFRelease(config_info); |
||
236 | CFRelease(io_surface_properties); |
||
237 | CFRelease(cv_pix_fmt); |
||
238 | CFRelease(buffer_attributes); |
||
239 | |||
240 | return status; |
||
241 | } |
||
242 | |||
243 | int ff_vda_destroy_decoder(struct vda_context *vda_ctx) |
||
244 | { |
||
245 | OSStatus status = kVDADecoderNoErr; |
||
246 | |||
247 | if (vda_ctx->decoder) |
||
248 | status = VDADecoderDestroy(vda_ctx->decoder); |
||
249 | |||
250 | av_freep(&vda_ctx->priv_bitstream); |
||
251 | |||
252 | return status; |
||
253 | } |
||
254 | |||
255 | AVHWAccel ff_h264_vda_hwaccel = { |
||
256 | .name = "h264_vda", |
||
257 | .type = AVMEDIA_TYPE_VIDEO, |
||
258 | .id = AV_CODEC_ID_H264, |
||
259 | .pix_fmt = AV_PIX_FMT_VDA_VLD, |
||
260 | .start_frame = vda_h264_start_frame, |
||
261 | .decode_slice = vda_h264_decode_slice, |
||
262 | .end_frame = vda_h264_end_frame, |
||
263 | };>><> |