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