Restricted activeVideoMaxFrameDuration to fix frame rate
[platform/upstream/gstreamer.git] / sys / applemedia / avfvideosrc.m
1 /*
2  * Copyright (C) 2010 Ole André Vadla Ravnås <oleavr@soundrop.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "avfvideosrc.h"
25
26 #import <AVFoundation/AVFoundation.h>
27 #if !HAVE_IOS
28 #import <AppKit/AppKit.h>
29 #endif
30 #include <gst/video/video.h>
31 #include <gst/gl/gstglcontext.h>
32 #include "coremediabuffer.h"
33 #include "corevideotexturecache.h"
34
35 #define DEFAULT_DEVICE_INDEX  -1
36 #define DEFAULT_DO_STATS      FALSE
37
38 #define DEVICE_FPS_N          25
39 #define DEVICE_FPS_D          1
40
41 #define BUFFER_QUEUE_SIZE     2
42
43 GST_DEBUG_CATEGORY (gst_avf_video_src_debug);
44 #define GST_CAT_DEFAULT gst_avf_video_src_debug
45
46 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
47     GST_PAD_SRC,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("video/x-raw, "
50         "format = (string) { NV12, UYVY, YUY2 }, "
51         "framerate = " GST_VIDEO_FPS_RANGE ", "
52         "width = " GST_VIDEO_SIZE_RANGE ", "
53         "height = " GST_VIDEO_SIZE_RANGE "; "
54
55         "video/x-raw, "
56         "format = (string) BGRA, "
57         "framerate = " GST_VIDEO_FPS_RANGE ", "
58         "width = " GST_VIDEO_SIZE_RANGE ", "
59         "height = " GST_VIDEO_SIZE_RANGE "; "
60
61         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
62         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
63             "RGBA") "; "
64 ));
65
66 typedef enum _QueueState {
67   NO_BUFFERS = 1,
68   HAS_BUFFER_OR_STOP_REQUEST,
69 } QueueState;
70
71 #define gst_avf_video_src_parent_class parent_class
72 G_DEFINE_TYPE (GstAVFVideoSrc, gst_avf_video_src, GST_TYPE_PUSH_SRC);
73
74 @interface GstAVFVideoSrcImpl : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> {
75   GstElement *element;
76   GstBaseSrc *baseSrc;
77   GstPushSrc *pushSrc;
78
79   gint deviceIndex;
80   BOOL doStats;
81
82   AVCaptureSession *session;
83   AVCaptureInput *input;
84   AVCaptureVideoDataOutput *output;
85   AVCaptureDevice *device;
86   CMClockRef inputClock;
87
88   dispatch_queue_t mainQueue;
89   dispatch_queue_t workerQueue;
90   NSConditionLock *bufQueueLock;
91   NSMutableArray *bufQueue;
92   BOOL stopRequest;
93
94   GstCaps *caps;
95   GstVideoFormat format;
96   gint width, height;
97   GstClockTime latency;
98   guint64 offset;
99   GstClockTime startAVFTimestamp;
100   GstClockTime startTimestamp;
101
102   GstClockTime lastSampling;
103   guint count;
104   gint fps;
105   BOOL captureScreen;
106   BOOL captureScreenCursor;
107   BOOL captureScreenMouseClicks;
108
109   BOOL useVideoMeta;
110   GstCoreVideoTextureCache *textureCache;
111 }
112
113 - (id)init;
114 - (id)initWithSrc:(GstPushSrc *)src;
115 - (void)finalize;
116
117 @property int deviceIndex;
118 @property BOOL doStats;
119 @property int fps;
120 @property BOOL captureScreen;
121 @property BOOL captureScreenCursor;
122 @property BOOL captureScreenMouseClicks;
123
124 - (BOOL)openScreenInput;
125 - (BOOL)openDeviceInput;
126 - (BOOL)openDevice;
127 - (void)closeDevice;
128 - (GstVideoFormat)getGstVideoFormat:(NSNumber *)pixel_format;
129 #if !HAVE_IOS
130 - (CGDirectDisplayID)getDisplayIdFromDeviceIndex;
131 #endif
132 - (BOOL)getDeviceCaps:(GstCaps *)result;
133 - (BOOL)setDeviceCaps:(GstVideoInfo *)info;
134 - (BOOL)getSessionPresetCaps:(GstCaps *)result;
135 - (BOOL)setSessionPresetCaps:(GstVideoInfo *)info;
136 - (GstCaps *)getCaps;
137 - (BOOL)setCaps:(GstCaps *)new_caps;
138 - (BOOL)start;
139 - (BOOL)stop;
140 - (BOOL)unlock;
141 - (BOOL)unlockStop;
142 - (BOOL)query:(GstQuery *)query;
143 - (GstStateChangeReturn)changeState:(GstStateChange)transition;
144 - (GstFlowReturn)create:(GstBuffer **)buf;
145 - (void)updateStatistics;
146 - (void)captureOutput:(AVCaptureOutput *)captureOutput
147 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
148        fromConnection:(AVCaptureConnection *)connection;
149
150 @end
151
152 @implementation GstAVFVideoSrcImpl
153
154 @synthesize deviceIndex, doStats, fps, captureScreen,
155             captureScreenCursor, captureScreenMouseClicks;
156
157 - (id)init
158 {
159   return [self initWithSrc:NULL];
160 }
161
162 - (id)initWithSrc:(GstPushSrc *)src
163 {
164   if ((self = [super init])) {
165     element = GST_ELEMENT_CAST (src);
166     baseSrc = GST_BASE_SRC_CAST (src);
167     pushSrc = src;
168
169     deviceIndex = DEFAULT_DEVICE_INDEX;
170     captureScreen = NO;
171     captureScreenCursor = NO;
172     captureScreenMouseClicks = NO;
173     useVideoMeta = NO;
174     textureCache = NULL;
175
176     mainQueue =
177         dispatch_queue_create ("org.freedesktop.gstreamer.avfvideosrc.main", NULL);
178     workerQueue =
179         dispatch_queue_create ("org.freedesktop.gstreamer.avfvideosrc.output", NULL);
180
181     gst_base_src_set_live (baseSrc, TRUE);
182     gst_base_src_set_format (baseSrc, GST_FORMAT_TIME);
183   }
184
185   return self;
186 }
187
188 - (void)finalize
189 {
190   dispatch_release (mainQueue);
191   mainQueue = NULL;
192   dispatch_release (workerQueue);
193   workerQueue = NULL;
194
195   [super finalize];
196 }
197
198 - (BOOL)openDeviceInput
199 {
200   NSString *mediaType = AVMediaTypeVideo;
201   NSError *err;
202
203   if (deviceIndex == DEFAULT_DEVICE_INDEX) {
204     device = [AVCaptureDevice defaultDeviceWithMediaType:mediaType];
205     if (device == nil) {
206       GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
207                           ("No video capture devices found"), (NULL));
208       return NO;
209     }
210   } else {
211     NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
212     if (deviceIndex >= [devices count]) {
213       GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
214                           ("Invalid video capture device index"), (NULL));
215       return NO;
216     }
217     device = [devices objectAtIndex:deviceIndex];
218   }
219   g_assert (device != nil);
220   [device retain];
221
222   GST_INFO ("Opening '%s'", [[device localizedName] UTF8String]);
223
224   input = [AVCaptureDeviceInput deviceInputWithDevice:device
225                                                 error:&err];
226   if (input == nil) {
227     GST_ELEMENT_ERROR (element, RESOURCE, BUSY,
228         ("Failed to open device: %s",
229         [[err localizedDescription] UTF8String]),
230         (NULL));
231     [device release];
232     device = nil;
233     return NO;
234   }
235   [input retain];
236   return YES;
237 }
238
239 - (BOOL)openScreenInput
240 {
241 #if HAVE_IOS
242   return NO;
243 #else
244   CGDirectDisplayID displayId;
245
246   GST_DEBUG_OBJECT (element, "Opening screen input");
247
248   displayId = [self getDisplayIdFromDeviceIndex];
249   if (displayId == 0)
250     return NO;
251
252   AVCaptureScreenInput *screenInput =
253       [[AVCaptureScreenInput alloc] initWithDisplayID:displayId];
254
255
256   @try {
257     [screenInput setValue:[NSNumber numberWithBool:captureScreenCursor]
258                  forKey:@"capturesCursor"];
259
260   } @catch (NSException *exception) {
261     if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
262       GST_WARNING ("An unexpected error occured: %s",
263                    [[exception reason] UTF8String]);
264     }
265     GST_WARNING ("Capturing cursor is only supported in OS X >= 10.8");
266   }
267   screenInput.capturesMouseClicks = captureScreenMouseClicks;
268   input = screenInput;
269   [input retain];
270   return YES;
271 #endif
272 }
273
274 - (BOOL)openDevice
275 {
276   BOOL success = NO, *successPtr = &success;
277
278   GST_DEBUG_OBJECT (element, "Opening device");
279
280   dispatch_sync (mainQueue, ^{
281     BOOL ret;
282
283     if (captureScreen)
284       ret = [self openScreenInput];
285     else
286       ret = [self openDeviceInput];
287
288     if (!ret)
289       return;
290
291     output = [[AVCaptureVideoDataOutput alloc] init];
292     [output setSampleBufferDelegate:self
293                               queue:workerQueue];
294     output.alwaysDiscardsLateVideoFrames = YES;
295     output.videoSettings = nil; /* device native format */
296
297     session = [[AVCaptureSession alloc] init];
298     [session addInput:input];
299     [session addOutput:output];
300
301     *successPtr = YES;
302   });
303
304   GST_DEBUG_OBJECT (element, "Opening device %s", success ? "succeed" : "failed");
305
306   return success;
307 }
308
309 - (void)closeDevice
310 {
311   GST_DEBUG_OBJECT (element, "Closing device");
312
313   dispatch_sync (mainQueue, ^{
314     g_assert (![session isRunning]);
315
316     [session removeInput:input];
317     [session removeOutput:output];
318
319     [session release];
320     session = nil;
321
322     [input release];
323     input = nil;
324
325     [output release];
326     output = nil;
327
328     if (!captureScreen) {
329       [device release];
330       device = nil;
331     }
332
333     if (caps)
334       gst_caps_unref (caps);
335     caps = NULL;
336   });
337 }
338
339
340 #define GST_AVF_CAPS_NEW(format, w, h, fps_n, fps_d)                  \
341     (gst_caps_new_simple ("video/x-raw",                              \
342         "width", G_TYPE_INT, w,                                       \
343         "height", G_TYPE_INT, h,                                      \
344         "format", G_TYPE_STRING, gst_video_format_to_string (format), \
345         "framerate", GST_TYPE_FRACTION, (fps_n), (fps_d),             \
346         NULL))
347
348 - (GstVideoFormat)getGstVideoFormat:(NSNumber *)pixel_format
349 {
350   GstVideoFormat gst_format = GST_VIDEO_FORMAT_UNKNOWN;
351
352   switch ([pixel_format integerValue]) {
353   case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: /* 420v */
354     gst_format = GST_VIDEO_FORMAT_NV12;
355     break;
356   case kCVPixelFormatType_422YpCbCr8: /* 2vuy */
357     gst_format = GST_VIDEO_FORMAT_UYVY;
358     break;
359   case kCVPixelFormatType_32BGRA: /* BGRA */
360     gst_format = GST_VIDEO_FORMAT_BGRA;
361     break;
362   case kCVPixelFormatType_32RGBA: /* RGBA */
363     gst_format = GST_VIDEO_FORMAT_RGBA;
364     break;
365   case kCVPixelFormatType_422YpCbCr8_yuvs: /* yuvs */
366     gst_format = GST_VIDEO_FORMAT_YUY2;
367     break;
368   default:
369     GST_LOG_OBJECT (element, "Pixel format %s is not handled by avfvideosrc",
370         [[pixel_format stringValue] UTF8String]);
371     break;
372   }
373
374   return gst_format;
375 }
376
377 #if !HAVE_IOS
378 - (CGDirectDisplayID)getDisplayIdFromDeviceIndex
379 {
380   NSDictionary *description;
381   NSNumber *displayId;
382   NSArray *screens = [NSScreen screens];
383
384   if (deviceIndex == DEFAULT_DEVICE_INDEX)
385     return kCGDirectMainDisplay;
386   if (deviceIndex >= [screens count]) {
387     GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
388                         ("Invalid screen capture device index"), (NULL));
389     return 0;
390   }
391   description = [[screens objectAtIndex:deviceIndex] deviceDescription];
392   displayId = [description objectForKey:@"NSScreenNumber"];
393   return [displayId unsignedIntegerValue];
394 }
395 #endif
396
397 - (BOOL)getDeviceCaps:(GstCaps *)result
398 {
399   NSArray *formats = [device valueForKey:@"formats"];
400   NSArray *pixel_formats = output.availableVideoCVPixelFormatTypes;
401
402   GST_DEBUG_OBJECT (element, "Getting device caps");
403
404   /* Do not use AVCaptureDeviceFormat or AVFrameRateRange only
405    * available in iOS >= 7.0. We use a dynamic approach with key-value
406    * coding or performSelector */
407   for (NSObject *f in [formats reverseObjectEnumerator]) {
408     CMFormatDescriptionRef formatDescription;
409     CMVideoDimensions dimensions;
410
411     /* formatDescription can't be retrieved with valueForKey so use a selector here */
412     formatDescription = (CMFormatDescriptionRef) [f performSelector:@selector(formatDescription)];
413     dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
414     for (NSObject *rate in [f valueForKey:@"videoSupportedFrameRateRanges"]) {
415       int fps_n, fps_d;
416       gdouble max_fps;
417
418       [[rate valueForKey:@"maxFrameRate"] getValue:&max_fps];
419       gst_util_double_to_fraction (max_fps, &fps_n, &fps_d);
420
421       for (NSNumber *pixel_format in pixel_formats) {
422         GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
423         if (gst_format != GST_VIDEO_FORMAT_UNKNOWN)
424           gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, dimensions.width, dimensions.height, fps_n, fps_d));
425
426         if (gst_format == GST_VIDEO_FORMAT_BGRA) {
427           GstCaps *rgba_caps = GST_AVF_CAPS_NEW (GST_VIDEO_FORMAT_RGBA, dimensions.width, dimensions.height, fps_n, fps_d);
428           gst_caps_set_features (rgba_caps, 0, gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, NULL));
429           gst_caps_append (result, rgba_caps);
430         }
431       }
432     }
433   }
434   GST_LOG_OBJECT (element, "Device returned the following caps %" GST_PTR_FORMAT, result);
435   return YES;
436 }
437
438 - (BOOL)setDeviceCaps:(GstVideoInfo *)info
439 {
440   double framerate;
441   gboolean found_format = FALSE, found_framerate = FALSE;
442   NSArray *formats = [device valueForKey:@"formats"];
443   gst_util_fraction_to_double (info->fps_n, info->fps_d, &framerate);
444
445   GST_DEBUG_OBJECT (element, "Setting device caps");
446
447   if ([device lockForConfiguration:NULL] == YES) {
448     for (NSObject *f in formats) {
449       CMFormatDescriptionRef formatDescription;
450       CMVideoDimensions dimensions;
451
452       formatDescription = (CMFormatDescriptionRef) [f performSelector:@selector(formatDescription)];
453       dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
454       if (dimensions.width == info->width && dimensions.height == info->height) {
455         found_format = TRUE;
456         [device setValue:f forKey:@"activeFormat"];
457         for (NSObject *rate in [f valueForKey:@"videoSupportedFrameRateRanges"]) {
458           gdouble max_frame_rate;
459
460           [[rate valueForKey:@"maxFrameRate"] getValue:&max_frame_rate];
461           if (abs (framerate - max_frame_rate) < 0.00001) {
462             NSValue *min_frame_duration, *max_frame_duration;
463
464             found_framerate = TRUE;
465             min_frame_duration = [rate valueForKey:@"minFrameDuration"];
466             max_frame_duration = [rate valueForKey:@"maxFrameDuration"];
467             [device setValue:min_frame_duration forKey:@"activeVideoMinFrameDuration"];
468             @try {
469               /* Only available on OSX >= 10.8 and iOS >= 7.0 */
470               // Restrict activeVideoMaxFrameDuration to the minimum value so we get a better capture frame rate
471               [device setValue:min_frame_duration forKey:@"activeVideoMaxFrameDuration"];
472             } @catch (NSException *exception) {
473               if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
474                 GST_WARNING ("An unexcepted error occured: %s",
475                               [exception.reason UTF8String]);
476               }
477             }
478             break;
479           }
480         }
481       }
482     }
483     if (!found_format) {
484       GST_WARNING ("Unsupported capture dimensions %dx%d", info->width, info->height);
485       return NO;
486     }
487     if (!found_framerate) {
488       GST_WARNING ("Unsupported capture framerate %d/%d", info->fps_n, info->fps_d);
489       return NO;
490     }
491   } else {
492     GST_WARNING ("Couldn't lock device for configuration");
493     return NO;
494   }
495   return YES;
496 }
497
498 - (BOOL)getSessionPresetCaps:(GstCaps *)result
499 {
500   NSArray *pixel_formats = output.availableVideoCVPixelFormatTypes;
501   for (NSNumber *pixel_format in pixel_formats) {
502     GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
503     if (gst_format == GST_VIDEO_FORMAT_UNKNOWN)
504       continue;
505
506 #if HAVE_IOS
507     if ([session canSetSessionPreset:AVCaptureSessionPreset1920x1080])
508       gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 1920, 1080, DEVICE_FPS_N, DEVICE_FPS_D));
509 #endif
510     if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720])
511       gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 1280, 720, DEVICE_FPS_N, DEVICE_FPS_D));
512     if ([session canSetSessionPreset:AVCaptureSessionPreset640x480])
513       gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 640, 480, DEVICE_FPS_N, DEVICE_FPS_D));
514     if ([session canSetSessionPreset:AVCaptureSessionPresetMedium])
515       gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 480, 360, DEVICE_FPS_N, DEVICE_FPS_D));
516     if ([session canSetSessionPreset:AVCaptureSessionPreset352x288])
517       gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 352, 288, DEVICE_FPS_N, DEVICE_FPS_D));
518     if ([session canSetSessionPreset:AVCaptureSessionPresetLow])
519       gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 192, 144, DEVICE_FPS_N, DEVICE_FPS_D));
520   }
521
522   GST_LOG_OBJECT (element, "Session presets returned the following caps %" GST_PTR_FORMAT, result);
523
524   return YES;
525 }
526
527 - (BOOL)setSessionPresetCaps:(GstVideoInfo *)info;
528 {
529   GST_DEBUG_OBJECT (element, "Setting session presset caps");
530
531   if ([device lockForConfiguration:NULL] != YES) {
532     GST_WARNING ("Couldn't lock device for configuration");
533     return NO;
534   }
535
536   switch (info->width) {
537   case 192:
538     session.sessionPreset = AVCaptureSessionPresetLow;
539     break;
540   case 352:
541     session.sessionPreset = AVCaptureSessionPreset352x288;
542     break;
543   case 480:
544     session.sessionPreset = AVCaptureSessionPresetMedium;
545     break;
546   case 640:
547     session.sessionPreset = AVCaptureSessionPreset640x480;
548     break;
549   case 1280:
550     session.sessionPreset = AVCaptureSessionPreset1280x720;
551     break;
552 #if HAVE_IOS
553   case 1920:
554     session.sessionPreset = AVCaptureSessionPreset1920x1080;
555     break;
556 #endif
557   default:
558     GST_WARNING ("Unsupported capture dimensions %dx%d", info->width, info->height);
559     return NO;
560   }
561   return YES;
562 }
563
564 - (GstCaps *)getCaps
565 {
566   GstCaps *result;
567   NSArray *pixel_formats;
568
569   if (session == nil)
570     return NULL; /* BaseSrc will return template caps */
571
572   result = gst_caps_new_empty ();
573   pixel_formats = output.availableVideoCVPixelFormatTypes;
574
575   if (captureScreen) {
576 #if !HAVE_IOS
577     CGRect rect = CGDisplayBounds ([self getDisplayIdFromDeviceIndex]);
578     for (NSNumber *pixel_format in pixel_formats) {
579       GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
580       if (gst_format != GST_VIDEO_FORMAT_UNKNOWN)
581         gst_caps_append (result, gst_caps_new_simple ("video/x-raw",
582             "width", G_TYPE_INT, (int)rect.size.width,
583             "height", G_TYPE_INT, (int)rect.size.height,
584             "format", G_TYPE_STRING, gst_video_format_to_string (gst_format),
585             NULL));
586     }
587 #else
588     GST_WARNING ("Screen capture is not supported by iOS");
589 #endif
590     return result;
591   }
592
593   @try {
594
595     [self getDeviceCaps:result];
596
597   } @catch (NSException *exception) {
598
599     if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
600       GST_WARNING ("An unexcepted error occured: %s", [exception.reason UTF8String]);
601       return result;
602     }
603
604     /* Fallback on session presets API for iOS < 7.0 */
605     [self getSessionPresetCaps:result];
606   }
607
608   return result;
609 }
610
611 - (BOOL)setCaps:(GstCaps *)new_caps
612 {
613   GstVideoInfo info;
614   BOOL success = YES, *successPtr = &success;
615
616   gst_video_info_init (&info);
617   gst_video_info_from_caps (&info, new_caps);
618
619   width = info.width;
620   height = info.height;
621   format = info.finfo->format;
622   latency = gst_util_uint64_scale (GST_SECOND, info.fps_d, info.fps_n);
623
624   dispatch_sync (mainQueue, ^{
625     int newformat;
626
627     g_assert (![session isRunning]);
628
629     if (captureScreen) {
630 #if !HAVE_IOS
631       AVCaptureScreenInput *screenInput = (AVCaptureScreenInput *)input;
632       screenInput.minFrameDuration = CMTimeMake(info.fps_d, info.fps_n);
633 #else
634       GST_WARNING ("Screen capture is not supported by iOS");
635       *successPtr = NO;
636       return;
637 #endif
638     } else {
639       @try {
640
641         /* formats and activeFormat keys are only available on OSX >= 10.7 and iOS >= 7.0 */
642         *successPtr = [self setDeviceCaps:(GstVideoInfo *)&info];
643         if (*successPtr != YES)
644           return;
645
646       } @catch (NSException *exception) {
647
648         if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
649           GST_WARNING ("An unexcepted error occured: %s", [exception.reason UTF8String]);
650           *successPtr = NO;
651           return;
652         }
653
654         /* Fallback on session presets API for iOS < 7.0 */
655         *successPtr = [self setSessionPresetCaps:(GstVideoInfo *)&info];
656         if (*successPtr != YES)
657           return;
658       }
659     }
660
661     switch (format) {
662       case GST_VIDEO_FORMAT_NV12:
663         newformat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
664         break;
665       case GST_VIDEO_FORMAT_UYVY:
666          newformat = kCVPixelFormatType_422YpCbCr8;
667         break;
668       case GST_VIDEO_FORMAT_YUY2:
669          newformat = kCVPixelFormatType_422YpCbCr8_yuvs;
670         break;
671       case GST_VIDEO_FORMAT_RGBA:
672         /* In order to do RGBA, we negotiate BGRA (since RGBA is not supported
673          * if not in textures) and then we get RGBA textures via
674          * CVOpenGL*TextureCacheCreateTextureFromImage. Computers. */
675       case GST_VIDEO_FORMAT_BGRA:
676          newformat = kCVPixelFormatType_32BGRA;
677         break;
678       default:
679         *successPtr = NO;
680         GST_WARNING ("Unsupported output format %s",
681             gst_video_format_to_string (format));
682         return;
683     }
684
685     GST_DEBUG_OBJECT(element,
686        "Width: %d Height: %d Format: %" GST_FOURCC_FORMAT,
687        width, height,
688        GST_FOURCC_ARGS (gst_video_format_to_fourcc (format)));
689
690     output.videoSettings = [NSDictionary
691         dictionaryWithObject:[NSNumber numberWithInt:newformat]
692         forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
693
694     if (caps)
695       gst_caps_unref (caps);
696     caps = gst_caps_copy (new_caps);
697     [session startRunning];
698
699     /* Unlock device configuration only after session is started so the session
700      * won't reset the capture formats */
701     [device unlockForConfiguration];
702   });
703
704   return success;
705 }
706
707 - (BOOL)start
708 {
709   bufQueueLock = [[NSConditionLock alloc] initWithCondition:NO_BUFFERS];
710   bufQueue = [[NSMutableArray alloc] initWithCapacity:BUFFER_QUEUE_SIZE];
711   stopRequest = NO;
712
713   offset = 0;
714   latency = GST_CLOCK_TIME_NONE;
715   startAVFTimestamp = GST_CLOCK_TIME_NONE;
716   startTimestamp = GST_CLOCK_TIME_NONE;
717   inputClock = nil;
718
719   lastSampling = GST_CLOCK_TIME_NONE;
720   count = 0;
721   fps = -1;
722
723   return YES;
724 }
725
726 - (BOOL)stop
727 {
728   dispatch_sync (mainQueue, ^{ [session stopRunning]; });
729   dispatch_sync (workerQueue, ^{});
730
731   [bufQueueLock release];
732   bufQueueLock = nil;
733   [bufQueue release];
734   bufQueue = nil;
735   inputClock = nil;
736
737   if (textureCache)
738       gst_core_video_texture_cache_free (textureCache);
739   textureCache = NULL;
740
741   return YES;
742 }
743
744 - (BOOL)query:(GstQuery *)query
745 {
746   BOOL result = NO;
747
748   if (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY) {
749     if (device != nil && caps != NULL) {
750       GstClockTime min_latency, max_latency;
751
752       min_latency = max_latency = latency;
753       result = YES;
754
755       GST_DEBUG_OBJECT (element, "reporting latency of min %" GST_TIME_FORMAT
756           " max %" GST_TIME_FORMAT,
757           GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
758       gst_query_set_latency (query, TRUE, min_latency, max_latency);
759     }
760   } else {
761     result = GST_BASE_SRC_CLASS (parent_class)->query (baseSrc, query);
762   }
763
764   return result;
765 }
766
767 - (BOOL)decideAllocation:(GstQuery *)query
768 {
769   useVideoMeta = gst_query_find_allocation_meta (query,
770       GST_VIDEO_META_API_TYPE, NULL);
771
772   guint idx;
773   if (gst_query_find_allocation_meta (query,
774         GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx)) {
775     GstGLContext *context;
776     const GstStructure *upload_meta_params;
777
778     gst_query_parse_nth_allocation_meta (query, idx, &upload_meta_params);
779     if (gst_structure_get (upload_meta_params, "gst.gl.GstGLContext",
780           GST_GL_TYPE_CONTEXT, &context, NULL) && context) {
781       GstCaps *query_caps;
782       GstCapsFeatures *features;
783
784       gst_query_parse_allocation (query, &query_caps, NULL);
785       features = gst_caps_get_features (query_caps, 0);
786       if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
787         textureCache = gst_core_video_texture_cache_new (context);
788         gst_core_video_texture_cache_set_format (textureCache,
789             "NV12", query_caps);
790       }
791       gst_object_unref (context);
792     }
793   }
794
795   return YES;
796 }
797
798 - (BOOL)unlock
799 {
800   [bufQueueLock lock];
801   stopRequest = YES;
802   [bufQueueLock unlockWithCondition:HAS_BUFFER_OR_STOP_REQUEST];
803
804   return YES;
805 }
806
807 - (BOOL)unlockStop
808 {
809   [bufQueueLock lock];
810   stopRequest = NO;
811   [bufQueueLock unlock];
812
813   return YES;
814 }
815
816 - (GstStateChangeReturn)changeState:(GstStateChange)transition
817 {
818   GstStateChangeReturn ret;
819
820   if (transition == GST_STATE_CHANGE_NULL_TO_READY) {
821     if (![self openDevice])
822       return GST_STATE_CHANGE_FAILURE;
823   }
824
825   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
826
827   if (transition == GST_STATE_CHANGE_READY_TO_NULL)
828     [self closeDevice];
829
830   return ret;
831 }
832
833 - (void)captureOutput:(AVCaptureOutput *)captureOutput
834 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
835        fromConnection:(AVCaptureConnection *)connection
836 {
837   GstClockTime timestamp, duration;
838
839   [bufQueueLock lock];
840
841   if (stopRequest) {
842     [bufQueueLock unlock];
843     return;
844   }
845
846   if (inputClock == nil)
847     inputClock = ((AVCaptureInputPort *)connection.inputPorts[0]).clock;
848   [self getSampleBuffer:sampleBuffer timestamp:&timestamp duration:&duration];
849
850   if ([bufQueue count] == BUFFER_QUEUE_SIZE)
851     [bufQueue removeLastObject];
852
853   [bufQueue insertObject:@{@"sbuf": (id)sampleBuffer,
854                            @"timestamp": @(timestamp),
855                            @"duration": @(duration)}
856                  atIndex:0];
857
858   [bufQueueLock unlockWithCondition:HAS_BUFFER_OR_STOP_REQUEST];
859 }
860
861 - (GstFlowReturn)create:(GstBuffer **)buf
862 {
863   CMSampleBufferRef sbuf;
864   CVImageBufferRef image_buf;
865   CVPixelBufferRef pixel_buf;
866   size_t cur_width, cur_height;
867   GstClockTime timestamp, duration;
868
869   [bufQueueLock lockWhenCondition:HAS_BUFFER_OR_STOP_REQUEST];
870   if (stopRequest) {
871     [bufQueueLock unlock];
872     return GST_FLOW_FLUSHING;
873   }
874
875   NSDictionary *dic = (NSDictionary *) [bufQueue lastObject];
876   sbuf = (CMSampleBufferRef) dic[@"sbuf"];
877   timestamp = (GstClockTime) [dic[@"timestamp"] longLongValue];
878   duration = (GstClockTime) [dic[@"duration"] longLongValue];
879   CFRetain (sbuf);
880   [bufQueue removeLastObject];
881   [bufQueueLock unlockWithCondition:
882       ([bufQueue count] == 0) ? NO_BUFFERS : HAS_BUFFER_OR_STOP_REQUEST];
883
884   /* Check output frame size dimensions */
885   image_buf = CMSampleBufferGetImageBuffer (sbuf);
886   if (image_buf) {
887     pixel_buf = (CVPixelBufferRef) image_buf;
888     cur_width = CVPixelBufferGetWidth (pixel_buf);
889     cur_height = CVPixelBufferGetHeight (pixel_buf);
890
891     if (width != cur_width || height != cur_height) {
892       /* Set new caps according to current frame dimensions */
893       GST_WARNING ("Output frame size has changed %dx%d -> %dx%d, updating caps",
894           width, height, (int)cur_width, (int)cur_height);
895       width = cur_width;
896       height = cur_height;
897       gst_caps_set_simple (caps,
898         "width", G_TYPE_INT, width,
899         "height", G_TYPE_INT, height,
900         NULL);
901       gst_pad_push_event (GST_BASE_SINK_PAD (baseSrc), gst_event_new_caps (caps));
902     }
903   }
904
905   *buf = gst_core_media_buffer_new (sbuf, useVideoMeta, textureCache == NULL);
906   if (format == GST_VIDEO_FORMAT_RGBA) {
907     /* So now buf contains BGRA data (!) . Since downstream is actually going to
908      * use the GL upload meta to get RGBA textures (??), we need to override the
909      * VideoMeta format (!!!). Yes this is confusing, see setCaps:  */
910     GstVideoMeta *video_meta = gst_buffer_get_video_meta (*buf);
911     if (video_meta) {
912       video_meta->format = format;
913     }
914   }
915   CFRelease (sbuf);
916
917   if (textureCache != NULL)
918     *buf = gst_core_video_texture_cache_get_gl_buffer (textureCache, *buf);
919
920   GST_BUFFER_OFFSET (*buf) = offset++;
921   GST_BUFFER_OFFSET_END (*buf) = GST_BUFFER_OFFSET (buf) + 1;
922   GST_BUFFER_TIMESTAMP (*buf) = timestamp;
923   GST_BUFFER_DURATION (*buf) = duration;
924
925   if (doStats)
926     [self updateStatistics];
927
928   return GST_FLOW_OK;
929 }
930
931 - (void)getSampleBuffer:(CMSampleBufferRef)sbuf
932               timestamp:(GstClockTime *)outTimestamp
933                duration:(GstClockTime *)outDuration
934 {
935   CMSampleTimingInfo time_info;
936   GstClockTime timestamp, duration, inputClockNow, running_time;
937   CMItemCount num_timings;
938   GstClock *clock;
939   CMTime now;
940
941   timestamp = GST_CLOCK_TIME_NONE;
942   duration = GST_CLOCK_TIME_NONE;
943   if (CMSampleBufferGetOutputSampleTimingInfoArray(sbuf, 1, &time_info, &num_timings) == noErr) {
944     timestamp = gst_util_uint64_scale (GST_SECOND,
945             time_info.presentationTimeStamp.value, time_info.presentationTimeStamp.timescale);
946
947     if (CMTIME_IS_VALID (time_info.duration) && time_info.duration.timescale != 0)
948       duration = gst_util_uint64_scale (GST_SECOND,
949           time_info.duration.value, time_info.duration.timescale);
950
951     now = CMClockGetTime(inputClock);
952     inputClockNow = gst_util_uint64_scale (GST_SECOND,
953         now.value, now.timescale);
954
955     GST_OBJECT_LOCK (element);
956     clock = GST_ELEMENT_CLOCK (element);
957     running_time = gst_clock_get_time (clock) - element->base_time;
958     timestamp = running_time + (inputClockNow - timestamp);
959     GST_OBJECT_UNLOCK (element);
960   }
961
962   *outTimestamp = timestamp;
963   *outDuration = duration;
964 }
965
966 - (void)updateStatistics
967 {
968   GstClock *clock;
969
970   GST_OBJECT_LOCK (element);
971   clock = GST_ELEMENT_CLOCK (element);
972   if (clock != NULL)
973     gst_object_ref (clock);
974   GST_OBJECT_UNLOCK (element);
975
976   if (clock != NULL) {
977     GstClockTime now = gst_clock_get_time (clock);
978     gst_object_unref (clock);
979
980     count++;
981
982     if (GST_CLOCK_TIME_IS_VALID (lastSampling)) {
983       if (now - lastSampling >= GST_SECOND) {
984         GST_OBJECT_LOCK (element);
985         fps = count;
986         GST_OBJECT_UNLOCK (element);
987
988         g_object_notify (G_OBJECT (element), "fps");
989
990         lastSampling = now;
991         count = 0;
992       }
993     } else {
994       lastSampling = now;
995     }
996   }
997 }
998
999 @end
1000
1001 /*
1002  * Glue code
1003  */
1004
1005 enum
1006 {
1007   PROP_0,
1008   PROP_DEVICE_INDEX,
1009   PROP_DO_STATS,
1010   PROP_FPS,
1011 #if !HAVE_IOS
1012   PROP_CAPTURE_SCREEN,
1013   PROP_CAPTURE_SCREEN_CURSOR,
1014   PROP_CAPTURE_SCREEN_MOUSE_CLICKS,
1015 #endif
1016 };
1017
1018
1019 static void gst_avf_video_src_finalize (GObject * obj);
1020 static void gst_avf_video_src_get_property (GObject * object, guint prop_id,
1021     GValue * value, GParamSpec * pspec);
1022 static void gst_avf_video_src_set_property (GObject * object, guint prop_id,
1023     const GValue * value, GParamSpec * pspec);
1024 static GstStateChangeReturn gst_avf_video_src_change_state (
1025     GstElement * element, GstStateChange transition);
1026 static GstCaps * gst_avf_video_src_get_caps (GstBaseSrc * basesrc,
1027     GstCaps * filter);
1028 static gboolean gst_avf_video_src_set_caps (GstBaseSrc * basesrc,
1029     GstCaps * caps);
1030 static gboolean gst_avf_video_src_start (GstBaseSrc * basesrc);
1031 static gboolean gst_avf_video_src_stop (GstBaseSrc * basesrc);
1032 static gboolean gst_avf_video_src_query (GstBaseSrc * basesrc,
1033     GstQuery * query);
1034 static gboolean gst_avf_video_src_decide_allocation (GstBaseSrc * basesrc,
1035     GstQuery * query);
1036 static gboolean gst_avf_video_src_unlock (GstBaseSrc * basesrc);
1037 static gboolean gst_avf_video_src_unlock_stop (GstBaseSrc * basesrc);
1038 static GstFlowReturn gst_avf_video_src_create (GstPushSrc * pushsrc,
1039     GstBuffer ** buf);
1040 static gboolean gst_avf_video_src_negotiate (GstBaseSrc * basesrc);
1041
1042
1043 static void
1044 gst_avf_video_src_class_init (GstAVFVideoSrcClass * klass)
1045 {
1046   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1047   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1048   GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
1049   GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
1050
1051   gobject_class->finalize = gst_avf_video_src_finalize;
1052   gobject_class->get_property = gst_avf_video_src_get_property;
1053   gobject_class->set_property = gst_avf_video_src_set_property;
1054
1055   gstelement_class->change_state = gst_avf_video_src_change_state;
1056
1057   gstbasesrc_class->get_caps = gst_avf_video_src_get_caps;
1058   gstbasesrc_class->set_caps = gst_avf_video_src_set_caps;
1059   gstbasesrc_class->start = gst_avf_video_src_start;
1060   gstbasesrc_class->stop = gst_avf_video_src_stop;
1061   gstbasesrc_class->query = gst_avf_video_src_query;
1062   gstbasesrc_class->unlock = gst_avf_video_src_unlock;
1063   gstbasesrc_class->unlock_stop = gst_avf_video_src_unlock_stop;
1064   gstbasesrc_class->decide_allocation = gst_avf_video_src_decide_allocation;
1065   gstbasesrc_class->negotiate = gst_avf_video_src_negotiate;
1066
1067   gstpushsrc_class->create = gst_avf_video_src_create;
1068
1069   gst_element_class_set_metadata (gstelement_class,
1070       "Video Source (AVFoundation)", "Source/Video",
1071       "Reads frames from an iOS AVFoundation device",
1072       "Ole André Vadla Ravnås <oleavr@soundrop.com>");
1073
1074   gst_element_class_add_pad_template (gstelement_class,
1075       gst_static_pad_template_get (&src_template));
1076
1077   g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
1078       g_param_spec_int ("device-index", "Device Index",
1079           "The zero-based device index",
1080           -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
1081           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1082   g_object_class_install_property (gobject_class, PROP_DO_STATS,
1083       g_param_spec_boolean ("do-stats", "Enable statistics",
1084           "Enable logging of statistics", DEFAULT_DO_STATS,
1085           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1086   g_object_class_install_property (gobject_class, PROP_FPS,
1087       g_param_spec_int ("fps", "Frames per second",
1088           "Last measured framerate, if statistics are enabled",
1089           -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1090 #if !HAVE_IOS
1091   g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN,
1092       g_param_spec_boolean ("capture-screen", "Enable screen capture",
1093           "Enable screen capture functionality", FALSE,
1094           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1095   g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN_CURSOR,
1096       g_param_spec_boolean ("capture-screen-cursor", "Capture screen cursor",
1097           "Enable cursor capture while capturing screen", FALSE,
1098           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1099   g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN_MOUSE_CLICKS,
1100       g_param_spec_boolean ("capture-screen-mouse-clicks", "Enable mouse clicks capture",
1101           "Enable mouse clicks capture while capturing screen", FALSE,
1102           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1103 #endif
1104
1105   GST_DEBUG_CATEGORY_INIT (gst_avf_video_src_debug, "avfvideosrc",
1106       0, "iOS AVFoundation video source");
1107 }
1108
1109 #define OBJC_CALLOUT_BEGIN() \
1110   NSAutoreleasePool *pool; \
1111   \
1112   pool = [[NSAutoreleasePool alloc] init]
1113 #define OBJC_CALLOUT_END() \
1114   [pool release]
1115
1116
1117 static void
1118 gst_avf_video_src_init (GstAVFVideoSrc * src)
1119 {
1120   OBJC_CALLOUT_BEGIN ();
1121   src->impl = [[GstAVFVideoSrcImpl alloc] initWithSrc:GST_PUSH_SRC (src)];
1122   OBJC_CALLOUT_END ();
1123 }
1124
1125 static void
1126 gst_avf_video_src_finalize (GObject * obj)
1127 {
1128   OBJC_CALLOUT_BEGIN ();
1129   [GST_AVF_VIDEO_SRC_IMPL (obj) release];
1130   OBJC_CALLOUT_END ();
1131
1132   G_OBJECT_CLASS (parent_class)->finalize (obj);
1133 }
1134
1135 static void
1136 gst_avf_video_src_get_property (GObject * object, guint prop_id, GValue * value,
1137     GParamSpec * pspec)
1138 {
1139   GstAVFVideoSrcImpl *impl = GST_AVF_VIDEO_SRC_IMPL (object);
1140
1141   switch (prop_id) {
1142 #if !HAVE_IOS
1143     case PROP_CAPTURE_SCREEN:
1144       g_value_set_boolean (value, impl.captureScreen);
1145       break;
1146     case PROP_CAPTURE_SCREEN_CURSOR:
1147       g_value_set_boolean (value, impl.captureScreenCursor);
1148       break;
1149     case PROP_CAPTURE_SCREEN_MOUSE_CLICKS:
1150       g_value_set_boolean (value, impl.captureScreenMouseClicks);
1151       break;
1152 #endif
1153     case PROP_DEVICE_INDEX:
1154       g_value_set_int (value, impl.deviceIndex);
1155       break;
1156     case PROP_DO_STATS:
1157       g_value_set_boolean (value, impl.doStats);
1158       break;
1159     case PROP_FPS:
1160       GST_OBJECT_LOCK (object);
1161       g_value_set_int (value, impl.fps);
1162       GST_OBJECT_UNLOCK (object);
1163       break;
1164     default:
1165       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1166       break;
1167   }
1168 }
1169
1170 static void
1171 gst_avf_video_src_set_property (GObject * object, guint prop_id,
1172     const GValue * value, GParamSpec * pspec)
1173 {
1174   GstAVFVideoSrcImpl *impl = GST_AVF_VIDEO_SRC_IMPL (object);
1175
1176   switch (prop_id) {
1177 #if !HAVE_IOS
1178     case PROP_CAPTURE_SCREEN:
1179       impl.captureScreen = g_value_get_boolean (value);
1180       break;
1181     case PROP_CAPTURE_SCREEN_CURSOR:
1182       impl.captureScreenCursor = g_value_get_boolean (value);
1183       break;
1184     case PROP_CAPTURE_SCREEN_MOUSE_CLICKS:
1185       impl.captureScreenMouseClicks = g_value_get_boolean (value);
1186       break;
1187 #endif
1188     case PROP_DEVICE_INDEX:
1189       impl.deviceIndex = g_value_get_int (value);
1190       break;
1191     case PROP_DO_STATS:
1192       impl.doStats = g_value_get_boolean (value);
1193       break;
1194     default:
1195       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1196       break;
1197   }
1198 }
1199
1200 static GstStateChangeReturn
1201 gst_avf_video_src_change_state (GstElement * element, GstStateChange transition)
1202 {
1203   GstStateChangeReturn ret;
1204
1205   OBJC_CALLOUT_BEGIN ();
1206   ret = [GST_AVF_VIDEO_SRC_IMPL (element) changeState: transition];
1207   OBJC_CALLOUT_END ();
1208
1209   return ret;
1210 }
1211
1212 static GstCaps *
1213 gst_avf_video_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
1214 {
1215   GstCaps *ret;
1216
1217   OBJC_CALLOUT_BEGIN ();
1218   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) getCaps];
1219   OBJC_CALLOUT_END ();
1220
1221   return ret;
1222 }
1223
1224 static gboolean
1225 gst_avf_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
1226 {
1227   gboolean ret;
1228
1229   OBJC_CALLOUT_BEGIN ();
1230   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) setCaps:caps];
1231   OBJC_CALLOUT_END ();
1232
1233   return ret;
1234 }
1235
1236 static gboolean
1237 gst_avf_video_src_start (GstBaseSrc * basesrc)
1238 {
1239   gboolean ret;
1240
1241   OBJC_CALLOUT_BEGIN ();
1242   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) start];
1243   OBJC_CALLOUT_END ();
1244
1245   return ret;
1246 }
1247
1248 static gboolean
1249 gst_avf_video_src_stop (GstBaseSrc * basesrc)
1250 {
1251   gboolean ret;
1252
1253   OBJC_CALLOUT_BEGIN ();
1254   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) stop];
1255   OBJC_CALLOUT_END ();
1256
1257   return ret;
1258 }
1259
1260 static gboolean
1261 gst_avf_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
1262 {
1263   gboolean ret;
1264
1265   OBJC_CALLOUT_BEGIN ();
1266   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) query:query];
1267   OBJC_CALLOUT_END ();
1268
1269   return ret;
1270 }
1271
1272 static gboolean
1273 gst_avf_video_src_decide_allocation (GstBaseSrc * basesrc, GstQuery * query)
1274 {
1275   gboolean ret;
1276
1277   OBJC_CALLOUT_BEGIN ();
1278   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) decideAllocation:query];
1279   OBJC_CALLOUT_END ();
1280
1281   return ret;
1282 }
1283
1284 static gboolean
1285 gst_avf_video_src_unlock (GstBaseSrc * basesrc)
1286 {
1287   gboolean ret;
1288
1289   OBJC_CALLOUT_BEGIN ();
1290   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) unlock];
1291   OBJC_CALLOUT_END ();
1292
1293   return ret;
1294 }
1295
1296 static gboolean
1297 gst_avf_video_src_unlock_stop (GstBaseSrc * basesrc)
1298 {
1299   gboolean ret;
1300
1301   OBJC_CALLOUT_BEGIN ();
1302   ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) unlockStop];
1303   OBJC_CALLOUT_END ();
1304
1305   return ret;
1306 }
1307
1308 static GstFlowReturn
1309 gst_avf_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
1310 {
1311   GstFlowReturn ret;
1312
1313   OBJC_CALLOUT_BEGIN ();
1314   ret = [GST_AVF_VIDEO_SRC_IMPL (pushsrc) create: buf];
1315   OBJC_CALLOUT_END ();
1316
1317   return ret;
1318 }
1319
1320 static gboolean
1321 gst_avf_video_src_negotiate (GstBaseSrc * basesrc)
1322 {
1323   /* FIXME: We don't support reconfiguration yet */
1324   if (gst_pad_has_current_caps (GST_BASE_SRC_PAD (basesrc)))
1325     return TRUE;
1326
1327   return GST_BASE_SRC_CLASS (parent_class)->negotiate (basesrc);
1328 }
1329