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