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, 36 GstStructure *props); 37G_DEFINE_TYPE (GstAVFDeviceProvider, gst_avf_device_provider, 38 GST_TYPE_DEVICE_PROVIDER); 39 40static GList *gst_avf_device_provider_probe (GstDeviceProvider * provider); 41 42static void 43gst_avf_device_provider_class_init (GstAVFDeviceProviderClass * klass) 44{ 45 GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass); 46 47 // TODO: Add start/stop callbacks to receive device notifications. 48 // https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/886 49 dm_class->probe = gst_avf_device_provider_probe; 50 51 gst_device_provider_class_set_static_metadata (dm_class, 52 "AVF Device Provider", "Source/Video", 53 "List and provide AVF source devices", 54 "Josh Matthews <josh@joshmatthews.net>"); 55} 56 57static void 58gst_avf_device_provider_init (GstAVFDeviceProvider * self) 59{ 60} 61 62static GstStructure * 63gst_av_capture_device_get_props (AVCaptureDevice *device) 64{ 65 char *unique_id, *model_id; 66 GstStructure *props = gst_structure_new_empty ("avf-proplist"); 67 68 unique_id = g_strdup ([[device uniqueID] UTF8String]); 69 model_id = g_strdup ([[device modelID] UTF8String]); 70 71 gst_structure_set (props, 72 "device.api", G_TYPE_STRING, "avf", 73 "avf.unique_id", G_TYPE_STRING, unique_id, 74 "avf.model_id", G_TYPE_STRING, model_id, 75 "avf.has_flash", G_TYPE_BOOLEAN, [device hasFlash], 76 "avf.has_torch", G_TYPE_BOOLEAN, [device hasTorch], 77 NULL); 78 79#if !HAVE_IOS 80 char *manufacturer = g_strdup ([[device manufacturer] UTF8String]); 81 gst_structure_set (props, 82 "avf.manufacturer", G_TYPE_STRING, manufacturer, 83 NULL); 84#endif 85 86 return props; 87} 88 89static GList * 90gst_avf_device_provider_probe (GstDeviceProvider * provider) 91{ 92 GList *result; 93 94 result = NULL; 95 96 NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 97 AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init]; 98 for (int i = 0; i < [devices count]; i++) { 99 AVCaptureDevice *device = [devices objectAtIndex:i]; 100 g_assert (device != nil); 101 102 GstCaps *caps = gst_av_capture_device_get_caps (device, output, GST_AVF_VIDEO_SOURCE_ORIENTATION_DEFAULT); 103 GstStructure *props = gst_av_capture_device_get_props (device); 104 const gchar *deviceName = [[device localizedName] UTF8String]; 105 GstDevice *gst_device = gst_avf_device_new (deviceName, i, caps, GST_AVF_DEVICE_TYPE_VIDEO_SOURCE, props); 106 107 result = g_list_prepend (result, gst_object_ref_sink (gst_device)); 108 109 gst_structure_free (props); 110 } 111 112 result = g_list_reverse (result); 113 114 return result; 115} 116 117enum 118{ 119 PROP_DEVICE_INDEX = 1 120}; 121 122G_DEFINE_TYPE (GstAvfDevice, gst_avf_device, GST_TYPE_DEVICE); 123 124static GstElement *gst_avf_device_create_element (GstDevice * device, 125 const gchar * name); 126static gboolean gst_avf_device_reconfigure_element (GstDevice * device, 127 GstElement * element); 128 129static void gst_avf_device_get_property (GObject * object, guint prop_id, 130 GValue * value, GParamSpec * pspec); 131static void gst_avf_device_set_property (GObject * object, guint prop_id, 132 const GValue * value, GParamSpec * pspec); 133 134static void 135gst_avf_device_class_init (GstAvfDeviceClass * klass) 136{ 137 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass); 138 GObjectClass *object_class = G_OBJECT_CLASS (klass); 139 140 dev_class->create_element = gst_avf_device_create_element; 141 dev_class->reconfigure_element = gst_avf_device_reconfigure_element; 142 143 object_class->get_property = gst_avf_device_get_property; 144 object_class->set_property = gst_avf_device_set_property; 145 146 g_object_class_install_property (object_class, PROP_DEVICE_INDEX, 147 g_param_spec_int ("device-index", "Device Index", 148 "The zero-based device index", -1, G_MAXINT, 0, 149 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); 150} 151 152static void 153gst_avf_device_init (GstAvfDevice * device) 154{ 155} 156 157static GstElement * 158gst_avf_device_create_element (GstDevice * device, const gchar * name) 159{ 160 GstAvfDevice *avf_dev = GST_AVF_DEVICE (device); 161 GstElement *elem; 162 163 elem = gst_element_factory_make (avf_dev->element, name); 164 g_object_set (elem, "device-index", avf_dev->device_index, NULL); 165 166 return elem; 167} 168 169static gboolean 170gst_avf_device_reconfigure_element (GstDevice * device, GstElement * element) 171{ 172 GstAvfDevice *avf_dev = GST_AVF_DEVICE (device); 173 174 if (!strcmp (avf_dev->element, "avfvideosrc") && GST_IS_AVF_VIDEO_SRC (element)) { 175 g_object_set (element, "device-index", avf_dev->device_index, NULL); 176 return TRUE; 177 } 178 179 return FALSE; 180} 181 182static void 183gst_avf_device_get_property (GObject * object, guint prop_id, 184 GValue * value, GParamSpec * pspec) 185{ 186 GstAvfDevice *device; 187 188 device = GST_AVF_DEVICE_CAST (object); 189 190 switch (prop_id) { 191 case PROP_DEVICE_INDEX: 192 g_value_set_int (value, device->device_index); 193 break; 194 default: 195 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 196 break; 197 } 198} 199 200static void 201gst_avf_device_set_property (GObject * object, guint prop_id, 202 const GValue * value, GParamSpec * pspec) 203{ 204 GstAvfDevice *device; 205 206 device = GST_AVF_DEVICE_CAST (object); 207 208 switch (prop_id) { 209 case PROP_DEVICE_INDEX: 210 device->device_index = g_value_get_int (value); 211 break; 212 default: 213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 214 break; 215 } 216} 217 218static GstDevice * 219gst_avf_device_new (const gchar * device_name, int device_index, GstCaps * caps, GstAvfDeviceType type, GstStructure *props) 220{ 221 GstAvfDevice *gstdev; 222 const gchar *element = NULL; 223 const gchar *klass = NULL; 224 225 g_return_val_if_fail (device_name, NULL); 226 g_return_val_if_fail (caps, NULL); 227 228 229 switch (type) { 230 case GST_AVF_DEVICE_TYPE_VIDEO_SOURCE: 231 element = "avfvideosrc"; 232 klass = "Video/Source"; 233 break; 234 default: 235 g_assert_not_reached (); 236 break; 237 } 238 239 240 gstdev = g_object_new (GST_TYPE_AVF_DEVICE, 241 "display-name", device_name, "caps", caps, "device-class", klass, 242 "device-index", device_index, "properties", props, NULL); 243 244 gstdev->type = type; 245 gstdev->element = element; 246 247 return GST_DEVICE (gstdev); 248} 249