diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am
index f620de147d..491034979a 100644
--- a/docs/plugins/Makefile.am
+++ b/docs/plugins/Makefile.am
@@ -229,6 +229,7 @@ EXTRA_HFILES = \
$(top_srcdir)/sys/osxvideo/osxvideosink.h \
$(top_srcdir)/sys/v4l2/gstv4l2src.h \
$(top_srcdir)/sys/v4l2/gstv4l2sink.h \
+ $(top_srcdir)/sys/v4l2/gstv4l2radio.h \
$(top_srcdir)/sys/waveform/gstwaveformsink.h \
$(top_srcdir)/sys/ximage/gstximagesrc.h
diff --git a/docs/plugins/gst-plugins-good-plugins-docs.sgml b/docs/plugins/gst-plugins-good-plugins-docs.sgml
index 459a064c83..988bba3417 100644
--- a/docs/plugins/gst-plugins-good-plugins-docs.sgml
+++ b/docs/plugins/gst-plugins-good-plugins-docs.sgml
@@ -154,6 +154,7 @@
+
diff --git a/docs/plugins/gst-plugins-good-plugins-sections.txt b/docs/plugins/gst-plugins-good-plugins-sections.txt
index 6081c67633..b2e6de68cf 100644
--- a/docs/plugins/gst-plugins-good-plugins-sections.txt
+++ b/docs/plugins/gst-plugins-good-plugins-sections.txt
@@ -2048,6 +2048,20 @@ GST_IS_V4L2SINK_CLASS
gst_v4l2sink_get_type
+
+element-v4l2radio
+v4l2radio
+GstV4l2Radio
+
+GstV4l2RadioClass
+GST_V4L2RADIO
+GST_IS_V4L2RADIO
+GST_TYPE_V4L2RADIO
+GST_V4L2RADIO_CLASS
+GST_IS_V4L2RADIO_CLASS
+gst_v4l2radio_get_type
+
+
element-waveformsink
waveformsink
diff --git a/docs/plugins/inspect/plugin-video4linux2.xml b/docs/plugins/inspect/plugin-video4linux2.xml
index 5204cc02fa..8349f913d3 100644
--- a/docs/plugins/inspect/plugin-video4linux2.xml
+++ b/docs/plugins/inspect/plugin-video4linux2.xml
@@ -24,5 +24,12 @@
+
+ v4l2radio
+ Radio (video4linux2) Tuner
+ Tuner
+ Controls a Video4Linux2 radio device
+ Alexey Chernov <4ernov@gmail.com>
+
-
\ No newline at end of file
+
diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am
index ab996a15a8..a7a99dea31 100644
--- a/sys/v4l2/Makefile.am
+++ b/sys/v4l2/Makefile.am
@@ -13,6 +13,7 @@ libgstvideo4linux2_la_SOURCES = gstv4l2.c \
gstv4l2object.c \
gstv4l2bufferpool.c \
gstv4l2src.c \
+ gstv4l2radio.c \
gstv4l2tuner.c \
gstv4l2vidorient.c \
v4l2_calls.c \
@@ -51,6 +52,7 @@ noinst_HEADERS = \
gstv4l2object.h \
gstv4l2sink.h \
gstv4l2src.h \
+ gstv4l2radio.h \
gstv4l2tuner.h \
gstv4l2vidorient.h \
gstv4l2xoverlay.h \
diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c
index 4a7056fd52..95f64db785 100644
--- a/sys/v4l2/gstv4l2.c
+++ b/sys/v4l2/gstv4l2.c
@@ -35,6 +35,7 @@
#ifdef HAVE_EXPERIMENTAL
#include "gstv4l2sink.h"
#endif
+#include "gstv4l2radio.h"
/* #include "gstv4l2jpegsrc.h" */
/* #include "gstv4l2mjpegsrc.h" */
/* #include "gstv4l2mjpegsink.h" */
@@ -58,6 +59,8 @@ plugin_init (GstPlugin * plugin)
!gst_element_register (plugin, "v4l2sink", GST_RANK_NONE,
GST_TYPE_V4L2SINK) ||
#endif
+ !gst_element_register (plugin, "v4l2radio", GST_RANK_NONE,
+ GST_TYPE_V4L2RADIO) ||
/* !gst_element_register (plugin, "v4l2jpegsrc", */
/* GST_RANK_NONE, GST_TYPE_V4L2JPEGSRC) || */
/* !gst_element_register (plugin, "v4l2mjpegsrc", */
diff --git a/sys/v4l2/gstv4l2radio.c b/sys/v4l2/gstv4l2radio.c
new file mode 100644
index 0000000000..b1699d3814
--- /dev/null
+++ b/sys/v4l2/gstv4l2radio.c
@@ -0,0 +1,633 @@
+/* GStreamer v4l2 radio tuner element
+ * Copyright (C) 2010, 2011 Alexey Chernov <4ernov@gmail.com>
+ *
+ * gstv4l2radio.c - V4L2 radio tuner element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-v4l2radio
+ *
+ * v4l2radio can be used to control radio device
+ * and to tune it to different radiostations.
+ *
+ *
+ * Example launch lines
+ * |[
+ * gst-launch v4l2radio device=/dev/radio0 frequency=101200000
+ * gst-launch alsasrc device=hw:1 ! audioconvert ! audioresample ! alsasink
+ * ]|
+ * First pipeline tunes the radio device /dev/radio0 to station 101.2 MHz,
+ * second pipeline connects digital audio out (hw:1) to default sound card.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+
+#include "gst/gst-i18n-plugin.h"
+
+#include "gstv4l2tuner.h"
+#include "gstv4l2radio.h"
+#include "v4l2_calls.h"
+
+GST_DEBUG_CATEGORY_STATIC (v4l2radio_debug);
+#define GST_CAT_DEFAULT v4l2radio_debug
+
+#define DEFAULT_PROP_DEVICE "/dev/radio0"
+#define MIN_FREQUENCY 87500000
+#define DEFAULT_FREQUENCY 100000000
+#define MAX_FREQUENCY 108000000
+
+enum
+{
+ ARG_0,
+ ARG_DEVICE,
+ ARG_FREQUENCY
+};
+
+static gboolean
+gst_v4l2radio_fill_channel_list (GstV4l2Radio * radio)
+{
+ int res;
+ struct v4l2_tuner vtun;
+ struct v4l2_capability vc;
+ GstV4l2TunerChannel *v4l2channel;
+ GstTunerChannel *channel;
+
+ GstElement *e;
+
+ GstV4l2Object *v4l2object;
+
+ e = GST_ELEMENT (radio);
+ v4l2object = radio->v4l2object;
+
+ GST_DEBUG_OBJECT (e, "getting audio enumeration");
+ GST_V4L2_CHECK_OPEN (v4l2object);
+
+ GST_DEBUG_OBJECT (e, " audio input");
+
+ memset (&vc, 0, sizeof (vc));
+
+ res = v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &vc);
+ if (res < 0)
+ goto caps_failed;
+
+ if (!(vc.capabilities & V4L2_CAP_TUNER))
+ goto not_a_tuner;
+
+ /* getting audio input */
+ memset (&vtun, 0, sizeof (vtun));
+ vtun.index = 0;
+
+ res = v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &vtun);
+ if (res < 0)
+ goto tuner_failed;
+
+ GST_LOG_OBJECT (e, " index: %d", vtun.index);
+ GST_LOG_OBJECT (e, " name: '%s'", vtun.name);
+ GST_LOG_OBJECT (e, " type: %016x", (guint) vtun.type);
+ GST_LOG_OBJECT (e, " caps: %016x", (guint) vtun.capability);
+ GST_LOG_OBJECT (e, " rlow: %016x", (guint) vtun.rangelow);
+ GST_LOG_OBJECT (e, " rhigh: %016x", (guint) vtun.rangehigh);
+ GST_LOG_OBJECT (e, " audmode: %016x", (guint) vtun.audmode);
+
+ v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL);
+ channel = GST_TUNER_CHANNEL (v4l2channel);
+ channel->label = g_strdup ((const gchar *) vtun.name);
+ channel->flags = GST_TUNER_CHANNEL_FREQUENCY | GST_TUNER_CHANNEL_AUDIO;
+ v4l2channel->index = 0;
+ v4l2channel->tuner = 0;
+
+ channel->freq_multiplicator =
+ 62.5 * ((vtun.capability & V4L2_TUNER_CAP_LOW) ? 1 : 1000);
+ channel->min_frequency = vtun.rangelow * channel->freq_multiplicator;
+ channel->max_frequency = vtun.rangehigh * channel->freq_multiplicator;
+ channel->min_signal = 0;
+ channel->max_signal = 0xffff;
+
+ v4l2object->channels =
+ g_list_prepend (v4l2object->channels, (gpointer) channel);
+
+ v4l2object->channels = g_list_reverse (v4l2object->channels);
+
+ GST_DEBUG_OBJECT (e, "done");
+ return TRUE;
+
+ /* ERRORS */
+tuner_failed:
+ {
+ GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+ (_("Failed to get settings of tuner %d on device '%s'."),
+ vtun.index, v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+caps_failed:
+ {
+ GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+ (_("Error getting capabilities for device '%s'."),
+ v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+not_a_tuner:
+ {
+ GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+ (_("Device '%s' is not a tuner."),
+ v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_get_input (GstV4l2Object * v4l2object, gint * input)
+{
+ GST_DEBUG_OBJECT (v4l2object->element, "trying to get radio input");
+
+ if (!GST_V4L2_IS_OPEN (v4l2object))
+ return FALSE;
+
+ if (!v4l2object->channels)
+ goto input_failed;
+
+ *input = 0;
+
+ GST_DEBUG_OBJECT (v4l2object->element, "input: %d", 0);
+
+ return TRUE;
+
+ /* ERRORS */
+input_failed:
+ {
+ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+ (_("Failed to get radio input on device '%s'. "),
+ v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_set_input (GstV4l2Object * v4l2object, gint input)
+{
+ GST_DEBUG_OBJECT (v4l2object->element, "trying to set input to %d", input);
+
+ if (!GST_V4L2_IS_OPEN (v4l2object))
+ return FALSE;
+
+ if (!v4l2object->channels)
+ goto input_failed;
+
+ return TRUE;
+
+ /* ERRORS */
+input_failed:
+ {
+ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+ (_("Failed to set input %d on device %s."),
+ input, v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_set_mute_on (GstV4l2Radio * radio, gboolean on)
+{
+ gint res;
+ struct v4l2_control vctrl;
+
+ GST_DEBUG_OBJECT (radio, "setting current tuner mute state: %d", on);
+
+ if (!GST_V4L2_IS_OPEN (radio->v4l2object))
+ return FALSE;
+
+ memset (&vctrl, 0, sizeof (vctrl));
+ vctrl.id = V4L2_CID_AUDIO_MUTE;
+ vctrl.value = on;
+
+ GST_DEBUG_OBJECT (radio, "radio fd: %d", radio->v4l2object->video_fd);
+
+ res = ioctl (radio->v4l2object->video_fd, VIDIOC_S_CTRL, &vctrl);
+ GST_DEBUG_OBJECT (radio, "mute state change result: %d", res);
+ if (res < 0)
+ goto freq_failed;
+
+ return TRUE;
+
+ /* ERRORS */
+freq_failed:
+ {
+ GST_ELEMENT_WARNING (radio, RESOURCE, SETTINGS,
+ (_("Failed to change mute state for device '%s'."),
+ radio->v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_set_mute (GstV4l2Radio * radio)
+{
+ return gst_v4l2radio_set_mute_on (radio, TRUE);
+}
+
+static gboolean
+gst_v4l2radio_set_unmute (GstV4l2Radio * radio)
+{
+ return gst_v4l2radio_set_mute_on (radio, FALSE);
+}
+
+GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2RadioClass, gst_v4l2radio);
+GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Radio, gst_v4l2radio);
+
+static void gst_v4l2radio_uri_handler_init (gpointer g_iface,
+ gpointer iface_data);
+
+static gboolean
+gst_v4l2radio_interface_supported (GstImplementsInterface * iface,
+ GType iface_type)
+{
+ if (iface_type == GST_TYPE_TUNER)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+gst_v4l2radio_implements_interface_init (GstImplementsInterfaceClass * iface)
+{
+ iface->supported = gst_v4l2radio_interface_supported;
+}
+
+static void
+gst_v4l2radio_tuner_interface_reinit (GstTunerClass * iface)
+{
+ gst_v4l2radio_tuner_interface_init (iface);
+}
+
+static void
+gst_v4l2radio_interfaces (GType type)
+{
+ static const GInterfaceInfo urihandler_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ static const GInterfaceInfo implements_interface_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_implements_interface_init,
+ NULL,
+ NULL,
+ };
+
+ static const GInterfaceInfo propertyprobe_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_property_probe_interface_init,
+ NULL,
+ NULL,
+ };
+
+ static const GInterfaceInfo tuner_interface_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_tuner_interface_reinit,
+ NULL,
+ NULL,
+ };
+
+ g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
+ g_type_add_interface_static (type,
+ GST_TYPE_IMPLEMENTS_INTERFACE, &implements_interface_info);
+
+ g_type_add_interface_static (type, GST_TYPE_TUNER, &tuner_interface_info);
+
+ g_type_add_interface_static (type,
+ GST_TYPE_PROPERTY_PROBE, &propertyprobe_info);
+}
+
+GST_BOILERPLATE_FULL (GstV4l2Radio, gst_v4l2radio, GstElement, GST_TYPE_ELEMENT,
+ gst_v4l2radio_interfaces);
+
+static void gst_v4l2radio_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_v4l2radio_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_v4l2radio_finalize (GstV4l2Radio * radio);
+static void gst_v4l2radio_dispose (GObject * object);
+static GstStateChangeReturn gst_v4l2radio_change_state (GstElement * element,
+ GstStateChange transition);
+
+static void
+gst_v4l2radio_base_init (gpointer gclass)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (gclass);
+ GstV4l2RadioClass *gstv4l2radio_class = GST_V4L2RADIO_CLASS (gclass);
+
+ GST_DEBUG_CATEGORY_INIT (v4l2radio_debug, "v4l2radio", 0,
+ "V4l2 radio element");
+
+ gstv4l2radio_class->v4l2_class_devices = NULL;
+
+ gst_element_class_set_details_simple (gstelement_class,
+ "Radio (video4linux2) Tuner",
+ "Tuner",
+ "Controls a Video4Linux2 radio device",
+ "Alexey Chernov <4ernov@gmail.com>");
+}
+
+static void
+gst_v4l2radio_class_init (GstV4l2RadioClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = gst_v4l2radio_set_property;
+ gobject_class->get_property = gst_v4l2radio_get_property;
+
+ g_object_class_install_property (gobject_class, ARG_DEVICE,
+ g_param_spec_string ("device", "Radio device location",
+ "Video4Linux2 radio device location",
+ DEFAULT_PROP_DEVICE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, ARG_FREQUENCY,
+ g_param_spec_int ("frequency", "Station frequency",
+ "Station frequency in Hz",
+ MIN_FREQUENCY, MAX_FREQUENCY, DEFAULT_FREQUENCY, G_PARAM_READWRITE));
+
+ gobject_class->dispose = gst_v4l2radio_dispose;
+ gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2radio_finalize;
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_v4l2radio_change_state);
+
+}
+
+static void
+gst_v4l2radio_init (GstV4l2Radio * filter, GstV4l2RadioClass * gclass)
+{
+ filter->v4l2object = gst_v4l2_object_new (GST_ELEMENT (filter),
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE,
+ gst_v4l2radio_get_input, gst_v4l2radio_set_input, NULL);
+
+ filter->v4l2object->frequency = DEFAULT_FREQUENCY;
+ filter->v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE);
+}
+
+static void
+gst_v4l2radio_dispose (GObject * object)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (object);
+ gst_v4l2_close (radio->v4l2object);
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_v4l2radio_finalize (GstV4l2Radio * radio)
+{
+ gst_v4l2_object_destroy (radio->v4l2object);
+ G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (radio));
+}
+
+static gboolean
+gst_v4l2radio_open (GstV4l2Radio * radio)
+{
+ GstV4l2Object *v4l2object;
+
+ v4l2object = radio->v4l2object;
+ if (gst_v4l2_open (v4l2object))
+ return gst_v4l2radio_fill_channel_list (radio);
+ else
+ return FALSE;
+}
+
+static void
+gst_v4l2radio_set_defaults (GstV4l2Radio * radio)
+{
+ GstV4l2Object *v4l2object;
+ GstTunerChannel *channel = NULL;
+ GstTuner *tuner;
+
+ v4l2object = radio->v4l2object;
+
+ if (!GST_IS_TUNER (v4l2object->element))
+ return;
+
+ tuner = GST_TUNER (v4l2object->element);
+
+ if (v4l2object->channel)
+ channel = gst_tuner_find_channel_by_name (tuner, v4l2object->channel);
+ if (channel) {
+ gst_tuner_set_channel (tuner, channel);
+ } else {
+ channel =
+ GST_TUNER_CHANNEL (gst_tuner_get_channel (GST_TUNER
+ (v4l2object->element)));
+ if (channel) {
+ g_free (v4l2object->channel);
+ v4l2object->channel = g_strdup (channel->label);
+ gst_tuner_channel_changed (tuner, channel);
+ }
+ }
+
+ if (channel
+ && GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) {
+ if (v4l2object->frequency != 0) {
+ gst_tuner_set_frequency (tuner, channel, v4l2object->frequency);
+ } else {
+ v4l2object->frequency = gst_tuner_get_frequency (tuner, channel);
+ if (v4l2object->frequency == 0) {
+ /* guess */
+ gst_tuner_set_frequency (tuner, channel, MIN_FREQUENCY);
+ } else {
+ }
+ }
+ }
+}
+
+static gboolean
+gst_v4l2radio_start (GstV4l2Radio * radio)
+{
+ if (!gst_v4l2radio_open (radio))
+ return FALSE;
+
+ gst_v4l2radio_set_defaults (radio);
+
+ return TRUE;
+}
+
+static gboolean
+gst_v4l2radio_stop (GstV4l2Radio * radio)
+{
+ if (!gst_v4l2_object_stop (radio->v4l2object))
+ return FALSE;
+
+ return TRUE;
+}
+
+static GstStateChangeReturn
+gst_v4l2radio_change_state (GstElement * element, GstStateChange transition)
+{
+ GstV4l2Radio *radio;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ radio = GST_V4L2RADIO (element);
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ /*start radio */
+ if (!gst_v4l2radio_start (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ /*unmute radio */
+ if (!gst_v4l2radio_set_unmute (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ /*mute radio */
+ if (!gst_v4l2radio_set_mute (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ /*stop radio */
+ if (!gst_v4l2radio_stop (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+gst_v4l2radio_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (object);
+ gint frequency;
+ switch (prop_id) {
+ case ARG_DEVICE:
+ radio->v4l2object->videodev =
+ g_strdup ((gchar *) g_value_get_string (value));
+ break;
+ case ARG_FREQUENCY:
+ frequency = g_value_get_int (value);
+ if (frequency >= MIN_FREQUENCY && frequency <= MAX_FREQUENCY) {
+ radio->v4l2object->frequency = frequency;
+ gst_v4l2_set_frequency (radio->v4l2object, 0,
+ radio->v4l2object->frequency);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_v4l2radio_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (object);
+
+ switch (prop_id) {
+ case ARG_DEVICE:
+ g_value_set_string (value, radio->v4l2object->videodev);
+ break;
+ case ARG_FREQUENCY:
+ if (gst_v4l2_get_frequency (radio->v4l2object,
+ 0, &(radio->v4l2object->frequency)))
+ g_value_set_int (value, radio->v4l2object->frequency);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* GstURIHandler interface */
+static GstURIType
+gst_v4l2radio_uri_get_type (void)
+{
+ return GST_URI_SRC;
+}
+
+static gchar **
+gst_v4l2radio_uri_get_protocols (void)
+{
+ static gchar *protocols[] = { (char *) "radio", NULL };
+ return protocols;
+}
+
+static const gchar *
+gst_v4l2radio_uri_get_uri (GstURIHandler * handler)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (handler);
+
+ if (radio->v4l2object->videodev != NULL) {
+ if (gst_v4l2_get_frequency (radio->v4l2object,
+ 0, &(radio->v4l2object->frequency))) {
+ gchar uri[20];
+ gchar freq[6];
+ g_ascii_formatd (freq, 6, "%4.1f", radio->v4l2object->frequency / 1e6);
+ g_snprintf (uri, sizeof (uri), "radio://%s", freq);
+ return g_intern_string (uri);
+ }
+ }
+
+ return "radio://";
+}
+
+static gboolean
+gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (handler);
+ gdouble dfreq;
+ gint ifreq;
+ const gchar *freq;
+ gchar *end;
+
+ if (strcmp (uri, "radio://") != 0) {
+ freq = uri + 8;
+
+ dfreq = g_ascii_strtod (freq, &end);
+
+ if (errno || strlen (end))
+ goto uri_failed;
+
+ ifreq = dfreq * 1e6;
+ g_object_set (radio, "frequency", ifreq, NULL);
+
+ } else
+ goto uri_failed;
+
+ return TRUE;
+
+uri_failed:
+ return FALSE;
+}
+
+static void
+gst_v4l2radio_uri_handler_init (gpointer g_iface, gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+ iface->get_type = gst_v4l2radio_uri_get_type;
+ iface->get_protocols = gst_v4l2radio_uri_get_protocols;
+ iface->get_uri = gst_v4l2radio_uri_get_uri;
+ iface->set_uri = gst_v4l2radio_uri_set_uri;
+}
diff --git a/sys/v4l2/gstv4l2radio.h b/sys/v4l2/gstv4l2radio.h
new file mode 100644
index 0000000000..f1c99a277f
--- /dev/null
+++ b/sys/v4l2/gstv4l2radio.h
@@ -0,0 +1,68 @@
+/* GStreamer v4l2 radio tuner element
+ * Copyright (C) 2010, 2011 Alexey Chernov <4ernov@gmail.com>
+ *
+ * gstv4l2radio.h - V4L2 radio tuner element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_V4L2RADIO_H__
+#define __GST_V4L2RADIO_H__
+
+#include "gstv4l2object.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_V4L2RADIO \
+ (gst_v4l2radio_get_type())
+#define GST_V4L2RADIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2RADIO,GstV4l2Radio))
+#define GST_V4L2RADIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2RADIO,GstV4l2RadioClass))
+#define GST_IS_V4L2RADIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2RADIO))
+#define GST_IS_V4L2RADIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2RADIO))
+
+typedef struct _GstV4l2Radio GstV4l2Radio;
+typedef struct _GstV4l2RadioClass GstV4l2RadioClass;
+
+/**
+ * GstV4l2Radio:
+ * @v4l2object: private #GstV4l2Object
+ *
+ * Opaque video4linux2 radio tuner element
+ */
+struct _GstV4l2Radio
+{
+ GstElement element;
+
+ /*< private >*/
+ GstV4l2Object * v4l2object;
+};
+
+struct _GstV4l2RadioClass
+{
+ GstElementClass parent_class;
+
+ GList *v4l2_class_devices;
+};
+
+GType gst_v4l2radio_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_V4L2RADIO_H__ */