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