1 /* GStreamer
2 * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
3 *
4 * gstuvc_h264deviceprovider.c: UvcH264 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 /**
23 * SECTION:provider-uvch264deviceprovider
24 *
25 * Device provider for uvch264 devices, it basically contains
26 * the same information as the v4l2 device provider but on top
27 * set the following properties:
28 *
29 * ```
30 * device.api=uvch264
31 * device.is-camerasrc=TRUE
32 * ```
33 */
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "uvc_h264.h"
39 #include <gst/gst.h>
40 #include "gstuvch264deviceprovider.h"
41
42 enum
43 {
44 PROP_DEVICE_PATH = 1,
45 };
46
47 /* *INDENT-OFF* */
48
49 struct _GstUvcH264Device
50 {
51 GstDevice parent;
52 gchar *device_path;
53 };
54
55 G_DEFINE_TYPE (GstUvcH264Device, gst_uvc_h264_device, GST_TYPE_DEVICE);
56 /* *INDENT-ON* */
57 GST_DEVICE_PROVIDER_REGISTER_DEFINE (uvch264deviceprovider,
58 "uvch264deviceprovider", GST_RANK_PRIMARY,
59 gst_uvc_h264_device_provider_get_type ());
60
61 static void
gst_uvc_h264_device_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)62 gst_uvc_h264_device_get_property (GObject * object, guint prop_id,
63 GValue * value, GParamSpec * pspec)
64 {
65 GstUvcH264Device *self = (GstUvcH264Device *) object;
66
67 switch (prop_id) {
68 case PROP_DEVICE_PATH:
69 g_value_set_string (value, self->device_path);
70 break;
71 default:
72 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
73 break;
74 }
75 }
76
77 static GstElement *
gst_uvc_h264_device_create_element(GstDevice * device,const gchar * name)78 gst_uvc_h264_device_create_element (GstDevice * device, const gchar * name)
79 {
80 GstUvcH264Device *self = (GstUvcH264Device *) device;
81 GstElement *elem;
82
83 elem = gst_element_factory_make ("uvch264src", name);
84 g_object_set (elem, "device", self->device_path, NULL);
85
86 return elem;
87 }
88
89 static void
gst_uvc_h264_device_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)90 gst_uvc_h264_device_set_property (GObject * object, guint prop_id,
91 const GValue * value, GParamSpec * pspec)
92 {
93 GstUvcH264Device *self = (GstUvcH264Device *) object;
94
95 switch (prop_id) {
96 case PROP_DEVICE_PATH:
97 self->device_path = g_value_dup_string (value);
98 break;
99 default:
100 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
101 break;
102 }
103 }
104
105 static void
gst_uvc_h264_device_finalize(GObject * object)106 gst_uvc_h264_device_finalize (GObject * object)
107 {
108 GstUvcH264Device *self = (GstUvcH264Device *) object;
109
110 g_free (self->device_path);
111
112 G_OBJECT_CLASS (gst_uvc_h264_device_parent_class)->finalize (object);
113 }
114
115 static void
gst_uvc_h264_device_class_init(GstUvcH264DeviceClass * klass)116 gst_uvc_h264_device_class_init (GstUvcH264DeviceClass * klass)
117 {
118 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
119 GObjectClass *object_class = G_OBJECT_CLASS (klass);
120
121 dev_class->create_element = gst_uvc_h264_device_create_element;
122
123 object_class->get_property = gst_uvc_h264_device_get_property;
124 object_class->set_property = gst_uvc_h264_device_set_property;
125 object_class->finalize = gst_uvc_h264_device_finalize;
126
127 g_object_class_install_property (object_class, PROP_DEVICE_PATH,
128 g_param_spec_string ("device-path", "Device Path",
129 "The Path of the device node", "",
130 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
131 }
132
133 static void
gst_uvc_h264_device_init(GstUvcH264Device * device)134 gst_uvc_h264_device_init (GstUvcH264Device * device)
135 {
136 }
137
138 /* *INDENT-OFF* */
139 struct _GstUvcH264DeviceProvider
140 {
141 GstDeviceProvider parent;
142 GstDeviceProvider *v4l2;
143
144 guint bus_watch_id;
145 libusb_context *usb_ctx;
146 };
147
148 G_DEFINE_TYPE (GstUvcH264DeviceProvider, gst_uvc_h264_device_provider,
149 GST_TYPE_DEVICE_PROVIDER);
150 /* *INDENT-ON* */
151
152 static GstDevice *
create_device(GstUvcH264DeviceProvider * self,GstDevice * v4l2dev)153 create_device (GstUvcH264DeviceProvider * self, GstDevice * v4l2dev)
154 {
155 GstDevice *dev = NULL;
156 GstStructure *props = gst_device_get_properties (v4l2dev);
157 const gchar *devname = gst_structure_get_string (props, "device.path");
158 gchar *tmp, *device_name = NULL;
159 GstCaps *caps;
160
161 if (!xu_get_id (GST_OBJECT (self), devname, &self->usb_ctx)) {
162 GST_INFO_OBJECT (self, "%s is not a uvch264 device", devname);
163 goto done;
164 }
165
166 gst_structure_set (props, "device.api", G_TYPE_STRING, "uvch264",
167 "device.is-camerasrc", G_TYPE_BOOLEAN, TRUE, NULL);
168
169 caps = gst_device_get_caps (v4l2dev);
170 tmp = gst_device_get_display_name (v4l2dev);
171 device_name = g_strdup_printf ("UvcH264 %s", tmp);
172 g_free (tmp);
173 dev = g_object_new (gst_uvc_h264_device_get_type (), "device-path", devname,
174 "display-name", device_name, "caps", caps, "device-class",
175 "Video/CameraSource", "properties", props, NULL);
176 if (caps)
177 gst_caps_unref (caps);
178
179 done:
180 g_free (device_name);
181 gst_structure_free (props);
182
183 return dev;
184 }
185
186 static GList *
gst_uvc_h264_device_provider_probe(GstDeviceProvider * provider)187 gst_uvc_h264_device_provider_probe (GstDeviceProvider * provider)
188 {
189 GList *tmp, *v4l2devs, *devs = NULL;
190 GstUvcH264DeviceProvider *self = (GstUvcH264DeviceProvider *) provider;
191
192 if (!self->v4l2)
193 self->v4l2 = gst_device_provider_factory_get_by_name ("v4l2deviceprovider");
194
195 if (!self->v4l2)
196 return NULL;
197
198 v4l2devs = gst_device_provider_get_devices (self->v4l2);
199 for (tmp = v4l2devs; tmp; tmp = tmp->next) {
200 GstDevice *dev = create_device (self, tmp->data);
201
202 if (dev)
203 devs = g_list_prepend (devs, dev);
204 }
205 g_list_free_full (v4l2devs, gst_object_unref);
206
207 return devs;
208 }
209
210 static void
_bus_message_cb(GstBus * bus,GstMessage * msg,GstUvcH264DeviceProvider * self)211 _bus_message_cb (GstBus * bus, GstMessage * msg,
212 GstUvcH264DeviceProvider * self)
213 {
214 GstDevice *v4l2dev;
215 GstDeviceProvider *provider = (GstDeviceProvider *) self;
216
217 if (GST_MESSAGE_SRC (msg) != GST_OBJECT (self->v4l2))
218 return;
219
220 switch (GST_MESSAGE_TYPE (msg)) {
221 case GST_MESSAGE_DEVICE_ADDED:
222 {
223 GstDevice *dev;
224 gst_message_parse_device_added (msg, &v4l2dev);
225
226 if ((dev = create_device (self, v4l2dev)))
227 gst_device_provider_device_add (provider, dev);
228
229 break;
230 }
231 case GST_MESSAGE_DEVICE_REMOVED:
232 {
233 GList *item;
234 gchar *v4l2path;
235 GstDevice *dev = NULL;
236
237 gst_message_parse_device_removed (msg, &v4l2dev);
238
239 g_object_get (v4l2dev, "device-path", &v4l2path, NULL);
240
241 GST_OBJECT_LOCK (self);
242 for (item = provider->devices; item; item = item->next) {
243 if (!g_strcmp0 (((GstUvcH264Device *) item->data)->device_path,
244 v4l2path)) {
245 dev = item->data;
246 break;
247 }
248 }
249 GST_OBJECT_UNLOCK (self);
250
251 if (dev)
252 gst_device_provider_device_remove (provider, dev);
253 break;
254 }
255 default:
256 break;
257 }
258
259 }
260
261 static gboolean
gst_uvc_h264_device_provider_start(GstDeviceProvider * provider)262 gst_uvc_h264_device_provider_start (GstDeviceProvider * provider)
263 {
264 GstBus *bus;
265 GstUvcH264DeviceProvider *self = (GstUvcH264DeviceProvider *) provider;
266 GList *tmp, *devs = gst_uvc_h264_device_provider_probe (provider);
267 if (!self->v4l2)
268 return TRUE;
269
270 bus = gst_device_provider_get_bus (self->v4l2);
271 gst_bus_enable_sync_message_emission (bus);
272 self->bus_watch_id = g_signal_connect (bus, "sync-message",
273 G_CALLBACK (_bus_message_cb), self);
274 gst_object_unref (bus);
275
276 for (tmp = devs; tmp; tmp = tmp->next)
277 gst_device_provider_device_add (provider, tmp->data);
278 g_list_free (devs);
279
280 return TRUE;
281 }
282
283 static void
gst_uvc_h264_device_provider_stop(GstDeviceProvider * provider)284 gst_uvc_h264_device_provider_stop (GstDeviceProvider * provider)
285 {
286 GstBus *bus;
287 GstUvcH264DeviceProvider *self = (GstUvcH264DeviceProvider *) provider;
288
289 if (!self->v4l2)
290 return;
291
292 if (self->usb_ctx)
293 libusb_exit (self->usb_ctx);
294 self->usb_ctx = NULL;
295
296 bus = gst_device_provider_get_bus (self->v4l2);
297 g_signal_handler_disconnect (bus, self->bus_watch_id);
298 self->bus_watch_id = 0;
299 gst_clear_object (&self->v4l2);
300 gst_clear_object (&bus);
301
302 return;
303 }
304
305 static void
gst_uvc_h264_device_provider_class_init(GstUvcH264DeviceProviderClass * klass)306 gst_uvc_h264_device_provider_class_init (GstUvcH264DeviceProviderClass * klass)
307 {
308 GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
309
310 dm_class->probe = gst_uvc_h264_device_provider_probe;
311 dm_class->start = gst_uvc_h264_device_provider_start;
312 dm_class->stop = gst_uvc_h264_device_provider_stop;
313
314 gst_device_provider_class_set_static_metadata (dm_class,
315 "UVC H.264 Device Provider", "Video/CameraSource",
316 "List and provides UVC H.264 source devices",
317 "Thibault Saunier <tsaunier@igalia.com>");
318 }
319
320 static void
gst_uvc_h264_device_provider_init(GstUvcH264DeviceProvider * self)321 gst_uvc_h264_device_provider_init (GstUvcH264DeviceProvider * self)
322 {
323 }
324