gstreamer/sys/v4l2/gstv4l2radio.c
Wim Taymans d9e61954a1 v4l2: Let the device object manage the pool
Rename start and stop methods to open and close because that is what they do.
After setting the format on the device object, setup the bufferpools. Move this
code from the v4l2src_calls.c file, it is shared between source and sink.
Make new device start and stop method that merges various bits of common code
spread over several files.
2011-07-13 16:33:58 +02:00

579 lines
15 KiB
C

/* 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.
*
* <refsect2>
* <title>Example launch lines</title>
* |[
* 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.
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#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 void
gst_v4l2radio_tuner_interface_reinit (GstTunerClass * iface)
{
gst_v4l2radio_tuner_interface_init (iface);
}
#define gst_v4l2radio_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstV4l2Radio, gst_v4l2radio, GST_TYPE_ELEMENT,
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
gst_v4l2radio_uri_handler_init);
G_IMPLEMENT_INTERFACE (GST_TYPE_TUNER,
gst_v4l2radio_tuner_interface_reinit);
G_IMPLEMENT_INTERFACE (GST_TYPE_PROPERTY_PROBE,
gst_v4l2radio_property_probe_interface_init));
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_class_init (GstV4l2RadioClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->dispose = gst_v4l2radio_dispose;
gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2radio_finalize;
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));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_v4l2radio_change_state);
gst_element_class_set_details_simple (gstelement_class,
"Radio (video4linux2) Tuner",
"Tuner",
"Controls a Video4Linux2 radio device",
"Alexey Chernov <4ernov@gmail.com>");
klass->v4l2_class_devices = NULL;
GST_DEBUG_CATEGORY_INIT (v4l2radio_debug, "v4l2radio", 0,
"V4l2 radio element");
}
static void
gst_v4l2radio_init (GstV4l2Radio * filter)
{
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;
g_free (filter->v4l2object->videodev);
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_close (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:
g_free (radio->v4l2object->videodev);
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 (GType type)
{
return GST_URI_SRC;
}
static gchar **
gst_v4l2radio_uri_get_protocols (GType type)
{
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;
}