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);