20207eab7026b9c270049e9f7f86386ee6193138
[ffmpeg.git] / 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     if (extradata[4]==0xFE) {
183         // convert 3 byte NAL sizes to 4 byte
184         extradata[4] = 0xFF;
185     }
186
187     config_info = CFDictionaryCreateMutable(kCFAllocatorDefault,
188                                             4,
189                                             &kCFTypeDictionaryKeyCallBacks,
190                                             &kCFTypeDictionaryValueCallBacks);
191
192     height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->height);
193     width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->width);
194     format = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vda_ctx->format);
195     avc_data = CFDataCreate(kCFAllocatorDefault, extradata, extradata_size);
196
197     CFDictionarySetValue(config_info, kVDADecoderConfiguration_Height, height);
198     CFDictionarySetValue(config_info, kVDADecoderConfiguration_Width, width);
199     CFDictionarySetValue(config_info, kVDADecoderConfiguration_SourceFormat, format);
200     CFDictionarySetValue(config_info, kVDADecoderConfiguration_avcCData, avc_data);
201
202     buffer_attributes = CFDictionaryCreateMutable(kCFAllocatorDefault,
203                                                   2,
204                                                   &kCFTypeDictionaryKeyCallBacks,
205                                                   &kCFTypeDictionaryValueCallBacks);
206     io_surface_properties = CFDictionaryCreateMutable(kCFAllocatorDefault,
207                                                       0,
208                                                       &kCFTypeDictionaryKeyCallBacks,
209                                                       &kCFTypeDictionaryValueCallBacks);
210     cv_pix_fmt  = CFNumberCreate(kCFAllocatorDefault,
211                                  kCFNumberSInt32Type,
212                                  &vda_ctx->cv_pix_fmt_type);
213     CFDictionarySetValue(buffer_attributes,
214                          kCVPixelBufferPixelFormatTypeKey,
215                          cv_pix_fmt);
216     CFDictionarySetValue(buffer_attributes,
217                          kCVPixelBufferIOSurfacePropertiesKey,
218                          io_surface_properties);
219
220     status = VDADecoderCreate(config_info,
221                               buffer_attributes,
222                               (VDADecoderOutputCallback *)vda_decoder_callback,
223                               vda_ctx,
224                               &vda_ctx->decoder);
225
226     CFRelease(height);
227     CFRelease(width);
228     CFRelease(format);
229     CFRelease(avc_data);
230     CFRelease(config_info);
231     CFRelease(io_surface_properties);
232     CFRelease(cv_pix_fmt);
233     CFRelease(buffer_attributes);
234
235     if (kVDADecoderNoErr != status)
236         return status;
237
238     return 0;
239 }
240
241 int ff_vda_destroy_decoder(struct vda_context *vda_ctx)
242 {
243     OSStatus status = kVDADecoderNoErr;
244
245     if (vda_ctx->decoder)
246         status = VDADecoderDestroy(vda_ctx->decoder);
247
248     vda_clear_queue(vda_ctx);
249
250     if (vda_ctx->queue_mutex)
251         vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_DESTROY);
252
253     if (vda_ctx->bitstream)
254         av_freep(&vda_ctx->bitstream);
255
256     if (kVDADecoderNoErr != status)
257         return status;
258
259     return 0;
260 }
261
262 vda_frame *ff_vda_queue_pop(struct vda_context *vda_ctx)
263 {
264     vda_frame *top_frame;
265
266     if (!vda_ctx->queue)
267         return NULL;
268
269     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_OBTAIN);
270     top_frame = vda_ctx->queue;
271     vda_ctx->queue = top_frame->next_frame;
272     vda_lock_operation(&vda_ctx->queue_mutex, AV_LOCK_RELEASE);
273
274     return top_frame;
275 }
276
277 void ff_vda_release_vda_frame(vda_frame *frame)
278 {
279     if (frame) {
280         CVPixelBufferRelease(frame->cv_buffer);
281         av_freep(&frame);
282     }
283 }
284
285 int ff_vda_decoder_decode(struct vda_context *vda_ctx,
286                           uint8_t *bitstream,
287                           int bitstream_size,
288                           int64_t frame_pts)
289 {
290     OSStatus status = kVDADecoderNoErr;
291     CFDictionaryRef user_info;
292     CFDataRef coded_frame;
293
294     coded_frame = CFDataCreate(kCFAllocatorDefault, bitstream, bitstream_size);
295     user_info = vda_dictionary_with_pts(frame_pts);
296
297     status = VDADecoderDecode(vda_ctx->decoder, 0, coded_frame, user_info);
298
299     CFRelease(user_info);
300     CFRelease(coded_frame);
301
302     if (kVDADecoderNoErr != status)
303         return status;
304
305     return 0;
306 }
307
308 /* @} */