diff --git a/autogen.sh b/autogen.sh
index 884e9fb680..1fcf9f2827 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -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"
diff --git a/common b/common
index 6aec6b9716..1ccbe098d6 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 6aec6b9716c184c60c4bc6a5916a2471cfa8c8cd
+Subproject commit 1ccbe098d6379612fcef09f4000da23585af980a
diff --git a/configure.ac b/configure.ac
index f429d6ba20..7dc3ff3db0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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)
diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am
index f620de147d..491034979a 100644
--- a/docs/plugins/Makefile.am
+++ b/docs/plugins/Makefile.am
@@ -229,6 +229,7 @@ EXTRA_HFILES = \
$(top_srcdir)/sys/osxvideo/osxvideosink.h \
$(top_srcdir)/sys/v4l2/gstv4l2src.h \
$(top_srcdir)/sys/v4l2/gstv4l2sink.h \
+ $(top_srcdir)/sys/v4l2/gstv4l2radio.h \
$(top_srcdir)/sys/waveform/gstwaveformsink.h \
$(top_srcdir)/sys/ximage/gstximagesrc.h
diff --git a/docs/plugins/gst-plugins-good-plugins-docs.sgml b/docs/plugins/gst-plugins-good-plugins-docs.sgml
index 459a064c83..988bba3417 100644
--- a/docs/plugins/gst-plugins-good-plugins-docs.sgml
+++ b/docs/plugins/gst-plugins-good-plugins-docs.sgml
@@ -154,6 +154,7 @@
+
diff --git a/docs/plugins/gst-plugins-good-plugins-sections.txt b/docs/plugins/gst-plugins-good-plugins-sections.txt
index 6081c67633..b2e6de68cf 100644
--- a/docs/plugins/gst-plugins-good-plugins-sections.txt
+++ b/docs/plugins/gst-plugins-good-plugins-sections.txt
@@ -2048,6 +2048,20 @@ GST_IS_V4L2SINK_CLASS
gst_v4l2sink_get_type
+
+element-v4l2radio
+v4l2radio
+GstV4l2Radio
+
+GstV4l2RadioClass
+GST_V4L2RADIO
+GST_IS_V4L2RADIO
+GST_TYPE_V4L2RADIO
+GST_V4L2RADIO_CLASS
+GST_IS_V4L2RADIO_CLASS
+gst_v4l2radio_get_type
+
+
element-waveformsink
waveformsink
diff --git a/docs/plugins/inspect/plugin-video4linux2.xml b/docs/plugins/inspect/plugin-video4linux2.xml
index 5204cc02fa..8349f913d3 100644
--- a/docs/plugins/inspect/plugin-video4linux2.xml
+++ b/docs/plugins/inspect/plugin-video4linux2.xml
@@ -24,5 +24,12 @@
+
+ v4l2radio
+ Radio (video4linux2) Tuner
+ Tuner
+ Controls a Video4Linux2 radio device
+ Alexey Chernov <4ernov@gmail.com>
+
-
\ No newline at end of file
+
diff --git a/ext/cairo/Makefile.am b/ext/cairo/Makefile.am
index b9d97e6246..5a8d3be8f0 100644
--- a/ext/cairo/Makefile.am
+++ b/ext/cairo/Makefile.am
@@ -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
diff --git a/ext/dv/gstdvdemux.c b/ext/dv/gstdvdemux.c
index c1ecd9261c..90a26511bc 100644
--- a/ext/dv/gstdvdemux.c
+++ b/ext/dv/gstdvdemux.c
@@ -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;
}
diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c
index 4620bce712..13f69b7365 100644
--- a/ext/jack/gstjackaudiosink.c
+++ b/ext/jack/gstjackaudiosink.c
@@ -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);
diff --git a/ext/jack/gstjackaudiosrc.c b/ext/jack/gstjackaudiosrc.c
index 08b325ead7..b4840574a3 100644
--- a/ext/jack/gstjackaudiosrc.c
+++ b/ext/jack/gstjackaudiosrc.c
@@ -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);
diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c
index 59e14058d1..5a60cda0e6 100644
--- a/ext/pulse/pulsesink.c
+++ b/ext/pulse/pulsesink.c
@@ -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".
*
*/
@@ -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;
diff --git a/ext/speex/gstspeexdec.c b/ext/speex/gstspeexdec.c
index ef2300fede..46f774b8f9 100644
--- a/ext/speex/gstspeexdec.c
+++ b/ext/speex/gstspeexdec.c
@@ -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;
}
}
diff --git a/ext/speex/gstspeexdec.h b/ext/speex/gstspeexdec.h
index 6419a6ecf5..660d8053df 100644
--- a/ext/speex/gstspeexdec.h
+++ b/ext/speex/gstspeexdec.h
@@ -68,6 +68,9 @@ struct _GstSpeexDec {
guint64 packetno;
GstSegment segment; /* STREAM LOCK */
+
+ GstBuffer *streamheader;
+ GstBuffer *vorbiscomment;
};
struct _GstSpeexDecClass {
diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c
index d0d2f0ecc6..37f9291377 100644
--- a/gst/avi/gstavimux.c
+++ b/gst/avi/gstavimux.c
@@ -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,
diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c
index 87ebf0e869..79f83bc2b8 100644
--- a/gst/flv/gstflvdemux.c
+++ b/gst/flv/gstflvdemux.c
@@ -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;
diff --git a/gst/flv/gstflvdemux.h b/gst/flv/gstflvdemux.h
index dadff9428e..10ff306a2b 100644
--- a/gst/flv/gstflvdemux.h
+++ b/gst/flv/gstflvdemux.h
@@ -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;
diff --git a/gst/flv/gstflvmux.c b/gst/flv/gstflvmux.c
index 68a0df4fc7..c128d34132 100644
--- a/gst/flv/gstflvmux.c
+++ b/gst/flv/gstflvmux.c
@@ -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)) {
diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c
index 401554b435..aa714f6a0b 100644
--- a/gst/matroska/matroska-demux.c
+++ b/gst/matroska/matroska-demux.c
@@ -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}, {
diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h
index 3bb0610fe6..9027add1b3 100644
--- a/gst/matroska/matroska-ids.h
+++ b/gst/matroska/matroska-ids.h
@@ -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"
diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c
index 31fe40bfc3..4b7fdaf7f5 100644
--- a/gst/matroska/matroska-mux.c
+++ b/gst/matroska/matroska-mux.c
@@ -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);
diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c
index 9f08203221..1d253eb829 100644
--- a/gst/rtpmanager/gstrtpjitterbuffer.c
+++ b/gst/rtpmanager/gstrtpjitterbuffer.c
@@ -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;
diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c
index b78a8fa622..96c4d2ee65 100644
--- a/gst/rtsp/gstrtspsrc.c
+++ b/gst/rtsp/gstrtspsrc.c
@@ -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);
diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h
index 00861b6da9..460f29a0f5 100644
--- a/gst/rtsp/gstrtspsrc.h
+++ b/gst/rtsp/gstrtspsrc.h
@@ -233,7 +233,9 @@ struct _GstRTSPSrc {
/* supported methods */
gint methods;
+
gboolean seekable;
+ GstClockTime last_pos;
/* session management */
GstElement *manager;
diff --git a/gst/spectrum/gstspectrum.c b/gst/spectrum/gstspectrum.c
index 9e82829aef..bc4fca45c5 100644
--- a/gst/spectrum/gstspectrum.c
+++ b/gst/spectrum/gstspectrum.c
@@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen
- * <2006> Stefan Kost
+ * <2006,2011> Stefan Kost
* <2007-2009> Sebastian Dröge
*
* This library is free software; you can redistribute it and/or
@@ -71,7 +71,7 @@
* "magnitude":
* 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.
*
*
*
@@ -79,11 +79,15 @@
* #GstValueList of #gfloat
* "phase":
* 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.
*
*
*
*
+ * 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.
+ *
*
* Example application
* |[
@@ -91,7 +95,7 @@
* ]|
*
*
- * 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;
}
diff --git a/gst/spectrum/gstspectrum.h b/gst/spectrum/gstspectrum.h
index d172f6cee3..416072ffe8 100644
--- a/gst/spectrum/gstspectrum.h
+++ b/gst/spectrum/gstspectrum.h
@@ -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 */
/* */
- 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
diff --git a/gst/videofilter/gstvideoflip.c b/gst/videofilter/gstvideoflip.c
index 150017b3e6..a08d64f157 100644
--- a/gst/videofilter/gstvideoflip.c
+++ b/gst/videofilter/gstvideoflip.c
@@ -2,6 +2,7 @@
* Copyright (C) <1999> Erik Walthinsen
* Copyright (C) <2003> David Schleef
* Copyright (C) <2010> Sebastian Dröge
+ * Copyright (C) <2011> Youness Alaoui
*
* 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:
diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am
index ab996a15a8..a7a99dea31 100644
--- a/sys/v4l2/Makefile.am
+++ b/sys/v4l2/Makefile.am
@@ -13,6 +13,7 @@ libgstvideo4linux2_la_SOURCES = gstv4l2.c \
gstv4l2object.c \
gstv4l2bufferpool.c \
gstv4l2src.c \
+ gstv4l2radio.c \
gstv4l2tuner.c \
gstv4l2vidorient.c \
v4l2_calls.c \
@@ -51,6 +52,7 @@ noinst_HEADERS = \
gstv4l2object.h \
gstv4l2sink.h \
gstv4l2src.h \
+ gstv4l2radio.h \
gstv4l2tuner.h \
gstv4l2vidorient.h \
gstv4l2xoverlay.h \
diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c
index 4a7056fd52..95f64db785 100644
--- a/sys/v4l2/gstv4l2.c
+++ b/sys/v4l2/gstv4l2.c
@@ -35,6 +35,7 @@
#ifdef HAVE_EXPERIMENTAL
#include "gstv4l2sink.h"
#endif
+#include "gstv4l2radio.h"
/* #include "gstv4l2jpegsrc.h" */
/* #include "gstv4l2mjpegsrc.h" */
/* #include "gstv4l2mjpegsink.h" */
@@ -58,6 +59,8 @@ plugin_init (GstPlugin * plugin)
!gst_element_register (plugin, "v4l2sink", GST_RANK_NONE,
GST_TYPE_V4L2SINK) ||
#endif
+ !gst_element_register (plugin, "v4l2radio", GST_RANK_NONE,
+ GST_TYPE_V4L2RADIO) ||
/* !gst_element_register (plugin, "v4l2jpegsrc", */
/* GST_RANK_NONE, GST_TYPE_V4L2JPEGSRC) || */
/* !gst_element_register (plugin, "v4l2mjpegsrc", */
diff --git a/sys/v4l2/gstv4l2radio.c b/sys/v4l2/gstv4l2radio.c
new file mode 100644
index 0000000000..b1699d3814
--- /dev/null
+++ b/sys/v4l2/gstv4l2radio.c
@@ -0,0 +1,633 @@
+/* GStreamer v4l2 radio tuner element
+ * Copyright (C) 2010, 2011 Alexey Chernov <4ernov@gmail.com>
+ *
+ * gstv4l2radio.c - V4L2 radio tuner element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-v4l2radio
+ *
+ * v4l2radio can be used to control radio device
+ * and to tune it to different radiostations.
+ *
+ *
+ * Example launch lines
+ * |[
+ * gst-launch v4l2radio device=/dev/radio0 frequency=101200000
+ * gst-launch alsasrc device=hw:1 ! audioconvert ! audioresample ! alsasink
+ * ]|
+ * First pipeline tunes the radio device /dev/radio0 to station 101.2 MHz,
+ * second pipeline connects digital audio out (hw:1) to default sound card.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+
+#include "gst/gst-i18n-plugin.h"
+
+#include "gstv4l2tuner.h"
+#include "gstv4l2radio.h"
+#include "v4l2_calls.h"
+
+GST_DEBUG_CATEGORY_STATIC (v4l2radio_debug);
+#define GST_CAT_DEFAULT v4l2radio_debug
+
+#define DEFAULT_PROP_DEVICE "/dev/radio0"
+#define MIN_FREQUENCY 87500000
+#define DEFAULT_FREQUENCY 100000000
+#define MAX_FREQUENCY 108000000
+
+enum
+{
+ ARG_0,
+ ARG_DEVICE,
+ ARG_FREQUENCY
+};
+
+static gboolean
+gst_v4l2radio_fill_channel_list (GstV4l2Radio * radio)
+{
+ int res;
+ struct v4l2_tuner vtun;
+ struct v4l2_capability vc;
+ GstV4l2TunerChannel *v4l2channel;
+ GstTunerChannel *channel;
+
+ GstElement *e;
+
+ GstV4l2Object *v4l2object;
+
+ e = GST_ELEMENT (radio);
+ v4l2object = radio->v4l2object;
+
+ GST_DEBUG_OBJECT (e, "getting audio enumeration");
+ GST_V4L2_CHECK_OPEN (v4l2object);
+
+ GST_DEBUG_OBJECT (e, " audio input");
+
+ memset (&vc, 0, sizeof (vc));
+
+ res = v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &vc);
+ if (res < 0)
+ goto caps_failed;
+
+ if (!(vc.capabilities & V4L2_CAP_TUNER))
+ goto not_a_tuner;
+
+ /* getting audio input */
+ memset (&vtun, 0, sizeof (vtun));
+ vtun.index = 0;
+
+ res = v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &vtun);
+ if (res < 0)
+ goto tuner_failed;
+
+ GST_LOG_OBJECT (e, " index: %d", vtun.index);
+ GST_LOG_OBJECT (e, " name: '%s'", vtun.name);
+ GST_LOG_OBJECT (e, " type: %016x", (guint) vtun.type);
+ GST_LOG_OBJECT (e, " caps: %016x", (guint) vtun.capability);
+ GST_LOG_OBJECT (e, " rlow: %016x", (guint) vtun.rangelow);
+ GST_LOG_OBJECT (e, " rhigh: %016x", (guint) vtun.rangehigh);
+ GST_LOG_OBJECT (e, " audmode: %016x", (guint) vtun.audmode);
+
+ v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL);
+ channel = GST_TUNER_CHANNEL (v4l2channel);
+ channel->label = g_strdup ((const gchar *) vtun.name);
+ channel->flags = GST_TUNER_CHANNEL_FREQUENCY | GST_TUNER_CHANNEL_AUDIO;
+ v4l2channel->index = 0;
+ v4l2channel->tuner = 0;
+
+ channel->freq_multiplicator =
+ 62.5 * ((vtun.capability & V4L2_TUNER_CAP_LOW) ? 1 : 1000);
+ channel->min_frequency = vtun.rangelow * channel->freq_multiplicator;
+ channel->max_frequency = vtun.rangehigh * channel->freq_multiplicator;
+ channel->min_signal = 0;
+ channel->max_signal = 0xffff;
+
+ v4l2object->channels =
+ g_list_prepend (v4l2object->channels, (gpointer) channel);
+
+ v4l2object->channels = g_list_reverse (v4l2object->channels);
+
+ GST_DEBUG_OBJECT (e, "done");
+ return TRUE;
+
+ /* ERRORS */
+tuner_failed:
+ {
+ GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+ (_("Failed to get settings of tuner %d on device '%s'."),
+ vtun.index, v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+caps_failed:
+ {
+ GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+ (_("Error getting capabilities for device '%s'."),
+ v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+not_a_tuner:
+ {
+ GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+ (_("Device '%s' is not a tuner."),
+ v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_get_input (GstV4l2Object * v4l2object, gint * input)
+{
+ GST_DEBUG_OBJECT (v4l2object->element, "trying to get radio input");
+
+ if (!GST_V4L2_IS_OPEN (v4l2object))
+ return FALSE;
+
+ if (!v4l2object->channels)
+ goto input_failed;
+
+ *input = 0;
+
+ GST_DEBUG_OBJECT (v4l2object->element, "input: %d", 0);
+
+ return TRUE;
+
+ /* ERRORS */
+input_failed:
+ {
+ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+ (_("Failed to get radio input on device '%s'. "),
+ v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_set_input (GstV4l2Object * v4l2object, gint input)
+{
+ GST_DEBUG_OBJECT (v4l2object->element, "trying to set input to %d", input);
+
+ if (!GST_V4L2_IS_OPEN (v4l2object))
+ return FALSE;
+
+ if (!v4l2object->channels)
+ goto input_failed;
+
+ return TRUE;
+
+ /* ERRORS */
+input_failed:
+ {
+ GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+ (_("Failed to set input %d on device %s."),
+ input, v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_set_mute_on (GstV4l2Radio * radio, gboolean on)
+{
+ gint res;
+ struct v4l2_control vctrl;
+
+ GST_DEBUG_OBJECT (radio, "setting current tuner mute state: %d", on);
+
+ if (!GST_V4L2_IS_OPEN (radio->v4l2object))
+ return FALSE;
+
+ memset (&vctrl, 0, sizeof (vctrl));
+ vctrl.id = V4L2_CID_AUDIO_MUTE;
+ vctrl.value = on;
+
+ GST_DEBUG_OBJECT (radio, "radio fd: %d", radio->v4l2object->video_fd);
+
+ res = ioctl (radio->v4l2object->video_fd, VIDIOC_S_CTRL, &vctrl);
+ GST_DEBUG_OBJECT (radio, "mute state change result: %d", res);
+ if (res < 0)
+ goto freq_failed;
+
+ return TRUE;
+
+ /* ERRORS */
+freq_failed:
+ {
+ GST_ELEMENT_WARNING (radio, RESOURCE, SETTINGS,
+ (_("Failed to change mute state for device '%s'."),
+ radio->v4l2object->videodev), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_v4l2radio_set_mute (GstV4l2Radio * radio)
+{
+ return gst_v4l2radio_set_mute_on (radio, TRUE);
+}
+
+static gboolean
+gst_v4l2radio_set_unmute (GstV4l2Radio * radio)
+{
+ return gst_v4l2radio_set_mute_on (radio, FALSE);
+}
+
+GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2RadioClass, gst_v4l2radio);
+GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Radio, gst_v4l2radio);
+
+static void gst_v4l2radio_uri_handler_init (gpointer g_iface,
+ gpointer iface_data);
+
+static gboolean
+gst_v4l2radio_interface_supported (GstImplementsInterface * iface,
+ GType iface_type)
+{
+ if (iface_type == GST_TYPE_TUNER)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+gst_v4l2radio_implements_interface_init (GstImplementsInterfaceClass * iface)
+{
+ iface->supported = gst_v4l2radio_interface_supported;
+}
+
+static void
+gst_v4l2radio_tuner_interface_reinit (GstTunerClass * iface)
+{
+ gst_v4l2radio_tuner_interface_init (iface);
+}
+
+static void
+gst_v4l2radio_interfaces (GType type)
+{
+ static const GInterfaceInfo urihandler_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ static const GInterfaceInfo implements_interface_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_implements_interface_init,
+ NULL,
+ NULL,
+ };
+
+ static const GInterfaceInfo propertyprobe_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_property_probe_interface_init,
+ NULL,
+ NULL,
+ };
+
+ static const GInterfaceInfo tuner_interface_info = {
+ (GInterfaceInitFunc) gst_v4l2radio_tuner_interface_reinit,
+ NULL,
+ NULL,
+ };
+
+ g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
+ g_type_add_interface_static (type,
+ GST_TYPE_IMPLEMENTS_INTERFACE, &implements_interface_info);
+
+ g_type_add_interface_static (type, GST_TYPE_TUNER, &tuner_interface_info);
+
+ g_type_add_interface_static (type,
+ GST_TYPE_PROPERTY_PROBE, &propertyprobe_info);
+}
+
+GST_BOILERPLATE_FULL (GstV4l2Radio, gst_v4l2radio, GstElement, GST_TYPE_ELEMENT,
+ gst_v4l2radio_interfaces);
+
+static void gst_v4l2radio_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_v4l2radio_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_v4l2radio_finalize (GstV4l2Radio * radio);
+static void gst_v4l2radio_dispose (GObject * object);
+static GstStateChangeReturn gst_v4l2radio_change_state (GstElement * element,
+ GstStateChange transition);
+
+static void
+gst_v4l2radio_base_init (gpointer gclass)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (gclass);
+ GstV4l2RadioClass *gstv4l2radio_class = GST_V4L2RADIO_CLASS (gclass);
+
+ GST_DEBUG_CATEGORY_INIT (v4l2radio_debug, "v4l2radio", 0,
+ "V4l2 radio element");
+
+ gstv4l2radio_class->v4l2_class_devices = NULL;
+
+ gst_element_class_set_details_simple (gstelement_class,
+ "Radio (video4linux2) Tuner",
+ "Tuner",
+ "Controls a Video4Linux2 radio device",
+ "Alexey Chernov <4ernov@gmail.com>");
+}
+
+static void
+gst_v4l2radio_class_init (GstV4l2RadioClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = gst_v4l2radio_set_property;
+ gobject_class->get_property = gst_v4l2radio_get_property;
+
+ g_object_class_install_property (gobject_class, ARG_DEVICE,
+ g_param_spec_string ("device", "Radio device location",
+ "Video4Linux2 radio device location",
+ DEFAULT_PROP_DEVICE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, ARG_FREQUENCY,
+ g_param_spec_int ("frequency", "Station frequency",
+ "Station frequency in Hz",
+ MIN_FREQUENCY, MAX_FREQUENCY, DEFAULT_FREQUENCY, G_PARAM_READWRITE));
+
+ gobject_class->dispose = gst_v4l2radio_dispose;
+ gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2radio_finalize;
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_v4l2radio_change_state);
+
+}
+
+static void
+gst_v4l2radio_init (GstV4l2Radio * filter, GstV4l2RadioClass * gclass)
+{
+ filter->v4l2object = gst_v4l2_object_new (GST_ELEMENT (filter),
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE,
+ gst_v4l2radio_get_input, gst_v4l2radio_set_input, NULL);
+
+ filter->v4l2object->frequency = DEFAULT_FREQUENCY;
+ filter->v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE);
+}
+
+static void
+gst_v4l2radio_dispose (GObject * object)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (object);
+ gst_v4l2_close (radio->v4l2object);
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_v4l2radio_finalize (GstV4l2Radio * radio)
+{
+ gst_v4l2_object_destroy (radio->v4l2object);
+ G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (radio));
+}
+
+static gboolean
+gst_v4l2radio_open (GstV4l2Radio * radio)
+{
+ GstV4l2Object *v4l2object;
+
+ v4l2object = radio->v4l2object;
+ if (gst_v4l2_open (v4l2object))
+ return gst_v4l2radio_fill_channel_list (radio);
+ else
+ return FALSE;
+}
+
+static void
+gst_v4l2radio_set_defaults (GstV4l2Radio * radio)
+{
+ GstV4l2Object *v4l2object;
+ GstTunerChannel *channel = NULL;
+ GstTuner *tuner;
+
+ v4l2object = radio->v4l2object;
+
+ if (!GST_IS_TUNER (v4l2object->element))
+ return;
+
+ tuner = GST_TUNER (v4l2object->element);
+
+ if (v4l2object->channel)
+ channel = gst_tuner_find_channel_by_name (tuner, v4l2object->channel);
+ if (channel) {
+ gst_tuner_set_channel (tuner, channel);
+ } else {
+ channel =
+ GST_TUNER_CHANNEL (gst_tuner_get_channel (GST_TUNER
+ (v4l2object->element)));
+ if (channel) {
+ g_free (v4l2object->channel);
+ v4l2object->channel = g_strdup (channel->label);
+ gst_tuner_channel_changed (tuner, channel);
+ }
+ }
+
+ if (channel
+ && GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) {
+ if (v4l2object->frequency != 0) {
+ gst_tuner_set_frequency (tuner, channel, v4l2object->frequency);
+ } else {
+ v4l2object->frequency = gst_tuner_get_frequency (tuner, channel);
+ if (v4l2object->frequency == 0) {
+ /* guess */
+ gst_tuner_set_frequency (tuner, channel, MIN_FREQUENCY);
+ } else {
+ }
+ }
+ }
+}
+
+static gboolean
+gst_v4l2radio_start (GstV4l2Radio * radio)
+{
+ if (!gst_v4l2radio_open (radio))
+ return FALSE;
+
+ gst_v4l2radio_set_defaults (radio);
+
+ return TRUE;
+}
+
+static gboolean
+gst_v4l2radio_stop (GstV4l2Radio * radio)
+{
+ if (!gst_v4l2_object_stop (radio->v4l2object))
+ return FALSE;
+
+ return TRUE;
+}
+
+static GstStateChangeReturn
+gst_v4l2radio_change_state (GstElement * element, GstStateChange transition)
+{
+ GstV4l2Radio *radio;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ radio = GST_V4L2RADIO (element);
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ /*start radio */
+ if (!gst_v4l2radio_start (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ /*unmute radio */
+ if (!gst_v4l2radio_set_unmute (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ /*mute radio */
+ if (!gst_v4l2radio_set_mute (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ /*stop radio */
+ if (!gst_v4l2radio_stop (radio))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+gst_v4l2radio_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (object);
+ gint frequency;
+ switch (prop_id) {
+ case ARG_DEVICE:
+ radio->v4l2object->videodev =
+ g_strdup ((gchar *) g_value_get_string (value));
+ break;
+ case ARG_FREQUENCY:
+ frequency = g_value_get_int (value);
+ if (frequency >= MIN_FREQUENCY && frequency <= MAX_FREQUENCY) {
+ radio->v4l2object->frequency = frequency;
+ gst_v4l2_set_frequency (radio->v4l2object, 0,
+ radio->v4l2object->frequency);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_v4l2radio_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (object);
+
+ switch (prop_id) {
+ case ARG_DEVICE:
+ g_value_set_string (value, radio->v4l2object->videodev);
+ break;
+ case ARG_FREQUENCY:
+ if (gst_v4l2_get_frequency (radio->v4l2object,
+ 0, &(radio->v4l2object->frequency)))
+ g_value_set_int (value, radio->v4l2object->frequency);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* GstURIHandler interface */
+static GstURIType
+gst_v4l2radio_uri_get_type (void)
+{
+ return GST_URI_SRC;
+}
+
+static gchar **
+gst_v4l2radio_uri_get_protocols (void)
+{
+ static gchar *protocols[] = { (char *) "radio", NULL };
+ return protocols;
+}
+
+static const gchar *
+gst_v4l2radio_uri_get_uri (GstURIHandler * handler)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (handler);
+
+ if (radio->v4l2object->videodev != NULL) {
+ if (gst_v4l2_get_frequency (radio->v4l2object,
+ 0, &(radio->v4l2object->frequency))) {
+ gchar uri[20];
+ gchar freq[6];
+ g_ascii_formatd (freq, 6, "%4.1f", radio->v4l2object->frequency / 1e6);
+ g_snprintf (uri, sizeof (uri), "radio://%s", freq);
+ return g_intern_string (uri);
+ }
+ }
+
+ return "radio://";
+}
+
+static gboolean
+gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+{
+ GstV4l2Radio *radio = GST_V4L2RADIO (handler);
+ gdouble dfreq;
+ gint ifreq;
+ const gchar *freq;
+ gchar *end;
+
+ if (strcmp (uri, "radio://") != 0) {
+ freq = uri + 8;
+
+ dfreq = g_ascii_strtod (freq, &end);
+
+ if (errno || strlen (end))
+ goto uri_failed;
+
+ ifreq = dfreq * 1e6;
+ g_object_set (radio, "frequency", ifreq, NULL);
+
+ } else
+ goto uri_failed;
+
+ return TRUE;
+
+uri_failed:
+ return FALSE;
+}
+
+static void
+gst_v4l2radio_uri_handler_init (gpointer g_iface, gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+ iface->get_type = gst_v4l2radio_uri_get_type;
+ iface->get_protocols = gst_v4l2radio_uri_get_protocols;
+ iface->get_uri = gst_v4l2radio_uri_get_uri;
+ iface->set_uri = gst_v4l2radio_uri_set_uri;
+}
diff --git a/sys/v4l2/gstv4l2radio.h b/sys/v4l2/gstv4l2radio.h
new file mode 100644
index 0000000000..f1c99a277f
--- /dev/null
+++ b/sys/v4l2/gstv4l2radio.h
@@ -0,0 +1,68 @@
+/* GStreamer v4l2 radio tuner element
+ * Copyright (C) 2010, 2011 Alexey Chernov <4ernov@gmail.com>
+ *
+ * gstv4l2radio.h - V4L2 radio tuner element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_V4L2RADIO_H__
+#define __GST_V4L2RADIO_H__
+
+#include "gstv4l2object.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_V4L2RADIO \
+ (gst_v4l2radio_get_type())
+#define GST_V4L2RADIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2RADIO,GstV4l2Radio))
+#define GST_V4L2RADIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2RADIO,GstV4l2RadioClass))
+#define GST_IS_V4L2RADIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2RADIO))
+#define GST_IS_V4L2RADIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2RADIO))
+
+typedef struct _GstV4l2Radio GstV4l2Radio;
+typedef struct _GstV4l2RadioClass GstV4l2RadioClass;
+
+/**
+ * GstV4l2Radio:
+ * @v4l2object: private #GstV4l2Object
+ *
+ * Opaque video4linux2 radio tuner element
+ */
+struct _GstV4l2Radio
+{
+ GstElement element;
+
+ /*< private >*/
+ GstV4l2Object * v4l2object;
+};
+
+struct _GstV4l2RadioClass
+{
+ GstElementClass parent_class;
+
+ GList *v4l2_class_devices;
+};
+
+GType gst_v4l2radio_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_V4L2RADIO_H__ */
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index ccce33cf03..1ffb7a33f4 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -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
diff --git a/tests/check/elements/videofilter.c b/tests/check/elements/videofilter.c
index b3034a4dfa..7389478c0e 100644
--- a/tests/check/elements/videofilter.c
+++ b/tests/check/elements/videofilter.c
@@ -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);