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