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.
24 #include "avfvideosrc.h"
26 #import <AVFoundation/AVFoundation.h>
27 #include <gst/video/video.h>
28 #include "coremediabuffer.h"
30 #define DEFAULT_DEVICE_INDEX -1
31 #define DEFAULT_DO_STATS FALSE
33 #define DEVICE_FPS_N 25
34 #define DEVICE_FPS_D 1
36 #define BUFFER_QUEUE_SIZE 2
38 GST_DEBUG_CATEGORY (gst_avf_video_src_debug);
39 #define GST_CAT_DEFAULT gst_avf_video_src_debug
41 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
44 GST_STATIC_CAPS ("video/x-raw, "
45 "format = (string) { NV12, UYVY, YUY2 }, "
46 "framerate = " GST_VIDEO_FPS_RANGE ", "
47 "width = " GST_VIDEO_SIZE_RANGE ", "
48 "height = " GST_VIDEO_SIZE_RANGE "; "
51 "format = (string) { BGRA }, "
54 "endianness = (int) BIG_ENDIAN, "
55 "red_mask = (int) 0x0000FF00, "
56 "green_mask = (int) 0x00FF0000, "
57 "blue_mask = (int) 0xFF000000, "
58 "alpha_mask = (int) 0x000000FF, "
59 "framerate = " GST_VIDEO_FPS_RANGE ", "
60 "width = " GST_VIDEO_SIZE_RANGE ", "
61 "height = " GST_VIDEO_SIZE_RANGE "; "
64 typedef enum _QueueState {
66 HAS_BUFFER_OR_STOP_REQUEST,
69 #define gst_avf_video_src_parent_class parent_class
70 G_DEFINE_TYPE (GstAVFVideoSrc, gst_avf_video_src, GST_TYPE_PUSH_SRC);
72 @interface GstAVFVideoSrcImpl : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> {
80 CGDirectDisplayID displayId;
83 AVCaptureSession *session;
84 AVCaptureInput *input;
85 AVCaptureVideoDataOutput *output;
86 AVCaptureDevice *device;
88 dispatch_queue_t mainQueue;
89 dispatch_queue_t workerQueue;
90 NSConditionLock *bufQueueLock;
91 NSMutableArray *bufQueue;
95 GstVideoFormat format;
97 GstClockTime duration;
100 GstClockTime lastSampling;
106 - (id)initWithSrc:(GstPushSrc *)src;
109 @property int deviceIndex;
110 @property BOOL doStats;
112 @property BOOL captureScreen;
113 @property BOOL captureScreenCursor;
114 @property BOOL captureScreenMouseClicks;
116 - (BOOL)openScreenInput;
117 - (BOOL)openDeviceInput;
120 - (GstVideoFormat)getGstVideoFormat:(NSNumber *)pixel_format;
121 - (BOOL)getDeviceCaps:(GstCaps *)result;
122 - (BOOL)setDeviceCaps:(GstVideoInfo *)info;
123 - (BOOL)getSessionPresetCaps:(GstCaps *)result;
124 - (BOOL)setSessionPresetCaps:(GstVideoInfo *)info;
125 - (GstCaps *)getCaps;
126 - (BOOL)setCaps:(GstCaps *)new_caps;
131 - (BOOL)query:(GstQuery *)query;
132 - (GstStateChangeReturn)changeState:(GstStateChange)transition;
133 - (GstFlowReturn)create:(GstBuffer **)buf;
134 - (void)timestampBuffer:(GstBuffer *)buf;
135 - (void)updateStatistics;
136 - (void)captureOutput:(AVCaptureOutput *)captureOutput
137 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
138 fromConnection:(AVCaptureConnection *)connection;
142 @implementation GstAVFVideoSrcImpl
144 @synthesize deviceIndex, doStats, fps, captureScreen,
145 captureScreenCursor, captureScreenMouseClicks;
149 return [self initWithSrc:NULL];
152 - (id)initWithSrc:(GstPushSrc *)src
154 if ((self = [super init])) {
155 element = GST_ELEMENT_CAST (src);
156 baseSrc = GST_BASE_SRC_CAST (src);
159 deviceIndex = DEFAULT_DEVICE_INDEX;
161 captureScreenCursor = NO;
162 captureScreenMouseClicks = NO;
164 displayId = kCGDirectMainDisplay;
168 dispatch_queue_create ("org.freedesktop.gstreamer.avfvideosrc.main", NULL);
170 dispatch_queue_create ("org.freedesktop.gstreamer.avfvideosrc.output", NULL);
172 gst_base_src_set_live (baseSrc, TRUE);
173 gst_base_src_set_format (baseSrc, GST_FORMAT_TIME);
181 dispatch_release (mainQueue);
183 dispatch_release (workerQueue);
189 - (BOOL)openDeviceInput
191 NSString *mediaType = AVMediaTypeVideo;
194 if (deviceIndex == -1) {
195 device = [AVCaptureDevice defaultDeviceWithMediaType:mediaType];
197 GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
198 ("No video capture devices found"), (NULL));
202 NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
203 if (deviceIndex >= [devices count]) {
204 GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
205 ("Invalid video capture device index"), (NULL));
208 device = [devices objectAtIndex:deviceIndex];
210 g_assert (device != nil);
213 GST_INFO ("Opening '%s'", [[device localizedName] UTF8String]);
215 input = [AVCaptureDeviceInput deviceInputWithDevice:device
218 GST_ELEMENT_ERROR (element, RESOURCE, BUSY,
219 ("Failed to open device: %s",
220 [[err localizedDescription] UTF8String]),
230 - (BOOL)openScreenInput
235 GST_DEBUG_OBJECT (element, "Opening screen input");
237 AVCaptureScreenInput *screenInput =
238 [[AVCaptureScreenInput alloc] initWithDisplayID:displayId];
242 [screenInput setValue:[NSNumber numberWithBool:captureScreenCursor]
243 forKey:@"capturesCursor"];
245 } @catch (NSException *exception) {
246 if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
247 GST_WARNING ("An unexpected error occured: %s",
248 [[exception reason] UTF8String]);
250 GST_WARNING ("Capturing cursor is only supported in OS X >= 10.8");
252 screenInput.capturesMouseClicks = captureScreenMouseClicks;
261 BOOL success = NO, *successPtr = &success;
263 GST_DEBUG_OBJECT (element, "Opening device");
265 dispatch_sync (mainQueue, ^{
269 ret = [self openScreenInput];
271 ret = [self openDeviceInput];
276 output = [[AVCaptureVideoDataOutput alloc] init];
277 [output setSampleBufferDelegate:self
279 output.alwaysDiscardsLateVideoFrames = YES;
280 output.videoSettings = nil; /* device native format */
282 session = [[AVCaptureSession alloc] init];
283 [session addInput:input];
284 [session addOutput:output];
289 GST_DEBUG_OBJECT (element, "Opening device %s", success ? "succeed" : "failed");
296 GST_DEBUG_OBJECT (element, "Closing device");
298 dispatch_sync (mainQueue, ^{
299 g_assert (![session isRunning]);
301 [session removeInput:input];
302 [session removeOutput:output];
313 if (!captureScreen) {
319 gst_caps_unref (caps);
323 #define GST_AVF_CAPS_NEW(format, w, h, fps_n, fps_d) \
324 (gst_caps_new_simple ("video/x-raw", \
325 "width", G_TYPE_INT, w, \
326 "height", G_TYPE_INT, h, \
327 "format", G_TYPE_STRING, gst_video_format_to_string (format), \
328 "framerate", GST_TYPE_FRACTION, (fps_n), (fps_d), \
331 - (GstVideoFormat)getGstVideoFormat:(NSNumber *)pixel_format
333 GstVideoFormat gst_format = GST_VIDEO_FORMAT_UNKNOWN;
335 switch ([pixel_format integerValue]) {
336 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: /* 420v */
337 gst_format = GST_VIDEO_FORMAT_NV12;
339 case kCVPixelFormatType_422YpCbCr8: /* 2vuy */
340 gst_format = GST_VIDEO_FORMAT_UYVY;
342 case kCVPixelFormatType_32BGRA: /* BGRA */
343 gst_format = GST_VIDEO_FORMAT_BGRA;
345 case kCVPixelFormatType_422YpCbCr8_yuvs: /* yuvs */
346 gst_format = GST_VIDEO_FORMAT_YUY2;
349 GST_LOG_OBJECT (element, "Pixel format %s is not handled by avfvideosrc",
350 [[pixel_format stringValue] UTF8String]);
357 - (BOOL)getDeviceCaps:(GstCaps *)result
359 NSArray *formats = [device valueForKey:@"formats"];
360 NSArray *pixel_formats = output.availableVideoCVPixelFormatTypes;
362 GST_DEBUG_OBJECT (element, "Getting device caps");
364 /* Do not use AVCaptureDeviceFormat or AVFrameRateRange only
365 * available in iOS >= 7.0. We use a dynamic approach with key-value
366 * coding or performSelector */
367 for (NSObject *f in [formats reverseObjectEnumerator]) {
368 CMFormatDescriptionRef formatDescription;
369 CMVideoDimensions dimensions;
371 /* formatDescription can't be retrieved with valueForKey so use a selector here */
372 formatDescription = (CMFormatDescriptionRef) [f performSelector:@selector(formatDescription)];
373 dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
374 for (NSObject *rate in [f valueForKey:@"videoSupportedFrameRateRanges"]) {
378 [[rate valueForKey:@"maxFrameRate"] getValue:&max_fps];
379 gst_util_double_to_fraction (max_fps, &fps_n, &fps_d);
381 for (NSNumber *pixel_format in pixel_formats) {
382 GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
383 if (gst_format != GST_VIDEO_FORMAT_UNKNOWN)
384 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, dimensions.width, dimensions.height, fps_n, fps_d));
388 GST_LOG_OBJECT (element, "Device returned the following caps %" GST_PTR_FORMAT, result);
392 - (BOOL)setDeviceCaps:(GstVideoInfo *)info
395 gboolean found_format = FALSE, found_framerate = FALSE;
396 NSArray *formats = [device valueForKey:@"formats"];
397 gst_util_fraction_to_double (info->fps_n, info->fps_d, &framerate);
399 GST_DEBUG_OBJECT (element, "Setting device caps");
401 if ([device lockForConfiguration:NULL] == YES) {
402 for (NSObject *f in formats) {
403 CMFormatDescriptionRef formatDescription;
404 CMVideoDimensions dimensions;
406 formatDescription = (CMFormatDescriptionRef) [f performSelector:@selector(formatDescription)];
407 dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
408 if (dimensions.width == info->width && dimensions.height == info->height) {
410 [device setValue:f forKey:@"activeFormat"];
411 for (NSObject *rate in [f valueForKey:@"videoSupportedFrameRateRanges"]) {
412 gdouble max_frame_rate;
414 [[rate valueForKey:@"maxFrameRate"] getValue:&max_frame_rate];
415 if (abs (framerate - max_frame_rate) < 0.00001) {
416 NSValue *min_frame_duration, *max_frame_duration;
418 found_framerate = TRUE;
419 min_frame_duration = [rate valueForKey:@"minFrameDuration"];
420 max_frame_duration = [rate valueForKey:@"maxFrameDuration"];
421 [device setValue:min_frame_duration forKey:@"activeVideoMinFrameDuration"];
423 /* Only available on OSX >= 10.8 and iOS >= 7.0 */
424 [device setValue:max_frame_duration forKey:@"activeVideoMaxFrameDuration"];
425 } @catch (NSException *exception) {
426 if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
427 GST_WARNING ("An unexcepted error occured: %s",
428 [exception.reason UTF8String]);
437 GST_WARNING ("Unsupported capture dimensions %dx%d", info->width, info->height);
440 if (!found_framerate) {
441 GST_WARNING ("Unsupported capture framerate %d/%d", info->fps_n, info->fps_d);
445 GST_WARNING ("Couldn't lock device for configuration");
451 - (BOOL)getSessionPresetCaps:(GstCaps *)result
453 NSArray *pixel_formats = output.availableVideoCVPixelFormatTypes;
454 for (NSNumber *pixel_format in pixel_formats) {
455 GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
456 if (gst_format == GST_VIDEO_FORMAT_UNKNOWN)
460 if ([session canSetSessionPreset:AVCaptureSessionPreset1920x1080])
461 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 1920, 1080, DEVICE_FPS_N, DEVICE_FPS_D));
463 if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720])
464 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 1280, 720, DEVICE_FPS_N, DEVICE_FPS_D));
465 if ([session canSetSessionPreset:AVCaptureSessionPreset640x480])
466 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 640, 480, DEVICE_FPS_N, DEVICE_FPS_D));
467 if ([session canSetSessionPreset:AVCaptureSessionPresetMedium])
468 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 480, 360, DEVICE_FPS_N, DEVICE_FPS_D));
469 if ([session canSetSessionPreset:AVCaptureSessionPreset352x288])
470 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 352, 288, DEVICE_FPS_N, DEVICE_FPS_D));
471 if ([session canSetSessionPreset:AVCaptureSessionPresetLow])
472 gst_caps_append (result, GST_AVF_CAPS_NEW (gst_format, 192, 144, DEVICE_FPS_N, DEVICE_FPS_D));
475 GST_LOG_OBJECT (element, "Session presets returned the following caps %" GST_PTR_FORMAT, result);
480 - (BOOL)setSessionPresetCaps:(GstVideoInfo *)info;
482 GST_DEBUG_OBJECT (element, "Setting session presset caps");
484 if ([device lockForConfiguration:NULL] != YES) {
485 GST_WARNING ("Couldn't lock device for configuration");
489 switch (info->width) {
491 session.sessionPreset = AVCaptureSessionPresetLow;
494 session.sessionPreset = AVCaptureSessionPreset352x288;
497 session.sessionPreset = AVCaptureSessionPresetMedium;
500 session.sessionPreset = AVCaptureSessionPreset640x480;
503 session.sessionPreset = AVCaptureSessionPreset1280x720;
507 session.sessionPreset = AVCaptureSessionPreset1920x1080;
511 GST_WARNING ("Unsupported capture dimensions %dx%d", info->width, info->height);
520 NSArray *pixel_formats;
523 return NULL; /* BaseSrc will return template caps */
525 result = gst_caps_new_empty ();
526 pixel_formats = output.availableVideoCVPixelFormatTypes;
530 CGRect rect = CGDisplayBounds (displayId);
531 for (NSNumber *pixel_format in pixel_formats) {
532 GstVideoFormat gst_format = [self getGstVideoFormat:pixel_format];
533 if (gst_format != GST_VIDEO_FORMAT_UNKNOWN)
534 gst_caps_append (result, gst_caps_new_simple ("video/x-raw",
535 "width", G_TYPE_INT, (int)rect.size.width,
536 "height", G_TYPE_INT, (int)rect.size.height,
537 "format", G_TYPE_STRING, gst_video_format_to_string (gst_format),
541 GST_WARNING ("Screen capture is not supported by iOS");
548 [self getDeviceCaps:result];
550 } @catch (NSException *exception) {
552 if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
553 GST_WARNING ("An unexcepted error occured: %s", [exception.reason UTF8String]);
557 /* Fallback on session presets API for iOS < 7.0 */
558 [self getSessionPresetCaps:result];
564 - (BOOL)setCaps:(GstCaps *)new_caps
567 BOOL success = YES, *successPtr = &success;
569 gst_video_info_init (&info);
570 gst_video_info_from_caps (&info, new_caps);
573 height = info.height;
574 format = info.finfo->format;
576 dispatch_sync (mainQueue, ^{
579 g_assert (![session isRunning]);
583 AVCaptureScreenInput *screenInput = (AVCaptureScreenInput *)input;
584 screenInput.minFrameDuration = CMTimeMake(info.fps_d, info.fps_n);
586 GST_WARNING ("Screen capture is not supported by iOS");
593 /* formats and activeFormat keys are only available on OSX >= 10.7 and iOS >= 7.0 */
594 *successPtr = [self setDeviceCaps:(GstVideoInfo *)&info];
595 if (*successPtr != YES)
598 } @catch (NSException *exception) {
600 if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
601 GST_WARNING ("An unexcepted error occured: %s", [exception.reason UTF8String]);
606 /* Fallback on session presets API for iOS < 7.0 */
607 *successPtr = [self setSessionPresetCaps:(GstVideoInfo *)&info];
608 if (*successPtr != YES)
614 case GST_VIDEO_FORMAT_NV12:
615 newformat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
617 case GST_VIDEO_FORMAT_UYVY:
618 newformat = kCVPixelFormatType_422YpCbCr8;
620 case GST_VIDEO_FORMAT_YUY2:
621 newformat = kCVPixelFormatType_422YpCbCr8_yuvs;
623 case GST_VIDEO_FORMAT_BGRA:
624 newformat = kCVPixelFormatType_32BGRA;
628 GST_WARNING ("Unsupported output format %s",
629 gst_video_format_to_string (format));
633 GST_DEBUG_OBJECT(element,
634 "Width: %d Height: %d Format: %" GST_FOURCC_FORMAT,
636 GST_FOURCC_ARGS (gst_video_format_to_fourcc (format)));
638 output.videoSettings = [NSDictionary
639 dictionaryWithObject:[NSNumber numberWithInt:newformat]
640 forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
642 caps = gst_caps_copy (new_caps);
643 [session startRunning];
645 /* Unlock device configuration only after session is started so the session
646 * won't reset the capture formats */
647 [device unlockForConfiguration];
655 bufQueueLock = [[NSConditionLock alloc] initWithCondition:NO_BUFFERS];
656 bufQueue = [[NSMutableArray alloc] initWithCapacity:BUFFER_QUEUE_SIZE];
659 duration = gst_util_uint64_scale (GST_SECOND, DEVICE_FPS_D, DEVICE_FPS_N);
662 lastSampling = GST_CLOCK_TIME_NONE;
671 dispatch_sync (mainQueue, ^{ [session stopRunning]; });
672 dispatch_sync (workerQueue, ^{});
674 [bufQueueLock release];
682 - (BOOL)query:(GstQuery *)query
686 if (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY) {
688 GstClockTime min_latency, max_latency;
690 min_latency = max_latency = duration; /* for now */
693 GST_DEBUG_OBJECT (element, "reporting latency of min %" GST_TIME_FORMAT
694 " max %" GST_TIME_FORMAT,
695 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
696 gst_query_set_latency (query, TRUE, min_latency, max_latency);
699 result = GST_BASE_SRC_CLASS (parent_class)->query (baseSrc, query);
709 [bufQueueLock unlockWithCondition:HAS_BUFFER_OR_STOP_REQUEST];
718 [bufQueueLock unlock];
723 - (GstStateChangeReturn)changeState:(GstStateChange)transition
725 GstStateChangeReturn ret;
727 if (transition == GST_STATE_CHANGE_NULL_TO_READY) {
728 if (![self openDevice])
729 return GST_STATE_CHANGE_FAILURE;
732 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
734 if (transition == GST_STATE_CHANGE_READY_TO_NULL)
740 - (void)captureOutput:(AVCaptureOutput *)captureOutput
741 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
742 fromConnection:(AVCaptureConnection *)connection
747 [bufQueueLock unlock];
751 if ([bufQueue count] == BUFFER_QUEUE_SIZE)
752 [bufQueue removeLastObject];
754 [bufQueue insertObject:(id)sampleBuffer
757 [bufQueueLock unlockWithCondition:HAS_BUFFER_OR_STOP_REQUEST];
760 - (GstFlowReturn)create:(GstBuffer **)buf
762 CMSampleBufferRef sbuf;
763 CVImageBufferRef image_buf;
764 CVPixelBufferRef pixel_buf;
765 size_t cur_width, cur_height;
767 [bufQueueLock lockWhenCondition:HAS_BUFFER_OR_STOP_REQUEST];
769 [bufQueueLock unlock];
770 return GST_FLOW_FLUSHING;
773 sbuf = (CMSampleBufferRef) [bufQueue lastObject];
775 [bufQueue removeLastObject];
776 [bufQueueLock unlockWithCondition:
777 ([bufQueue count] == 0) ? NO_BUFFERS : HAS_BUFFER_OR_STOP_REQUEST];
779 /* Check output frame size dimensions */
780 image_buf = CMSampleBufferGetImageBuffer (sbuf);
782 pixel_buf = (CVPixelBufferRef) image_buf;
783 cur_width = CVPixelBufferGetWidth (pixel_buf);
784 cur_height = CVPixelBufferGetHeight (pixel_buf);
786 if (width != cur_width || height != cur_height) {
787 /* Set new caps according to current frame dimensions */
788 GST_WARNING ("Output frame size has changed %dx%d -> %dx%d, updating caps",
789 width, height, (int)cur_width, (int)cur_height);
792 gst_caps_set_simple (caps,
793 "width", G_TYPE_INT, width,
794 "height", G_TYPE_INT, height,
796 gst_pad_push_event (GST_BASE_SINK_PAD (baseSrc), gst_event_new_caps (caps));
800 *buf = gst_core_media_buffer_new (sbuf);
803 [self timestampBuffer:*buf];
806 [self updateStatistics];
811 - (void)timestampBuffer:(GstBuffer *)buf
814 GstClockTime timestamp;
816 GST_OBJECT_LOCK (element);
817 clock = GST_ELEMENT_CLOCK (element);
819 gst_object_ref (clock);
820 timestamp = element->base_time;
822 timestamp = GST_CLOCK_TIME_NONE;
824 GST_OBJECT_UNLOCK (element);
827 timestamp = gst_clock_get_time (clock) - timestamp;
828 if (timestamp > duration)
829 timestamp -= duration;
833 gst_object_unref (clock);
837 GST_BUFFER_OFFSET (buf) = offset++;
838 GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1;
839 GST_BUFFER_TIMESTAMP (buf) = timestamp;
840 GST_BUFFER_DURATION (buf) = duration;
843 - (void)updateStatistics
847 GST_OBJECT_LOCK (element);
848 clock = GST_ELEMENT_CLOCK (element);
850 gst_object_ref (clock);
851 GST_OBJECT_UNLOCK (element);
854 GstClockTime now = gst_clock_get_time (clock);
855 gst_object_unref (clock);
859 if (GST_CLOCK_TIME_IS_VALID (lastSampling)) {
860 if (now - lastSampling >= GST_SECOND) {
861 GST_OBJECT_LOCK (element);
863 GST_OBJECT_UNLOCK (element);
865 g_object_notify (G_OBJECT (element), "fps");
890 PROP_CAPTURE_SCREEN_CURSOR,
891 PROP_CAPTURE_SCREEN_MOUSE_CLICKS,
896 static void gst_avf_video_src_finalize (GObject * obj);
897 static void gst_avf_video_src_get_property (GObject * object, guint prop_id,
898 GValue * value, GParamSpec * pspec);
899 static void gst_avf_video_src_set_property (GObject * object, guint prop_id,
900 const GValue * value, GParamSpec * pspec);
901 static GstStateChangeReturn gst_avf_video_src_change_state (
902 GstElement * element, GstStateChange transition);
903 static GstCaps * gst_avf_video_src_get_caps (GstBaseSrc * basesrc,
905 static gboolean gst_avf_video_src_set_caps (GstBaseSrc * basesrc,
907 static gboolean gst_avf_video_src_start (GstBaseSrc * basesrc);
908 static gboolean gst_avf_video_src_stop (GstBaseSrc * basesrc);
909 static gboolean gst_avf_video_src_query (GstBaseSrc * basesrc,
911 static gboolean gst_avf_video_src_unlock (GstBaseSrc * basesrc);
912 static gboolean gst_avf_video_src_unlock_stop (GstBaseSrc * basesrc);
913 static GstFlowReturn gst_avf_video_src_create (GstPushSrc * pushsrc,
918 gst_avf_video_src_class_init (GstAVFVideoSrcClass * klass)
920 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
921 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
922 GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
923 GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
925 gobject_class->finalize = gst_avf_video_src_finalize;
926 gobject_class->get_property = gst_avf_video_src_get_property;
927 gobject_class->set_property = gst_avf_video_src_set_property;
929 gstelement_class->change_state = gst_avf_video_src_change_state;
931 gstbasesrc_class->get_caps = gst_avf_video_src_get_caps;
932 gstbasesrc_class->set_caps = gst_avf_video_src_set_caps;
933 gstbasesrc_class->start = gst_avf_video_src_start;
934 gstbasesrc_class->stop = gst_avf_video_src_stop;
935 gstbasesrc_class->query = gst_avf_video_src_query;
936 gstbasesrc_class->unlock = gst_avf_video_src_unlock;
937 gstbasesrc_class->unlock_stop = gst_avf_video_src_unlock_stop;
939 gstpushsrc_class->create = gst_avf_video_src_create;
941 gst_element_class_set_metadata (gstelement_class,
942 "Video Source (AVFoundation)", "Source/Video",
943 "Reads frames from an iOS AVFoundation device",
944 "Ole André Vadla Ravnås <oleavr@soundrop.com>");
946 gst_element_class_add_pad_template (gstelement_class,
947 gst_static_pad_template_get (&src_template));
949 g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
950 g_param_spec_int ("device-index", "Device Index",
951 "The zero-based device index",
952 -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
953 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
954 g_object_class_install_property (gobject_class, PROP_DO_STATS,
955 g_param_spec_boolean ("do-stats", "Enable statistics",
956 "Enable logging of statistics", DEFAULT_DO_STATS,
957 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
958 g_object_class_install_property (gobject_class, PROP_FPS,
959 g_param_spec_int ("fps", "Frames per second",
960 "Last measured framerate, if statistics are enabled",
961 -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
963 g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN,
964 g_param_spec_boolean ("capture-screen", "Enable screen capture",
965 "Enable screen capture functionality", FALSE,
966 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
967 g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN_CURSOR,
968 g_param_spec_boolean ("capture-screen-cursor", "Capture screen cursor",
969 "Enable cursor capture while capturing screen", FALSE,
970 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
971 g_object_class_install_property (gobject_class, PROP_CAPTURE_SCREEN_MOUSE_CLICKS,
972 g_param_spec_boolean ("capture-screen-mouse-clicks", "Enable mouse clicks capture",
973 "Enable mouse clicks capture while capturing screen", FALSE,
974 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
977 GST_DEBUG_CATEGORY_INIT (gst_avf_video_src_debug, "avfvideosrc",
978 0, "iOS AVFoundation video source");
981 #define OBJC_CALLOUT_BEGIN() \
982 NSAutoreleasePool *pool; \
984 pool = [[NSAutoreleasePool alloc] init]
985 #define OBJC_CALLOUT_END() \
990 gst_avf_video_src_init (GstAVFVideoSrc * src)
992 OBJC_CALLOUT_BEGIN ();
993 src->impl = [[GstAVFVideoSrcImpl alloc] initWithSrc:GST_PUSH_SRC (src)];
998 gst_avf_video_src_finalize (GObject * obj)
1000 OBJC_CALLOUT_BEGIN ();
1001 [GST_AVF_VIDEO_SRC_IMPL (obj) release];
1002 OBJC_CALLOUT_END ();
1004 G_OBJECT_CLASS (parent_class)->finalize (obj);
1008 gst_avf_video_src_get_property (GObject * object, guint prop_id, GValue * value,
1011 GstAVFVideoSrcImpl *impl = GST_AVF_VIDEO_SRC_IMPL (object);
1015 case PROP_CAPTURE_SCREEN:
1016 g_value_set_boolean (value, impl.captureScreen);
1018 case PROP_CAPTURE_SCREEN_CURSOR:
1019 g_value_set_boolean (value, impl.captureScreenCursor);
1021 case PROP_CAPTURE_SCREEN_MOUSE_CLICKS:
1022 g_value_set_boolean (value, impl.captureScreenMouseClicks);
1025 case PROP_DEVICE_INDEX:
1026 g_value_set_int (value, impl.deviceIndex);
1029 g_value_set_boolean (value, impl.doStats);
1032 GST_OBJECT_LOCK (object);
1033 g_value_set_int (value, impl.fps);
1034 GST_OBJECT_UNLOCK (object);
1037 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1043 gst_avf_video_src_set_property (GObject * object, guint prop_id,
1044 const GValue * value, GParamSpec * pspec)
1046 GstAVFVideoSrcImpl *impl = GST_AVF_VIDEO_SRC_IMPL (object);
1050 case PROP_CAPTURE_SCREEN:
1051 impl.captureScreen = g_value_get_boolean (value);
1053 case PROP_CAPTURE_SCREEN_CURSOR:
1054 impl.captureScreenCursor = g_value_get_boolean (value);
1056 case PROP_CAPTURE_SCREEN_MOUSE_CLICKS:
1057 impl.captureScreenMouseClicks = g_value_get_boolean (value);
1060 case PROP_DEVICE_INDEX:
1061 impl.deviceIndex = g_value_get_int (value);
1064 impl.doStats = g_value_get_boolean (value);
1067 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1072 static GstStateChangeReturn
1073 gst_avf_video_src_change_state (GstElement * element, GstStateChange transition)
1075 GstStateChangeReturn ret;
1077 OBJC_CALLOUT_BEGIN ();
1078 ret = [GST_AVF_VIDEO_SRC_IMPL (element) changeState: transition];
1079 OBJC_CALLOUT_END ();
1085 gst_avf_video_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
1089 OBJC_CALLOUT_BEGIN ();
1090 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) getCaps];
1091 OBJC_CALLOUT_END ();
1097 gst_avf_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
1101 OBJC_CALLOUT_BEGIN ();
1102 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) setCaps:caps];
1103 OBJC_CALLOUT_END ();
1109 gst_avf_video_src_start (GstBaseSrc * basesrc)
1113 OBJC_CALLOUT_BEGIN ();
1114 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) start];
1115 OBJC_CALLOUT_END ();
1121 gst_avf_video_src_stop (GstBaseSrc * basesrc)
1125 OBJC_CALLOUT_BEGIN ();
1126 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) stop];
1127 OBJC_CALLOUT_END ();
1133 gst_avf_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
1137 OBJC_CALLOUT_BEGIN ();
1138 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) query:query];
1139 OBJC_CALLOUT_END ();
1145 gst_avf_video_src_unlock (GstBaseSrc * basesrc)
1149 OBJC_CALLOUT_BEGIN ();
1150 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) unlock];
1151 OBJC_CALLOUT_END ();
1157 gst_avf_video_src_unlock_stop (GstBaseSrc * basesrc)
1161 OBJC_CALLOUT_BEGIN ();
1162 ret = [GST_AVF_VIDEO_SRC_IMPL (basesrc) unlockStop];
1163 OBJC_CALLOUT_END ();
1168 static GstFlowReturn
1169 gst_avf_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
1173 OBJC_CALLOUT_BEGIN ();
1174 ret = [GST_AVF_VIDEO_SRC_IMPL (pushsrc) create: buf];
1175 OBJC_CALLOUT_END ();