2 * Copyright (C) 2010 Ole André Vadla Ravnås <oleavr@soundrop.com>
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.
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.
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.
20 #include "avfvideosrc.h"
22 #import <AVFoundation/AVFoundation.h>
23 #include <gst/video/video.h>
24 #include "coremediabuffer.h"
26 #define DEFAULT_DEVICE_INDEX -1
27 #define DEFAULT_DO_STATS FALSE
29 #define DEVICE_FPS_N 25
30 #define DEVICE_FPS_D 1
32 #define BUFFER_QUEUE_SIZE 2
34 GST_DEBUG_CATEGORY (gst_avf_video_src_debug);
35 #define GST_CAT_DEFAULT gst_avf_video_src_debug
37 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
40 GST_STATIC_CAPS ("video/x-raw, "
41 "format = (string) { NV12, UYVY, YUY2 }, "
42 "framerate = " GST_VIDEO_FPS_RANGE ", "
43 "width = " GST_VIDEO_SIZE_RANGE ", "
44 "height = " GST_VIDEO_SIZE_RANGE "; "
47 "format = (string) { BGRA }, "
50 "endianness = (int) BIG_ENDIAN, "
51 "red_mask = (int) 0x0000FF00, "
52 "green_mask = (int) 0x00FF0000, "
53 "blue_mask = (int) 0xFF000000, "
54 "alpha_mask = (int) 0x000000FF, "
55 "framerate = " GST_VIDEO_FPS_RANGE ", "
56 "width = " GST_VIDEO_SIZE_RANGE ", "
57 "height = " GST_VIDEO_SIZE_RANGE "; "
60 typedef enum _QueueState {
62 HAS_BUFFER_OR_STOP_REQUEST,
65 #define gst_avf_video_src_parent_class parent_class
66 G_DEFINE_TYPE (GstAVFVideoSrc, gst_avf_video_src, GST_TYPE_PUSH_SRC);
68 @interface GstAVFVideoSrcImpl : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> {
76 CGDirectDisplayID displayId;
79 AVCaptureSession *session;
80 AVCaptureInput *input;
81 AVCaptureVideoDataOutput *output;
82 AVCaptureDevice *device;
84 dispatch_queue_t mainQueue;
85 dispatch_queue_t workerQueue;
86 NSConditionLock *bufQueueLock;
87 NSMutableArray *bufQueue;
91 GstVideoFormat format;
93 GstClockTime duration;
96 GstClockTime lastSampling;
102 - (id)initWithSrc:(GstPushSrc *)src;
105 @property int deviceIndex;
106 @property BOOL doStats;
108 @property BOOL captureScreen;
109 @property BOOL captureScreenCursor;
110 @property BOOL captureScreenMouseClicks;
112 - (BOOL)openScreenInput;
113 - (BOOL)openDeviceInput;
116 - (GstVideoFormat)getGstVideoFormat:(NSNumber *)pixel_format;
117 - (BOOL)getDeviceCaps:(GstCaps *)result;
118 - (BOOL)setDeviceCaps:(GstVideoInfo *)info;
119 - (BOOL)getSessionPresetCaps:(GstCaps *)result;
120 - (BOOL)setSessionPresetCaps:(GstVideoInfo *)info;
121 - (GstCaps *)getCaps;
122 - (BOOL)setCaps:(GstCaps *)new_caps;
127 - (BOOL)query:(GstQuery *)query;
128 - (GstStateChangeReturn)changeState:(GstStateChange)transition;
129 - (GstFlowReturn)create:(GstBuffer **)buf;
130 - (void)timestampBuffer:(GstBuffer *)buf;
131 - (void)updateStatistics;
132 - (void)captureOutput:(AVCaptureOutput *)captureOutput
133 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
134 fromConnection:(AVCaptureConnection *)connection;
138 @implementation GstAVFVideoSrcImpl
142 return [self initWithSrc:NULL];
145 - (id)initWithSrc:(GstPushSrc *)src
147 if ((self = [super init])) {
148 element = GST_ELEMENT_CAST (src);
149 baseSrc = GST_BASE_SRC_CAST (src);
152 deviceIndex = DEFAULT_DEVICE_INDEX;
154 captureScreenCursor = NO;
155 captureScreenMouseClicks = NO;
157 displayId = kCGDirectMainDisplay;
161 dispatch_queue_create ("org.freedesktop.gstreamer.avfvideosrc.main", NULL);
163 dispatch_queue_create ("org.freedesktop.gstreamer.avfvideosrc.output", NULL);
165 gst_base_src_set_live (baseSrc, TRUE);
166 gst_base_src_set_format (baseSrc, GST_FORMAT_TIME);
174 dispatch_release (mainQueue);
176 dispatch_release (workerQueue);
182 @synthesize deviceIndex, doStats, fps, captureScreen,
183 captureScreenCursor, captureScreenMouseClicks;
185 - (BOOL)openDeviceInput
187 NSString *mediaType = AVMediaTypeVideo;
190 if (deviceIndex == -1) {
191 device = [AVCaptureDevice defaultDeviceWithMediaType:mediaType];
193 GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
194 ("No video capture devices found"), (NULL));
198 NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
199 if (deviceIndex >= [devices count]) {
200 GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
201 ("Invalid video capture device index"), (NULL));
204 device = [devices objectAtIndex:deviceIndex];
206 g_assert (device != nil);
209 GST_INFO ("Opening '%s'", [[device localizedName] UTF8String]);
211 input = [AVCaptureDeviceInput deviceInputWithDevice:device
214 GST_ELEMENT_ERROR (element, RESOURCE, BUSY,
215 ("Failed to open device: %s",
216 [[err localizedDescription] UTF8String]),
226 - (BOOL)openScreenInput
231 AVCaptureScreenInput *screenInput =
232 [[AVCaptureScreenInput alloc] initWithDisplayID:displayId];
235 [screenInput setValue:[NSNumber numberWithBool:captureScreenCursor]
236 forKey:@"capturesCursor"];
238 } @catch (NSException *exception) {
239 if (![[exception name] isEqualTo:NSUndefinedKeyException]) {
240 GST_WARNING ("An unexpected error occured: %s",
241 [[exception reason] UTF8String]);
243 GST_WARNING ("Capturing cursor is only supported in OS X >= 10.8");
245 screenInput.capturesMouseClicks = captureScreenMouseClicks;
254 BOOL success = NO, *successPtr = &success;
256 dispatch_sync (mainQueue, ^{
260 ret = [self openScreenInput];
262 ret = [self openDeviceInput];
267 output = [[AVCaptureVideoDataOutput alloc] init];
268 [output setSampleBufferDelegate:self
270 output.alwaysDiscardsLateVideoFrames = YES;
271 output.videoSettings = nil; /* device native format */
273 session = [[AVCaptureSession alloc] init];
274 [session addInput:input];
275 [session addOutput:output];
285 dispatch_sync (mainQueue, ^{
286 g_assert (![session isRunning]);
288 [session removeInput:input];
289 [session removeOutput:output];
300 if (!captureScreen) {
306 gst_caps_unref (caps);
310 #define GST_AVF_CAPS_NEW(format, w, h, fps_n, fps_d) \
311 (gst_caps_new_simple ("video/x-raw", \
312 "width", G_TYPE_INT, w, \
313 "height", G_TYPE_INT, h, \
314 "format", G_TYPE_STRING, gst_video_format_to_string (format), \
315 "framerate", GST_TYPE_FRACTION, (fps_n), (fps_d), \
318 - (GstVideoFormat)getGstVideoFormat:(NSNumber *)pixel_format
320 GstVideoFormat gst_format = GST_VIDEO_FORMAT_UNKNOWN;
322 switch ([pixel_format integerValue]) {
323 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: /* 420v */
324 gst_format = GST_VIDEO_FORMAT_NV12;
326 case kCVPixelFormatType_422YpCbCr8: /* 2vuy */
327 gst_format = GST_VIDEO_FORMAT_UYVY;
329 case kCVPixelFormatType_32BGRA: /* BGRA */
330 gst_format = GST_VIDEO_FORMAT_BGRA;
332 case kCVPixelFormatType_422YpCbCr8_yuvs: /* yuvs */
333 gst_format = GST_VIDEO_FORMAT_YUY2;
336 GST_DEBUG ("Pixel format %s is not handled by avfvideosrc", [[pixel_format stringValue] UTF8String]);
343 - (BOOL)getDeviceCaps:(GstCaps *)result
345 NSArray *formats = [device valueForKey:@"formats"];
346 NSArray *pixel_formats = output.availableVideoCVPixelFormatTypes;
348 for (AVCaptureDeviceFormat *f in [formats reverseObjectEnumerator]) {
349 CMFormatDescriptionRef formatDescription = f.formatDescription;
350 CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
352 for (AVFrameRateRange *rate in f.videoSupportedFrameRateRanges) {
355 gst_util_double_to_fraction (rate.maxFrameRate, &fps_n, &fps_d);
357 for (NSNumber *pixel_format in pixel_formats) {
358 GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
359 if (gst_format != GST_VIDEO_FORMAT_UNKNOWN)
360 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, dimensions.width, dimensions.height, fps_n, fps_d));
367 - (BOOL)setDeviceCaps:(GstVideoInfo *)info
370 gboolean found_format = FALSE, found_framerate = FALSE;
371 NSArray *formats = [device valueForKey:@"formats"];
372 gst_util_fraction_to_double (info->fps_n, info->fps_d, &framerate);
374 if ([device lockForConfiguration:NULL] == YES) {
375 for (AVCaptureDeviceFormat *f in formats) {
376 CMFormatDescriptionRef formatDescription = f.formatDescription;
377 CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
378 if (dimensions.width == info->width && dimensions.height == info->height) {
380 device.activeFormat = f;
381 for (AVFrameRateRange *rate in f.videoSupportedFrameRateRanges) {
382 if (abs (framerate - rate.maxFrameRate) < 0.00001) {
383 found_framerate = TRUE;
384 device.activeVideoMinFrameDuration = rate.minFrameDuration;
385 [device setValue:[NSValue valueWithCMTime:rate.minFrameDuration]
386 forKey:@"activeVideoMinFrameDuration"];
388 /* Only available on OSX >= 10.8 and iOS >= 7.0 */
389 [device setValue:[NSValue valueWithCMTime:rate.maxFrameDuration]
390 forKey:@"activeVideoMaxFrameDuration"];
391 } @catch (NSException *exception) {
392 if (![[exception name] isEqualTo:NSUndefinedKeyException]) {
393 GST_WARNING ("An unexcepted error occured: %s",
394 [exception.reason UTF8String]);
403 GST_WARNING ("Unsupported capture dimensions %dx%d", info->width, info->height);
406 if (!found_framerate) {
407 GST_WARNING ("Unsupported capture framerate %d/%d", info->fps_n, info->fps_d);
411 GST_WARNING ("Couldn't lock device for configuration");
417 - (BOOL)getSessionPresetCaps:(GstCaps *)result
419 NSArray *pixel_formats = output.availableVideoCVPixelFormatTypes;
420 for (NSNumber *pixel_format in pixel_formats) {
421 GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
422 if (gst_format == GST_VIDEO_FORMAT_UNKNOWN)
426 if ([session canSetSessionPreset:AVCaptureSessionPreset1920x1080])
427 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 1920, 1080, DEVICE_FPS_N, DEVICE_FPS_D));
429 if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720])
430 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 1280, 720, DEVICE_FPS_N, DEVICE_FPS_D));
431 if ([session canSetSessionPreset:AVCaptureSessionPreset640x480])
432 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 640, 480, DEVICE_FPS_N, DEVICE_FPS_D));
433 if ([session canSetSessionPreset:AVCaptureSessionPresetMedium])
434 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 480, 360, DEVICE_FPS_N, DEVICE_FPS_D));
435 if ([session canSetSessionPreset:AVCaptureSessionPreset352x288])
436 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 352, 288, DEVICE_FPS_N, DEVICE_FPS_D));
437 if ([session canSetSessionPreset:AVCaptureSessionPresetLow])
438 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 192, 144, DEVICE_FPS_N, DEVICE_FPS_D));
443 - (BOOL)setSessionPresetCaps:(GstVideoInfo *)info;
446 if ([device lockForConfiguration:NULL] != YES) {
447 GST_WARNING ("Couldn't lock device for configuration");
451 switch (info->width) {
453 session.sessionPreset = AVCaptureSessionPresetLow;
456 session.sessionPreset = AVCaptureSessionPreset352x288;
459 session.sessionPreset = AVCaptureSessionPresetMedium;
462 session.sessionPreset = AVCaptureSessionPreset640x480;
465 session.sessionPreset = AVCaptureSessionPreset1280x720;
469 session.sessionPreset = AVCaptureSessionPreset1920x1080;
473 GST_WARNING ("Unsupported capture dimensions %dx%d", info->width, info->height);
482 NSArray *pixel_formats;
485 return NULL; /* BaseSrc will return template caps */
487 result = gst_caps_new_empty ();
488 pixel_formats = output.availableVideoCVPixelFormatTypes;
492 CGRect rect = CGDisplayBounds (displayId);
493 for (NSNumber *pixel_format in pixel_formats) {
494 GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
495 if (gst_format != GST_VIDEO_FORMAT_UNKNOWN)
496 gst_caps_append (result, gst_caps_new_simple ("video/x-raw",
497 "width", G_TYPE_INT, (int)rect.size.width,
498 "height", G_TYPE_INT, (int)rect.size.height,
499 "format", G_TYPE_STRING, gst_video_format_to_string (gst_format),
503 GST_WARNING ("Screen capture is not supported by iOS");
510 [self getDeviceCaps:result];
512 } @catch (NSException *exception) {
514 if (![[exception name] isEqualTo:NSUndefinedKeyException]) {
515 GST_WARNING ("An unexcepted error occured: %s", [exception.reason UTF8String]);
519 /* Fallback on session presets API for iOS < 7.0 */
520 [self getSessionPresetCaps:result];
526 - (BOOL)setCaps:(GstCaps *)new_caps
529 BOOL success = YES, *successPtr = &success;
531 gst_video_info_init (&info);
532 gst_video_info_from_caps (&info, new_caps);
535 height = info.height;
536 format = info.finfo->format;
538 dispatch_sync (mainQueue, ^{
541 g_assert (![session isRunning]);
545 AVCaptureScreenInput *screenInput = (AVCaptureScreenInput *)input;
546 screenInput.minFrameDuration = CMTimeMake(info.fps_d, info.fps_n);
548 GST_WARNING ("Screen capture is not supported by iOS");
555 /* formats and activeFormat keys are only available on OSX >= 10.7 and iOS >= 7.0 */
556 *successPtr = [self setDeviceCaps:(GstVideoInfo *)&info];
557 if (*successPtr != YES)
560 } @catch (NSException *exception) {
562 if (![[exception name] isEqualTo:NSUndefinedKeyException]) {
563 GST_WARNING ("An unexcepted error occured: %s", [exception.reason UTF8String]);
568 /* Fallback on session presets API for iOS < 7.0 */
569 *successPtr = [self setSessionPresetCaps:(GstVideoInfo *)&info];
570 if (*successPtr != YES)
576 case GST_VIDEO_FORMAT_NV12:
577 newformat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
579 case GST_VIDEO_FORMAT_UYVY:
580 newformat = kCVPixelFormatType_422YpCbCr8;
582 case GST_VIDEO_FORMAT_YUY2:
583 newformat = kCVPixelFormatType_422YpCbCr8_yuvs;
585 case GST_VIDEO_FORMAT_BGRA:
586 newformat = kCVPixelFormatType_32BGRA;
590 GST_WARNING ("Unsupported output format %s",
591 gst_video_format_to_string (format));
595 GST_DEBUG_OBJECT(element,
596 "Width: %d Height: %d Format: %" GST_FOURCC_FORMAT,
598 GST_FOURCC_ARGS (gst_video_format_to_fourcc (format)));
600 output.videoSettings = [NSDictionary
601 dictionaryWithObject:[NSNumber numberWithInt:newformat]
602 forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
604 caps = gst_caps_copy (new_caps);
605 [session startRunning];
607 /* Unlock device configuration only after session is started so the session
608 * won't reset the capture formats */
609 [device unlockForConfiguration];
617 bufQueueLock = [[NSConditionLock alloc] initWithCondition:NO_BUFFERS];
618 bufQueue = [[NSMutableArray alloc] initWithCapacity:BUFFER_QUEUE_SIZE];
621 duration = gst_util_uint64_scale (GST_SECOND, DEVICE_FPS_D, DEVICE_FPS_N);
624 lastSampling = GST_CLOCK_TIME_NONE;
633 dispatch_sync (mainQueue, ^{ [session stopRunning]; });
634 dispatch_sync (workerQueue, ^{});
636 [bufQueueLock release];
644 - (BOOL)query:(GstQuery *)query
648 if (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY) {
650 GstClockTime min_latency, max_latency;
652 min_latency = max_latency = duration; /* for now */
655 GST_DEBUG_OBJECT (element, "reporting latency of min %" GST_TIME_FORMAT
656 " max %" GST_TIME_FORMAT,
657 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
658 gst_query_set_latency (query, TRUE, min_latency, max_latency);
661 result = GST_BASE_SRC_CLASS (parent_class)->query (baseSrc, query);
671 [bufQueueLock unlockWithCondition:HAS_BUFFER_OR_STOP_REQUEST];
680 [bufQueueLock unlock];
685 - (GstStateChangeReturn)changeState:(GstStateChange)transition
687 GstStateChangeReturn ret;
689 if (transition == GST_STATE_CHANGE_NULL_TO_READY) {
690 if (![self openDevice])
691 return GST_STATE_CHANGE_FAILURE;
694 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
696 if (transition == GST_STATE_CHANGE_READY_TO_NULL)
702 - (void)captureOutput:(AVCaptureOutput *)captureOutput
703 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
704 fromConnection:(AVCaptureConnection *)connection
709 [bufQueueLock unlock];
713 if ([bufQueue count] == BUFFER_QUEUE_SIZE)
714 [bufQueue removeLastObject];
716 [bufQueue insertObject:(id)sampleBuffer
719 [bufQueueLock unlockWithCondition:HAS_BUFFER_OR_STOP_REQUEST];
722 - (GstFlowReturn)create:(GstBuffer **)buf
724 CMSampleBufferRef sbuf;
725 CVImageBufferRef image_buf;
726 CVPixelBufferRef pixel_buf;
727 size_t cur_width, cur_height;
729 [bufQueueLock lockWhenCondition:HAS_BUFFER_OR_STOP_REQUEST];
731 [bufQueueLock unlock];
732 return GST_FLOW_FLUSHING;
735 sbuf = (CMSampleBufferRef) [bufQueue lastObject];
737 [bufQueue removeLastObject];
738 [bufQueueLock unlockWithCondition:
739 ([bufQueue count] == 0) ? NO_BUFFERS : HAS_BUFFER_OR_STOP_REQUEST];
741 /* Check output frame size dimensions */
742 image_buf = CMSampleBufferGetImageBuffer (sbuf);
744 pixel_buf = (CVPixelBufferRef) image_buf;
745 cur_width = CVPixelBufferGetWidth (pixel_buf);
746 cur_height = CVPixelBufferGetHeight (pixel_buf);
748 if (width != cur_width || height != cur_height) {
749 /* Set new caps according to current frame dimensions */
750 GST_WARNING ("Output frame size has changed %dx%d -> %dx%d, updating caps",
751 width, height, (int)cur_width, (int)cur_height);
754 gst_caps_set_simple (caps,
755 "width", G_TYPE_INT, width,
756 "height", G_TYPE_INT, height,
758 gst_pad_push_event (GST_BASE_SINK_PAD (baseSrc), gst_event_new_caps (caps));
762 *buf = gst_core_media_buffer_new (sbuf);
765 [self timestampBuffer:*buf];
768 [self updateStatistics];
773 - (void)timestampBuffer:(GstBuffer *)buf
776 GstClockTime timestamp;
778 GST_OBJECT_LOCK (element);
779 clock = GST_ELEMENT_CLOCK (element);
781 gst_object_ref (clock);
782 timestamp = element->base_time;
784 timestamp = GST_CLOCK_TIME_NONE;
786 GST_OBJECT_UNLOCK (element);
789 timestamp = gst_clock_get_time (clock) - timestamp;
790 if (timestamp > duration)
791 timestamp -= duration;
795 gst_object_unref (clock);
799 GST_BUFFER_OFFSET (buf) = offset++;
800 GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1;
801 GST_BUFFER_TIMESTAMP (buf) = timestamp;
802 GST_BUFFER_DURATION (buf) = duration;
805 - (void)updateStatistics
809 GST_OBJECT_LOCK (element);
810 clock = GST_ELEMENT_CLOCK (element);
812 gst_object_ref (clock);
813 GST_OBJECT_UNLOCK (element);
816 GstClockTime now = gst_clock_get_time (clock);
817 gst_object_unref (clock);
821 if (GST_CLOCK_TIME_IS_VALID (lastSampling)) {
822 if (now - lastSampling >= GST_SECOND) {
823 GST_OBJECT_LOCK (element);
825 GST_OBJECT_UNLOCK (element);
827 g_object_notify (G_OBJECT (element), "fps");
852 PROP_CAPTURE_SCREEN_CURSOR,
853 PROP_CAPTURE_SCREEN_MOUSE_CLICKS,
858 static void gst_avf_video_src_finalize (GObject * obj);
859 static void gst_avf_video_src_get_property (GObject * object, guint prop_id,
860 GValue * value, GParamSpec * pspec);
861 static void gst_avf_video_src_set_property (GObject * object, guint prop_id,
862 const GValue * value, GParamSpec * pspec);
863 static GstStateChangeReturn gst_avf_video_src_change_state (
864 GstElement * element, GstStateChange transition);
865 static GstCaps * gst_avf_video_src_get_caps (GstBaseSrc * basesrc,
867 static gboolean gst_avf_video_src_set_caps (GstBaseSrc * basesrc,
869 static gboolean gst_avf_video_src_start (GstBaseSrc * basesrc);
870 static gboolean gst_avf_video_src_stop (GstBaseSrc * basesrc);
871 static gboolean gst_avf_video_src_query (GstBaseSrc * basesrc,
873 static gboolean gst_avf_video_src_unlock (GstBaseSrc * basesrc);
874 static gboolean gst_avf_video_src_unlock_stop (GstBaseSrc * basesrc);
875 static GstFlowReturn gst_avf_video_src_create (GstPushSrc * pushsrc,
880 gst_avf_video_src_class_init (GstAVFVideoSrcClass * klass)
882 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
883 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
884 GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
885 GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
887 gobject_class->finalize = gst_avf_video_src_finalize;
888 gobject_class->get_property = gst_avf_video_src_get_property;
889 gobject_class->set_property = gst_avf_video_src_set_property;
891 gstelement_class->change_state = gst_avf_video_src_change_state;
893 gstbasesrc_class->get_caps = gst_avf_video_src_get_caps;
894 gstbasesrc_class->set_caps = gst_avf_video_src_set_caps;
895 gstbasesrc_class->start = gst_avf_video_src_start;
896 gstbasesrc_class->stop = gst_avf_video_src_stop;
897 gstbasesrc_class->query = gst_avf_video_src_query;
898 gstbasesrc_class->unlock = gst_avf_video_src_unlock;
899 gstbasesrc_class->unlock_stop = gst_avf_video_src_unlock_stop;
901 gstpushsrc_class->create = gst_avf_video_src_create;
903 gst_element_class_set_metadata (gstelement_class,
904 "Video Source (AVFoundation)", "Source/Video",
905 "Reads frames from an iOS AVFoundation device",
906 "Ole André Vadla Ravnås <oleavr@soundrop.com>");
908 gst_element_class_add_pad_template (gstelement_class,
909 gst_static_pad_template_get (&src_template));
911 g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
912 g_param_spec_int ("device-index", "Device Index",
913 "The zero-based device index",
914 -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
915 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
916 g_object_class_install_property (gobject_class, PROP_DO_STATS,
917 g_param_spec_boolean ("do-stats", "Enable statistics",
918 "Enable logging of statistics", DEFAULT_DO_STATS,
919 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
920 g_object_class_install_property (gobject_class, PROP_FPS,
921 g_param_spec_int ("fps", "Frames per second",
922 "Last measured framerate, if statistics are enabled",
923 -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
925 g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN,
926 g_param_spec_boolean ("capture-screen", "Enable screen capture",
927 "Enable screen capture functionality", FALSE,
928 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
929 g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN_CURSOR,
930 g_param_spec_boolean ("capture-screen-cursor", "Capture screen cursor",
931 "Enable cursor capture while capturing screen", FALSE,
932 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
933 g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN_MOUSE_CLICKS,
934 g_param_spec_boolean ("capture-screen-mouse-clicks", "Enable mouse clicks capture",
935 "Enable mouse clicks capture while capturing screen", FALSE,
936 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
939 GST_DEBUG_CATEGORY_INIT (gst_avf_video_src_debug, "avfvideosrc",
940 0, "iOS AVFoundation video source");
943 #define OBJC_CALLOUT_BEGIN() \
944 NSAutoreleasePool *pool; \
946 pool = [[NSAutoreleasePool alloc] init]
947 #define OBJC_CALLOUT_END() \
952 gst_avf_video_src_init (GstAVFVideoSrc * src)
954 OBJC_CALLOUT_BEGIN ();
955 src->impl = [[GstAVFVideoSrcImpl alloc] initWithSrc:GST_PUSH_SRC (src)];
960 gst_avf_video_src_finalize (GObject * obj)
962 OBJC_CALLOUT_BEGIN ();
963 [GST_AVF_VIDEO_SRC_IMPL (obj) release];
966 G_OBJECT_CLASS (parent_class)->finalize (obj);
970 gst_avf_video_src_get_property (GObject * object, guint prop_id, GValue * value,
973 GstAVFVideoSrcImpl *impl = GST_AVF_VIDEO_SRC_IMPL (object);
977 case PROP_CAPTURE_SCREEN:
978 g_value_set_boolean (value, impl.captureScreen);
980 case PROP_CAPTURE_SCREEN_CURSOR:
981 g_value_set_boolean (value, impl.captureScreenCursor);
983 case PROP_CAPTURE_SCREEN_MOUSE_CLICKS:
984 g_value_set_boolean (value, impl.captureScreenMouseClicks);
987 case PROP_DEVICE_INDEX:
988 g_value_set_int (value, impl.deviceIndex);
991 g_value_set_boolean (value, impl.doStats);
994 GST_OBJECT_LOCK (object);
995 g_value_set_int (value, impl.fps);
996 GST_OBJECT_UNLOCK (object);
999 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1005 gst_avf_video_src_set_property (GObject * object, guint prop_id,
1006 const GValue * value, GParamSpec * pspec)
1008 GstAVFVideoSrcImpl *impl = GST_AVF_VIDEO_SRC_IMPL (object);
1012 case PROP_CAPTURE_SCREEN:
1013 impl.captureScreen = g_value_get_boolean (value);
1015 case PROP_CAPTURE_SCREEN_CURSOR:
1016 impl.captureScreenCursor = g_value_get_boolean (value);
1018 case PROP_CAPTURE_SCREEN_MOUSE_CLICKS:
1019 impl.captureScreenMouseClicks = g_value_get_boolean (value);
1022 case PROP_DEVICE_INDEX:
1023 impl.deviceIndex = g_value_get_int (value);
1026 impl.doStats = g_value_get_boolean (value);
1029 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1034 static GstStateChangeReturn
1035 gst_avf_video_src_change_state (GstElement * element, GstStateChange transition)
1037 GstStateChangeReturn ret;
1039 OBJC_CALLOUT_BEGIN ();
1040 ret = [GST_AVF_VIDEO_SRC_IMPL (element) changeState: transition];
1041 OBJC_CALLOUT_END ();
1047 gst_avf_video_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
1051 OBJC_CALLOUT_BEGIN ();
1052 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) getCaps];
1053 OBJC_CALLOUT_END ();
1059 gst_avf_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
1063 OBJC_CALLOUT_BEGIN ();
1064 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) setCaps:caps];
1065 OBJC_CALLOUT_END ();
1071 gst_avf_video_src_start (GstBaseSrc * basesrc)
1075 OBJC_CALLOUT_BEGIN ();
1076 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) start];
1077 OBJC_CALLOUT_END ();
1083 gst_avf_video_src_stop (GstBaseSrc * basesrc)
1087 OBJC_CALLOUT_BEGIN ();
1088 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) stop];
1089 OBJC_CALLOUT_END ();
1095 gst_avf_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
1099 OBJC_CALLOUT_BEGIN ();
1100 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) query:query];
1101 OBJC_CALLOUT_END ();
1107 gst_avf_video_src_unlock (GstBaseSrc * basesrc)
1111 OBJC_CALLOUT_BEGIN ();
1112 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) unlock];
1113 OBJC_CALLOUT_END ();
1119 gst_avf_video_src_unlock_stop (GstBaseSrc * basesrc)
1123 OBJC_CALLOUT_BEGIN ();
1124 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) unlockStop];
1125 OBJC_CALLOUT_END ();
1130 static GstFlowReturn
1131 gst_avf_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
1135 OBJC_CALLOUT_BEGIN ();
1136 ret = [GST_AVF_VIDEO_SRC_IMPL (pushsrc) create: buf];
1137 OBJC_CALLOUT_END ();