• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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