gstreamer/sys/v4l2/gstv4l2radio.c
Nicolas Dufresne e575e4eda8 v4l2object: Use a debug object for tracing
This way we can pass the pad name instead of the element for tracing
which helps identifying which v4l2object is used withing M2M element
like decoder, encoder and transform. For the reference, pads are name
<parent-name>:<pad-name>.
2017-12-13 14:42:25 -05:00

582 lines
16 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, 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-1.0 v4l2radio device=/dev/radio0 frequency=101200000
* gst-launch-1.0 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 "gstv4l2object.h"
#include "gstv4l2tuner.h"
#include "gstv4l2radio.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 = v4l2object->ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &vc);
if (res < 0)
goto caps_failed;
if (vc.capabilities & V4L2_CAP_DEVICE_CAPS)
v4l2object->device_caps = vc.device_caps;
else
v4l2object->device_caps = vc.capabilities;
if (!(v4l2object->device_caps & V4L2_CAP_TUNER))
goto not_a_tuner;
/* getting audio input */
memset (&vtun, 0, sizeof (vtun));
vtun.index = 0;
res = v4l2object->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_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 (GstTunerInterface * 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));
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_static_metadata (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),
GST_OBJECT (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 const gchar *const *
gst_v4l2radio_uri_get_protocols (GType type)
{
static const gchar *protocols[] = { "radio", NULL };
return protocols;
}
static 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))) {
return g_strdup_printf ("radio://%4.1f",
radio->v4l2object->frequency / 1e6);
}
}
return g_strdup ("radio://");
}
static gboolean
gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri,
GError ** error)
{
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:
g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"Bad radio URI, could not parse frequency");
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;
}