Merge branch 'master' into 0.11-fdo

This commit is contained in:
Wim Taymans 2011-03-28 20:50:59 +02:00
commit 8f22a09dc4
33 changed files with 1971 additions and 268 deletions

View file

@ -80,7 +80,7 @@ tool_run "$libtoolize" "--copy --force"
tool_run "$aclocal" "-I m4 -I common/m4 $ACLOCAL_FLAGS"
tool_run "$autoheader"
# touch the stamp-h.in build stamp so we don't re-run autoheader in maintainer mode -- wingo
# touch the stamp-h.in build stamp so we don't re-run autoheader in maintainer mode
echo timestamp > stamp-h.in 2> /dev/null
tool_run "$autoconf"

2
common

@ -1 +1 @@
Subproject commit 6aec6b9716c184c60c4bc6a5916a2471cfa8c8cd
Subproject commit 1ccbe098d6379612fcef09f4000da23585af980a

View file

@ -766,6 +766,13 @@ AG_GST_CHECK_FEATURE(JACK, Jack, jack, [
PKG_CHECK_MODULES(JACK, jack >= 0.99.10, HAVE_JACK="yes", HAVE_JACK="no")
AC_SUBST(JACK_CFLAGS)
AC_SUBST(JACK_LIBS)
dnl upcomming jack2 (1.9.7 will have the new api as well
AG_GST_PKG_CHECK_MODULES(JACK_0_120_2, jack >= 0.120.2 jack < 1.0)
if test x$HAVE_JACK_0_120_2 = xyes; then
AC_DEFINE(HAVE_JACK_0_120_2, 1, [defined if jack >= 0.120.2 is available])
fi
])
dnl *** jpeg ***
@ -921,7 +928,6 @@ AG_GST_CHECK_FEATURE(SOUP, [soup http client plugin (2.4)], souphttpsrc, [
],[
PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.26, HAVE_SOUP="yes", [
HAVE_SOUP="no"
AC_MSG_RESULT(no)
])
])
AC_SUBST(SOUP_CFLAGS)
@ -998,7 +1004,6 @@ AG_GST_CHECK_FEATURE(WAVPACK, [wavpack plug-in], wavpack, [
AC_DEFINE(WAVPACK_OLD_API, 1, [old wavpack API])
],[
HAVE_WAVPACK=no
AC_MSG_RESULT(no)
])
])
AC_SUBST(WAVPACK_CFLAGS)

View file

@ -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

View file

@ -154,6 +154,7 @@
<xi:include href="xml/element-udpsink.xml" />
<xi:include href="xml/element-v4l2src.xml" />
<xi:include href="xml/element-v4l2sink.xml" />
<xi:include href="xml/element-v4l2radio.xml" />
<xi:include href="xml/element-vertigotv.xml" />
<xi:include href="xml/element-videobalance.xml" />
<xi:include href="xml/element-videobox.xml" />

View file

@ -2048,6 +2048,20 @@ GST_IS_V4L2SINK_CLASS
gst_v4l2sink_get_type
</SECTION>
<SECTION>
<FILE>element-v4l2radio</FILE>
<TITLE>v4l2radio</TITLE>
GstV4l2Radio
<SUBSECTION Standard>
GstV4l2RadioClass
GST_V4L2RADIO
GST_IS_V4L2RADIO
GST_TYPE_V4L2RADIO
GST_V4L2RADIO_CLASS
GST_IS_V4L2RADIO_CLASS
gst_v4l2radio_get_type
</SECTION>
<SECTION>
<FILE>element-waveformsink</FILE>
<TITLE>waveformsink</TITLE>

View file

@ -24,5 +24,12 @@
</caps>
</pads>
</element>
<element>
<name>v4l2radio</name>
<longname>Radio (video4linux2) Tuner</longname>
<class>Tuner</class>
<description>Controls a Video4Linux2 radio device</description>
<author>Alexey Chernov &lt;4ernov@gmail.com&gt;</author>
</element>
</elements>
</plugin>
</plugin>

View file

@ -41,5 +41,5 @@ libgstcairo_la_LIBADD = \
libgstcairo_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstcairo_la_LIBTOOLFLAGS = --tag=disable-static
EXTRA_DIST = cairo-marshal.list
EXTRA_DIST = gstcairo-marshal.list

View file

@ -915,7 +915,7 @@ gst_dvdemux_handle_push_seek (GstDVDemux * dvdemux, GstPad * pad,
/* First try if upstream can handle time based seeks */
if (format == GST_FORMAT_TIME)
res = gst_pad_push_event (dvdemux->sinkpad, event);
res = gst_pad_push_event (dvdemux->sinkpad, gst_event_ref (event));
if (!res) {
/* we convert the start/stop on the srcpad to the byte format
@ -1153,12 +1153,15 @@ gst_dvdemux_send_event (GstElement * element, GstEvent * event)
} else {
GST_OBJECT_UNLOCK (dvdemux);
if (dvdemux->seek_handler)
if (dvdemux->seek_handler) {
res = dvdemux->seek_handler (dvdemux, dvdemux->videosrcpad, event);
gst_event_unref (event);
}
}
break;
}
default:
res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
break;
}

View file

@ -592,16 +592,27 @@ static guint
gst_jack_ring_buffer_delay (GstRingBuffer * buf)
{
GstJackAudioSink *sink;
guint i, res = 0, latency;
guint i, res = 0;
#ifdef HAVE_JACK_0_120_2
jack_latency_range_t range;
#else
guint latency;
#endif
jack_client_t *client;
sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
client = gst_jack_audio_client_get_client (sink->client);
for (i = 0; i < sink->port_count; i++) {
#ifdef HAVE_JACK_0_120_2
jack_port_get_latency_range (sink->ports[i], JackPlaybackLatency, &range);
if (range.max > res)
res = range.max;
#else
latency = jack_port_get_total_latency (client, sink->ports[i]);
if (latency > res)
res = latency;
#endif
}
GST_LOG_OBJECT (sink, "delay %u", res);

View file

@ -603,16 +603,27 @@ static guint
gst_jack_ring_buffer_delay (GstRingBuffer * buf)
{
GstJackAudioSrc *src;
guint i, res = 0, latency;
guint i, res = 0;
#ifdef HAVE_JACK_0_120_2
jack_latency_range_t range;
#else
guint latency;
#endif
jack_client_t *client;
src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf));
client = gst_jack_audio_client_get_client (src->client);
for (i = 0; i < src->port_count; i++) {
#ifdef HAVE_JACK_0_120_2
jack_port_get_latency_range (src->ports[i], JackCaptureLatency, &range);
if (range.max > res)
res = range.max;
#else
latency = jack_port_get_total_latency (client, src->ports[i]);
if (latency > res)
res = latency;
#endif
}
GST_DEBUG_OBJECT (src, "delay %u", res);

View file

@ -38,7 +38,7 @@
* ]| Play a 440Hz sine wave.
* |[
* gst-launch -v audiotestsrc ! pulsesink stream-properties="props,media.title=test"
* ]] Play a sine wave and set a stream property. The property can be checked
* ]| Play a sine wave and set a stream property. The property can be checked
* with "pactl list".
* </refsect2>
*/
@ -442,6 +442,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
GstPulseRingBuffer *pbuf;
GstPulseContext *pctx;
pa_mainloop_api *api;
gboolean need_unlock_shared;
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (buf));
pbuf = GST_PULSERING_BUFFER_CAST (buf);
@ -458,6 +459,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
pa_threaded_mainloop_lock (mainloop);
g_mutex_lock (pa_shared_resource_mutex);
need_unlock_shared = TRUE;
pctx = g_hash_table_lookup (gst_pulse_shared_contexts, pbuf->context_name);
if (pctx == NULL) {
@ -481,7 +483,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
gst_pulsering_context_subscribe_cb, pctx);
#endif
/* try to connect to the server and wait for completioni, we don't want to
/* try to connect to the server and wait for completion, we don't want to
* autospawn a deamon */
GST_LOG_OBJECT (psink, "connect to server %s",
GST_STR_NULL (psink->server));
@ -496,6 +498,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
}
g_mutex_unlock (pa_shared_resource_mutex);
need_unlock_shared = FALSE;
/* context created or shared okay */
pbuf->context = pa_context_ref (pctx->context);
@ -527,6 +530,8 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
/* ERRORS */
unlock_and_fail:
{
if (need_unlock_shared)
g_mutex_unlock (pa_shared_resource_mutex);
gst_pulsering_destroy_context (pbuf);
pa_threaded_mainloop_unlock (mainloop);
return FALSE;

View file

@ -86,6 +86,7 @@ static GstStateChangeReturn speex_dec_change_state (GstElement * element,
static gboolean speex_dec_src_event (GstPad * pad, GstEvent * event);
static gboolean speex_dec_src_query (GstPad * pad, GstQuery * query);
static gboolean speex_dec_sink_query (GstPad * pad, GstQuery * query);
static gboolean speex_dec_sink_setcaps (GstPad * pad, GstCaps * caps);
static const GstQueryType *speex_get_src_query_types (GstPad * pad);
static const GstQueryType *speex_get_sink_query_types (GstPad * pad);
static gboolean speex_dec_convert (GstPad * pad,
@ -100,6 +101,11 @@ static void gst_speex_dec_set_property (GObject * object, guint prop_id,
static GstFlowReturn speex_dec_chain_parse_data (GstSpeexDec * dec,
GstBuffer * buf, GstClockTime timestamp, GstClockTime duration);
static GstFlowReturn speex_dec_chain_parse_header (GstSpeexDec * dec,
GstBuffer * buf);
static GstFlowReturn speex_dec_chain_parse_comments (GstSpeexDec * dec,
GstBuffer * buf);
static void
gst_speex_dec_base_init (gpointer g_class)
{
@ -148,6 +154,9 @@ gst_speex_dec_reset (GstSpeexDec * dec)
dec->header = NULL;
speex_bits_destroy (&dec->bits);
gst_buffer_replace (&dec->streamheader, NULL);
gst_buffer_replace (&dec->vorbiscomment, NULL);
if (dec->stereo) {
speex_stereo_state_destroy (dec->stereo);
dec->stereo = NULL;
@ -172,6 +181,8 @@ gst_speex_dec_init (GstSpeexDec * dec, GstSpeexDecClass * g_class)
GST_DEBUG_FUNCPTR (speex_get_sink_query_types));
gst_pad_set_query_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (speex_dec_sink_query));
gst_pad_set_setcaps_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (speex_dec_sink_setcaps));
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
dec->srcpad =
@ -190,6 +201,46 @@ gst_speex_dec_init (GstSpeexDec * dec, GstSpeexDecClass * g_class)
gst_speex_dec_reset (dec);
}
static gboolean
speex_dec_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstSpeexDec *dec = GST_SPEEX_DEC (gst_pad_get_parent (pad));
gboolean ret = TRUE;
GstStructure *s;
const GValue *streamheader;
s = gst_caps_get_structure (caps, 0);
if ((streamheader = gst_structure_get_value (s, "streamheader")) &&
G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) &&
gst_value_array_get_size (streamheader) >= 2) {
const GValue *header, *vorbiscomment;
GstBuffer *buf;
GstFlowReturn res = GST_FLOW_OK;
header = gst_value_array_get_value (streamheader, 0);
if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) {
buf = gst_value_get_buffer (header);
res = speex_dec_chain_parse_header (dec, buf);
if (res != GST_FLOW_OK)
goto done;
gst_buffer_replace (&dec->streamheader, buf);
}
vorbiscomment = gst_value_array_get_value (streamheader, 1);
if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) {
buf = gst_value_get_buffer (vorbiscomment);
res = speex_dec_chain_parse_comments (dec, buf);
if (res != GST_FLOW_OK)
goto done;
gst_buffer_replace (&dec->vorbiscomment, buf);
}
}
done:
gst_object_unref (dec);
return ret;
}
static gboolean
speex_dec_convert (GstPad * pad,
GstFormat src_format, gint64 src_value,
@ -662,10 +713,11 @@ speex_dec_chain_parse_data (GstSpeexDec * dec, GstBuffer * buf,
/* send data to the bitstream */
speex_bits_read_from (&dec->bits, (char *) data, size);
fpp = 0;
fpp = dec->header->frames_per_packet;
bits = &dec->bits;
GST_DEBUG_OBJECT (dec, "received buffer of size %u, fpp %d", size, fpp);
GST_DEBUG_OBJECT (dec, "received buffer of size %u, fpp %d, %d bits", size,
fpp, speex_bits_remaining (bits));
} else {
/* concealment data, pass NULL as the bits parameters */
GST_DEBUG_OBJECT (dec, "creating concealment data");
@ -675,13 +727,13 @@ speex_dec_chain_parse_data (GstSpeexDec * dec, GstBuffer * buf,
/* now decode each frame, catering for unknown number of them (e.g. rtp) */
for (i = 0; (!fpp || i < fpp) && (!bits || speex_bits_remaining (bits) > 0);
i++) {
for (i = 0; i < fpp; i++) {
GstBuffer *outbuf;
gint16 *out_data;
gint ret;
GST_LOG_OBJECT (dec, "decoding frame %d/%d", i, fpp);
GST_LOG_OBJECT (dec, "decoding frame %d/%d, %d bits remaining", i, fpp,
bits ? speex_bits_remaining (bits) : -1);
res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad,
GST_BUFFER_OFFSET_NONE, dec->frame_size * dec->header->nb_channels * 2,
@ -697,7 +749,12 @@ speex_dec_chain_parse_data (GstSpeexDec * dec, GstBuffer * buf,
ret = speex_decode_int (dec->state, bits, out_data);
if (ret == -1) {
/* uh? end of stream */
GST_WARNING_OBJECT (dec, "Unexpected end of stream found");
if (fpp == 0 && speex_bits_remaining (bits) < 8) {
/* if we did not know how many frames to expect, then we get this
at the end if there are leftover bits to pad to the next byte */
} else {
GST_WARNING_OBJECT (dec, "Unexpected end of stream found");
}
gst_buffer_unref (outbuf);
outbuf = NULL;
break;
@ -754,19 +811,37 @@ speex_dec_chain (GstPad * pad, GstBuffer * buf)
dec = GST_SPEEX_DEC (gst_pad_get_parent (pad));
switch (dec->packetno) {
case 0:
res = speex_dec_chain_parse_header (dec, buf);
break;
case 1:
res = speex_dec_chain_parse_comments (dec, buf);
break;
default:
{
/* If we have the streamheader and vorbiscomment from the caps already
* ignore them here */
if (dec->streamheader && dec->vorbiscomment) {
if (GST_BUFFER_SIZE (dec->streamheader) == GST_BUFFER_SIZE (buf)
&& memcmp (GST_BUFFER_DATA (dec->streamheader), GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf)) == 0) {
res = GST_FLOW_OK;
} else if (GST_BUFFER_SIZE (dec->vorbiscomment) == GST_BUFFER_SIZE (buf)
&& memcmp (GST_BUFFER_DATA (dec->vorbiscomment), GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf)) == 0) {
res = GST_FLOW_OK;
} else {
res =
speex_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
break;
}
} else {
/* Otherwise fall back to packet counting and assume that the
* first two packets are the headers. */
switch (dec->packetno) {
case 0:
res = speex_dec_chain_parse_header (dec, buf);
break;
case 1:
res = speex_dec_chain_parse_comments (dec, buf);
break;
default:
res =
speex_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
break;
}
}

View file

@ -68,6 +68,9 @@ struct _GstSpeexDec {
guint64 packetno;
GstSegment segment; /* STREAM LOCK */
GstBuffer *streamheader;
GstBuffer *vorbiscomment;
};
struct _GstSpeexDecClass {

View file

@ -1916,6 +1916,11 @@ gst_avi_mux_do_buffer (GstAviMux * avimux, GstAviPad * avipad)
guint flags;
data = gst_collect_pads_pop (avimux->collect, avipad->collect);
/* arrange downstream running time */
data = gst_buffer_make_metadata_writable (data);
GST_BUFFER_TIMESTAMP (data) =
gst_segment_to_running_time (&avipad->collect->segment,
GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (data));
/* Prepend a special buffer to the first one for some formats */
if (avipad->is_video) {
@ -2042,6 +2047,19 @@ gst_avi_mux_do_one_buffer (GstAviMux * avimux)
time = GST_BUFFER_TIMESTAMP (buffer);
gst_buffer_unref (buffer);
/* invalid should pass */
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
time = gst_segment_to_running_time (&avipad->collect->segment,
GST_FORMAT_TIME, time);
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
GST_DEBUG_OBJECT (avimux, "clipping buffer on pad %s outside segment",
GST_PAD_NAME (avipad->collect->pad));
buffer = gst_collect_pads_pop (avimux->collect, avipad->collect);
gst_buffer_unref (buffer);
return GST_FLOW_OK;
}
}
delay = avipad->is_video ? GST_SECOND / 2 : 0;
/* invalid timestamp buffers pass first,

View file

@ -103,6 +103,10 @@ gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
"adding key=%d association %" GST_TIME_FORMAT "-> %" G_GUINT64_FORMAT,
keyframe, GST_TIME_ARGS (ts), pos);
/* if upstream is not seekable there is no point in building an index */
if (!demux->upstream_seekable)
return;
/* entry may already have been added before, avoid adding indefinitely */
entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
GST_INDEX_LOOKUP_EXACT, GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, pos);
@ -186,6 +190,41 @@ gst_flv_demux_query_types (GstPad * pad)
return query_types;
}
static void
gst_flv_demux_check_seekability (GstFlvDemux * demux)
{
GstQuery *query;
gint64 start = -1, stop = -1;
demux->upstream_seekable = FALSE;
query = gst_query_new_seeking (GST_FORMAT_BYTES);
if (!gst_pad_peer_query (demux->sinkpad, query)) {
GST_DEBUG_OBJECT (demux, "seeking query failed");
return;
}
gst_query_parse_seeking (query, NULL, &demux->upstream_seekable,
&start, &stop);
/* try harder to query upstream size if we didn't get it the first time */
if (demux->upstream_seekable && stop == -1) {
GstFormat fmt = GST_FORMAT_BYTES;
GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
gst_pad_query_peer_duration (demux->sinkpad, &fmt, &stop);
}
/* if upstream doesn't know the size, it's likely that it's not seekable in
* practice even if it technically may be seekable */
if (demux->upstream_seekable && (start != 0 || stop <= start)) {
GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
demux->upstream_seekable = FALSE;
}
GST_DEBUG_OBJECT (demux, "upstream seekable: %d", demux->upstream_seekable);
}
static void
parse_flv_demux_parse_date_string (GDate * date, const gchar * s)
{
@ -1491,6 +1530,9 @@ gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer)
}
}
/* do a one-time seekability check */
gst_flv_demux_check_seekability (demux);
/* We don't care about the rest */
demux->need_header = FALSE;
@ -1544,6 +1586,7 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
demux->got_par = FALSE;
demux->indexed = FALSE;
demux->upstream_seekable = FALSE;
demux->file_size = 0;
demux->index_max_pos = 0;

View file

@ -123,6 +123,7 @@ struct _GstFlvDemux
gboolean seeking;
gboolean building_index;
gboolean indexed; /* TRUE if index is completely built */
gboolean upstream_seekable; /* TRUE if upstream is seekable */
gint64 file_size;
GstEvent *seek_event;
gint64 seek_time;

View file

@ -1199,6 +1199,12 @@ gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad)
gst_collect_pads_pop (mux->collect, (GstCollectData *) cpad);
GstFlowReturn ret;
/* arrange downstream running time */
buffer = gst_buffer_make_metadata_writable (buffer);
GST_BUFFER_TIMESTAMP (buffer) =
gst_segment_to_running_time (&cpad->collect.segment,
GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer));
if (!mux->streamable)
gst_flv_mux_update_index (mux, buffer, cpad);
@ -1437,6 +1443,15 @@ gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data)
break;
}
time = gst_segment_to_running_time (&cpad->collect.segment,
GST_FORMAT_TIME, time);
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
GST_PAD_NAME (cpad->collect.pad));
buffer = gst_collect_pads_pop (pads, (GstCollectData *) cpad);
gst_buffer_unref (buffer);
return GST_FLOW_OK;
}
if (best == NULL || (GST_CLOCK_TIME_IS_VALID (best_time)
&& time < best_time)) {

View file

@ -2689,8 +2689,8 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
entry->pos + demux->ebml_segment_start);
}
flush = !!(flags & GST_SEEK_FLAG_FLUSH);
keyunit = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
keyunit = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
if (flush) {
GST_DEBUG_OBJECT (demux, "Starting flush");
@ -3631,7 +3631,7 @@ gst_matroska_demux_parse_metadata_id_simple_tag (GstMatroskaDemux * demux,
GstEbmlRead * ebml, GstTagList ** p_taglist)
{
/* FIXME: check if there are more useful mappings */
struct
static const struct
{
const gchar *matroska_tagname;
const gchar *gstreamer_tagname;
@ -3639,6 +3639,7 @@ gst_matroska_demux_parse_metadata_id_simple_tag (GstMatroskaDemux * demux,
tag_conv[] = {
{
GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {

View file

@ -395,6 +395,7 @@
#define GST_MATROSKA_TAG_ID_TITLE "TITLE"
#define GST_MATROSKA_TAG_ID_AUTHOR "AUTHOR"
#define GST_MATROSKA_TAG_ID_ARTIST "ARTIST"
#define GST_MATROSKA_TAG_ID_ALBUM "ALBUM"
#define GST_MATROSKA_TAG_ID_COMMENTS "COMMENTS"
#define GST_MATROSKA_TAG_ID_BITSPS "BITSPS"

View file

@ -644,22 +644,23 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
/* handled this, don't want collectpads to forward it downstream */
ret = FALSE;
gst_event_unref (event);
/* handled this, don't want collectpads to forward it downstream */
event = NULL;
break;
}
case GST_EVENT_NEWSEGMENT:
/* We don't support NEWSEGMENT events */
ret = FALSE;
gst_event_unref (event);
event = NULL;
break;
default:
break;
}
/* now GstCollectPads can take care of the rest, e.g. EOS */
if (ret)
if (event)
ret = mux->collect_event (pad, event);
gst_object_unref (mux);
@ -2168,7 +2169,7 @@ gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
gpointer data)
{
/* TODO: more sensible tag mappings */
struct
static const struct
{
const gchar *matroska_tagname;
const gchar *gstreamer_tagname;
@ -2176,7 +2177,7 @@ gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
tag_conv[] = {
{
GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
@ -2408,8 +2409,29 @@ gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
collect_pad->buffer = gst_collect_pads_pop (mux->collect,
(GstCollectData *) collect_pad);
if (collect_pad->buffer != NULL)
if (collect_pad->buffer != NULL) {
GstClockTime time;
*popped = TRUE;
/* convert to running time */
time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
/* invalid should pass */
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
time = gst_segment_to_running_time (&collect_pad->collect.segment,
GST_FORMAT_TIME, time);
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
GST_PAD_NAME (collect_pad->collect.pad));
gst_buffer_unref (collect_pad->buffer);
collect_pad->buffer = NULL;
return NULL;
} else {
collect_pad->buffer =
gst_buffer_make_metadata_writable (collect_pad->buffer);
GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
}
}
}
}
/* if we have a buffer check if it is better then the current best one */
@ -2771,7 +2793,7 @@ gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
GstEbmlWrite *ebml = mux->ebml_write;
GstMatroskaPad *best;
gboolean popped;
GstFlowReturn ret;
GstFlowReturn ret = GST_FLOW_OK;
GST_DEBUG_OBJECT (mux, "Collected pads");
@ -2795,6 +2817,9 @@ gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
/* if there is no best pad, we have reached EOS */
if (best == NULL) {
/* buffer popped, but none returned means it was clipped */
if (popped)
break;
GST_DEBUG_OBJECT (mux, "No best pad finishing...");
if (!mux->streamable) {
gst_matroska_mux_finish (mux);

View file

@ -2129,6 +2129,35 @@ gst_rtp_jitter_buffer_query (GstPad * pad, GstQuery * query)
}
break;
}
case GST_QUERY_POSITION:
{
GstClockTime start, last_out;
GstFormat fmt;
gst_query_parse_position (query, &fmt, NULL);
if (fmt != GST_FORMAT_TIME) {
res = gst_pad_query_default (pad, query);
break;
}
JBUF_LOCK (priv);
start = priv->npt_start;
last_out = priv->last_out_time;
JBUF_UNLOCK (priv);
GST_DEBUG_OBJECT (jitterbuffer, "npt start %" GST_TIME_FORMAT
", last out %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
GST_TIME_ARGS (last_out));
if (GST_CLOCK_TIME_IS_VALID (start) && GST_CLOCK_TIME_IS_VALID (last_out)) {
/* bring 0-based outgoing time to stream time */
gst_query_set_position (query, GST_FORMAT_TIME, start + last_out);
res = TRUE;
} else {
res = gst_pad_query_default (pad, query);
}
break;
}
default:
res = gst_pad_query_default (pad, query);
break;

View file

@ -1712,6 +1712,33 @@ gst_rtspsrc_connection_receive (GstRTSPSrc * src, GstRTSPConnection * conn,
return ret;
}
static void
gst_rtspsrc_get_position (GstRTSPSrc * src)
{
GstQuery *query;
GList *walk;
query = gst_query_new_position (GST_FORMAT_TIME);
/* should be known somewhere down the stream (e.g. jitterbuffer) */
for (walk = src->streams; walk; walk = g_list_next (walk)) {
GstRTSPStream *stream = (GstRTSPStream *) walk->data;
GstFormat fmt;
gint64 pos;
if (stream->srcpad) {
if (gst_pad_query (stream->srcpad, query)) {
gst_query_parse_position (query, &fmt, &pos);
GST_DEBUG_OBJECT (src, "retaining position %" GST_TIME_FORMAT,
GST_TIME_ARGS (pos));
src->last_pos = pos;
return;
}
}
}
src->last_pos = 0;
}
static gboolean
gst_rtspsrc_do_seek (GstRTSPSrc * src, GstSegment * segment)
{
@ -1804,8 +1831,11 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
playing = (src->state == GST_RTSP_STATE_PLAYING);
/* if we were playing, pause first */
if (playing)
if (playing) {
/* obtain current position in case seek fails */
gst_rtspsrc_get_position (src);
gst_rtspsrc_pause (src, FALSE);
}
gst_rtspsrc_do_seek (src, &seeksegment);
@ -5879,7 +5909,7 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment)
/* NOTE the above also disables npt based eos detection */
/* and below forces position to 0,
* which is visible feedback we lost the plot */
segment->start = segment->last_stop = 0;
segment->start = segment->last_stop = src->last_pos;
}
gst_rtsp_message_unset (&request);

View file

@ -233,7 +233,9 @@ struct _GstRTSPSrc {
/* supported methods */
gint methods;
gboolean seekable;
GstClockTime last_pos;
/* session management */
GstElement *manager;

View file

@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* <2006> Stefan Kost <ensonic@users.sf.net>
* <2006,2011> Stefan Kost <ensonic@users.sf.net>
* <2007-2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
@ -71,7 +71,7 @@
* <classname>&quot;magnitude&quot;</classname>:
* the level for each frequency band in dB. All values below the value of the
* #GstSpectrum:threshold property will be set to the threshold. Only present
* if the message-magnitude property is true.
* if the #GstSpectrum:message-magnitude property is %TRUE.
* </para>
* </listitem>
* <listitem>
@ -79,11 +79,15 @@
* #GstValueList of #gfloat
* <classname>&quot;phase&quot;</classname>:
* The phase for each frequency band. The value is between -pi and pi. Only
* present if the message-phase property is true.
* present if the #GstSpectrum:message-phase property is %TRUE.
* </para>
* </listitem>
* </itemizedlist>
*
* If #GstSpectrum:multi-channel property is set to true. magnitude and phase
* fields will be each a nested #GstValueArray. The first dimension are the
* channels and the second dimension are the values.
*
* <refsect2>
* <title>Example application</title>
* |[
@ -91,7 +95,7 @@
* ]|
* </refsect2>
*
* Last reviewed on 2009-01-14 (0.10.12)
* Last reviewed on 2011-03-10 (0.10.29)
*/
#ifdef HAVE_CONFIG_H
@ -143,6 +147,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_spectrum_debug);
#define DEFAULT_INTERVAL (GST_SECOND / 10)
#define DEFAULT_BANDS 128
#define DEFAULT_THRESHOLD -60
#define DEFAULT_MULTI_CHANNEL FALSE
enum
{
@ -153,7 +158,8 @@ enum
PROP_MESSAGE_PHASE,
PROP_INTERVAL,
PROP_BANDS,
PROP_THRESHOLD
PROP_THRESHOLD,
PROP_MULTI_CHANNEL
};
GST_BOILERPLATE (GstSpectrum, gst_spectrum, GstAudioFilter,
@ -257,6 +263,18 @@ gst_spectrum_class_init (GstSpectrumClass * klass)
G_MININT, 0, DEFAULT_THRESHOLD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstSpectrum:multi-channel
*
* Send separate results for each channel
*
* Since: 0.10.29
*/
g_object_class_install_property (gobject_class, PROP_MULTI_CHANNEL,
g_param_spec_boolean ("multi-channel", "Multichannel results",
"Send separate results for each channel",
DEFAULT_MULTI_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
GST_DEBUG_CATEGORY_INIT (gst_spectrum_debug, "spectrum", 0,
"audio spectrum analyser element");
}
@ -272,6 +290,59 @@ gst_spectrum_init (GstSpectrum * spectrum, GstSpectrumClass * g_class)
spectrum->threshold = DEFAULT_THRESHOLD;
}
static void
gst_spectrum_alloc_channel_data (GstSpectrum * spectrum)
{
gint i;
GstSpectrumChannel *cd;
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
g_assert (spectrum->channel_data == NULL);
spectrum->num_channels = (spectrum->multi_channel) ?
GST_AUDIO_FILTER (spectrum)->format.channels : 1;
GST_DEBUG_OBJECT (spectrum, "allocating data for %d channels",
spectrum->num_channels);
spectrum->channel_data = g_new (GstSpectrumChannel, spectrum->num_channels);
for (i = 0; i < spectrum->num_channels; i++) {
cd = &spectrum->channel_data[i];
cd->fft_ctx = gst_fft_f32_new (nfft, FALSE);
cd->input = g_new0 (gfloat, nfft);
cd->input_tmp = g_new0 (gfloat, nfft);
cd->freqdata = g_new0 (GstFFTF32Complex, bands);
cd->spect_magnitude = g_new0 (gfloat, bands);
cd->spect_phase = g_new0 (gfloat, bands);
}
}
static void
gst_spectrum_free_channel_data (GstSpectrum * spectrum)
{
if (spectrum->channel_data) {
gint i;
GstSpectrumChannel *cd;
GST_DEBUG_OBJECT (spectrum, "freeing data for %d channels",
spectrum->num_channels);
for (i = 0; i < spectrum->num_channels; i++) {
cd = &spectrum->channel_data[i];
if (cd->fft_ctx)
gst_fft_f32_free (cd->fft_ctx);
g_free (cd->input);
g_free (cd->input_tmp);
g_free (cd->freqdata);
g_free (cd->spect_magnitude);
g_free (cd->spect_phase);
}
g_free (spectrum->channel_data);
spectrum->channel_data = NULL;
}
}
static void
gst_spectrum_flush (GstSpectrum * spectrum)
{
@ -286,21 +357,7 @@ gst_spectrum_reset_state (GstSpectrum * spectrum)
{
GST_DEBUG_OBJECT (spectrum, "resetting state");
if (spectrum->fft_ctx)
gst_fft_f32_free (spectrum->fft_ctx);
g_free (spectrum->input);
g_free (spectrum->input_tmp);
g_free (spectrum->freqdata);
g_free (spectrum->spect_magnitude);
g_free (spectrum->spect_phase);
spectrum->fft_ctx = NULL;
spectrum->input = NULL;
spectrum->input_tmp = NULL;
spectrum->freqdata = NULL;
spectrum->spect_magnitude = NULL;
spectrum->spect_phase = NULL;
gst_spectrum_free_channel_data (spectrum);
gst_spectrum_flush (spectrum);
}
@ -335,7 +392,7 @@ gst_spectrum_set_property (GObject * object, guint prop_id,
guint64 interval = g_value_get_uint64 (value);
if (filter->interval != interval) {
GST_BASE_TRANSFORM_LOCK (filter);
filter->interval = g_value_get_uint64 (value);
filter->interval = interval;
gst_spectrum_reset_state (filter);
GST_BASE_TRANSFORM_UNLOCK (filter);
}
@ -354,6 +411,16 @@ gst_spectrum_set_property (GObject * object, guint prop_id,
case PROP_THRESHOLD:
filter->threshold = g_value_get_int (value);
break;
case PROP_MULTI_CHANNEL:{
gboolean multi_channel = g_value_get_boolean (value);
if (filter->multi_channel != multi_channel) {
GST_BASE_TRANSFORM_LOCK (filter);
filter->multi_channel = multi_channel;
gst_spectrum_reset_state (filter);
GST_BASE_TRANSFORM_UNLOCK (filter);
}
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -386,6 +453,9 @@ gst_spectrum_get_property (GObject * object, guint prop_id,
case PROP_THRESHOLD:
g_value_set_int (value, filter->threshold);
break;
case PROP_MULTI_CHANNEL:
g_value_set_boolean (value, filter->multi_channel);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -412,31 +482,388 @@ gst_spectrum_stop (GstBaseTransform * trans)
return TRUE;
}
/* mixing data readers */
static void
input_data_mixed_float (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j, ip = 0;
gfloat v;
gfloat *in = (gfloat *) _in;
for (j = 0; j < len; j++) {
v = in[ip++];
for (i = 1; i < channels; i++)
v += in[ip++];
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
static void
input_data_mixed_double (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j, ip = 0;
gfloat v;
gdouble *in = (gdouble *) _in;
for (j = 0; j < len; j++) {
v = in[ip++];
for (i = 1; i < channels; i++)
v += in[ip++];
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
static void
input_data_mixed_int32 (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j, ip = 0;
gint32 *in = (gint32 *) _in;
gfloat v;
for (j = 0; j < len; j++) {
v = in[ip++] * 2 + 1;
for (i = 1; i < channels; i++)
v += in[ip++] * 2 + 1;
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
static void
input_data_mixed_int32_max (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j, ip = 0;
gint32 *in = (gint32 *) _in;
gfloat v;
for (j = 0; j < len; j++) {
v = in[ip++] / max_value;
for (i = 1; i < channels; i++)
v += in[ip++] / max_value;
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
static void
input_data_mixed_int24 (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j;
gfloat v = 0.0;
for (j = 0; j < len; j++) {
for (i = 0; i < channels; i++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN
gint32 value = GST_READ_UINT24_BE (_in);
#else
gint32 value = GST_READ_UINT24_LE (_in);
#endif
if (value & 0x00800000)
value |= 0xff000000;
v += value * 2 + 1;
_in += 3;
}
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
static void
input_data_mixed_int24_max (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j;
gfloat v = 0.0;
for (j = 0; j < len; j++) {
for (i = 0; i < channels; i++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN
gint32 value = GST_READ_UINT24_BE (_in);
#else
gint32 value = GST_READ_UINT24_LE (_in);
#endif
if (value & 0x00800000)
value |= 0xff000000;
v += value / max_value;
_in += 3;
}
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
static void
input_data_mixed_int16 (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j, ip = 0;
gint16 *in = (gint16 *) _in;
gfloat v;
for (j = 0; j < len; j++) {
v = in[ip++] * 2 + 1;
for (i = 1; i < channels; i++)
v += in[ip++] * 2 + 1;
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
static void
input_data_mixed_int16_max (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint i, j, ip = 0;
gint16 *in = (gint16 *) _in;
gfloat v;
for (j = 0; j < len; j++) {
v = in[ip++] / max_value;
for (i = 1; i < channels; i++)
v += in[ip++] / max_value;
out[op] = v / channels;
op = (op + 1) % nfft;
}
}
/* non mixing data readers */
static void
input_data_float (const guint8 * _in, gfloat * out, guint len, guint channels,
gfloat max_value, guint op, guint nfft)
{
guint j, ip;
gfloat *in = (gfloat *) _in;
for (j = 0, ip = 0; j < len; j++, ip += channels) {
out[op] = in[ip];
op = (op + 1) % nfft;
}
}
static void
input_data_double (const guint8 * _in, gfloat * out, guint len, guint channels,
gfloat max_value, guint op, guint nfft)
{
guint j, ip;
gdouble *in = (gdouble *) _in;
for (j = 0, ip = 0; j < len; j++, ip += channels) {
out[op] = in[ip];
op = (op + 1) % nfft;
}
}
static void
input_data_int32 (const guint8 * _in, gfloat * out, guint len, guint channels,
gfloat max_value, guint op, guint nfft)
{
guint j, ip;
gint32 *in = (gint32 *) _in;
for (j = 0, ip = 0; j < len; j++, ip += channels) {
out[op] = in[ip] * 2 + 1;
op = (op + 1) % nfft;
}
}
static void
input_data_int32_max (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint j, ip;
gint32 *in = (gint32 *) _in;
for (j = 0, ip = 0; j < len; j++, ip += channels) {
out[op] = in[ip] / max_value;
op = (op + 1) % nfft;
}
}
static void
input_data_int24 (const guint8 * _in, gfloat * out, guint len, guint channels,
gfloat max_value, guint op, guint nfft)
{
guint j;
for (j = 0; j < len; j++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN
gint32 v = GST_READ_UINT24_BE (_in);
#else
gint32 v = GST_READ_UINT24_LE (_in);
#endif
if (v & 0x00800000)
v |= 0xff000000;
_in += 3 * channels;
out[op] = v * 2 + 1;
op = (op + 1) % nfft;
}
}
static void
input_data_int24_max (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint j;
for (j = 0; j < len; j++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN
gint32 v = GST_READ_UINT24_BE (_in);
#else
gint32 v = GST_READ_UINT24_LE (_in);
#endif
if (v & 0x00800000)
v |= 0xff000000;
_in += 3 * channels;
out[op] = v / max_value;
op = (op + 1) % nfft;
}
}
static void
input_data_int16 (const guint8 * _in, gfloat * out, guint len, guint channels,
gfloat max_value, guint op, guint nfft)
{
guint j, ip;
gint16 *in = (gint16 *) _in;
for (j = 0, ip = 0; j < len; j++, ip += channels) {
out[op] = in[ip] * 2 + 1;
op = (op + 1) % nfft;
}
}
static void
input_data_int16_max (const guint8 * _in, gfloat * out, guint len,
guint channels, gfloat max_value, guint op, guint nfft)
{
guint j, ip;
gint16 *in = (gint16 *) _in;
for (j = 0, ip = 0; j < len; j++, ip += channels) {
out[op] = in[ip] / max_value;
op = (op + 1) % nfft;
}
}
static gboolean
gst_spectrum_setup (GstAudioFilter * base, GstRingBufferSpec * format)
{
GstSpectrum *filter = GST_SPECTRUM (base);
GstSpectrum *spectrum = GST_SPECTRUM (base);
guint width = format->width / 8;
gboolean is_float = (format->type == GST_BUFTYPE_FLOAT);
/* max_value will be 0 when depth is 1,
* interpret -1 and 0 as -1 and +1 if that's the case. */
guint max_value = (1UL << (format->depth - 1)) - 1;
gboolean multi_channel = spectrum->multi_channel;
GstSpectrumInputData input_data = NULL;
gst_spectrum_reset_state (filter);
if (is_float) {
if (width == 4) {
input_data = multi_channel ? input_data_float : input_data_mixed_float;
} else if (width == 8) {
input_data = multi_channel ? input_data_double : input_data_mixed_double;
} else {
g_assert_not_reached ();
}
} else {
if (width == 4) {
if (max_value) {
input_data =
multi_channel ? input_data_int32_max : input_data_mixed_int32_max;
} else {
input_data = multi_channel ? input_data_int32 : input_data_mixed_int32;
}
} else if (width == 3) {
if (max_value) {
input_data =
multi_channel ? input_data_int24_max : input_data_mixed_int24_max;
} else {
input_data = multi_channel ? input_data_int24 : input_data_mixed_int24;
}
} else if (width == 2) {
if (max_value) {
input_data =
multi_channel ? input_data_int16_max : input_data_mixed_int16_max;
} else {
input_data = multi_channel ? input_data_int16 : input_data_mixed_int16;
}
} else {
g_assert_not_reached ();
}
}
spectrum->input_data = input_data;
gst_spectrum_reset_state (spectrum);
return TRUE;
}
static GValue *
gst_spectrum_message_add_container (GstStructure * s, GType type,
const gchar * name)
{
GValue v = { 0, };
g_value_init (&v, type);
/* will copy-by-value */
gst_structure_set_value (s, name, &v);
g_value_unset (&v);
return (GValue *) gst_structure_get_value (s, name);
}
static void
gst_spectrum_message_add_list (GValue * cv, gfloat * data, guint num_values)
{
GValue v = { 0, };
guint i;
g_value_init (&v, G_TYPE_FLOAT);
for (i = 0; i < num_values; i++) {
g_value_set_float (&v, data[i]);
gst_value_list_append_value (cv, &v); /* copies by value */
}
g_value_unset (&v);
}
static void
gst_spectrum_message_add_array (GValue * cv, gfloat * data, guint num_values)
{
GValue v = { 0, };
GValue a = { 0, };
guint i;
g_value_init (&a, GST_TYPE_ARRAY);
g_value_init (&v, G_TYPE_FLOAT);
for (i = 0; i < num_values; i++) {
g_value_set_float (&v, data[i]);
gst_value_array_append_value (&a, &v); /* copies by value */
}
g_value_unset (&v);
gst_value_array_append_value (cv, &a); /* copies by value */
g_value_unset (&a);
}
static GstMessage *
gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
GstClockTime duration)
{
GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (spectrum);
GstSpectrumChannel *cd;
GstStructure *s;
GValue v = { 0, };
GValue *l;
guint i;
gfloat *spect_magnitude = spectrum->spect_magnitude;
gfloat *spect_phase = spectrum->spect_phase;
GValue *mcv = NULL, *pcv = NULL;
GstClockTime endtime, running_time, stream_time;
GST_DEBUG_OBJECT (spectrum, "preparing message, spect = %p, bands =%d ",
spect_magnitude, spectrum->bands);
GST_DEBUG_OBJECT (spectrum, "preparing message, bands =%d ", spectrum->bands);
running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
timestamp);
@ -452,65 +879,145 @@ gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
"running-time", G_TYPE_UINT64, running_time,
"duration", G_TYPE_UINT64, duration, NULL);
if (spectrum->message_magnitude) {
/* FIXME 0.11: this should be an array, not a list */
g_value_init (&v, GST_TYPE_LIST);
/* will copy-by-value */
gst_structure_set_value (s, "magnitude", &v);
g_value_unset (&v);
if (!spectrum->multi_channel) {
cd = &spectrum->channel_data[0];
g_value_init (&v, G_TYPE_FLOAT);
l = (GValue *) gst_structure_get_value (s, "magnitude");
for (i = 0; i < spectrum->bands; i++) {
g_value_set_float (&v, spect_magnitude[i]);
gst_value_list_append_value (l, &v); /* copies by value */
if (spectrum->message_magnitude) {
/* FIXME 0.11: this should be an array, not a list */
mcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "magnitude");
gst_spectrum_message_add_list (mcv, cd->spect_magnitude, spectrum->bands);
}
if (spectrum->message_phase) {
/* FIXME 0.11: this should be an array, not a list */
pcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "phase");
gst_spectrum_message_add_list (pcv, cd->spect_phase, spectrum->bands);
}
} else {
guint c;
guint channels = GST_AUDIO_FILTER (spectrum)->format.channels;
if (spectrum->message_magnitude) {
mcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "magnitude");
}
if (spectrum->message_phase) {
pcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "phase");
}
for (c = 0; c < channels; c++) {
cd = &spectrum->channel_data[c];
if (spectrum->message_magnitude) {
gst_spectrum_message_add_array (mcv, cd->spect_magnitude,
spectrum->bands);
}
if (spectrum->message_phase) {
gst_spectrum_message_add_array (pcv, cd->spect_magnitude,
spectrum->bands);
}
}
}
return gst_message_new_element (GST_OBJECT (spectrum), s);
}
static void
gst_spectrum_run_fft (GstSpectrum * spectrum, GstSpectrumChannel * cd,
guint input_pos)
{
guint i;
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
gint threshold = spectrum->threshold;
gfloat *input = cd->input;
gfloat *input_tmp = cd->input_tmp;
gfloat *spect_magnitude = cd->spect_magnitude;
gfloat *spect_phase = cd->spect_phase;
GstFFTF32Complex *freqdata = cd->freqdata;
GstFFTF32 *fft_ctx = cd->fft_ctx;
for (i = 0; i < nfft; i++)
input_tmp[i] = input[(input_pos + i) % nfft];
gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
if (spectrum->message_magnitude) {
gdouble val;
/* Calculate magnitude in db */
for (i = 0; i < bands; i++) {
val = freqdata[i].r * freqdata[i].r;
val += freqdata[i].i * freqdata[i].i;
val /= nfft * nfft;
val = 10.0 * log10 (val);
if (val < threshold)
val = threshold;
spect_magnitude[i] += val;
}
g_value_unset (&v);
}
if (spectrum->message_phase) {
/* FIXME 0.11: this should be an array, not a list */
g_value_init (&v, GST_TYPE_LIST);
/* will copy-by-value */
gst_structure_set_value (s, "phase", &v);
g_value_unset (&v);
g_value_init (&v, G_TYPE_FLOAT);
l = (GValue *) gst_structure_get_value (s, "phase");
for (i = 0; i < spectrum->bands; i++) {
g_value_set_float (&v, spect_phase[i]);
gst_value_list_append_value (l, &v); /* copies by value */
}
g_value_unset (&v);
/* Calculate phase */
for (i = 0; i < bands; i++)
spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
}
}
return gst_message_new_element (GST_OBJECT (spectrum), s);
static void
gst_spectrum_prepare_message_data (GstSpectrum * spectrum,
GstSpectrumChannel * cd)
{
guint i;
guint bands = spectrum->bands;
guint num_fft = spectrum->num_fft;
/* Calculate average */
if (spectrum->message_magnitude) {
gfloat *spect_magnitude = cd->spect_magnitude;
for (i = 0; i < bands; i++)
spect_magnitude[i] /= num_fft;
}
if (spectrum->message_phase) {
gfloat *spect_phase = cd->spect_phase;
for (i = 0; i < bands; i++)
spect_phase[i] /= num_fft;
}
}
static void
gst_spectrum_reset_message_data (GstSpectrum * spectrum,
GstSpectrumChannel * cd)
{
guint bands = spectrum->bands;
gfloat *spect_magnitude = cd->spect_magnitude;
gfloat *spect_phase = cd->spect_phase;
/* reset spectrum accumulators */
memset (spect_magnitude, 0, bands * sizeof (gfloat));
memset (spect_phase, 0, bands * sizeof (gfloat));
}
static GstFlowReturn
gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
{
GstSpectrum *spectrum = GST_SPECTRUM (trans);
guint i;
guint rate = GST_AUDIO_FILTER (spectrum)->format.rate;
guint channels = GST_AUDIO_FILTER (spectrum)->format.channels;
gfloat max_value =
(1UL << (GST_AUDIO_FILTER (spectrum)->format.depth - 1)) - 1;
guint width = GST_AUDIO_FILTER (spectrum)->format.width / 8;
gboolean fp = (GST_AUDIO_FILTER (spectrum)->format.type == GST_BUFTYPE_FLOAT);
GstRingBufferSpec *format = &GST_AUDIO_FILTER (spectrum)->format;
guint rate = format->rate;
guint channels = format->channels;
guint output_channels = spectrum->multi_channel ? channels : 1;
guint c;
guint width = format->width / 8;
gfloat max_value = (1UL << (format->depth - 1)) - 1;
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
gint threshold = spectrum->threshold;
guint input_pos;
gfloat *input;
gfloat *input_tmp;
GstFFTF32Complex *freqdata;
gfloat *spect_magnitude;
gfloat *spect_phase;
GstFFTF32 *fft_ctx;
const guint8 *data = GST_BUFFER_DATA (buffer);
guint size = GST_BUFFER_SIZE (buffer);
guint frame_size = width * channels;
guint fft_todo, msg_todo, block_size;
gboolean have_full_interval;
GstSpectrumChannel *cd;
GstSpectrumInputData input_data;
GST_LOG_OBJECT (spectrum, "input size: %d bytes", GST_BUFFER_SIZE (buffer));
@ -522,22 +1029,27 @@ gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
/* If we don't have a FFT context yet (or it was reset due to parameter
* changes) get one and allocate memory for everything
*/
if (spectrum->fft_ctx == NULL) {
if (spectrum->channel_data == NULL) {
GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
spectrum->fft_ctx = gst_fft_f32_new (nfft, FALSE);
spectrum->input = g_new0 (gfloat, nfft);
spectrum->input_tmp = g_new0 (gfloat, nfft);
spectrum->freqdata = g_new0 (GstFFTF32Complex, bands);
spectrum->spect_magnitude = g_new0 (gfloat, bands);
spectrum->spect_phase = g_new0 (gfloat, bands);
gst_spectrum_alloc_channel_data (spectrum);
/* number of sample frames we process before posting a message
* interval is in ns */
spectrum->frames_per_interval =
gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND);
spectrum->frames_todo = spectrum->frames_per_interval;
/* rounding error for frames_per_interval in ns,
* aggregated it in accumulated_error */
spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
if (spectrum->frames_per_interval == 0)
spectrum->frames_per_interval = 1;
GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval,
GST_TIME_ARGS (spectrum->error_per_interval));
spectrum->input_pos = 0;
gst_spectrum_flush (spectrum);
@ -546,124 +1058,70 @@ gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
if (spectrum->num_frames == 0)
spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
input = spectrum->input;
input_tmp = spectrum->input_tmp;
freqdata = spectrum->freqdata;
spect_magnitude = spectrum->spect_magnitude;
spect_phase = spectrum->spect_phase;
fft_ctx = spectrum->fft_ctx;
input_pos = spectrum->input_pos;
input_data = spectrum->input_data;
while (size >= width * channels) {
while (size >= frame_size) {
/* run input_data for a chunk of data */
fft_todo = nfft - (spectrum->num_frames % nfft);
msg_todo = spectrum->frames_todo - spectrum->num_frames;
GST_LOG_OBJECT (spectrum,
"message frames todo: %u, fft frames todo: %u, input frames %u",
msg_todo, fft_todo, (size / frame_size));
block_size = msg_todo;
if (block_size > (size / frame_size))
block_size = (size / frame_size);
if (block_size > fft_todo)
block_size = fft_todo;
/* Move the current frame into our ringbuffer and
* take the average of all channels
*/
input[input_pos] = 0.0;
if (fp && width == 4) {
gfloat *in = (gfloat *) data;
for (i = 0; i < channels; i++)
input[input_pos] += in[i];
} else if (fp && width == 8) {
gdouble *in = (gdouble *) data;
for (i = 0; i < channels; i++)
input[input_pos] += in[i];
} else if (!fp && width == 4) {
gint32 *in = (gint32 *) data;
for (i = 0; i < channels; i++)
/* max_value will be 0 when depth is 1, interpret -1 and 0
* as -1 and +1 if that's the case.
*/
input[input_pos] += max_value ? in[i] / max_value : in[i] * 2 + 1;
} else if (!fp && width == 3) {
for (i = 0; i < channels; i++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN
gint32 value = GST_READ_UINT24_BE (data);
#else
gint32 value = GST_READ_UINT24_LE (data);
#endif
if (value & 0x00800000)
value |= 0xff000000;
input[input_pos] += max_value ? value / max_value : value * 2 + 1;
}
} else if (!fp && width == 2) {
gint16 *in = (gint16 *) data;
for (i = 0; i < channels; i++)
input[input_pos] += max_value ? in[i] / max_value : in[i] * 2 + 1;
} else {
g_assert_not_reached ();
for (c = 0; c < output_channels; c++) {
cd = &spectrum->channel_data[c];
input = cd->input;
/* Move the current frames into our ringbuffers */
input_data (data + c * width, input, block_size, channels, max_value,
input_pos, nfft);
}
input[input_pos] /= channels;
data += block_size * frame_size;
size -= block_size * frame_size;
input_pos = (input_pos + block_size) % nfft;
spectrum->num_frames += block_size;
data += width * channels;
size -= width * channels;
input_pos = (input_pos + 1) % nfft;
spectrum->num_frames++;
have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
have_full_interval = (
(spectrum->accumulated_error < GST_SECOND
&& spectrum->num_frames == spectrum->frames_per_interval) ||
(spectrum->accumulated_error >= GST_SECOND
&& spectrum->num_frames - 1 == spectrum->frames_per_interval)
);
GST_LOG_OBJECT (spectrum, "size: %u, do-fft = %d, do-message = %d", size,
(spectrum->num_frames % nfft == 0), have_full_interval);
/* If we have enough frames for an FFT or we
* have all frames required for the interval run
* an FFT. In the last case we probably take the
* FFT of frames that we already handled.
*/
if ((spectrum->num_frames % nfft == 0) || have_full_interval) {
for (i = 0; i < nfft; i++)
input_tmp[i] = input[(input_pos + i) % nfft];
gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
/* If we have enough frames for an FFT or we have all frames required for
* the interval and we haven't run a FFT, then run an FFT */
if ((spectrum->num_frames % nfft == 0) ||
(have_full_interval && !spectrum->num_fft)) {
for (c = 0; c < output_channels; c++) {
cd = &spectrum->channel_data[c];
gst_spectrum_run_fft (spectrum, cd, input_pos);
}
spectrum->num_fft++;
if (spectrum->message_magnitude) {
gdouble val;
/* Calculate magnitude in db */
for (i = 0; i < bands; i++) {
val = freqdata[i].r * freqdata[i].r;
val += freqdata[i].i * freqdata[i].i;
val /= nfft * nfft;
val = 10.0 * log10 (val);
if (val < threshold)
val = threshold;
spect_magnitude[i] += val;
}
}
if (spectrum->message_phase) {
/* Calculate phase */
for (i = 0; i < bands; i++)
spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
}
}
/* Do we have the FFTs for one interval? */
if (have_full_interval) {
GST_INFO ("nfft: %u num_frames: %" G_GUINT64_FORMAT " fpi: %"
G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT
" fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
spectrum->num_frames, spectrum->frames_per_interval,
GST_TIME_ARGS (spectrum->accumulated_error));
if (spectrum->accumulated_error >= GST_SECOND)
spectrum->frames_todo = spectrum->frames_per_interval;
if (spectrum->accumulated_error >= GST_SECOND) {
spectrum->accumulated_error -= GST_SECOND;
else
spectrum->accumulated_error += spectrum->error_per_interval;
spectrum->frames_todo++;
}
spectrum->accumulated_error += spectrum->error_per_interval;
if (spectrum->post_messages) {
GstMessage *m;
/* Calculate average */
for (i = 0; i < bands; i++) {
spect_magnitude[i] /= spectrum->num_fft;
spect_phase[i] /= spectrum->num_fft;
for (c = 0; c < output_channels; c++) {
cd = &spectrum->channel_data[c];
gst_spectrum_prepare_message_data (spectrum, cd);
}
m = gst_spectrum_message_new (spectrum, spectrum->message_ts,
@ -676,9 +1134,10 @@ gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
spectrum->message_ts +=
gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
/* reset spectrum accumulators */
memset (spect_magnitude, 0, bands * sizeof (gfloat));
memset (spect_phase, 0, bands * sizeof (gfloat));
for (c = 0; c < channels; c++) {
cd = &spectrum->channel_data[c];
gst_spectrum_reset_message_data (spectrum, cd);
}
spectrum->num_frames = 0;
spectrum->num_fft = 0;
}

View file

@ -35,6 +35,20 @@ G_BEGIN_DECLS
#define GST_IS_SPECTRUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_SPECTRUM))
typedef struct _GstSpectrum GstSpectrum;
typedef struct _GstSpectrumClass GstSpectrumClass;
typedef struct _GstSpectrumChannel GstSpectrumChannel;
typedef void (*GstSpectrumInputData)(const guint8 * in, gfloat * out,
guint len, guint channels, gfloat max_value, guint op, guint nfft);
struct _GstSpectrumChannel
{
gfloat *input;
gfloat *input_tmp;
GstFFTF32Complex *freqdata;
gfloat *spect_magnitude; /* accumulated mangitude and phase */
gfloat *spect_phase; /* will be scaled by num_fft before sending */
GstFFTF32 *fft_ctx;
};
struct _GstSpectrum
{
@ -46,8 +60,10 @@ struct _GstSpectrum
gboolean message_phase;
guint64 interval; /* how many nanoseconds between emits */
guint64 frames_per_interval; /* how many frames per interval */
guint64 frames_todo;
guint bands; /* number of spectrum bands */
gint threshold; /* energy level treshold */
gboolean multi_channel; /* send separate channel results */
guint64 num_frames; /* frame count (1 sample per channel)
* since last emit */
@ -55,16 +71,14 @@ struct _GstSpectrum
GstClockTime message_ts; /* starttime for next message */
/* <private> */
gfloat *input;
guint input_pos;
gfloat *input_tmp;
GstFFTF32Complex *freqdata;
gfloat *spect_magnitude; /* accumulated mangitude and phase */
gfloat *spect_phase; /* will be scaled by num_fft before sending */
GstFFTF32 *fft_ctx;
GstSpectrumChannel *channel_data;
guint num_channels;
guint input_pos;
guint64 error_per_interval;
guint64 accumulated_error;
GstSpectrumInputData input_data;
};
struct _GstSpectrumClass

View file

@ -2,6 +2,7 @@
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2003> David Schleef <ds@schleef.org>
* Copyright (C) <2010> Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Copyright (C) <2011> Youness Alaoui <youness.alaoui@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -75,7 +76,10 @@ static GstStaticPadTemplate gst_video_flip_src_template =
GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
GST_VIDEO_CAPS_YUV ("I420") ";"
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV")
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV") ";"
GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
GST_VIDEO_CAPS_YUV ("YVYU")
)
);
@ -91,7 +95,9 @@ static GstStaticPadTemplate gst_video_flip_sink_template =
GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
GST_VIDEO_CAPS_YUV ("I420") ";"
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV")
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV") ";"
GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
GST_VIDEO_CAPS_YUV ("YVYU")
)
);
@ -563,6 +569,225 @@ gst_video_flip_packed_simple (GstVideoFlip * videoflip, guint8 * dest,
}
}
static void
gst_video_flip_y422 (GstVideoFlip * videoflip, guint8 * dest,
const guint8 * src)
{
gint x, y;
guint8 const *s = src;
guint8 *d = dest;
GstVideoFormat format = videoflip->format;
gint sw = videoflip->from_width;
gint sh = videoflip->from_height;
gint dw = videoflip->to_width;
gint dh = videoflip->to_height;
gint src_stride, dest_stride;
gint bpp;
gint y_offset;
gint u_offset;
gint v_offset;
gint y_stride;
gint u_stride;
gint v_stride;
src_stride = gst_video_format_get_row_stride (format, 0, sw);
dest_stride = gst_video_format_get_row_stride (format, 0, dw);
y_offset = gst_video_format_get_component_offset (format, 0, sw, sh);
u_offset = gst_video_format_get_component_offset (format, 1, sw, sh);
v_offset = gst_video_format_get_component_offset (format, 2, sw, sh);
y_stride = gst_video_format_get_pixel_stride (format, 0);
u_stride = gst_video_format_get_pixel_stride (format, 1);
v_stride = gst_video_format_get_pixel_stride (format, 2);
bpp = y_stride;
switch (videoflip->method) {
case GST_VIDEO_FLIP_METHOD_90R:
for (y = 0; y < dh; y++) {
for (x = 0; x < dw; x += 2) {
guint8 u;
guint8 v;
/* u/v must be calculated using the offset of the even column */
gint even_y = (y & ~1);
u = s[(sh - 1 - x) * src_stride + even_y * bpp + u_offset];
if (x + 1 < dw)
u = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + u_offset]
+ u) >> 1;
v = s[(sh - 1 - x) * src_stride + even_y * bpp + v_offset];
if (x + 1 < dw)
v = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + v_offset]
+ v) >> 1;
d[y * dest_stride + x * bpp + u_offset] = u;
d[y * dest_stride + x * bpp + v_offset] = v;
d[y * dest_stride + x * bpp + y_offset] =
s[(sh - 1 - x) * src_stride + y * bpp + y_offset];
if (x + 1 < dw)
d[y * dest_stride + (x + 1) * bpp + y_offset] =
s[(sh - 1 - (x + 1)) * src_stride + y * bpp + y_offset];
}
}
break;
case GST_VIDEO_FLIP_METHOD_90L:
for (y = 0; y < dh; y++) {
for (x = 0; x < dw; x += 2) {
guint8 u;
guint8 v;
/* u/v must be calculated using the offset of the even column */
gint even_y = ((sw - 1 - y) & ~1);
u = s[x * src_stride + even_y * bpp + u_offset];
if (x + 1 < dw)
u = (s[(x + 1) * src_stride + even_y * bpp + u_offset] + u) >> 1;
v = s[x * src_stride + even_y * bpp + v_offset];
if (x + 1 < dw)
v = (s[(x + 1) * src_stride + even_y * bpp + v_offset] + v) >> 1;
d[y * dest_stride + x * bpp + u_offset] = u;
d[y * dest_stride + x * bpp + v_offset] = v;
d[y * dest_stride + x * bpp + y_offset] =
s[x * src_stride + (sw - 1 - y) * bpp + y_offset];
if (x + 1 < dw)
d[y * dest_stride + (x + 1) * bpp + y_offset] =
s[(x + 1) * src_stride + (sw - 1 - y) * bpp + y_offset];
}
}
break;
case GST_VIDEO_FLIP_METHOD_180:
for (y = 0; y < dh; y++) {
for (x = 0; x < dw; x += 2) {
guint8 u;
guint8 v;
/* u/v must be calculated using the offset of the even column */
gint even_x = ((sw - 1 - x) & ~1);
u = (s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset] +
s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset]) / 2;
v = (s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset] +
s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset]) / 2;
d[y * dest_stride + x * bpp + u_offset] = u;
d[y * dest_stride + x * bpp + v_offset] = v;
d[y * dest_stride + x * bpp + y_offset] =
s[(sh - 1 - y) * src_stride + (sw - 1 - x) * bpp + y_offset];
if (x + 1 < dw)
d[y * dest_stride + (x + 1) * bpp + y_offset] =
s[(sh - 1 - y) * src_stride + (sw - 1 - (x + 1)) * bpp +
y_offset];
}
}
break;
case GST_VIDEO_FLIP_METHOD_HORIZ:
for (y = 0; y < dh; y++) {
for (x = 0; x < dw; x += 2) {
guint8 u;
guint8 v;
/* u/v must be calculated using the offset of the even column */
gint even_x = ((sw - 1 - x) & ~1);
u = (s[y * src_stride + even_x * bpp + u_offset] +
s[y * src_stride + even_x * bpp + u_offset]) / 2;
v = (s[y * src_stride + even_x * bpp + v_offset] +
s[y * src_stride + even_x * bpp + v_offset]) / 2;
d[y * dest_stride + x * bpp + u_offset] = u;
d[y * dest_stride + x * bpp + v_offset] = v;
d[y * dest_stride + x * bpp + y_offset] =
s[y * src_stride + (sw - 1 - x) * bpp + y_offset];
if (x + 1 < dw)
d[y * dest_stride + (x + 1) * bpp + y_offset] =
s[y * src_stride + (sw - 1 - (x + 1)) * bpp + y_offset];
}
}
break;
case GST_VIDEO_FLIP_METHOD_VERT:
for (y = 0; y < dh; y++) {
for (x = 0; x < dw; x += 2) {
guint8 u;
guint8 v;
/* u/v must be calculated using the offset of the even column */
gint even_x = (x & ~1);
u = (s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset] +
s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset]) / 2;
v = (s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset] +
s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset]) / 2;
d[y * dest_stride + x * bpp + u_offset] = u;
d[y * dest_stride + x * bpp + v_offset] = v;
d[y * dest_stride + x * bpp + y_offset] =
s[(sh - 1 - y) * src_stride + x * bpp + y_offset];
if (x + 1 < dw)
d[y * dest_stride + (x + 1) * bpp + y_offset] =
s[(sh - 1 - y) * src_stride + (x + 1) * bpp + y_offset];
}
}
break;
case GST_VIDEO_FLIP_METHOD_TRANS:
for (y = 0; y < dh; y++) {
for (x = 0; x < dw; x += 2) {
guint8 u;
guint8 v;
/* u/v must be calculated using the offset of the even column */
gint even_y = (y & ~1);
u = s[x * src_stride + even_y * bpp + u_offset];
if (x + 1 < dw)
u = (s[(x + 1) * src_stride + even_y * bpp + u_offset] + u) >> 1;
v = s[x * src_stride + even_y * bpp + v_offset];
if (x + 1 < dw)
v = (s[(x + 1) * src_stride + even_y * bpp + v_offset] + v) >> 1;
d[y * dest_stride + x * bpp + u_offset] = u;
d[y * dest_stride + x * bpp + v_offset] = v;
d[y * dest_stride + x * bpp + y_offset] =
s[x * src_stride + y * bpp + y_offset];
if (x + 1 < dw)
d[y * dest_stride + (x + 1) * bpp + y_offset] =
s[(x + 1) * src_stride + y * bpp + y_offset];
}
}
break;
case GST_VIDEO_FLIP_METHOD_OTHER:
for (y = 0; y < dh; y++) {
for (x = 0; x < dw; x += 2) {
guint8 u;
guint8 v;
/* u/v must be calculated using the offset of the even column */
gint even_y = ((sw - 1 - y) & ~1);
u = s[(sh - 1 - x) * src_stride + even_y * bpp + u_offset];
if (x + 1 < dw)
u = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + u_offset]
+ u) >> 1;
v = s[(sh - 1 - x) * src_stride + even_y * bpp + v_offset];
if (x + 1 < dw)
v = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + v_offset]
+ v) >> 1;
d[y * dest_stride + x * bpp + u_offset] = u;
d[y * dest_stride + x * bpp + v_offset] = v;
d[y * dest_stride + x * bpp + y_offset] =
s[(sh - 1 - x) * src_stride + (sw - 1 - y) * bpp + y_offset];
if (x + 1 < dw)
d[y * dest_stride + (x + 1) * bpp + y_offset] =
s[(sh - 1 - (x + 1)) * src_stride + (sw - 1 - y) * bpp +
y_offset];
}
}
break;
case GST_VIDEO_FLIP_METHOD_IDENTITY:
g_assert_not_reached ();
break;
default:
g_assert_not_reached ();
break;
}
}
static gboolean
gst_video_flip_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
GstCaps * outcaps)
@ -624,6 +849,11 @@ gst_video_flip_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
case GST_VIDEO_FORMAT_Y444:
vf->process = gst_video_flip_planar_yuv;
break;
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_UYVY:
case GST_VIDEO_FORMAT_YVYU:
vf->process = gst_video_flip_y422;
break;
case GST_VIDEO_FORMAT_AYUV:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:

View file

@ -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 \

View file

@ -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", */

633
sys/v4l2/gstv4l2radio.c Normal file
View file

@ -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.
*
* <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 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;
}

68
sys/v4l2/gstv4l2radio.h Normal file
View file

@ -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__ */

View file

@ -13,8 +13,9 @@ TESTS_ENVIRONMENT = \
GST_PLUGIN_LOADING_WHITELIST="gstreamer@$(GST_PLUGINS_DIR):gst-plugins-base@$(GSTPB_PLUGINS_DIR):gst-plugins-good@$(top_builddir)" \
GST_STATE_IGNORE_ELEMENTS="aasink autoaudiosrc autoaudiosink autovideosrc autovideosink \
cacasink cairotextoverlay gconfaudiosrc gconfvideosrc gconfaudiosink gconfvideosink \
halaudiosrc halaudiosink v4l2src osssrc osssink pulsesink pulsesrc pulsemixer \
osxaudiosink osxaudiosrc osxvideosrc osxvideosink"
halaudiosrc halaudiosink jackaudiosrc jackaudiosink \
osssrc osssink osxaudiosink osxaudiosrc osxvideosrc osxvideosink \
pulsesink pulsesrc pulsemixer v4l2src"
# fake device drivers: we could run hardware element tests against dummy drivers
# v4l2: vivo (part of normal kernel)
@ -39,7 +40,7 @@ check_annodex = \
elements/cmmldec \
elements/cmmlenc
else
check_annodex =
check_annodex =
endif
if USE_FLAC
@ -51,7 +52,7 @@ endif
if USE_GDK_PIXBUF
check_gdkpixbuf = elements/gdkpixbufsink
else
check_gdkpixbuf =
check_gdkpixbuf =
endif
if USE_JPEG
@ -69,7 +70,7 @@ endif
if USE_SUNAUDIO
check_sunaudio = elements/sunaudio
else
check_sunaudio =
check_sunaudio =
endif
if USE_TAGLIB

View file

@ -36,6 +36,9 @@ GstPad *mysrcpad, *mysinkpad;
#define VIDEO_CAPS_TEMPLATE_STRING \
GST_VIDEO_CAPS_YUV ("I420") ";" \
GST_VIDEO_CAPS_YUV ("AYUV") ";" \
GST_VIDEO_CAPS_YUV ("YUY2") ";" \
GST_VIDEO_CAPS_YUV ("UYVY") ";" \
GST_VIDEO_CAPS_YUV ("YVYU") ";" \
GST_VIDEO_CAPS_xRGB
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
@ -129,7 +132,14 @@ check_filter_caps (const gchar * name, GstCaps * caps, gint size,
static void
check_filter (const gchar * name, gint num_buffers, const gchar * prop, ...)
{
gint i, n;
static const struct
{
const int width, height;
} resolutions[] = { {
384, 288}, {
385, 289}, {
385, 385}};
gint i, n, r;
GstVideoFormat format;
gint size;
GstCaps *templ = gst_caps_from_string (VIDEO_CAPS_TEMPLATE_STRING);
@ -142,16 +152,23 @@ check_filter (const gchar * name, gint num_buffers, const gchar * prop, ...)
GstCaps *caps = gst_caps_new_empty ();
gst_caps_append_structure (caps, gst_structure_copy (s));
gst_caps_set_simple (caps, "width", G_TYPE_INT, 384, "height", G_TYPE_INT,
288, "framerate", GST_TYPE_FRACTION, 25, 1, NULL);
GST_DEBUG ("Testing with caps: %" GST_PTR_FORMAT, caps);
/* try various resolutions */
for (r = 0; r < G_N_ELEMENTS (resolutions); ++r) {
caps = gst_caps_make_writable (caps);
gst_caps_set_simple (caps, "width", G_TYPE_INT, resolutions[r].width,
"height", G_TYPE_INT, resolutions[r].height,
"framerate", GST_TYPE_FRACTION, 25, 1, NULL);
gst_video_format_parse_caps (caps, &format, NULL, NULL);
size = gst_video_format_get_size (format, 384, 288);
va_start (varargs, prop);
check_filter_caps (name, caps, size, num_buffers, prop, varargs);
va_end (varargs);
GST_DEBUG ("Testing with caps: %" GST_PTR_FORMAT, caps);
gst_video_format_parse_caps (caps, &format, NULL, NULL);
size = gst_video_format_get_size (format, resolutions[r].width,
resolutions[r].height);
va_start (varargs, prop);
check_filter_caps (name, caps, size, num_buffers, prop, varargs);
va_end (varargs);
}
gst_caps_unref (caps);
}
@ -189,36 +206,14 @@ GST_END_TEST;
static Suite *
videobalance_suite (void)
videofilter_suite (void)
{
Suite *s = suite_create ("videobalance");
Suite *s = suite_create ("videofilter");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_videobalance);
return s;
}
static Suite *
videoflip_suite (void)
{
Suite *s = suite_create ("videoflip");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_videoflip);
return s;
}
static Suite *
gamma_suite (void)
{
Suite *s = suite_create ("gamma");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_gamma);
return s;
@ -229,12 +224,9 @@ main (int argc, char **argv)
{
int nf;
Suite *s = videobalance_suite ();
Suite *s = videofilter_suite ();
SRunner *sr = srunner_create (s);
srunner_add_suite (sr, videoflip_suite ());
srunner_add_suite (sr, gamma_suite ());
gst_check_init (&argc, &argv);
srunner_run_all (sr, CK_NORMAL);