]> git.sesse.net Git - ffmpeg/blob - libavcodec/vda.c
vda: check allocation result.
[ffmpeg] / libavcodec / vda.c
1 /*
2  * VDA 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 <pthread.h>
24 #include <CoreFoundation/CFDictionary.h>
25 #include <CoreFoundation/CFNumber.h>
26 #include <CoreFoundation/CFData.h>
27 #include <CoreFoundation/CFString.h>
28
29 #include "avcodec.h"
30 #include "vda_internal.h"
31
32 /**
33  * \addtogroup VDA_Decoding
34  *
35  * @{
36  */
37
38 /* Mutex manager callback. */
39 static int vda_lock_operation(void **mtx, enum AVLockOp op)
40 {
41     switch(op)
42     {
43         case AV_LOCK_CREATE:
44             *mtx = av_malloc(sizeof(pthread_mutex_t));
45             if(!*mtx)
46                 return 1;
47             return !!pthread_mutex_init(*mtx, NULL);
48         case AV_LOCK_OBTAIN:
49             return !!pthread_mutex_lock(*mtx);
50         case AV_LOCK_RELEASE:
51             return !!pthread_mutex_unlock(*mtx);
52         case AV_LOCK_DESTROY:
53             pthread_mutex_destroy(*mtx);
54             av_freep(mtx);
55             return 0;
56     }
57     return 1;
58 }
59
60 /* Helper to create a dictionary according to the given pts. */
61 static CFDictionaryRef vda_dictionary_with_pts(int64_t i_pts)
62 {
63     CFStringRef key = CFSTR("FF_VDA_DECODER_PTS_KEY");
64     CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &i_pts);
65     CFDictionaryRef user_info = CFDictionaryCreate( kCFAllocatorDefault,
66                                                     (const void **)&key,
67                                                     (const void **)&value,
68                                                     1,
69                                                     &kCFTypeDictionaryKeyCallBacks,
70                                                     &kCFTypeDictionaryValueCallBacks);
71     CFRelease(value);
72     return user_info;
73 }
74
75 /* Helper to retrieve the pts from the given dictionary. */
76 static int64_t vda_pts_from_dictionary(CFDictionaryRef user_info)
77 {
78     CFNumberRef pts;
79     int64_t outValue = 0;
80
81     if (!user_info)
82         return 0;
83
84     pts = CFDictionaryGetValue(user_info, CFSTR("FF_VDA_DECODER_PTS_KEY"));
85
86     if (pts)
87         CFNumberGetValue(pts, kCFNumberSInt64Type, &outValue);
88
89     return outValue;
90 }
91
92 /* Removes and releases all frames from the queue. */
93 static void vda_clear_queue(struct vda_context *vda_ctx)
94 {
95     vda_frame *top_frame;
96
97     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_OBTAIN);
98
99     while (vda_ctx->queue)
100     {
101         top_frame = vda_ctx->queue;
102         vda_ctx->queue = top_frame->next_frame;
103         ff_vda_release_vda_frame(top_frame);
104     }
105
106     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_RELEASE);
107 }
108
109
110 /* Decoder callback that adds the vda frame to the queue in display order. */
111 static void vda_decoder_callback (void *vda_hw_ctx,
112                                   CFDictionaryRef user_info,
113                                   OSStatus status,
114                                   uint32_t infoFlags,
115                                   CVImageBufferRef image_buffer)
116 {
117     struct vda_context *vda_ctx = (struct vda_context*)vda_hw_ctx;
118     vda_frame *new_frame;
119     vda_frame *queue_walker;
120
121     if (!image_buffer)
122         return;
123
124     if (vda_ctx->cv_pix_fmt_type != CVPixelBufferGetPixelFormatType(image_buffer))
125         return;
126
127     new_frame = av_mallocz(sizeof(vda_frame));
128     if (!new_frame)
129         return;
130
131     new_frame->next_frame = NULL;
132     new_frame->cv_buffer = CVPixelBufferRetain(image_buffer);
133     new_frame->pts = vda_pts_from_dictionary(user_info);
134
135     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_OBTAIN);
136
137     queue_walker = vda_ctx->queue;
138
139     if (!queue_walker || (new_frame->pts < queue_walker->pts))
140     {
141         /* we have an empty queue, or this frame earlier than the current queue head */
142         new_frame->next_frame = queue_walker;
143         vda_ctx->queue = new_frame;
144     }
145     else
146     {
147         /* walk the queue and insert this frame where it belongs in display order */
148         vda_frame *next_frame;
149
150         while (1)
151         {
152             next_frame = queue_walker->next_frame;
153
154             if (!next_frame || (new_frame->pts < next_frame->pts))
155             {
156                 new_frame->next_frame = next_frame;
157                 queue_walker->next_frame = new_frame;
158                 break;
159             }
160             queue_walker = next_frame;
161         }
162     }
163
164     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_RELEASE);
165 }
166
167 int ff_vda_create_decoder(struct vda_context *vda_ctx,
168                           uint8_t *extradata,
169                           int extradata_size)
170 {
171     OSStatus status = kVDADecoderNoErr;
172     CFNumberRef height;
173     CFNumberRef width;
174     CFNumberRef format;
175     CFDataRef avc_data;
176     CFMutableDictionaryRef config_info;
177     CFMutableDictionaryRef buffer_attributes;
178     CFMutableDictionaryRef io_surface_properties;
179     CFNumberRef cv_pix_fmt;
180
181     vda_ctx->bitstream = NULL;
182     vda_ctx->ref_size = 0;
183
184     if (av_lockmgr_register(vda_lock_operation))
185         return -1;
186
187     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_CREATE);
188
189     config_info = CFDictionaryCreateMutable(kCFAllocatorDefault,
190                                             4,
191                                             &kCFTypeDictionaryKeyCallBacks,
192                                             &kCFTypeDictionaryValueCallBacks);
193
194     height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->height);
195     width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->width);
196     format = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->format);
197     avc_data = CFDataCreate(kCFAllocatorDefault, extradata, extradata_size);
198
199     CFDictionarySetValue(config_info, kVDADecoderConfiguration_Height, height);
200     CFDictionarySetValue(config_info, kVDADecoderConfiguration_Width, width);
201     CFDictionarySetValue(config_info, kVDADecoderConfiguration_SourceFormat, format);
202     CFDictionarySetValue(config_info, kVDADecoderConfiguration_avcCData, avc_data);
203
204     buffer_attributes = CFDictionaryCreateMutable(kCFAllocatorDefault,
205                                                   2,
206                                                   &kCFTypeDictionaryKeyCallBacks,
207                                                   &kCFTypeDictionaryValueCallBacks);
208     io_surface_properties = CFDictionaryCreateMutable(kCFAllocatorDefault,
209                                                       0,
210                                                       &kCFTypeDictionaryKeyCallBacks,
211                                                       &kCFTypeDictionaryValueCallBacks);
212     cv_pix_fmt  = CFNumberCreate(kCFAllocatorDefault,
213                                  kCFNumberSInt32Type,
214                                  &vda_ctx->cv_pix_fmt_type);
215     CFDictionarySetValue(buffer_attributes,
216                          kCVPixelBufferPixelFormatTypeKey,
217                          cv_pix_fmt);
218     CFDictionarySetValue(buffer_attributes,
219                          kCVPixelBufferIOSurfacePropertiesKey,
220                          io_surface_properties);
221
222     status = VDADecoderCreate( config_info,
223                                buffer_attributes,
224                                (VDADecoderOutputCallback *)vda_decoder_callback,
225                                vda_ctx,
226                                &vda_ctx->decoder );
227
228     CFRelease(height);
229     CFRelease(width);
230     CFRelease(format);
231     CFRelease(avc_data);
232     CFRelease(config_info);
233     CFRelease(io_surface_properties);
234     CFRelease(cv_pix_fmt);
235     CFRelease(buffer_attributes);
236
237     if (kVDADecoderNoErr != status)
238         return status;
239
240     return 0;
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     vda_clear_queue(vda_ctx);
251
252     if (vda_ctx->queue_mutex)
253         vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_DESTROY);
254
255     if (vda_ctx->bitstream)
256         av_freep(&vda_ctx->bitstream);
257
258     if (kVDADecoderNoErr != status)
259         return status;
260
261     return 0;
262 }
263
264 vda_frame *ff_vda_queue_pop(struct vda_context *vda_ctx)
265 {
266     vda_frame *top_frame;
267
268     if (!vda_ctx->queue)
269         return NULL;
270
271     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_OBTAIN);
272     top_frame = vda_ctx->queue;
273     vda_ctx->queue = top_frame->next_frame;
274     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_RELEASE);
275
276     return top_frame;
277 }
278
279 void ff_vda_release_vda_frame(vda_frame *frame)
280 {
281     if (frame)
282     {
283         CVPixelBufferRelease(frame->cv_buffer);
284         av_freep(&frame);
285     }
286 }
287
288 int ff_vda_decoder_decode(struct vda_context *vda_ctx,
289                           uint8_t *bitstream,
290                           int bitstream_size,
291                           int64_t frame_pts)
292 {
293     OSStatus status = kVDADecoderNoErr;
294     CFDictionaryRef user_info;
295     CFDataRef coded_frame;
296
297     coded_frame = CFDataCreate(kCFAllocatorDefault, bitstream, bitstream_size);
298     user_info = vda_dictionary_with_pts(frame_pts);
299
300     status = VDADecoderDecode(vda_ctx->decoder, 0, coded_frame, user_info);
301
302     CFRelease(user_info);
303     CFRelease(coded_frame);
304
305     if (kVDADecoderNoErr != status)
306         return status;
307
308     return 0;
309 }
310
311 /* @} */