mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-29 02:28:27 +00:00
Merge branch 'master' into 0.11-fdo
This commit is contained in:
commit
8f22a09dc4
33 changed files with 1971 additions and 268 deletions
|
@ -80,7 +80,7 @@ tool_run "$libtoolize" "--copy --force"
|
|||
tool_run "$aclocal" "-I m4 -I common/m4 $ACLOCAL_FLAGS"
|
||||
tool_run "$autoheader"
|
||||
|
||||
# touch the stamp-h.in build stamp so we don't re-run autoheader in maintainer mode -- wingo
|
||||
# touch the stamp-h.in build stamp so we don't re-run autoheader in maintainer mode
|
||||
echo timestamp > stamp-h.in 2> /dev/null
|
||||
|
||||
tool_run "$autoconf"
|
||||
|
|
2
common
2
common
|
@ -1 +1 @@
|
|||
Subproject commit 6aec6b9716c184c60c4bc6a5916a2471cfa8c8cd
|
||||
Subproject commit 1ccbe098d6379612fcef09f4000da23585af980a
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -154,6 +154,7 @@
|
|||
<xi:include href="xml/element-udpsink.xml" />
|
||||
<xi:include href="xml/element-v4l2src.xml" />
|
||||
<xi:include href="xml/element-v4l2sink.xml" />
|
||||
<xi:include href="xml/element-v4l2radio.xml" />
|
||||
<xi:include href="xml/element-vertigotv.xml" />
|
||||
<xi:include href="xml/element-videobalance.xml" />
|
||||
<xi:include href="xml/element-videobox.xml" />
|
||||
|
|
|
@ -2048,6 +2048,20 @@ GST_IS_V4L2SINK_CLASS
|
|||
gst_v4l2sink_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>element-v4l2radio</FILE>
|
||||
<TITLE>v4l2radio</TITLE>
|
||||
GstV4l2Radio
|
||||
<SUBSECTION Standard>
|
||||
GstV4l2RadioClass
|
||||
GST_V4L2RADIO
|
||||
GST_IS_V4L2RADIO
|
||||
GST_TYPE_V4L2RADIO
|
||||
GST_V4L2RADIO_CLASS
|
||||
GST_IS_V4L2RADIO_CLASS
|
||||
gst_v4l2radio_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>element-waveformsink</FILE>
|
||||
<TITLE>waveformsink</TITLE>
|
||||
|
|
|
@ -24,5 +24,12 @@
|
|||
</caps>
|
||||
</pads>
|
||||
</element>
|
||||
<element>
|
||||
<name>v4l2radio</name>
|
||||
<longname>Radio (video4linux2) Tuner</longname>
|
||||
<class>Tuner</class>
|
||||
<description>Controls a Video4Linux2 radio device</description>
|
||||
<author>Alexey Chernov <4ernov@gmail.com></author>
|
||||
</element>
|
||||
</elements>
|
||||
</plugin>
|
||||
</plugin>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
* ]| Play a 440Hz sine wave.
|
||||
* |[
|
||||
* gst-launch -v audiotestsrc ! pulsesink stream-properties="props,media.title=test"
|
||||
* ]] Play a sine wave and set a stream property. The property can be checked
|
||||
* ]| Play a sine wave and set a stream property. The property can be checked
|
||||
* with "pactl list".
|
||||
* </refsect2>
|
||||
*/
|
||||
|
@ -442,6 +442,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
|
|||
GstPulseRingBuffer *pbuf;
|
||||
GstPulseContext *pctx;
|
||||
pa_mainloop_api *api;
|
||||
gboolean need_unlock_shared;
|
||||
|
||||
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (buf));
|
||||
pbuf = GST_PULSERING_BUFFER_CAST (buf);
|
||||
|
@ -458,6 +459,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
|
|||
pa_threaded_mainloop_lock (mainloop);
|
||||
|
||||
g_mutex_lock (pa_shared_resource_mutex);
|
||||
need_unlock_shared = TRUE;
|
||||
|
||||
pctx = g_hash_table_lookup (gst_pulse_shared_contexts, pbuf->context_name);
|
||||
if (pctx == NULL) {
|
||||
|
@ -481,7 +483,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
|
|||
gst_pulsering_context_subscribe_cb, pctx);
|
||||
#endif
|
||||
|
||||
/* try to connect to the server and wait for completioni, we don't want to
|
||||
/* try to connect to the server and wait for completion, we don't want to
|
||||
* autospawn a deamon */
|
||||
GST_LOG_OBJECT (psink, "connect to server %s",
|
||||
GST_STR_NULL (psink->server));
|
||||
|
@ -496,6 +498,7 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
|
|||
}
|
||||
|
||||
g_mutex_unlock (pa_shared_resource_mutex);
|
||||
need_unlock_shared = FALSE;
|
||||
|
||||
/* context created or shared okay */
|
||||
pbuf->context = pa_context_ref (pctx->context);
|
||||
|
@ -527,6 +530,8 @@ gst_pulseringbuffer_open_device (GstRingBuffer * buf)
|
|||
/* ERRORS */
|
||||
unlock_and_fail:
|
||||
{
|
||||
if (need_unlock_shared)
|
||||
g_mutex_unlock (pa_shared_resource_mutex);
|
||||
gst_pulsering_destroy_context (pbuf);
|
||||
pa_threaded_mainloop_unlock (mainloop);
|
||||
return FALSE;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ struct _GstSpeexDec {
|
|||
guint64 packetno;
|
||||
|
||||
GstSegment segment; /* STREAM LOCK */
|
||||
|
||||
GstBuffer *streamheader;
|
||||
GstBuffer *vorbiscomment;
|
||||
};
|
||||
|
||||
struct _GstSpeexDecClass {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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}, {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -233,7 +233,9 @@ struct _GstRTSPSrc {
|
|||
|
||||
/* supported methods */
|
||||
gint methods;
|
||||
|
||||
gboolean seekable;
|
||||
GstClockTime last_pos;
|
||||
|
||||
/* session management */
|
||||
GstElement *manager;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* <2006> Stefan Kost <ensonic@users.sf.net>
|
||||
* <2006,2011> Stefan Kost <ensonic@users.sf.net>
|
||||
* <2007-2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -71,7 +71,7 @@
|
|||
* <classname>"magnitude"</classname>:
|
||||
* the level for each frequency band in dB. All values below the value of the
|
||||
* #GstSpectrum:threshold property will be set to the threshold. Only present
|
||||
* if the message-magnitude property is true.
|
||||
* if the #GstSpectrum:message-magnitude property is %TRUE.
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
|
@ -79,11 +79,15 @@
|
|||
* #GstValueList of #gfloat
|
||||
* <classname>"phase"</classname>:
|
||||
* The phase for each frequency band. The value is between -pi and pi. Only
|
||||
* present if the message-phase property is true.
|
||||
* present if the #GstSpectrum:message-phase property is %TRUE.
|
||||
* </para>
|
||||
* </listitem>
|
||||
* </itemizedlist>
|
||||
*
|
||||
* If #GstSpectrum:multi-channel property is set to true. magnitude and phase
|
||||
* fields will be each a nested #GstValueArray. The first dimension are the
|
||||
* channels and the second dimension are the values.
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example application</title>
|
||||
* |[
|
||||
|
@ -91,7 +95,7 @@
|
|||
* ]|
|
||||
* </refsect2>
|
||||
*
|
||||
* Last reviewed on 2009-01-14 (0.10.12)
|
||||
* Last reviewed on 2011-03-10 (0.10.29)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
@ -143,6 +147,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_spectrum_debug);
|
|||
#define DEFAULT_INTERVAL (GST_SECOND / 10)
|
||||
#define DEFAULT_BANDS 128
|
||||
#define DEFAULT_THRESHOLD -60
|
||||
#define DEFAULT_MULTI_CHANNEL FALSE
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -153,7 +158,8 @@ enum
|
|||
PROP_MESSAGE_PHASE,
|
||||
PROP_INTERVAL,
|
||||
PROP_BANDS,
|
||||
PROP_THRESHOLD
|
||||
PROP_THRESHOLD,
|
||||
PROP_MULTI_CHANNEL
|
||||
};
|
||||
|
||||
GST_BOILERPLATE (GstSpectrum, gst_spectrum, GstAudioFilter,
|
||||
|
@ -257,6 +263,18 @@ gst_spectrum_class_init (GstSpectrumClass * klass)
|
|||
G_MININT, 0, DEFAULT_THRESHOLD,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstSpectrum:multi-channel
|
||||
*
|
||||
* Send separate results for each channel
|
||||
*
|
||||
* Since: 0.10.29
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_MULTI_CHANNEL,
|
||||
g_param_spec_boolean ("multi-channel", "Multichannel results",
|
||||
"Send separate results for each channel",
|
||||
DEFAULT_MULTI_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_spectrum_debug, "spectrum", 0,
|
||||
"audio spectrum analyser element");
|
||||
}
|
||||
|
@ -272,6 +290,59 @@ gst_spectrum_init (GstSpectrum * spectrum, GstSpectrumClass * g_class)
|
|||
spectrum->threshold = DEFAULT_THRESHOLD;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_spectrum_alloc_channel_data (GstSpectrum * spectrum)
|
||||
{
|
||||
gint i;
|
||||
GstSpectrumChannel *cd;
|
||||
guint bands = spectrum->bands;
|
||||
guint nfft = 2 * bands - 2;
|
||||
|
||||
g_assert (spectrum->channel_data == NULL);
|
||||
|
||||
spectrum->num_channels = (spectrum->multi_channel) ?
|
||||
GST_AUDIO_FILTER (spectrum)->format.channels : 1;
|
||||
|
||||
GST_DEBUG_OBJECT (spectrum, "allocating data for %d channels",
|
||||
spectrum->num_channels);
|
||||
|
||||
spectrum->channel_data = g_new (GstSpectrumChannel, spectrum->num_channels);
|
||||
for (i = 0; i < spectrum->num_channels; i++) {
|
||||
cd = &spectrum->channel_data[i];
|
||||
cd->fft_ctx = gst_fft_f32_new (nfft, FALSE);
|
||||
cd->input = g_new0 (gfloat, nfft);
|
||||
cd->input_tmp = g_new0 (gfloat, nfft);
|
||||
cd->freqdata = g_new0 (GstFFTF32Complex, bands);
|
||||
cd->spect_magnitude = g_new0 (gfloat, bands);
|
||||
cd->spect_phase = g_new0 (gfloat, bands);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_spectrum_free_channel_data (GstSpectrum * spectrum)
|
||||
{
|
||||
if (spectrum->channel_data) {
|
||||
gint i;
|
||||
GstSpectrumChannel *cd;
|
||||
|
||||
GST_DEBUG_OBJECT (spectrum, "freeing data for %d channels",
|
||||
spectrum->num_channels);
|
||||
|
||||
for (i = 0; i < spectrum->num_channels; i++) {
|
||||
cd = &spectrum->channel_data[i];
|
||||
if (cd->fft_ctx)
|
||||
gst_fft_f32_free (cd->fft_ctx);
|
||||
g_free (cd->input);
|
||||
g_free (cd->input_tmp);
|
||||
g_free (cd->freqdata);
|
||||
g_free (cd->spect_magnitude);
|
||||
g_free (cd->spect_phase);
|
||||
}
|
||||
g_free (spectrum->channel_data);
|
||||
spectrum->channel_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_spectrum_flush (GstSpectrum * spectrum)
|
||||
{
|
||||
|
@ -286,21 +357,7 @@ gst_spectrum_reset_state (GstSpectrum * spectrum)
|
|||
{
|
||||
GST_DEBUG_OBJECT (spectrum, "resetting state");
|
||||
|
||||
if (spectrum->fft_ctx)
|
||||
gst_fft_f32_free (spectrum->fft_ctx);
|
||||
g_free (spectrum->input);
|
||||
g_free (spectrum->input_tmp);
|
||||
g_free (spectrum->freqdata);
|
||||
g_free (spectrum->spect_magnitude);
|
||||
g_free (spectrum->spect_phase);
|
||||
|
||||
spectrum->fft_ctx = NULL;
|
||||
spectrum->input = NULL;
|
||||
spectrum->input_tmp = NULL;
|
||||
spectrum->freqdata = NULL;
|
||||
spectrum->spect_magnitude = NULL;
|
||||
spectrum->spect_phase = NULL;
|
||||
|
||||
gst_spectrum_free_channel_data (spectrum);
|
||||
gst_spectrum_flush (spectrum);
|
||||
}
|
||||
|
||||
|
@ -335,7 +392,7 @@ gst_spectrum_set_property (GObject * object, guint prop_id,
|
|||
guint64 interval = g_value_get_uint64 (value);
|
||||
if (filter->interval != interval) {
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
filter->interval = g_value_get_uint64 (value);
|
||||
filter->interval = interval;
|
||||
gst_spectrum_reset_state (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
}
|
||||
|
@ -354,6 +411,16 @@ gst_spectrum_set_property (GObject * object, guint prop_id,
|
|||
case PROP_THRESHOLD:
|
||||
filter->threshold = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MULTI_CHANNEL:{
|
||||
gboolean multi_channel = g_value_get_boolean (value);
|
||||
if (filter->multi_channel != multi_channel) {
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
filter->multi_channel = multi_channel;
|
||||
gst_spectrum_reset_state (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -386,6 +453,9 @@ gst_spectrum_get_property (GObject * object, guint prop_id,
|
|||
case PROP_THRESHOLD:
|
||||
g_value_set_int (value, filter->threshold);
|
||||
break;
|
||||
case PROP_MULTI_CHANNEL:
|
||||
g_value_set_boolean (value, filter->multi_channel);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -412,31 +482,388 @@ gst_spectrum_stop (GstBaseTransform * trans)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/* mixing data readers */
|
||||
|
||||
static void
|
||||
input_data_mixed_float (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j, ip = 0;
|
||||
gfloat v;
|
||||
gfloat *in = (gfloat *) _in;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
v = in[ip++];
|
||||
for (i = 1; i < channels; i++)
|
||||
v += in[ip++];
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_mixed_double (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j, ip = 0;
|
||||
gfloat v;
|
||||
gdouble *in = (gdouble *) _in;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
v = in[ip++];
|
||||
for (i = 1; i < channels; i++)
|
||||
v += in[ip++];
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_mixed_int32 (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j, ip = 0;
|
||||
gint32 *in = (gint32 *) _in;
|
||||
gfloat v;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
v = in[ip++] * 2 + 1;
|
||||
for (i = 1; i < channels; i++)
|
||||
v += in[ip++] * 2 + 1;
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_mixed_int32_max (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j, ip = 0;
|
||||
gint32 *in = (gint32 *) _in;
|
||||
gfloat v;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
v = in[ip++] / max_value;
|
||||
for (i = 1; i < channels; i++)
|
||||
v += in[ip++] / max_value;
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_mixed_int24 (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j;
|
||||
gfloat v = 0.0;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
gint32 value = GST_READ_UINT24_BE (_in);
|
||||
#else
|
||||
gint32 value = GST_READ_UINT24_LE (_in);
|
||||
#endif
|
||||
if (value & 0x00800000)
|
||||
value |= 0xff000000;
|
||||
v += value * 2 + 1;
|
||||
_in += 3;
|
||||
}
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_mixed_int24_max (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j;
|
||||
gfloat v = 0.0;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
gint32 value = GST_READ_UINT24_BE (_in);
|
||||
#else
|
||||
gint32 value = GST_READ_UINT24_LE (_in);
|
||||
#endif
|
||||
if (value & 0x00800000)
|
||||
value |= 0xff000000;
|
||||
v += value / max_value;
|
||||
_in += 3;
|
||||
}
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_mixed_int16 (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j, ip = 0;
|
||||
gint16 *in = (gint16 *) _in;
|
||||
gfloat v;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
v = in[ip++] * 2 + 1;
|
||||
for (i = 1; i < channels; i++)
|
||||
v += in[ip++] * 2 + 1;
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_mixed_int16_max (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint i, j, ip = 0;
|
||||
gint16 *in = (gint16 *) _in;
|
||||
gfloat v;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
v = in[ip++] / max_value;
|
||||
for (i = 1; i < channels; i++)
|
||||
v += in[ip++] / max_value;
|
||||
out[op] = v / channels;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
/* non mixing data readers */
|
||||
|
||||
static void
|
||||
input_data_float (const guint8 * _in, gfloat * out, guint len, guint channels,
|
||||
gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j, ip;
|
||||
gfloat *in = (gfloat *) _in;
|
||||
|
||||
for (j = 0, ip = 0; j < len; j++, ip += channels) {
|
||||
out[op] = in[ip];
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_double (const guint8 * _in, gfloat * out, guint len, guint channels,
|
||||
gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j, ip;
|
||||
gdouble *in = (gdouble *) _in;
|
||||
|
||||
for (j = 0, ip = 0; j < len; j++, ip += channels) {
|
||||
out[op] = in[ip];
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_int32 (const guint8 * _in, gfloat * out, guint len, guint channels,
|
||||
gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j, ip;
|
||||
gint32 *in = (gint32 *) _in;
|
||||
|
||||
for (j = 0, ip = 0; j < len; j++, ip += channels) {
|
||||
out[op] = in[ip] * 2 + 1;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_int32_max (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j, ip;
|
||||
gint32 *in = (gint32 *) _in;
|
||||
|
||||
for (j = 0, ip = 0; j < len; j++, ip += channels) {
|
||||
out[op] = in[ip] / max_value;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_int24 (const guint8 * _in, gfloat * out, guint len, guint channels,
|
||||
gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
gint32 v = GST_READ_UINT24_BE (_in);
|
||||
#else
|
||||
gint32 v = GST_READ_UINT24_LE (_in);
|
||||
#endif
|
||||
if (v & 0x00800000)
|
||||
v |= 0xff000000;
|
||||
_in += 3 * channels;
|
||||
out[op] = v * 2 + 1;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_int24_max (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
gint32 v = GST_READ_UINT24_BE (_in);
|
||||
#else
|
||||
gint32 v = GST_READ_UINT24_LE (_in);
|
||||
#endif
|
||||
if (v & 0x00800000)
|
||||
v |= 0xff000000;
|
||||
_in += 3 * channels;
|
||||
out[op] = v / max_value;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_int16 (const guint8 * _in, gfloat * out, guint len, guint channels,
|
||||
gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j, ip;
|
||||
gint16 *in = (gint16 *) _in;
|
||||
|
||||
for (j = 0, ip = 0; j < len; j++, ip += channels) {
|
||||
out[op] = in[ip] * 2 + 1;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_data_int16_max (const guint8 * _in, gfloat * out, guint len,
|
||||
guint channels, gfloat max_value, guint op, guint nfft)
|
||||
{
|
||||
guint j, ip;
|
||||
gint16 *in = (gint16 *) _in;
|
||||
|
||||
for (j = 0, ip = 0; j < len; j++, ip += channels) {
|
||||
out[op] = in[ip] / max_value;
|
||||
op = (op + 1) % nfft;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_spectrum_setup (GstAudioFilter * base, GstRingBufferSpec * format)
|
||||
{
|
||||
GstSpectrum *filter = GST_SPECTRUM (base);
|
||||
GstSpectrum *spectrum = GST_SPECTRUM (base);
|
||||
guint width = format->width / 8;
|
||||
gboolean is_float = (format->type == GST_BUFTYPE_FLOAT);
|
||||
/* max_value will be 0 when depth is 1,
|
||||
* interpret -1 and 0 as -1 and +1 if that's the case. */
|
||||
guint max_value = (1UL << (format->depth - 1)) - 1;
|
||||
gboolean multi_channel = spectrum->multi_channel;
|
||||
GstSpectrumInputData input_data = NULL;
|
||||
|
||||
gst_spectrum_reset_state (filter);
|
||||
if (is_float) {
|
||||
if (width == 4) {
|
||||
input_data = multi_channel ? input_data_float : input_data_mixed_float;
|
||||
} else if (width == 8) {
|
||||
input_data = multi_channel ? input_data_double : input_data_mixed_double;
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
} else {
|
||||
if (width == 4) {
|
||||
if (max_value) {
|
||||
input_data =
|
||||
multi_channel ? input_data_int32_max : input_data_mixed_int32_max;
|
||||
} else {
|
||||
input_data = multi_channel ? input_data_int32 : input_data_mixed_int32;
|
||||
}
|
||||
} else if (width == 3) {
|
||||
if (max_value) {
|
||||
input_data =
|
||||
multi_channel ? input_data_int24_max : input_data_mixed_int24_max;
|
||||
} else {
|
||||
input_data = multi_channel ? input_data_int24 : input_data_mixed_int24;
|
||||
}
|
||||
} else if (width == 2) {
|
||||
if (max_value) {
|
||||
input_data =
|
||||
multi_channel ? input_data_int16_max : input_data_mixed_int16_max;
|
||||
} else {
|
||||
input_data = multi_channel ? input_data_int16 : input_data_mixed_int16;
|
||||
}
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
spectrum->input_data = input_data;
|
||||
gst_spectrum_reset_state (spectrum);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GValue *
|
||||
gst_spectrum_message_add_container (GstStructure * s, GType type,
|
||||
const gchar * name)
|
||||
{
|
||||
GValue v = { 0, };
|
||||
|
||||
g_value_init (&v, type);
|
||||
/* will copy-by-value */
|
||||
gst_structure_set_value (s, name, &v);
|
||||
g_value_unset (&v);
|
||||
return (GValue *) gst_structure_get_value (s, name);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_spectrum_message_add_list (GValue * cv, gfloat * data, guint num_values)
|
||||
{
|
||||
GValue v = { 0, };
|
||||
guint i;
|
||||
|
||||
g_value_init (&v, G_TYPE_FLOAT);
|
||||
for (i = 0; i < num_values; i++) {
|
||||
g_value_set_float (&v, data[i]);
|
||||
gst_value_list_append_value (cv, &v); /* copies by value */
|
||||
}
|
||||
g_value_unset (&v);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_spectrum_message_add_array (GValue * cv, gfloat * data, guint num_values)
|
||||
{
|
||||
GValue v = { 0, };
|
||||
GValue a = { 0, };
|
||||
guint i;
|
||||
|
||||
g_value_init (&a, GST_TYPE_ARRAY);
|
||||
|
||||
g_value_init (&v, G_TYPE_FLOAT);
|
||||
for (i = 0; i < num_values; i++) {
|
||||
g_value_set_float (&v, data[i]);
|
||||
gst_value_array_append_value (&a, &v); /* copies by value */
|
||||
}
|
||||
g_value_unset (&v);
|
||||
|
||||
gst_value_array_append_value (cv, &a); /* copies by value */
|
||||
g_value_unset (&a);
|
||||
}
|
||||
|
||||
static GstMessage *
|
||||
gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
|
||||
GstClockTime duration)
|
||||
{
|
||||
GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (spectrum);
|
||||
GstSpectrumChannel *cd;
|
||||
GstStructure *s;
|
||||
GValue v = { 0, };
|
||||
GValue *l;
|
||||
guint i;
|
||||
gfloat *spect_magnitude = spectrum->spect_magnitude;
|
||||
gfloat *spect_phase = spectrum->spect_phase;
|
||||
GValue *mcv = NULL, *pcv = NULL;
|
||||
GstClockTime endtime, running_time, stream_time;
|
||||
|
||||
GST_DEBUG_OBJECT (spectrum, "preparing message, spect = %p, bands =%d ",
|
||||
spect_magnitude, spectrum->bands);
|
||||
GST_DEBUG_OBJECT (spectrum, "preparing message, bands =%d ", spectrum->bands);
|
||||
|
||||
running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
|
||||
timestamp);
|
||||
|
@ -452,65 +879,145 @@ gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
|
|||
"running-time", G_TYPE_UINT64, running_time,
|
||||
"duration", G_TYPE_UINT64, duration, NULL);
|
||||
|
||||
if (spectrum->message_magnitude) {
|
||||
/* FIXME 0.11: this should be an array, not a list */
|
||||
g_value_init (&v, GST_TYPE_LIST);
|
||||
/* will copy-by-value */
|
||||
gst_structure_set_value (s, "magnitude", &v);
|
||||
g_value_unset (&v);
|
||||
if (!spectrum->multi_channel) {
|
||||
cd = &spectrum->channel_data[0];
|
||||
|
||||
g_value_init (&v, G_TYPE_FLOAT);
|
||||
l = (GValue *) gst_structure_get_value (s, "magnitude");
|
||||
for (i = 0; i < spectrum->bands; i++) {
|
||||
g_value_set_float (&v, spect_magnitude[i]);
|
||||
gst_value_list_append_value (l, &v); /* copies by value */
|
||||
if (spectrum->message_magnitude) {
|
||||
/* FIXME 0.11: this should be an array, not a list */
|
||||
mcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "magnitude");
|
||||
gst_spectrum_message_add_list (mcv, cd->spect_magnitude, spectrum->bands);
|
||||
}
|
||||
if (spectrum->message_phase) {
|
||||
/* FIXME 0.11: this should be an array, not a list */
|
||||
pcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "phase");
|
||||
gst_spectrum_message_add_list (pcv, cd->spect_phase, spectrum->bands);
|
||||
}
|
||||
} else {
|
||||
guint c;
|
||||
guint channels = GST_AUDIO_FILTER (spectrum)->format.channels;
|
||||
|
||||
if (spectrum->message_magnitude) {
|
||||
mcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "magnitude");
|
||||
}
|
||||
if (spectrum->message_phase) {
|
||||
pcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "phase");
|
||||
}
|
||||
|
||||
for (c = 0; c < channels; c++) {
|
||||
cd = &spectrum->channel_data[c];
|
||||
|
||||
if (spectrum->message_magnitude) {
|
||||
gst_spectrum_message_add_array (mcv, cd->spect_magnitude,
|
||||
spectrum->bands);
|
||||
}
|
||||
if (spectrum->message_phase) {
|
||||
gst_spectrum_message_add_array (pcv, cd->spect_magnitude,
|
||||
spectrum->bands);
|
||||
}
|
||||
}
|
||||
}
|
||||
return gst_message_new_element (GST_OBJECT (spectrum), s);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_spectrum_run_fft (GstSpectrum * spectrum, GstSpectrumChannel * cd,
|
||||
guint input_pos)
|
||||
{
|
||||
guint i;
|
||||
guint bands = spectrum->bands;
|
||||
guint nfft = 2 * bands - 2;
|
||||
gint threshold = spectrum->threshold;
|
||||
gfloat *input = cd->input;
|
||||
gfloat *input_tmp = cd->input_tmp;
|
||||
gfloat *spect_magnitude = cd->spect_magnitude;
|
||||
gfloat *spect_phase = cd->spect_phase;
|
||||
GstFFTF32Complex *freqdata = cd->freqdata;
|
||||
GstFFTF32 *fft_ctx = cd->fft_ctx;
|
||||
|
||||
for (i = 0; i < nfft; i++)
|
||||
input_tmp[i] = input[(input_pos + i) % nfft];
|
||||
|
||||
gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
|
||||
|
||||
gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
|
||||
|
||||
if (spectrum->message_magnitude) {
|
||||
gdouble val;
|
||||
/* Calculate magnitude in db */
|
||||
for (i = 0; i < bands; i++) {
|
||||
val = freqdata[i].r * freqdata[i].r;
|
||||
val += freqdata[i].i * freqdata[i].i;
|
||||
val /= nfft * nfft;
|
||||
val = 10.0 * log10 (val);
|
||||
if (val < threshold)
|
||||
val = threshold;
|
||||
spect_magnitude[i] += val;
|
||||
}
|
||||
g_value_unset (&v);
|
||||
}
|
||||
|
||||
if (spectrum->message_phase) {
|
||||
/* FIXME 0.11: this should be an array, not a list */
|
||||
g_value_init (&v, GST_TYPE_LIST);
|
||||
/* will copy-by-value */
|
||||
gst_structure_set_value (s, "phase", &v);
|
||||
g_value_unset (&v);
|
||||
|
||||
g_value_init (&v, G_TYPE_FLOAT);
|
||||
l = (GValue *) gst_structure_get_value (s, "phase");
|
||||
for (i = 0; i < spectrum->bands; i++) {
|
||||
g_value_set_float (&v, spect_phase[i]);
|
||||
gst_value_list_append_value (l, &v); /* copies by value */
|
||||
}
|
||||
g_value_unset (&v);
|
||||
/* Calculate phase */
|
||||
for (i = 0; i < bands; i++)
|
||||
spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
|
||||
}
|
||||
}
|
||||
|
||||
return gst_message_new_element (GST_OBJECT (spectrum), s);
|
||||
static void
|
||||
gst_spectrum_prepare_message_data (GstSpectrum * spectrum,
|
||||
GstSpectrumChannel * cd)
|
||||
{
|
||||
guint i;
|
||||
guint bands = spectrum->bands;
|
||||
guint num_fft = spectrum->num_fft;
|
||||
|
||||
/* Calculate average */
|
||||
if (spectrum->message_magnitude) {
|
||||
gfloat *spect_magnitude = cd->spect_magnitude;
|
||||
for (i = 0; i < bands; i++)
|
||||
spect_magnitude[i] /= num_fft;
|
||||
}
|
||||
if (spectrum->message_phase) {
|
||||
gfloat *spect_phase = cd->spect_phase;
|
||||
for (i = 0; i < bands; i++)
|
||||
spect_phase[i] /= num_fft;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_spectrum_reset_message_data (GstSpectrum * spectrum,
|
||||
GstSpectrumChannel * cd)
|
||||
{
|
||||
guint bands = spectrum->bands;
|
||||
gfloat *spect_magnitude = cd->spect_magnitude;
|
||||
gfloat *spect_phase = cd->spect_phase;
|
||||
|
||||
/* reset spectrum accumulators */
|
||||
memset (spect_magnitude, 0, bands * sizeof (gfloat));
|
||||
memset (spect_phase, 0, bands * sizeof (gfloat));
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
|
||||
{
|
||||
GstSpectrum *spectrum = GST_SPECTRUM (trans);
|
||||
guint i;
|
||||
guint rate = GST_AUDIO_FILTER (spectrum)->format.rate;
|
||||
guint channels = GST_AUDIO_FILTER (spectrum)->format.channels;
|
||||
gfloat max_value =
|
||||
(1UL << (GST_AUDIO_FILTER (spectrum)->format.depth - 1)) - 1;
|
||||
guint width = GST_AUDIO_FILTER (spectrum)->format.width / 8;
|
||||
gboolean fp = (GST_AUDIO_FILTER (spectrum)->format.type == GST_BUFTYPE_FLOAT);
|
||||
GstRingBufferSpec *format = &GST_AUDIO_FILTER (spectrum)->format;
|
||||
guint rate = format->rate;
|
||||
guint channels = format->channels;
|
||||
guint output_channels = spectrum->multi_channel ? channels : 1;
|
||||
guint c;
|
||||
guint width = format->width / 8;
|
||||
gfloat max_value = (1UL << (format->depth - 1)) - 1;
|
||||
guint bands = spectrum->bands;
|
||||
guint nfft = 2 * bands - 2;
|
||||
gint threshold = spectrum->threshold;
|
||||
guint input_pos;
|
||||
gfloat *input;
|
||||
gfloat *input_tmp;
|
||||
GstFFTF32Complex *freqdata;
|
||||
gfloat *spect_magnitude;
|
||||
gfloat *spect_phase;
|
||||
GstFFTF32 *fft_ctx;
|
||||
const guint8 *data = GST_BUFFER_DATA (buffer);
|
||||
guint size = GST_BUFFER_SIZE (buffer);
|
||||
guint frame_size = width * channels;
|
||||
guint fft_todo, msg_todo, block_size;
|
||||
gboolean have_full_interval;
|
||||
GstSpectrumChannel *cd;
|
||||
GstSpectrumInputData input_data;
|
||||
|
||||
GST_LOG_OBJECT (spectrum, "input size: %d bytes", GST_BUFFER_SIZE (buffer));
|
||||
|
||||
|
@ -522,22 +1029,27 @@ gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
|
|||
/* If we don't have a FFT context yet (or it was reset due to parameter
|
||||
* changes) get one and allocate memory for everything
|
||||
*/
|
||||
if (spectrum->fft_ctx == NULL) {
|
||||
if (spectrum->channel_data == NULL) {
|
||||
GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
|
||||
|
||||
spectrum->fft_ctx = gst_fft_f32_new (nfft, FALSE);
|
||||
spectrum->input = g_new0 (gfloat, nfft);
|
||||
spectrum->input_tmp = g_new0 (gfloat, nfft);
|
||||
spectrum->freqdata = g_new0 (GstFFTF32Complex, bands);
|
||||
spectrum->spect_magnitude = g_new0 (gfloat, bands);
|
||||
spectrum->spect_phase = g_new0 (gfloat, bands);
|
||||
gst_spectrum_alloc_channel_data (spectrum);
|
||||
|
||||
/* number of sample frames we process before posting a message
|
||||
* interval is in ns */
|
||||
spectrum->frames_per_interval =
|
||||
gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND);
|
||||
spectrum->frames_todo = spectrum->frames_per_interval;
|
||||
/* rounding error for frames_per_interval in ns,
|
||||
* aggregated it in accumulated_error */
|
||||
spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
|
||||
if (spectrum->frames_per_interval == 0)
|
||||
spectrum->frames_per_interval = 1;
|
||||
|
||||
GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
|
||||
G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval,
|
||||
GST_TIME_ARGS (spectrum->error_per_interval));
|
||||
|
||||
spectrum->input_pos = 0;
|
||||
|
||||
gst_spectrum_flush (spectrum);
|
||||
|
@ -546,124 +1058,70 @@ gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
|
|||
if (spectrum->num_frames == 0)
|
||||
spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
|
||||
|
||||
input = spectrum->input;
|
||||
input_tmp = spectrum->input_tmp;
|
||||
freqdata = spectrum->freqdata;
|
||||
spect_magnitude = spectrum->spect_magnitude;
|
||||
spect_phase = spectrum->spect_phase;
|
||||
fft_ctx = spectrum->fft_ctx;
|
||||
|
||||
input_pos = spectrum->input_pos;
|
||||
input_data = spectrum->input_data;
|
||||
|
||||
while (size >= width * channels) {
|
||||
while (size >= frame_size) {
|
||||
/* run input_data for a chunk of data */
|
||||
fft_todo = nfft - (spectrum->num_frames % nfft);
|
||||
msg_todo = spectrum->frames_todo - spectrum->num_frames;
|
||||
GST_LOG_OBJECT (spectrum,
|
||||
"message frames todo: %u, fft frames todo: %u, input frames %u",
|
||||
msg_todo, fft_todo, (size / frame_size));
|
||||
block_size = msg_todo;
|
||||
if (block_size > (size / frame_size))
|
||||
block_size = (size / frame_size);
|
||||
if (block_size > fft_todo)
|
||||
block_size = fft_todo;
|
||||
|
||||
/* Move the current frame into our ringbuffer and
|
||||
* take the average of all channels
|
||||
*/
|
||||
input[input_pos] = 0.0;
|
||||
if (fp && width == 4) {
|
||||
gfloat *in = (gfloat *) data;
|
||||
for (i = 0; i < channels; i++)
|
||||
input[input_pos] += in[i];
|
||||
} else if (fp && width == 8) {
|
||||
gdouble *in = (gdouble *) data;
|
||||
for (i = 0; i < channels; i++)
|
||||
input[input_pos] += in[i];
|
||||
} else if (!fp && width == 4) {
|
||||
gint32 *in = (gint32 *) data;
|
||||
for (i = 0; i < channels; i++)
|
||||
/* max_value will be 0 when depth is 1, interpret -1 and 0
|
||||
* as -1 and +1 if that's the case.
|
||||
*/
|
||||
input[input_pos] += max_value ? in[i] / max_value : in[i] * 2 + 1;
|
||||
} else if (!fp && width == 3) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
gint32 value = GST_READ_UINT24_BE (data);
|
||||
#else
|
||||
gint32 value = GST_READ_UINT24_LE (data);
|
||||
#endif
|
||||
if (value & 0x00800000)
|
||||
value |= 0xff000000;
|
||||
input[input_pos] += max_value ? value / max_value : value * 2 + 1;
|
||||
}
|
||||
} else if (!fp && width == 2) {
|
||||
gint16 *in = (gint16 *) data;
|
||||
for (i = 0; i < channels; i++)
|
||||
input[input_pos] += max_value ? in[i] / max_value : in[i] * 2 + 1;
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
for (c = 0; c < output_channels; c++) {
|
||||
cd = &spectrum->channel_data[c];
|
||||
input = cd->input;
|
||||
/* Move the current frames into our ringbuffers */
|
||||
input_data (data + c * width, input, block_size, channels, max_value,
|
||||
input_pos, nfft);
|
||||
}
|
||||
input[input_pos] /= channels;
|
||||
data += block_size * frame_size;
|
||||
size -= block_size * frame_size;
|
||||
input_pos = (input_pos + block_size) % nfft;
|
||||
spectrum->num_frames += block_size;
|
||||
|
||||
data += width * channels;
|
||||
size -= width * channels;
|
||||
input_pos = (input_pos + 1) % nfft;
|
||||
spectrum->num_frames++;
|
||||
have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
|
||||
|
||||
have_full_interval = (
|
||||
(spectrum->accumulated_error < GST_SECOND
|
||||
&& spectrum->num_frames == spectrum->frames_per_interval) ||
|
||||
(spectrum->accumulated_error >= GST_SECOND
|
||||
&& spectrum->num_frames - 1 == spectrum->frames_per_interval)
|
||||
);
|
||||
GST_LOG_OBJECT (spectrum, "size: %u, do-fft = %d, do-message = %d", size,
|
||||
(spectrum->num_frames % nfft == 0), have_full_interval);
|
||||
|
||||
/* If we have enough frames for an FFT or we
|
||||
* have all frames required for the interval run
|
||||
* an FFT. In the last case we probably take the
|
||||
* FFT of frames that we already handled.
|
||||
*/
|
||||
if ((spectrum->num_frames % nfft == 0) || have_full_interval) {
|
||||
|
||||
for (i = 0; i < nfft; i++)
|
||||
input_tmp[i] = input[(input_pos + i) % nfft];
|
||||
|
||||
gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
|
||||
|
||||
gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
|
||||
/* If we have enough frames for an FFT or we have all frames required for
|
||||
* the interval and we haven't run a FFT, then run an FFT */
|
||||
if ((spectrum->num_frames % nfft == 0) ||
|
||||
(have_full_interval && !spectrum->num_fft)) {
|
||||
for (c = 0; c < output_channels; c++) {
|
||||
cd = &spectrum->channel_data[c];
|
||||
gst_spectrum_run_fft (spectrum, cd, input_pos);
|
||||
}
|
||||
spectrum->num_fft++;
|
||||
|
||||
if (spectrum->message_magnitude) {
|
||||
gdouble val;
|
||||
/* Calculate magnitude in db */
|
||||
for (i = 0; i < bands; i++) {
|
||||
val = freqdata[i].r * freqdata[i].r;
|
||||
val += freqdata[i].i * freqdata[i].i;
|
||||
val /= nfft * nfft;
|
||||
val = 10.0 * log10 (val);
|
||||
if (val < threshold)
|
||||
val = threshold;
|
||||
spect_magnitude[i] += val;
|
||||
}
|
||||
}
|
||||
|
||||
if (spectrum->message_phase) {
|
||||
/* Calculate phase */
|
||||
for (i = 0; i < bands; i++)
|
||||
spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
|
||||
}
|
||||
}
|
||||
|
||||
/* Do we have the FFTs for one interval? */
|
||||
if (have_full_interval) {
|
||||
|
||||
GST_INFO ("nfft: %u num_frames: %" G_GUINT64_FORMAT " fpi: %"
|
||||
G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
|
||||
GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT
|
||||
" fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
|
||||
spectrum->num_frames, spectrum->frames_per_interval,
|
||||
GST_TIME_ARGS (spectrum->accumulated_error));
|
||||
|
||||
if (spectrum->accumulated_error >= GST_SECOND)
|
||||
spectrum->frames_todo = spectrum->frames_per_interval;
|
||||
if (spectrum->accumulated_error >= GST_SECOND) {
|
||||
spectrum->accumulated_error -= GST_SECOND;
|
||||
else
|
||||
spectrum->accumulated_error += spectrum->error_per_interval;
|
||||
spectrum->frames_todo++;
|
||||
}
|
||||
spectrum->accumulated_error += spectrum->error_per_interval;
|
||||
|
||||
if (spectrum->post_messages) {
|
||||
GstMessage *m;
|
||||
|
||||
/* Calculate average */
|
||||
for (i = 0; i < bands; i++) {
|
||||
spect_magnitude[i] /= spectrum->num_fft;
|
||||
spect_phase[i] /= spectrum->num_fft;
|
||||
for (c = 0; c < output_channels; c++) {
|
||||
cd = &spectrum->channel_data[c];
|
||||
gst_spectrum_prepare_message_data (spectrum, cd);
|
||||
}
|
||||
|
||||
m = gst_spectrum_message_new (spectrum, spectrum->message_ts,
|
||||
|
@ -676,9 +1134,10 @@ gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
|
|||
spectrum->message_ts +=
|
||||
gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
|
||||
|
||||
/* reset spectrum accumulators */
|
||||
memset (spect_magnitude, 0, bands * sizeof (gfloat));
|
||||
memset (spect_phase, 0, bands * sizeof (gfloat));
|
||||
for (c = 0; c < channels; c++) {
|
||||
cd = &spectrum->channel_data[c];
|
||||
gst_spectrum_reset_message_data (spectrum, cd);
|
||||
}
|
||||
spectrum->num_frames = 0;
|
||||
spectrum->num_fft = 0;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,20 @@ G_BEGIN_DECLS
|
|||
#define GST_IS_SPECTRUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_SPECTRUM))
|
||||
typedef struct _GstSpectrum GstSpectrum;
|
||||
typedef struct _GstSpectrumClass GstSpectrumClass;
|
||||
typedef struct _GstSpectrumChannel GstSpectrumChannel;
|
||||
|
||||
typedef void (*GstSpectrumInputData)(const guint8 * in, gfloat * out,
|
||||
guint len, guint channels, gfloat max_value, guint op, guint nfft);
|
||||
|
||||
struct _GstSpectrumChannel
|
||||
{
|
||||
gfloat *input;
|
||||
gfloat *input_tmp;
|
||||
GstFFTF32Complex *freqdata;
|
||||
gfloat *spect_magnitude; /* accumulated mangitude and phase */
|
||||
gfloat *spect_phase; /* will be scaled by num_fft before sending */
|
||||
GstFFTF32 *fft_ctx;
|
||||
};
|
||||
|
||||
struct _GstSpectrum
|
||||
{
|
||||
|
@ -46,8 +60,10 @@ struct _GstSpectrum
|
|||
gboolean message_phase;
|
||||
guint64 interval; /* how many nanoseconds between emits */
|
||||
guint64 frames_per_interval; /* how many frames per interval */
|
||||
guint64 frames_todo;
|
||||
guint bands; /* number of spectrum bands */
|
||||
gint threshold; /* energy level treshold */
|
||||
gboolean multi_channel; /* send separate channel results */
|
||||
|
||||
guint64 num_frames; /* frame count (1 sample per channel)
|
||||
* since last emit */
|
||||
|
@ -55,16 +71,14 @@ struct _GstSpectrum
|
|||
GstClockTime message_ts; /* starttime for next message */
|
||||
|
||||
/* <private> */
|
||||
gfloat *input;
|
||||
guint input_pos;
|
||||
gfloat *input_tmp;
|
||||
GstFFTF32Complex *freqdata;
|
||||
gfloat *spect_magnitude; /* accumulated mangitude and phase */
|
||||
gfloat *spect_phase; /* will be scaled by num_fft before sending */
|
||||
GstFFTF32 *fft_ctx;
|
||||
GstSpectrumChannel *channel_data;
|
||||
guint num_channels;
|
||||
|
||||
guint input_pos;
|
||||
guint64 error_per_interval;
|
||||
guint64 accumulated_error;
|
||||
|
||||
GstSpectrumInputData input_data;
|
||||
};
|
||||
|
||||
struct _GstSpectrumClass
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* Copyright (C) <2003> David Schleef <ds@schleef.org>
|
||||
* Copyright (C) <2010> Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
* Copyright (C) <2011> Youness Alaoui <youness.alaoui@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -75,7 +76,10 @@ static GstStaticPadTemplate gst_video_flip_src_template =
|
|||
GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
|
||||
GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
|
||||
GST_VIDEO_CAPS_YUV ("I420") ";"
|
||||
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV")
|
||||
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV") ";"
|
||||
GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
|
||||
GST_VIDEO_CAPS_YUV ("YVYU")
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -91,7 +95,9 @@ static GstStaticPadTemplate gst_video_flip_sink_template =
|
|||
GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
|
||||
GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
|
||||
GST_VIDEO_CAPS_YUV ("I420") ";"
|
||||
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV")
|
||||
GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV") ";"
|
||||
GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
|
||||
GST_VIDEO_CAPS_YUV ("YVYU")
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -563,6 +569,225 @@ gst_video_flip_packed_simple (GstVideoFlip * videoflip, guint8 * dest,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_video_flip_y422 (GstVideoFlip * videoflip, guint8 * dest,
|
||||
const guint8 * src)
|
||||
{
|
||||
gint x, y;
|
||||
guint8 const *s = src;
|
||||
guint8 *d = dest;
|
||||
GstVideoFormat format = videoflip->format;
|
||||
gint sw = videoflip->from_width;
|
||||
gint sh = videoflip->from_height;
|
||||
gint dw = videoflip->to_width;
|
||||
gint dh = videoflip->to_height;
|
||||
gint src_stride, dest_stride;
|
||||
gint bpp;
|
||||
gint y_offset;
|
||||
gint u_offset;
|
||||
gint v_offset;
|
||||
gint y_stride;
|
||||
gint u_stride;
|
||||
gint v_stride;
|
||||
|
||||
src_stride = gst_video_format_get_row_stride (format, 0, sw);
|
||||
dest_stride = gst_video_format_get_row_stride (format, 0, dw);
|
||||
|
||||
y_offset = gst_video_format_get_component_offset (format, 0, sw, sh);
|
||||
u_offset = gst_video_format_get_component_offset (format, 1, sw, sh);
|
||||
v_offset = gst_video_format_get_component_offset (format, 2, sw, sh);
|
||||
y_stride = gst_video_format_get_pixel_stride (format, 0);
|
||||
u_stride = gst_video_format_get_pixel_stride (format, 1);
|
||||
v_stride = gst_video_format_get_pixel_stride (format, 2);
|
||||
bpp = y_stride;
|
||||
|
||||
switch (videoflip->method) {
|
||||
case GST_VIDEO_FLIP_METHOD_90R:
|
||||
for (y = 0; y < dh; y++) {
|
||||
for (x = 0; x < dw; x += 2) {
|
||||
guint8 u;
|
||||
guint8 v;
|
||||
/* u/v must be calculated using the offset of the even column */
|
||||
gint even_y = (y & ~1);
|
||||
|
||||
u = s[(sh - 1 - x) * src_stride + even_y * bpp + u_offset];
|
||||
if (x + 1 < dw)
|
||||
u = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + u_offset]
|
||||
+ u) >> 1;
|
||||
v = s[(sh - 1 - x) * src_stride + even_y * bpp + v_offset];
|
||||
if (x + 1 < dw)
|
||||
v = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + v_offset]
|
||||
+ v) >> 1;
|
||||
|
||||
d[y * dest_stride + x * bpp + u_offset] = u;
|
||||
d[y * dest_stride + x * bpp + v_offset] = v;
|
||||
d[y * dest_stride + x * bpp + y_offset] =
|
||||
s[(sh - 1 - x) * src_stride + y * bpp + y_offset];
|
||||
if (x + 1 < dw)
|
||||
d[y * dest_stride + (x + 1) * bpp + y_offset] =
|
||||
s[(sh - 1 - (x + 1)) * src_stride + y * bpp + y_offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_VIDEO_FLIP_METHOD_90L:
|
||||
for (y = 0; y < dh; y++) {
|
||||
for (x = 0; x < dw; x += 2) {
|
||||
guint8 u;
|
||||
guint8 v;
|
||||
/* u/v must be calculated using the offset of the even column */
|
||||
gint even_y = ((sw - 1 - y) & ~1);
|
||||
|
||||
u = s[x * src_stride + even_y * bpp + u_offset];
|
||||
if (x + 1 < dw)
|
||||
u = (s[(x + 1) * src_stride + even_y * bpp + u_offset] + u) >> 1;
|
||||
v = s[x * src_stride + even_y * bpp + v_offset];
|
||||
if (x + 1 < dw)
|
||||
v = (s[(x + 1) * src_stride + even_y * bpp + v_offset] + v) >> 1;
|
||||
|
||||
d[y * dest_stride + x * bpp + u_offset] = u;
|
||||
d[y * dest_stride + x * bpp + v_offset] = v;
|
||||
d[y * dest_stride + x * bpp + y_offset] =
|
||||
s[x * src_stride + (sw - 1 - y) * bpp + y_offset];
|
||||
if (x + 1 < dw)
|
||||
d[y * dest_stride + (x + 1) * bpp + y_offset] =
|
||||
s[(x + 1) * src_stride + (sw - 1 - y) * bpp + y_offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_VIDEO_FLIP_METHOD_180:
|
||||
for (y = 0; y < dh; y++) {
|
||||
for (x = 0; x < dw; x += 2) {
|
||||
guint8 u;
|
||||
guint8 v;
|
||||
/* u/v must be calculated using the offset of the even column */
|
||||
gint even_x = ((sw - 1 - x) & ~1);
|
||||
|
||||
u = (s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset] +
|
||||
s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset]) / 2;
|
||||
v = (s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset] +
|
||||
s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset]) / 2;
|
||||
|
||||
d[y * dest_stride + x * bpp + u_offset] = u;
|
||||
d[y * dest_stride + x * bpp + v_offset] = v;
|
||||
d[y * dest_stride + x * bpp + y_offset] =
|
||||
s[(sh - 1 - y) * src_stride + (sw - 1 - x) * bpp + y_offset];
|
||||
if (x + 1 < dw)
|
||||
d[y * dest_stride + (x + 1) * bpp + y_offset] =
|
||||
s[(sh - 1 - y) * src_stride + (sw - 1 - (x + 1)) * bpp +
|
||||
y_offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_VIDEO_FLIP_METHOD_HORIZ:
|
||||
for (y = 0; y < dh; y++) {
|
||||
for (x = 0; x < dw; x += 2) {
|
||||
guint8 u;
|
||||
guint8 v;
|
||||
/* u/v must be calculated using the offset of the even column */
|
||||
gint even_x = ((sw - 1 - x) & ~1);
|
||||
|
||||
u = (s[y * src_stride + even_x * bpp + u_offset] +
|
||||
s[y * src_stride + even_x * bpp + u_offset]) / 2;
|
||||
v = (s[y * src_stride + even_x * bpp + v_offset] +
|
||||
s[y * src_stride + even_x * bpp + v_offset]) / 2;
|
||||
|
||||
d[y * dest_stride + x * bpp + u_offset] = u;
|
||||
d[y * dest_stride + x * bpp + v_offset] = v;
|
||||
d[y * dest_stride + x * bpp + y_offset] =
|
||||
s[y * src_stride + (sw - 1 - x) * bpp + y_offset];
|
||||
if (x + 1 < dw)
|
||||
d[y * dest_stride + (x + 1) * bpp + y_offset] =
|
||||
s[y * src_stride + (sw - 1 - (x + 1)) * bpp + y_offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_VIDEO_FLIP_METHOD_VERT:
|
||||
for (y = 0; y < dh; y++) {
|
||||
for (x = 0; x < dw; x += 2) {
|
||||
guint8 u;
|
||||
guint8 v;
|
||||
/* u/v must be calculated using the offset of the even column */
|
||||
gint even_x = (x & ~1);
|
||||
|
||||
u = (s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset] +
|
||||
s[(sh - 1 - y) * src_stride + even_x * bpp + u_offset]) / 2;
|
||||
v = (s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset] +
|
||||
s[(sh - 1 - y) * src_stride + even_x * bpp + v_offset]) / 2;
|
||||
|
||||
d[y * dest_stride + x * bpp + u_offset] = u;
|
||||
d[y * dest_stride + x * bpp + v_offset] = v;
|
||||
d[y * dest_stride + x * bpp + y_offset] =
|
||||
s[(sh - 1 - y) * src_stride + x * bpp + y_offset];
|
||||
if (x + 1 < dw)
|
||||
d[y * dest_stride + (x + 1) * bpp + y_offset] =
|
||||
s[(sh - 1 - y) * src_stride + (x + 1) * bpp + y_offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_VIDEO_FLIP_METHOD_TRANS:
|
||||
for (y = 0; y < dh; y++) {
|
||||
for (x = 0; x < dw; x += 2) {
|
||||
guint8 u;
|
||||
guint8 v;
|
||||
/* u/v must be calculated using the offset of the even column */
|
||||
gint even_y = (y & ~1);
|
||||
|
||||
u = s[x * src_stride + even_y * bpp + u_offset];
|
||||
if (x + 1 < dw)
|
||||
u = (s[(x + 1) * src_stride + even_y * bpp + u_offset] + u) >> 1;
|
||||
v = s[x * src_stride + even_y * bpp + v_offset];
|
||||
if (x + 1 < dw)
|
||||
v = (s[(x + 1) * src_stride + even_y * bpp + v_offset] + v) >> 1;
|
||||
|
||||
d[y * dest_stride + x * bpp + u_offset] = u;
|
||||
d[y * dest_stride + x * bpp + v_offset] = v;
|
||||
d[y * dest_stride + x * bpp + y_offset] =
|
||||
s[x * src_stride + y * bpp + y_offset];
|
||||
if (x + 1 < dw)
|
||||
d[y * dest_stride + (x + 1) * bpp + y_offset] =
|
||||
s[(x + 1) * src_stride + y * bpp + y_offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_VIDEO_FLIP_METHOD_OTHER:
|
||||
for (y = 0; y < dh; y++) {
|
||||
for (x = 0; x < dw; x += 2) {
|
||||
guint8 u;
|
||||
guint8 v;
|
||||
/* u/v must be calculated using the offset of the even column */
|
||||
gint even_y = ((sw - 1 - y) & ~1);
|
||||
|
||||
u = s[(sh - 1 - x) * src_stride + even_y * bpp + u_offset];
|
||||
if (x + 1 < dw)
|
||||
u = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + u_offset]
|
||||
+ u) >> 1;
|
||||
v = s[(sh - 1 - x) * src_stride + even_y * bpp + v_offset];
|
||||
if (x + 1 < dw)
|
||||
v = (s[(sh - 1 - (x + 1)) * src_stride + even_y * bpp + v_offset]
|
||||
+ v) >> 1;
|
||||
|
||||
d[y * dest_stride + x * bpp + u_offset] = u;
|
||||
d[y * dest_stride + x * bpp + v_offset] = v;
|
||||
d[y * dest_stride + x * bpp + y_offset] =
|
||||
s[(sh - 1 - x) * src_stride + (sw - 1 - y) * bpp + y_offset];
|
||||
if (x + 1 < dw)
|
||||
d[y * dest_stride + (x + 1) * bpp + y_offset] =
|
||||
s[(sh - 1 - (x + 1)) * src_stride + (sw - 1 - y) * bpp +
|
||||
y_offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_VIDEO_FLIP_METHOD_IDENTITY:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_video_flip_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
|
||||
GstCaps * outcaps)
|
||||
|
@ -624,6 +849,11 @@ gst_video_flip_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
|
|||
case GST_VIDEO_FORMAT_Y444:
|
||||
vf->process = gst_video_flip_planar_yuv;
|
||||
break;
|
||||
case GST_VIDEO_FORMAT_YUY2:
|
||||
case GST_VIDEO_FORMAT_UYVY:
|
||||
case GST_VIDEO_FORMAT_YVYU:
|
||||
vf->process = gst_video_flip_y422;
|
||||
break;
|
||||
case GST_VIDEO_FORMAT_AYUV:
|
||||
case GST_VIDEO_FORMAT_ARGB:
|
||||
case GST_VIDEO_FORMAT_ABGR:
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#ifdef HAVE_EXPERIMENTAL
|
||||
#include "gstv4l2sink.h"
|
||||
#endif
|
||||
#include "gstv4l2radio.h"
|
||||
/* #include "gstv4l2jpegsrc.h" */
|
||||
/* #include "gstv4l2mjpegsrc.h" */
|
||||
/* #include "gstv4l2mjpegsink.h" */
|
||||
|
@ -58,6 +59,8 @@ plugin_init (GstPlugin * plugin)
|
|||
!gst_element_register (plugin, "v4l2sink", GST_RANK_NONE,
|
||||
GST_TYPE_V4L2SINK) ||
|
||||
#endif
|
||||
!gst_element_register (plugin, "v4l2radio", GST_RANK_NONE,
|
||||
GST_TYPE_V4L2RADIO) ||
|
||||
/* !gst_element_register (plugin, "v4l2jpegsrc", */
|
||||
/* GST_RANK_NONE, GST_TYPE_V4L2JPEGSRC) || */
|
||||
/* !gst_element_register (plugin, "v4l2mjpegsrc", */
|
||||
|
|
633
sys/v4l2/gstv4l2radio.c
Normal file
633
sys/v4l2/gstv4l2radio.c
Normal file
|
@ -0,0 +1,633 @@
|
|||
/* GStreamer v4l2 radio tuner element
|
||||
* Copyright (C) 2010, 2011 Alexey Chernov <4ernov@gmail.com>
|
||||
*
|
||||
* gstv4l2radio.c - V4L2 radio tuner element
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-v4l2radio
|
||||
*
|
||||
* v4l2radio can be used to control radio device
|
||||
* and to tune it to different radiostations.
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch lines</title>
|
||||
* |[
|
||||
* gst-launch v4l2radio device=/dev/radio0 frequency=101200000
|
||||
* gst-launch alsasrc device=hw:1 ! audioconvert ! audioresample ! alsasink
|
||||
* ]|
|
||||
* First pipeline tunes the radio device /dev/radio0 to station 101.2 MHz,
|
||||
* second pipeline connects digital audio out (hw:1) to default sound card.
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gst/gst-i18n-plugin.h"
|
||||
|
||||
#include "gstv4l2tuner.h"
|
||||
#include "gstv4l2radio.h"
|
||||
#include "v4l2_calls.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (v4l2radio_debug);
|
||||
#define GST_CAT_DEFAULT v4l2radio_debug
|
||||
|
||||
#define DEFAULT_PROP_DEVICE "/dev/radio0"
|
||||
#define MIN_FREQUENCY 87500000
|
||||
#define DEFAULT_FREQUENCY 100000000
|
||||
#define MAX_FREQUENCY 108000000
|
||||
|
||||
enum
|
||||
{
|
||||
ARG_0,
|
||||
ARG_DEVICE,
|
||||
ARG_FREQUENCY
|
||||
};
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_fill_channel_list (GstV4l2Radio * radio)
|
||||
{
|
||||
int res;
|
||||
struct v4l2_tuner vtun;
|
||||
struct v4l2_capability vc;
|
||||
GstV4l2TunerChannel *v4l2channel;
|
||||
GstTunerChannel *channel;
|
||||
|
||||
GstElement *e;
|
||||
|
||||
GstV4l2Object *v4l2object;
|
||||
|
||||
e = GST_ELEMENT (radio);
|
||||
v4l2object = radio->v4l2object;
|
||||
|
||||
GST_DEBUG_OBJECT (e, "getting audio enumeration");
|
||||
GST_V4L2_CHECK_OPEN (v4l2object);
|
||||
|
||||
GST_DEBUG_OBJECT (e, " audio input");
|
||||
|
||||
memset (&vc, 0, sizeof (vc));
|
||||
|
||||
res = v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &vc);
|
||||
if (res < 0)
|
||||
goto caps_failed;
|
||||
|
||||
if (!(vc.capabilities & V4L2_CAP_TUNER))
|
||||
goto not_a_tuner;
|
||||
|
||||
/* getting audio input */
|
||||
memset (&vtun, 0, sizeof (vtun));
|
||||
vtun.index = 0;
|
||||
|
||||
res = v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &vtun);
|
||||
if (res < 0)
|
||||
goto tuner_failed;
|
||||
|
||||
GST_LOG_OBJECT (e, " index: %d", vtun.index);
|
||||
GST_LOG_OBJECT (e, " name: '%s'", vtun.name);
|
||||
GST_LOG_OBJECT (e, " type: %016x", (guint) vtun.type);
|
||||
GST_LOG_OBJECT (e, " caps: %016x", (guint) vtun.capability);
|
||||
GST_LOG_OBJECT (e, " rlow: %016x", (guint) vtun.rangelow);
|
||||
GST_LOG_OBJECT (e, " rhigh: %016x", (guint) vtun.rangehigh);
|
||||
GST_LOG_OBJECT (e, " audmode: %016x", (guint) vtun.audmode);
|
||||
|
||||
v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL);
|
||||
channel = GST_TUNER_CHANNEL (v4l2channel);
|
||||
channel->label = g_strdup ((const gchar *) vtun.name);
|
||||
channel->flags = GST_TUNER_CHANNEL_FREQUENCY | GST_TUNER_CHANNEL_AUDIO;
|
||||
v4l2channel->index = 0;
|
||||
v4l2channel->tuner = 0;
|
||||
|
||||
channel->freq_multiplicator =
|
||||
62.5 * ((vtun.capability & V4L2_TUNER_CAP_LOW) ? 1 : 1000);
|
||||
channel->min_frequency = vtun.rangelow * channel->freq_multiplicator;
|
||||
channel->max_frequency = vtun.rangehigh * channel->freq_multiplicator;
|
||||
channel->min_signal = 0;
|
||||
channel->max_signal = 0xffff;
|
||||
|
||||
v4l2object->channels =
|
||||
g_list_prepend (v4l2object->channels, (gpointer) channel);
|
||||
|
||||
v4l2object->channels = g_list_reverse (v4l2object->channels);
|
||||
|
||||
GST_DEBUG_OBJECT (e, "done");
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
tuner_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
|
||||
(_("Failed to get settings of tuner %d on device '%s'."),
|
||||
vtun.index, v4l2object->videodev), GST_ERROR_SYSTEM);
|
||||
return FALSE;
|
||||
}
|
||||
caps_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
|
||||
(_("Error getting capabilities for device '%s'."),
|
||||
v4l2object->videodev), GST_ERROR_SYSTEM);
|
||||
return FALSE;
|
||||
}
|
||||
not_a_tuner:
|
||||
{
|
||||
GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
|
||||
(_("Device '%s' is not a tuner."),
|
||||
v4l2object->videodev), GST_ERROR_SYSTEM);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_get_input (GstV4l2Object * v4l2object, gint * input)
|
||||
{
|
||||
GST_DEBUG_OBJECT (v4l2object->element, "trying to get radio input");
|
||||
|
||||
if (!GST_V4L2_IS_OPEN (v4l2object))
|
||||
return FALSE;
|
||||
|
||||
if (!v4l2object->channels)
|
||||
goto input_failed;
|
||||
|
||||
*input = 0;
|
||||
|
||||
GST_DEBUG_OBJECT (v4l2object->element, "input: %d", 0);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
input_failed:
|
||||
{
|
||||
GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
|
||||
(_("Failed to get radio input on device '%s'. "),
|
||||
v4l2object->videodev), GST_ERROR_SYSTEM);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_set_input (GstV4l2Object * v4l2object, gint input)
|
||||
{
|
||||
GST_DEBUG_OBJECT (v4l2object->element, "trying to set input to %d", input);
|
||||
|
||||
if (!GST_V4L2_IS_OPEN (v4l2object))
|
||||
return FALSE;
|
||||
|
||||
if (!v4l2object->channels)
|
||||
goto input_failed;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
input_failed:
|
||||
{
|
||||
GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
|
||||
(_("Failed to set input %d on device %s."),
|
||||
input, v4l2object->videodev), GST_ERROR_SYSTEM);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_set_mute_on (GstV4l2Radio * radio, gboolean on)
|
||||
{
|
||||
gint res;
|
||||
struct v4l2_control vctrl;
|
||||
|
||||
GST_DEBUG_OBJECT (radio, "setting current tuner mute state: %d", on);
|
||||
|
||||
if (!GST_V4L2_IS_OPEN (radio->v4l2object))
|
||||
return FALSE;
|
||||
|
||||
memset (&vctrl, 0, sizeof (vctrl));
|
||||
vctrl.id = V4L2_CID_AUDIO_MUTE;
|
||||
vctrl.value = on;
|
||||
|
||||
GST_DEBUG_OBJECT (radio, "radio fd: %d", radio->v4l2object->video_fd);
|
||||
|
||||
res = ioctl (radio->v4l2object->video_fd, VIDIOC_S_CTRL, &vctrl);
|
||||
GST_DEBUG_OBJECT (radio, "mute state change result: %d", res);
|
||||
if (res < 0)
|
||||
goto freq_failed;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
freq_failed:
|
||||
{
|
||||
GST_ELEMENT_WARNING (radio, RESOURCE, SETTINGS,
|
||||
(_("Failed to change mute state for device '%s'."),
|
||||
radio->v4l2object->videodev), GST_ERROR_SYSTEM);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_set_mute (GstV4l2Radio * radio)
|
||||
{
|
||||
return gst_v4l2radio_set_mute_on (radio, TRUE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_set_unmute (GstV4l2Radio * radio)
|
||||
{
|
||||
return gst_v4l2radio_set_mute_on (radio, FALSE);
|
||||
}
|
||||
|
||||
GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2RadioClass, gst_v4l2radio);
|
||||
GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Radio, gst_v4l2radio);
|
||||
|
||||
static void gst_v4l2radio_uri_handler_init (gpointer g_iface,
|
||||
gpointer iface_data);
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_interface_supported (GstImplementsInterface * iface,
|
||||
GType iface_type)
|
||||
{
|
||||
if (iface_type == GST_TYPE_TUNER)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_implements_interface_init (GstImplementsInterfaceClass * iface)
|
||||
{
|
||||
iface->supported = gst_v4l2radio_interface_supported;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_tuner_interface_reinit (GstTunerClass * iface)
|
||||
{
|
||||
gst_v4l2radio_tuner_interface_init (iface);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_interfaces (GType type)
|
||||
{
|
||||
static const GInterfaceInfo urihandler_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2radio_uri_handler_init,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const GInterfaceInfo implements_interface_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2radio_implements_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const GInterfaceInfo propertyprobe_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2radio_property_probe_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const GInterfaceInfo tuner_interface_info = {
|
||||
(GInterfaceInitFunc) gst_v4l2radio_tuner_interface_reinit,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
|
||||
g_type_add_interface_static (type,
|
||||
GST_TYPE_IMPLEMENTS_INTERFACE, &implements_interface_info);
|
||||
|
||||
g_type_add_interface_static (type, GST_TYPE_TUNER, &tuner_interface_info);
|
||||
|
||||
g_type_add_interface_static (type,
|
||||
GST_TYPE_PROPERTY_PROBE, &propertyprobe_info);
|
||||
}
|
||||
|
||||
GST_BOILERPLATE_FULL (GstV4l2Radio, gst_v4l2radio, GstElement, GST_TYPE_ELEMENT,
|
||||
gst_v4l2radio_interfaces);
|
||||
|
||||
static void gst_v4l2radio_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_v4l2radio_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_v4l2radio_finalize (GstV4l2Radio * radio);
|
||||
static void gst_v4l2radio_dispose (GObject * object);
|
||||
static GstStateChangeReturn gst_v4l2radio_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
static void
|
||||
gst_v4l2radio_base_init (gpointer gclass)
|
||||
{
|
||||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (gclass);
|
||||
GstV4l2RadioClass *gstv4l2radio_class = GST_V4L2RADIO_CLASS (gclass);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (v4l2radio_debug, "v4l2radio", 0,
|
||||
"V4l2 radio element");
|
||||
|
||||
gstv4l2radio_class->v4l2_class_devices = NULL;
|
||||
|
||||
gst_element_class_set_details_simple (gstelement_class,
|
||||
"Radio (video4linux2) Tuner",
|
||||
"Tuner",
|
||||
"Controls a Video4Linux2 radio device",
|
||||
"Alexey Chernov <4ernov@gmail.com>");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_class_init (GstV4l2RadioClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
|
||||
gobject_class->set_property = gst_v4l2radio_set_property;
|
||||
gobject_class->get_property = gst_v4l2radio_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class, ARG_DEVICE,
|
||||
g_param_spec_string ("device", "Radio device location",
|
||||
"Video4Linux2 radio device location",
|
||||
DEFAULT_PROP_DEVICE, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, ARG_FREQUENCY,
|
||||
g_param_spec_int ("frequency", "Station frequency",
|
||||
"Station frequency in Hz",
|
||||
MIN_FREQUENCY, MAX_FREQUENCY, DEFAULT_FREQUENCY, G_PARAM_READWRITE));
|
||||
|
||||
gobject_class->dispose = gst_v4l2radio_dispose;
|
||||
gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2radio_finalize;
|
||||
|
||||
gstelement_class->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_v4l2radio_change_state);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_init (GstV4l2Radio * filter, GstV4l2RadioClass * gclass)
|
||||
{
|
||||
filter->v4l2object = gst_v4l2_object_new (GST_ELEMENT (filter),
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE,
|
||||
gst_v4l2radio_get_input, gst_v4l2radio_set_input, NULL);
|
||||
|
||||
filter->v4l2object->frequency = DEFAULT_FREQUENCY;
|
||||
filter->v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_dispose (GObject * object)
|
||||
{
|
||||
GstV4l2Radio *radio = GST_V4L2RADIO (object);
|
||||
gst_v4l2_close (radio->v4l2object);
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_finalize (GstV4l2Radio * radio)
|
||||
{
|
||||
gst_v4l2_object_destroy (radio->v4l2object);
|
||||
G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (radio));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_open (GstV4l2Radio * radio)
|
||||
{
|
||||
GstV4l2Object *v4l2object;
|
||||
|
||||
v4l2object = radio->v4l2object;
|
||||
if (gst_v4l2_open (v4l2object))
|
||||
return gst_v4l2radio_fill_channel_list (radio);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_set_defaults (GstV4l2Radio * radio)
|
||||
{
|
||||
GstV4l2Object *v4l2object;
|
||||
GstTunerChannel *channel = NULL;
|
||||
GstTuner *tuner;
|
||||
|
||||
v4l2object = radio->v4l2object;
|
||||
|
||||
if (!GST_IS_TUNER (v4l2object->element))
|
||||
return;
|
||||
|
||||
tuner = GST_TUNER (v4l2object->element);
|
||||
|
||||
if (v4l2object->channel)
|
||||
channel = gst_tuner_find_channel_by_name (tuner, v4l2object->channel);
|
||||
if (channel) {
|
||||
gst_tuner_set_channel (tuner, channel);
|
||||
} else {
|
||||
channel =
|
||||
GST_TUNER_CHANNEL (gst_tuner_get_channel (GST_TUNER
|
||||
(v4l2object->element)));
|
||||
if (channel) {
|
||||
g_free (v4l2object->channel);
|
||||
v4l2object->channel = g_strdup (channel->label);
|
||||
gst_tuner_channel_changed (tuner, channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (channel
|
||||
&& GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) {
|
||||
if (v4l2object->frequency != 0) {
|
||||
gst_tuner_set_frequency (tuner, channel, v4l2object->frequency);
|
||||
} else {
|
||||
v4l2object->frequency = gst_tuner_get_frequency (tuner, channel);
|
||||
if (v4l2object->frequency == 0) {
|
||||
/* guess */
|
||||
gst_tuner_set_frequency (tuner, channel, MIN_FREQUENCY);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_start (GstV4l2Radio * radio)
|
||||
{
|
||||
if (!gst_v4l2radio_open (radio))
|
||||
return FALSE;
|
||||
|
||||
gst_v4l2radio_set_defaults (radio);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_stop (GstV4l2Radio * radio)
|
||||
{
|
||||
if (!gst_v4l2_object_stop (radio->v4l2object))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_v4l2radio_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstV4l2Radio *radio;
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
radio = GST_V4L2RADIO (element);
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
/*start radio */
|
||||
if (!gst_v4l2radio_start (radio))
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
/*unmute radio */
|
||||
if (!gst_v4l2radio_set_unmute (radio))
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
/*mute radio */
|
||||
if (!gst_v4l2radio_set_mute (radio))
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
/*stop radio */
|
||||
if (!gst_v4l2radio_stop (radio))
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstV4l2Radio *radio = GST_V4L2RADIO (object);
|
||||
gint frequency;
|
||||
switch (prop_id) {
|
||||
case ARG_DEVICE:
|
||||
radio->v4l2object->videodev =
|
||||
g_strdup ((gchar *) g_value_get_string (value));
|
||||
break;
|
||||
case ARG_FREQUENCY:
|
||||
frequency = g_value_get_int (value);
|
||||
if (frequency >= MIN_FREQUENCY && frequency <= MAX_FREQUENCY) {
|
||||
radio->v4l2object->frequency = frequency;
|
||||
gst_v4l2_set_frequency (radio->v4l2object, 0,
|
||||
radio->v4l2object->frequency);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstV4l2Radio *radio = GST_V4L2RADIO (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_DEVICE:
|
||||
g_value_set_string (value, radio->v4l2object->videodev);
|
||||
break;
|
||||
case ARG_FREQUENCY:
|
||||
if (gst_v4l2_get_frequency (radio->v4l2object,
|
||||
0, &(radio->v4l2object->frequency)))
|
||||
g_value_set_int (value, radio->v4l2object->frequency);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* GstURIHandler interface */
|
||||
static GstURIType
|
||||
gst_v4l2radio_uri_get_type (void)
|
||||
{
|
||||
return GST_URI_SRC;
|
||||
}
|
||||
|
||||
static gchar **
|
||||
gst_v4l2radio_uri_get_protocols (void)
|
||||
{
|
||||
static gchar *protocols[] = { (char *) "radio", NULL };
|
||||
return protocols;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
gst_v4l2radio_uri_get_uri (GstURIHandler * handler)
|
||||
{
|
||||
GstV4l2Radio *radio = GST_V4L2RADIO (handler);
|
||||
|
||||
if (radio->v4l2object->videodev != NULL) {
|
||||
if (gst_v4l2_get_frequency (radio->v4l2object,
|
||||
0, &(radio->v4l2object->frequency))) {
|
||||
gchar uri[20];
|
||||
gchar freq[6];
|
||||
g_ascii_formatd (freq, 6, "%4.1f", radio->v4l2object->frequency / 1e6);
|
||||
g_snprintf (uri, sizeof (uri), "radio://%s", freq);
|
||||
return g_intern_string (uri);
|
||||
}
|
||||
}
|
||||
|
||||
return "radio://";
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri)
|
||||
{
|
||||
GstV4l2Radio *radio = GST_V4L2RADIO (handler);
|
||||
gdouble dfreq;
|
||||
gint ifreq;
|
||||
const gchar *freq;
|
||||
gchar *end;
|
||||
|
||||
if (strcmp (uri, "radio://") != 0) {
|
||||
freq = uri + 8;
|
||||
|
||||
dfreq = g_ascii_strtod (freq, &end);
|
||||
|
||||
if (errno || strlen (end))
|
||||
goto uri_failed;
|
||||
|
||||
ifreq = dfreq * 1e6;
|
||||
g_object_set (radio, "frequency", ifreq, NULL);
|
||||
|
||||
} else
|
||||
goto uri_failed;
|
||||
|
||||
return TRUE;
|
||||
|
||||
uri_failed:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_v4l2radio_uri_handler_init (gpointer g_iface, gpointer iface_data)
|
||||
{
|
||||
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
|
||||
|
||||
iface->get_type = gst_v4l2radio_uri_get_type;
|
||||
iface->get_protocols = gst_v4l2radio_uri_get_protocols;
|
||||
iface->get_uri = gst_v4l2radio_uri_get_uri;
|
||||
iface->set_uri = gst_v4l2radio_uri_set_uri;
|
||||
}
|
68
sys/v4l2/gstv4l2radio.h
Normal file
68
sys/v4l2/gstv4l2radio.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* GStreamer v4l2 radio tuner element
|
||||
* Copyright (C) 2010, 2011 Alexey Chernov <4ernov@gmail.com>
|
||||
*
|
||||
* gstv4l2radio.h - V4L2 radio tuner element
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_V4L2RADIO_H__
|
||||
#define __GST_V4L2RADIO_H__
|
||||
|
||||
#include "gstv4l2object.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_V4L2RADIO \
|
||||
(gst_v4l2radio_get_type())
|
||||
#define GST_V4L2RADIO(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2RADIO,GstV4l2Radio))
|
||||
#define GST_V4L2RADIO_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2RADIO,GstV4l2RadioClass))
|
||||
#define GST_IS_V4L2RADIO(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2RADIO))
|
||||
#define GST_IS_V4L2RADIO_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2RADIO))
|
||||
|
||||
typedef struct _GstV4l2Radio GstV4l2Radio;
|
||||
typedef struct _GstV4l2RadioClass GstV4l2RadioClass;
|
||||
|
||||
/**
|
||||
* GstV4l2Radio:
|
||||
* @v4l2object: private #GstV4l2Object
|
||||
*
|
||||
* Opaque video4linux2 radio tuner element
|
||||
*/
|
||||
struct _GstV4l2Radio
|
||||
{
|
||||
GstElement element;
|
||||
|
||||
/*< private >*/
|
||||
GstV4l2Object * v4l2object;
|
||||
};
|
||||
|
||||
struct _GstV4l2RadioClass
|
||||
{
|
||||
GstElementClass parent_class;
|
||||
|
||||
GList *v4l2_class_devices;
|
||||
};
|
||||
|
||||
GType gst_v4l2radio_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_V4L2RADIO_H__ */
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue