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