1 /* GStreamer v4l2 radio tuner element
2 * Copyright (C) 2010, 2011 Alexey Chernov <4ernov@gmail.com>
3 *
4 * gstv4l2radio.c - V4L2 radio tuner element
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., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 /**
23 * SECTION:element-v4l2radio
24 * @title: v4l2radio
25 *
26 * v4l2radio can be used to control radio device
27 * and to tune it to different radiostations.
28 *
29 * ## Example launch lines
30 * |[
31 * gst-launch-1.0 v4l2radio device=/dev/radio0 frequency=101200000
32 * gst-launch-1.0 alsasrc device=hw:1 ! audioconvert ! audioresample ! alsasink
33 * ]|
34 * First pipeline tunes the radio device /dev/radio0 to station 101.2 MHz,
35 * second pipeline connects digital audio out (hw:1) to default sound card.
36 *
37 */
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <string.h>
44
45 #include "gst/gst-i18n-plugin.h"
46
47 #include "gstv4l2elements.h"
48 #include "gstv4l2object.h"
49 #include "gstv4l2tuner.h"
50 #include "gstv4l2radio.h"
51
52 GST_DEBUG_CATEGORY_STATIC (v4l2radio_debug);
53 #define GST_CAT_DEFAULT v4l2radio_debug
54
55 #define DEFAULT_PROP_DEVICE "/dev/radio0"
56 #define MIN_FREQUENCY 87500000
57 #define DEFAULT_FREQUENCY 100000000
58 #define MAX_FREQUENCY 108000000
59
60 enum
61 {
62 ARG_0,
63 ARG_DEVICE,
64 ARG_FREQUENCY
65 };
66
67 static gboolean
gst_v4l2radio_fill_channel_list(GstV4l2Radio * radio)68 gst_v4l2radio_fill_channel_list (GstV4l2Radio * radio)
69 {
70 int res;
71 struct v4l2_tuner vtun;
72 struct v4l2_capability vc;
73 GstV4l2TunerChannel *v4l2channel;
74 GstTunerChannel *channel;
75
76 GstElement *e;
77
78 GstV4l2Object *v4l2object;
79
80 e = GST_ELEMENT (radio);
81 v4l2object = radio->v4l2object;
82
83 GST_DEBUG_OBJECT (e, "getting audio enumeration");
84 GST_V4L2_CHECK_OPEN (v4l2object);
85
86 GST_DEBUG_OBJECT (e, " audio input");
87
88 memset (&vc, 0, sizeof (vc));
89
90 res = v4l2object->ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &vc);
91 if (res < 0)
92 goto caps_failed;
93
94 if (vc.capabilities & V4L2_CAP_DEVICE_CAPS)
95 v4l2object->device_caps = vc.device_caps;
96 else
97 v4l2object->device_caps = vc.capabilities;
98
99 if (!(v4l2object->device_caps & V4L2_CAP_TUNER))
100 goto not_a_tuner;
101
102 /* getting audio input */
103 memset (&vtun, 0, sizeof (vtun));
104 vtun.index = 0;
105
106 res = v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &vtun);
107 if (res < 0)
108 goto tuner_failed;
109
110 GST_LOG_OBJECT (e, " index: %d", vtun.index);
111 GST_LOG_OBJECT (e, " name: '%s'", vtun.name);
112 GST_LOG_OBJECT (e, " type: %016x", (guint) vtun.type);
113 GST_LOG_OBJECT (e, " caps: %016x", (guint) vtun.capability);
114 GST_LOG_OBJECT (e, " rlow: %016x", (guint) vtun.rangelow);
115 GST_LOG_OBJECT (e, " rhigh: %016x", (guint) vtun.rangehigh);
116 GST_LOG_OBJECT (e, " audmode: %016x", (guint) vtun.audmode);
117
118 v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL);
119 channel = GST_TUNER_CHANNEL (v4l2channel);
120 channel->label = g_strdup ((const gchar *) vtun.name);
121 channel->flags = GST_TUNER_CHANNEL_FREQUENCY | GST_TUNER_CHANNEL_AUDIO;
122 v4l2channel->index = 0;
123 v4l2channel->tuner = 0;
124
125 channel->freq_multiplicator =
126 62.5 * ((vtun.capability & V4L2_TUNER_CAP_LOW) ? 1 : 1000);
127 channel->min_frequency = vtun.rangelow * channel->freq_multiplicator;
128 channel->max_frequency = vtun.rangehigh * channel->freq_multiplicator;
129 channel->min_signal = 0;
130 channel->max_signal = 0xffff;
131
132 v4l2object->channels =
133 g_list_prepend (v4l2object->channels, (gpointer) channel);
134
135 v4l2object->channels = g_list_reverse (v4l2object->channels);
136
137 GST_DEBUG_OBJECT (e, "done");
138 return TRUE;
139
140 /* ERRORS */
141 tuner_failed:
142 {
143 GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
144 (_("Failed to get settings of tuner %d on device '%s'."),
145 vtun.index, v4l2object->videodev), GST_ERROR_SYSTEM);
146 return FALSE;
147 }
148 caps_failed:
149 {
150 GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
151 (_("Error getting capabilities for device '%s'."),
152 v4l2object->videodev), GST_ERROR_SYSTEM);
153 return FALSE;
154 }
155 not_a_tuner:
156 {
157 GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
158 (_("Device '%s' is not a tuner."),
159 v4l2object->videodev), GST_ERROR_SYSTEM);
160 return FALSE;
161 }
162 }
163
164 static gboolean
gst_v4l2radio_get_input(GstV4l2Object * v4l2object,guint32 * input)165 gst_v4l2radio_get_input (GstV4l2Object * v4l2object, guint32 * input)
166 {
167 GST_DEBUG_OBJECT (v4l2object->element, "trying to get radio input");
168
169 if (!GST_V4L2_IS_OPEN (v4l2object))
170 return FALSE;
171
172 if (!v4l2object->channels)
173 goto input_failed;
174
175 *input = 0;
176
177 GST_DEBUG_OBJECT (v4l2object->element, "input: %d", 0);
178
179 return TRUE;
180
181 /* ERRORS */
182 input_failed:
183 {
184 GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
185 (_("Failed to get radio input on device '%s'. "),
186 v4l2object->videodev), GST_ERROR_SYSTEM);
187 return FALSE;
188 }
189 }
190
191 static gboolean
gst_v4l2radio_set_input(GstV4l2Object * v4l2object,guint32 input)192 gst_v4l2radio_set_input (GstV4l2Object * v4l2object, guint32 input)
193 {
194 GST_DEBUG_OBJECT (v4l2object->element, "trying to set input to %d", input);
195
196 if (!GST_V4L2_IS_OPEN (v4l2object))
197 return FALSE;
198
199 if (!v4l2object->channels)
200 goto input_failed;
201
202 return TRUE;
203
204 /* ERRORS */
205 input_failed:
206 {
207 GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
208 (_("Failed to set input %d on device %s."),
209 input, v4l2object->videodev), GST_ERROR_SYSTEM);
210 return FALSE;
211 }
212 }
213
214 static gboolean
gst_v4l2radio_set_mute_on(GstV4l2Radio * radio,gboolean on)215 gst_v4l2radio_set_mute_on (GstV4l2Radio * radio, gboolean on)
216 {
217 gint res;
218 struct v4l2_control vctrl;
219
220 GST_DEBUG_OBJECT (radio, "setting current tuner mute state: %d", on);
221
222 if (!GST_V4L2_IS_OPEN (radio->v4l2object))
223 return FALSE;
224
225 memset (&vctrl, 0, sizeof (vctrl));
226 vctrl.id = V4L2_CID_AUDIO_MUTE;
227 vctrl.value = on;
228
229 GST_DEBUG_OBJECT (radio, "radio fd: %d", radio->v4l2object->video_fd);
230
231 res = ioctl (radio->v4l2object->video_fd, VIDIOC_S_CTRL, &vctrl);
232 GST_DEBUG_OBJECT (radio, "mute state change result: %d", res);
233 if (res < 0)
234 goto freq_failed;
235
236 return TRUE;
237
238 /* ERRORS */
239 freq_failed:
240 {
241 GST_ELEMENT_WARNING (radio, RESOURCE, SETTINGS,
242 (_("Failed to change mute state for device '%s'."),
243 radio->v4l2object->videodev), GST_ERROR_SYSTEM);
244 return FALSE;
245 }
246 }
247
248 static gboolean
gst_v4l2radio_set_mute(GstV4l2Radio * radio)249 gst_v4l2radio_set_mute (GstV4l2Radio * radio)
250 {
251 return gst_v4l2radio_set_mute_on (radio, TRUE);
252 }
253
254 static gboolean
gst_v4l2radio_set_unmute(GstV4l2Radio * radio)255 gst_v4l2radio_set_unmute (GstV4l2Radio * radio)
256 {
257 return gst_v4l2radio_set_mute_on (radio, FALSE);
258 }
259
260 GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Radio, gst_v4l2radio);
261
262 static void gst_v4l2radio_uri_handler_init (gpointer g_iface,
263 gpointer iface_data);
264
265 static void
gst_v4l2radio_tuner_interface_reinit(GstTunerInterface * iface)266 gst_v4l2radio_tuner_interface_reinit (GstTunerInterface * iface)
267 {
268 gst_v4l2radio_tuner_interface_init (iface);
269 }
270
271 #define gst_v4l2radio_parent_class parent_class
272 G_DEFINE_TYPE_WITH_CODE (GstV4l2Radio, gst_v4l2radio, GST_TYPE_ELEMENT,
273 G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
274 gst_v4l2radio_uri_handler_init);
275 G_IMPLEMENT_INTERFACE (GST_TYPE_TUNER,
276 gst_v4l2radio_tuner_interface_reinit));
277 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (v4l2radio,
278 "v4l2radio", GST_RANK_NONE, GST_TYPE_V4L2RADIO, v4l2_element_init (plugin));
279
280 static void gst_v4l2radio_set_property (GObject * object, guint prop_id,
281 const GValue * value, GParamSpec * pspec);
282 static void gst_v4l2radio_get_property (GObject * object, guint prop_id,
283 GValue * value, GParamSpec * pspec);
284 static void gst_v4l2radio_finalize (GstV4l2Radio * radio);
285 static void gst_v4l2radio_dispose (GObject * object);
286 static GstStateChangeReturn gst_v4l2radio_change_state (GstElement * element,
287 GstStateChange transition);
288
289 static void
gst_v4l2radio_class_init(GstV4l2RadioClass * klass)290 gst_v4l2radio_class_init (GstV4l2RadioClass * klass)
291 {
292 GObjectClass *gobject_class;
293 GstElementClass *gstelement_class;
294
295 gobject_class = (GObjectClass *) klass;
296 gstelement_class = (GstElementClass *) klass;
297
298 gobject_class->dispose = gst_v4l2radio_dispose;
299 gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2radio_finalize;
300 gobject_class->set_property = gst_v4l2radio_set_property;
301 gobject_class->get_property = gst_v4l2radio_get_property;
302
303 g_object_class_install_property (gobject_class, ARG_DEVICE,
304 g_param_spec_string ("device", "Radio device location",
305 "Video4Linux2 radio device location",
306 DEFAULT_PROP_DEVICE, G_PARAM_READWRITE));
307
308 g_object_class_install_property (gobject_class, ARG_FREQUENCY,
309 g_param_spec_int ("frequency", "Station frequency",
310 "Station frequency in Hz",
311 MIN_FREQUENCY, MAX_FREQUENCY, DEFAULT_FREQUENCY, G_PARAM_READWRITE));
312
313 gstelement_class->change_state =
314 GST_DEBUG_FUNCPTR (gst_v4l2radio_change_state);
315
316 gst_element_class_set_static_metadata (gstelement_class,
317 "Radio (video4linux2) Tuner",
318 "Tuner",
319 "Controls a Video4Linux2 radio device",
320 "Alexey Chernov <4ernov@gmail.com>");
321
322 klass->v4l2_class_devices = NULL;
323
324 GST_DEBUG_CATEGORY_INIT (v4l2radio_debug, "v4l2radio", 0,
325 "V4l2 radio element");
326 }
327
328 static void
gst_v4l2radio_init(GstV4l2Radio * filter)329 gst_v4l2radio_init (GstV4l2Radio * filter)
330 {
331 filter->v4l2object = gst_v4l2_object_new (GST_ELEMENT (filter),
332 GST_OBJECT (filter), V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE,
333 gst_v4l2radio_get_input, gst_v4l2radio_set_input, NULL);
334
335 filter->v4l2object->frequency = DEFAULT_FREQUENCY;
336 g_free (filter->v4l2object->videodev);
337 filter->v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE);
338 }
339
340 static void
gst_v4l2radio_dispose(GObject * object)341 gst_v4l2radio_dispose (GObject * object)
342 {
343 GstV4l2Radio *radio = GST_V4L2RADIO (object);
344 gst_v4l2_close (radio->v4l2object);
345 G_OBJECT_CLASS (parent_class)->dispose (object);
346 }
347
348 static void
gst_v4l2radio_finalize(GstV4l2Radio * radio)349 gst_v4l2radio_finalize (GstV4l2Radio * radio)
350 {
351 gst_v4l2_object_destroy (radio->v4l2object);
352 G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (radio));
353 }
354
355 static gboolean
gst_v4l2radio_open(GstV4l2Radio * radio,GstV4l2Error * error)356 gst_v4l2radio_open (GstV4l2Radio * radio, GstV4l2Error * error)
357 {
358 GstV4l2Object *v4l2object;
359
360 v4l2object = radio->v4l2object;
361 if (gst_v4l2_open (v4l2object, error))
362 return gst_v4l2radio_fill_channel_list (radio);
363 else
364 return FALSE;
365 }
366
367 static void
gst_v4l2radio_set_defaults(GstV4l2Radio * radio)368 gst_v4l2radio_set_defaults (GstV4l2Radio * radio)
369 {
370 GstV4l2Object *v4l2object;
371 GstTunerChannel *channel = NULL;
372 GstTuner *tuner;
373
374 v4l2object = radio->v4l2object;
375
376 if (!GST_IS_TUNER (v4l2object->element))
377 return;
378
379 tuner = GST_TUNER (v4l2object->element);
380
381 if (v4l2object->channel)
382 channel = gst_tuner_find_channel_by_name (tuner, v4l2object->channel);
383 if (channel) {
384 gst_tuner_set_channel (tuner, channel);
385 } else {
386 channel =
387 GST_TUNER_CHANNEL (gst_tuner_get_channel (GST_TUNER
388 (v4l2object->element)));
389 if (channel) {
390 g_free (v4l2object->channel);
391 v4l2object->channel = g_strdup (channel->label);
392 gst_tuner_channel_changed (tuner, channel);
393 }
394 }
395
396 if (channel
397 && GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) {
398 if (v4l2object->frequency != 0) {
399 gst_tuner_set_frequency (tuner, channel, v4l2object->frequency);
400 } else {
401 v4l2object->frequency = gst_tuner_get_frequency (tuner, channel);
402 if (v4l2object->frequency == 0) {
403 /* guess */
404 gst_tuner_set_frequency (tuner, channel, MIN_FREQUENCY);
405 } else {
406 }
407 }
408 }
409 }
410
411 static gboolean
gst_v4l2radio_start(GstV4l2Radio * radio,GstV4l2Error * error)412 gst_v4l2radio_start (GstV4l2Radio * radio, GstV4l2Error * error)
413 {
414 if (!gst_v4l2radio_open (radio, error))
415 return FALSE;
416
417 gst_v4l2radio_set_defaults (radio);
418
419 return TRUE;
420 }
421
422 static gboolean
gst_v4l2radio_stop(GstV4l2Radio * radio)423 gst_v4l2radio_stop (GstV4l2Radio * radio)
424 {
425 if (!gst_v4l2_object_close (radio->v4l2object))
426 return FALSE;
427
428 return TRUE;
429 }
430
431 static GstStateChangeReturn
gst_v4l2radio_change_state(GstElement * element,GstStateChange transition)432 gst_v4l2radio_change_state (GstElement * element, GstStateChange transition)
433 {
434 GstV4l2Radio *radio;
435 GstV4l2Error error = GST_V4L2_ERROR_INIT;
436 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
437
438 radio = GST_V4L2RADIO (element);
439 switch (transition) {
440 case GST_STATE_CHANGE_NULL_TO_READY:
441 /*start radio */
442 if (!gst_v4l2radio_start (radio, &error))
443 ret = GST_STATE_CHANGE_FAILURE;
444 break;
445 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
446 /*unmute radio */
447 if (!gst_v4l2radio_set_unmute (radio))
448 ret = GST_STATE_CHANGE_FAILURE;
449 break;
450 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
451 /*mute radio */
452 if (!gst_v4l2radio_set_mute (radio))
453 ret = GST_STATE_CHANGE_FAILURE;
454 break;
455 case GST_STATE_CHANGE_READY_TO_NULL:
456 /*stop radio */
457 if (!gst_v4l2radio_stop (radio))
458 ret = GST_STATE_CHANGE_FAILURE;
459 break;
460 default:
461 break;
462 }
463
464 gst_v4l2_error (radio, &error);
465 return ret;
466 }
467
468 static void
gst_v4l2radio_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)469 gst_v4l2radio_set_property (GObject * object, guint prop_id,
470 const GValue * value, GParamSpec * pspec)
471 {
472 GstV4l2Radio *radio = GST_V4L2RADIO (object);
473 gint frequency;
474 switch (prop_id) {
475 case ARG_DEVICE:
476 g_free (radio->v4l2object->videodev);
477 radio->v4l2object->videodev =
478 g_strdup ((gchar *) g_value_get_string (value));
479 break;
480 case ARG_FREQUENCY:
481 frequency = g_value_get_int (value);
482 if (frequency >= MIN_FREQUENCY && frequency <= MAX_FREQUENCY) {
483 radio->v4l2object->frequency = frequency;
484 gst_v4l2_set_frequency (radio->v4l2object, 0,
485 radio->v4l2object->frequency);
486 }
487 break;
488 default:
489 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
490 break;
491 }
492 }
493
494 static void
gst_v4l2radio_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)495 gst_v4l2radio_get_property (GObject * object, guint prop_id,
496 GValue * value, GParamSpec * pspec)
497 {
498 GstV4l2Radio *radio = GST_V4L2RADIO (object);
499
500 switch (prop_id) {
501 case ARG_DEVICE:
502 g_value_set_string (value, radio->v4l2object->videodev);
503 break;
504 case ARG_FREQUENCY:
505 if (gst_v4l2_get_frequency (radio->v4l2object,
506 0, &(radio->v4l2object->frequency)))
507 g_value_set_int (value, radio->v4l2object->frequency);
508 break;
509 default:
510 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
511 break;
512 }
513 }
514
515 /* GstURIHandler interface */
516 static GstURIType
gst_v4l2radio_uri_get_type(GType type)517 gst_v4l2radio_uri_get_type (GType type)
518 {
519 return GST_URI_SRC;
520 }
521
522 static const gchar *const *
gst_v4l2radio_uri_get_protocols(GType type)523 gst_v4l2radio_uri_get_protocols (GType type)
524 {
525 static const gchar *protocols[] = { "radio", NULL };
526
527 return protocols;
528 }
529
530 static gchar *
gst_v4l2radio_uri_get_uri(GstURIHandler * handler)531 gst_v4l2radio_uri_get_uri (GstURIHandler * handler)
532 {
533 GstV4l2Radio *radio = GST_V4L2RADIO (handler);
534
535 if (radio->v4l2object->videodev != NULL) {
536 if (gst_v4l2_get_frequency (radio->v4l2object,
537 0, &(radio->v4l2object->frequency))) {
538 return g_strdup_printf ("radio://%4.1f",
539 radio->v4l2object->frequency / 1e6);
540 }
541 }
542
543 return g_strdup ("radio://");
544 }
545
546 static gboolean
gst_v4l2radio_uri_set_uri(GstURIHandler * handler,const gchar * uri,GError ** error)547 gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri,
548 GError ** error)
549 {
550 GstV4l2Radio *radio = GST_V4L2RADIO (handler);
551 gdouble dfreq;
552 gint ifreq;
553 const gchar *freq;
554 gchar *end;
555
556 if (strcmp (uri, "radio://") != 0) {
557 freq = uri + 8;
558
559 dfreq = g_ascii_strtod (freq, &end);
560
561 if (errno || strlen (end))
562 goto uri_failed;
563
564 ifreq = dfreq * 1e6;
565 g_object_set (radio, "frequency", ifreq, NULL);
566
567 } else
568 goto uri_failed;
569
570 return TRUE;
571
572 uri_failed:
573 g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
574 "Bad radio URI, could not parse frequency");
575 return FALSE;
576 }
577
578 static void
gst_v4l2radio_uri_handler_init(gpointer g_iface,gpointer iface_data)579 gst_v4l2radio_uri_handler_init (gpointer g_iface, gpointer iface_data)
580 {
581 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
582
583 iface->get_type = gst_v4l2radio_uri_get_type;
584 iface->get_protocols = gst_v4l2radio_uri_get_protocols;
585 iface->get_uri = gst_v4l2radio_uri_get_uri;
586 iface->set_uri = gst_v4l2radio_uri_set_uri;
587 }
588