1/* GStreamer 2 * Copyright (C) 2019 Josh Matthews <josh@joshmatthews.net> 3 * 4 * avfdeviceprovider.c: AVF device probing and monitoring 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public 17 * License along with this library; if not, write to the 18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 02111-1307, USA. 20 */ 21 22#ifdef HAVE_CONFIG_H 23#include "config.h" 24#endif 25 26#import <AVFoundation/AVFoundation.h> 27#include "avfvideosrc.h" 28#include "avfdeviceprovider.h" 29 30#include <string.h> 31 32#include <gst/gst.h> 33 34static GstDevice *gst_avf_device_new (const gchar * device_name, int device_index, 35 GstCaps * caps, GstAvfDeviceType type); 36G_DEFINE_TYPE (GstAVFDeviceProvider, gst_avf_device_provider, 37 GST_TYPE_DEVICE_PROVIDER); 38 39static GList *gst_avf_device_provider_probe (GstDeviceProvider * provider); 40 41static void 42gst_avf_device_provider_class_init (GstAVFDeviceProviderClass * klass) 43{ 44 GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass); 45 46 // TODO: Add start/stop callbacks to receive device notifications. 47 // https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/886 48 dm_class->probe = gst_avf_device_provider_probe; 49 50 gst_device_provider_class_set_static_metadata (dm_class, 51 "AVF Device Provider", "Source/Video", 52 "List and provide AVF source devices", 53 "Josh Matthews <josh@joshmatthews.net>"); 54} 55 56static void 57gst_avf_device_provider_init (GstAVFDeviceProvider * self) 58{ 59} 60 61static GList * 62gst_avf_device_provider_probe (GstDeviceProvider * provider) 63{ 64 GList *result; 65 66 result = NULL; 67 68 NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 69 AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init]; 70 for (int i = 0; i < [devices count]; i++) { 71 AVCaptureDevice *device = [devices objectAtIndex:i]; 72 g_assert (device != nil); 73 GstCaps *caps = gst_av_capture_device_get_caps (device, output, GST_AVF_VIDEO_SOURCE_ORIENTATION_DEFAULT); 74 const gchar *deviceName = [[device localizedName] UTF8String]; 75 GstDevice *gst_device = gst_avf_device_new (deviceName, i, caps, GST_AVF_DEVICE_TYPE_VIDEO_SOURCE); 76 77 result = g_list_prepend (result, gst_object_ref_sink (gst_device)); 78 } 79 80 result = g_list_reverse (result); 81 82 return result; 83} 84 85enum 86{ 87 PROP_DEVICE_INDEX = 1 88}; 89 90G_DEFINE_TYPE (GstAvfDevice, gst_avf_device, GST_TYPE_DEVICE); 91 92static GstElement *gst_avf_device_create_element (GstDevice * device, 93 const gchar * name); 94static gboolean gst_avf_device_reconfigure_element (GstDevice * device, 95 GstElement * element); 96 97static void gst_avf_device_get_property (GObject * object, guint prop_id, 98 GValue * value, GParamSpec * pspec); 99static void gst_avf_device_set_property (GObject * object, guint prop_id, 100 const GValue * value, GParamSpec * pspec); 101 102static void 103gst_avf_device_class_init (GstAvfDeviceClass * klass) 104{ 105 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass); 106 GObjectClass *object_class = G_OBJECT_CLASS (klass); 107 108 dev_class->create_element = gst_avf_device_create_element; 109 dev_class->reconfigure_element = gst_avf_device_reconfigure_element; 110 111 object_class->get_property = gst_avf_device_get_property; 112 object_class->set_property = gst_avf_device_set_property; 113 114 g_object_class_install_property (object_class, PROP_DEVICE_INDEX, 115 g_param_spec_int ("device-index", "Device Index", 116 "The zero-based device index", -1, G_MAXINT, 0, 117 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); 118} 119 120static void 121gst_avf_device_init (GstAvfDevice * device) 122{ 123} 124 125static GstElement * 126gst_avf_device_create_element (GstDevice * device, const gchar * name) 127{ 128 GstAvfDevice *avf_dev = GST_AVF_DEVICE (device); 129 GstElement *elem; 130 131 elem = gst_element_factory_make (avf_dev->element, name); 132 g_object_set (elem, "device-index", avf_dev->device_index, NULL); 133 134 return elem; 135} 136 137static gboolean 138gst_avf_device_reconfigure_element (GstDevice * device, GstElement * element) 139{ 140 GstAvfDevice *avf_dev = GST_AVF_DEVICE (device); 141 142 if (!strcmp (avf_dev->element, "avfvideosrc") && GST_IS_AVF_VIDEO_SRC (element)) { 143 g_object_set (element, "device-index", avf_dev->device_index, NULL); 144 return TRUE; 145 } 146 147 return FALSE; 148} 149 150static void 151gst_avf_device_get_property (GObject * object, guint prop_id, 152 GValue * value, GParamSpec * pspec) 153{ 154 GstAvfDevice *device; 155 156 device = GST_AVF_DEVICE_CAST (object); 157 158 switch (prop_id) { 159 case PROP_DEVICE_INDEX: 160 g_value_set_int (value, device->device_index); 161 break; 162 default: 163 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 164 break; 165 } 166} 167 168static void 169gst_avf_device_set_property (GObject * object, guint prop_id, 170 const GValue * value, GParamSpec * pspec) 171{ 172 GstAvfDevice *device; 173 174 device = GST_AVF_DEVICE_CAST (object); 175 176 switch (prop_id) { 177 case PROP_DEVICE_INDEX: 178 device->device_index = g_value_get_int (value); 179 break; 180 default: 181 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 182 break; 183 } 184} 185 186static GstDevice * 187gst_avf_device_new (const gchar * device_name, int device_index, GstCaps * caps, GstAvfDeviceType type) 188{ 189 GstAvfDevice *gstdev; 190 const gchar *element = NULL; 191 const gchar *klass = NULL; 192 193 g_return_val_if_fail (device_name, NULL); 194 g_return_val_if_fail (caps, NULL); 195 196 197 switch (type) { 198 case GST_AVF_DEVICE_TYPE_VIDEO_SOURCE: 199 element = "avfvideosrc"; 200 klass = "Video/Source"; 201 break; 202 default: 203 g_assert_not_reached (); 204 break; 205 } 206 207 208 gstdev = g_object_new (GST_TYPE_AVF_DEVICE, 209 "display-name", device_name, "caps", caps, "device-class", klass, 210 "device-index", device_index, NULL); 211 212 gstdev->type = type; 213 gstdev->element = element; 214 215 return GST_DEVICE (gstdev); 216} 217