Merge branch 'master' into 0.11

Conflicts:
	ext/pulse/pulsesink.c
	ext/pulse/pulsesrc.c
	gst/audioparsers/gstac3parse.c
	gst/rtp/gstrtph264depay.c
	gst/rtp/gstrtph264pay.c
	gst/rtpmanager/gstrtpssrcdemux.c
This commit is contained in:
Wim Taymans 2011-08-03 18:25:30 +02:00
commit 4121021bb2
36 changed files with 1142 additions and 192 deletions

View file

@ -809,6 +809,10 @@ AG_GST_CHECK_FEATURE(PULSE, [pulseaudio plug-in], pulseaudio, [
if test x$HAVE_PULSE_0_9_20 = xyes; then
AC_DEFINE(HAVE_PULSE_0_9_20, 1, [defined if pulseaudio >= 0.9.20 is available])
fi
AG_GST_PKG_CHECK_MODULES(PULSE_1_0, libpulse >= 0.98)
if test x$HAVE_PULSE_1_0 = xyes; then
AC_DEFINE(HAVE_PULSE_1_0, 1, [defined if pulseaudio >= 1.0 is available])
fi
])
dnl *** dv1394 ***

View file

@ -83,6 +83,7 @@ gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels)
/* alloc enough output ports */
sink->ports = g_realloc (sink->ports, sizeof (jack_port_t *) * channels);
sink->buffers = g_realloc (sink->buffers, sizeof (sample_t *) * channels);
/* create an output port for each channel */
while (sink->port_count < channels) {
@ -123,6 +124,8 @@ gst_jack_audio_sink_free_channels (GstJackAudioSink * sink)
}
g_free (sink->ports);
sink->ports = NULL;
g_free (sink->buffers);
sink->buffers = NULL;
}
/* ringbuffer abstract base class */
@ -187,19 +190,17 @@ jack_process_cb (jack_nframes_t nframes, void *arg)
gint readseg, len;
guint8 *readptr;
gint i, j, flen, channels;
sample_t **buffers, *data;
sample_t *data;
buf = GST_RING_BUFFER_CAST (arg);
sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
channels = buf->spec.channels;
/* alloc pointers to samples */
buffers = g_alloca (sizeof (sample_t *) * channels);
/* get target buffers */
for (i = 0; i < channels; i++) {
buffers[i] = (sample_t *) jack_port_get_buffer (sink->ports[i], nframes);
sink->buffers[i] =
(sample_t *) jack_port_get_buffer (sink->ports[i], nframes);
}
if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) {
@ -217,7 +218,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg)
* deinterleave into the jack target buffers */
for (i = 0; i < nframes; i++) {
for (j = 0; j < channels; j++) {
buffers[j][i] = *data++;
sink->buffers[j][i] = *data++;
}
}
@ -231,7 +232,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg)
/* We are not allowed to read from the ringbuffer, write silence to all
* jack output buffers */
for (i = 0; i < channels; i++) {
memset (buffers[i], 0, nframes * sizeof (sample_t));
memset (sink->buffers[i], 0, nframes * sizeof (sample_t));
}
}
return 0;
@ -733,6 +734,7 @@ gst_jack_audio_sink_init (GstJackAudioSink * sink)
sink->jclient = NULL;
sink->ports = NULL;
sink->port_count = 0;
sink->buffers = NULL;
}
static void

View file

@ -65,6 +65,7 @@ struct _GstJackAudioSink {
/* our ports */
jack_port_t **ports;
int port_count;
sample_t **buffers;
};
struct _GstJackAudioSinkClass {

View file

@ -54,6 +54,7 @@
#include <gst/gsttaglist.h>
#include <gst/interfaces/streamvolume.h>
#include <gst/gst-i18n-plugin.h>
#include <gst/audio/gstaudioiec61937.h>
#include <gst/pbutils/pbutils.h> /* only used for GST_PLUGINS_BASE_VERSION_* */
@ -138,7 +139,12 @@ struct _GstPulseRingBuffer
pa_context *context;
pa_stream *stream;
#ifdef HAVE_PULSE_1_0
pa_format_info *format;
guint channels;
#else
pa_sample_spec sample_spec;
#endif
void *m_data;
size_t m_towrite;
@ -222,7 +228,12 @@ gst_pulseringbuffer_init (GstPulseRingBuffer * pbuf)
pbuf->context = NULL;
pbuf->stream = NULL;
#ifdef HAVE_PULSE_1_0
pbuf->format = NULL;
pbuf->channels = 0;
#else
pa_sample_spec_init (&pbuf->sample_spec);
#endif
pbuf->m_data = NULL;
pbuf->m_towrite = 0;
@ -251,6 +262,13 @@ gst_pulsering_destroy_stream (GstPulseRingBuffer * pbuf)
pbuf->m_offset = 0;
pbuf->m_lastoffset = 0;
}
#ifdef HAVE_PULSE_1_0
if (pbuf->format) {
pa_format_info_free (pbuf->format);
pbuf->format = NULL;
pbuf->channels = 0;
}
#endif
pa_stream_disconnect (pbuf->stream);
@ -695,11 +713,60 @@ gst_pulsering_stream_event_cb (pa_stream * p, const char *name,
gst_element_post_message (GST_ELEMENT_CAST (psink),
gst_message_new_request_state (GST_OBJECT_CAST (psink),
GST_STATE_PLAYING));
#ifdef HAVE_PULSE_1_0
} else if (!strcmp (name, PA_STREAM_EVENT_FORMAT_LOST)) {
GstEvent *renego;
if (g_atomic_int_get (&psink->format_lost)) {
/* Duplicate event before we're done reconfiguring, discard */
return;
}
GST_DEBUG_OBJECT (psink, "got FORMAT LOST");
g_atomic_int_set (&psink->format_lost, 1);
psink->format_lost_time = g_ascii_strtoull (pa_proplist_gets (pl,
"stream-time"), NULL, 0) * 1000;
g_free (psink->device);
psink->device = g_strdup (pa_proplist_gets (pl, "device"));
renego = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new ("pulse-format-lost", NULL));
if (!gst_pad_push_event (GST_BASE_SINK (psink)->sinkpad, renego)) {
/* Nobody handled the format change - emit an error */
GST_ELEMENT_ERROR (psink, STREAM, FORMAT, ("Sink format changed"),
("Sink format changed"));
}
#endif
} else {
GST_DEBUG_OBJECT (psink, "got unknown event %s", name);
}
}
/* Called with the mainloop locked */
static gboolean
gst_pulsering_wait_for_stream_ready (GstPulseSink * psink, pa_stream * stream)
{
pa_stream_state_t state;
for (;;) {
state = pa_stream_get_state (stream);
GST_LOG_OBJECT (psink, "stream state is now %d", state);
if (!PA_STREAM_IS_GOOD (state))
return FALSE;
if (state == PA_STREAM_READY)
return TRUE;
/* Wait until the stream is ready */
pa_threaded_mainloop_wait (mainloop);
}
}
/* This method should create a new stream of the given @spec. No playback should
* start yet so we start in the corked state. */
static gboolean
@ -718,14 +785,25 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
pa_stream_flags_t flags;
const gchar *name;
GstAudioClock *clock;
#ifdef HAVE_PULSE_1_0
pa_format_info *formats[1];
#ifndef GST_DISABLE_GST_DEBUG
gchar print_buf[PA_FORMAT_INFO_SNPRINT_MAX];
#endif
#endif
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (buf));
pbuf = GST_PULSERING_BUFFER_CAST (buf);
GST_LOG_OBJECT (psink, "creating sample spec");
/* convert the gstreamer sample spec to the pulseaudio format */
#ifdef HAVE_PULSE_1_0
if (!gst_pulse_fill_format_info (spec, &pbuf->format, &pbuf->channels))
goto invalid_spec;
#else
if (!gst_pulse_fill_sample_spec (spec, &pbuf->sample_spec))
goto invalid_spec;
#endif
pa_threaded_mainloop_lock (mainloop);
@ -742,7 +820,13 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
pa_operation_unref (o);
/* initialize the channel map */
#ifdef HAVE_PULSE_1_0
if (pa_format_info_is_pcm (pbuf->format) &&
gst_pulse_gst_to_channel_map (&channel_map, spec))
pa_format_info_set_channel_map (pbuf->format, &channel_map);
#else
gst_pulse_gst_to_channel_map (&channel_map, spec);
#endif
/* find a good name for the stream */
if (psink->stream_name)
@ -751,10 +835,17 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
name = "Playback Stream";
/* create a stream */
#ifdef HAVE_PULSE_1_0
formats[0] = pbuf->format;
if (!(pbuf->stream = pa_stream_new_extended (pbuf->context, name, formats, 1,
psink->proplist)))
goto stream_failed;
#else
GST_LOG_OBJECT (psink, "creating stream with name %s", name);
if (!(pbuf->stream = pa_stream_new_with_proplist (pbuf->context, name,
&pbuf->sample_spec, &channel_map, psink->proplist)))
goto stream_failed;
#endif
/* install essential callbacks */
pa_stream_set_state_callback (pbuf->stream,
@ -792,8 +883,17 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
if (psink->volume_set) {
GST_LOG_OBJECT (psink, "have volume of %f", psink->volume);
pv = &v;
#ifdef HAVE_PULSE_1_0
if (pa_format_info_is_pcm (pbuf->format))
gst_pulse_cvolume_from_linear (pv, pbuf->channels, psink->volume);
else {
GST_DEBUG_OBJECT (psink, "passthrough stream, not setting volume");
pv = NULL;
}
#else
gst_pulse_cvolume_from_linear (pv, pbuf->sample_spec.channels,
psink->volume);
#endif
} else {
pv = NULL;
}
@ -820,22 +920,19 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
clock = GST_AUDIO_CLOCK (GST_BASE_AUDIO_SINK (psink)->provided_clock);
gst_audio_clock_reset (clock, 0);
for (;;) {
pa_stream_state_t state;
if (!gst_pulsering_wait_for_stream_ready (psink, pbuf->stream))
goto connect_failed;
state = pa_stream_get_state (pbuf->stream);
#ifdef HAVE_PULSE_1_0
g_free (psink->device);
psink->device = g_strdup (pa_stream_get_device_name (pbuf->stream));
GST_LOG_OBJECT (psink, "stream state is now %d", state);
if (!PA_STREAM_IS_GOOD (state))
goto connect_failed;
if (state == PA_STREAM_READY)
break;
/* Wait until the stream is ready */
pa_threaded_mainloop_wait (mainloop);
}
#ifndef GST_DISABLE_GST_DEBUG
pa_format_info_snprint (print_buf, sizeof (print_buf),
pa_stream_get_format_info (pbuf->stream));
GST_INFO_OBJECT (psink, "negotiated to: %s", print_buf);
#endif
#endif
/* After we passed the volume off of to PA we never want to set it
again, since it is PA's job to save/restore volumes. */
@ -909,6 +1006,16 @@ gst_pulseringbuffer_release (GstRingBuffer * buf)
gst_pulsering_destroy_stream (pbuf);
pa_threaded_mainloop_unlock (mainloop);
#ifdef HAVE_PULSE_1_0
{
GstPulseSink *psink;
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
g_atomic_int_set (&psink->format_lost, FALSE);
psink->format_lost_time = GST_CLOCK_TIME_NONE;
}
#endif
return TRUE;
}
@ -930,6 +1037,13 @@ gst_pulsering_set_corked (GstPulseRingBuffer * pbuf, gboolean corked,
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
#ifdef HAVE_PULSE_1_0
if (g_atomic_int_get (&psink->format_lost)) {
/* Sink format changed, stream's gone so fake being paused */
return TRUE;
}
#endif
GST_DEBUG_OBJECT (psink, "setting corked state to %d", corked);
if (pbuf->corked != corked) {
if (!(o = pa_stream_cork (pbuf->stream, corked,
@ -1102,13 +1216,22 @@ gst_pulseringbuffer_stop (GstRingBuffer * buf)
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
pa_threaded_mainloop_lock (mainloop);
pbuf->paused = TRUE;
res = gst_pulsering_set_corked (pbuf, TRUE, TRUE);
/* Inform anyone waiting in _commit() call that it shall wakeup */
if (pbuf->in_commit) {
GST_DEBUG_OBJECT (psink, "signal commit thread");
pa_threaded_mainloop_signal (mainloop, 0);
}
#ifdef HAVE_PULSE_1_0
if (g_atomic_int_get (&psink->format_lost)) {
/* Don't try to flush, the stream's probably gone by now */
res = TRUE;
goto cleanup;
}
#endif
/* then try to flush, it's not fatal when this fails */
GST_DEBUG_OBJECT (psink, "flushing");
@ -1216,7 +1339,6 @@ G_STMT_START { \
GST_DEBUG ("rev_down end %d/%d",*accum,*toprocess); \
} G_STMT_END
/* our custom commit function because we write into the buffer of pulseaudio
* instead of keeping our own buffer */
static guint
@ -1255,6 +1377,7 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample,
}
pa_threaded_mainloop_lock (mainloop);
GST_DEBUG_OBJECT (psink, "entering commit");
pbuf->in_commit = TRUE;
@ -1279,6 +1402,13 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample,
* needed to properly handle reverse playback: it points to the last sample. */
data_end = data + (bps * inr);
#ifdef HAVE_PULSE_1_0
if (g_atomic_int_get (&psink->format_lost)) {
/* Sink format changed, drop the data and hope upstream renegotiates */
goto fake_done;
}
#endif
if (pbuf->paused)
goto was_paused;
@ -1328,6 +1458,13 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample,
for (;;) {
pbuf->m_writable = pa_stream_writable_size (pbuf->stream);
#ifdef HAVE_PULSE_1_0
if (g_atomic_int_get (&psink->format_lost)) {
/* Sink format changed, give up and hope upstream renegotiates */
goto fake_done;
}
#endif
if (pbuf->m_writable == (size_t) - 1)
goto writable_size_failed;
@ -1386,6 +1523,14 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample,
GST_LOG_OBJECT (psink, "writing %u samples at offset %" G_GUINT64_FORMAT,
(guint) avail, offset);
#ifdef HAVE_PULSE_1_0
/* No trick modes for passthrough streams */
if (G_UNLIKELY (inr != outr || reverse)) {
GST_WARNING_OBJECT (psink, "Passthrough stream can't run in trick mode");
goto unlock_and_fail;
}
#endif
if (G_LIKELY (inr == outr && !reverse)) {
/* no rate conversion, simply write out the samples */
/* copy the data into internal buffer */
@ -1465,6 +1610,10 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample,
}
}
}
#ifdef HAVE_PULSE_1_0
fake_done:
#endif
/* we consumed all samples here */
data = data_end + bps;
@ -1605,7 +1754,6 @@ static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
"width = (int) 32, "
"depth = (int) 32, "
"rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 32 ];"
#ifdef HAVE_PULSE_0_9_15
"audio/x-raw-int, "
"endianness = (int) { " ENDIANNESS " }, "
"signed = (boolean) TRUE, "
@ -1619,7 +1767,6 @@ static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
"width = (int) 32, "
"depth = (int) 24, "
"rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 32 ];"
#endif
"audio/x-raw-int, "
"signed = (boolean) FALSE, "
"width = (int) 8, "
@ -1630,15 +1777,19 @@ static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
"rate = (int) [ 1, MAX], "
"channels = (int) [ 1, 32 ];"
"audio/x-mulaw, "
"rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ]")
);
"rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ];"
#ifdef HAVE_PULSE_1_0
"audio/x-ac3, framed = (boolean) true;"
"audio/x-eac3, framed = (boolean) true; "
"audio/x-dts, framed = (boolean) true, "
" block_size = (int) { 512, 1024, 2048 }; "
"audio/mpeg, mpegversion = (int)1, "
" mpegaudioversion = (int) [ 1, 2 ], parsed = (boolean) true; "
#endif
));
GST_IMPLEMENT_PULSEPROBE_METHODS (GstPulseSink, gst_pulsesink);
#define _do_init(type) \
gst_pulsesink_init_contexts (); \
gst_pulsesink_init_interfaces (type);
#define gst_pulsesink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstPulseSink, gst_pulsesink, GST_TYPE_BASE_AUDIO_SINK,
gst_pulsesink_init_contexts ();
@ -1659,6 +1810,40 @@ gst_pulsesink_create_ringbuffer (GstBaseAudioSink * sink)
return buffer;
}
static GstBuffer *
gst_pulsesink_payload (GstBaseAudioSink * sink, GstBuffer * buf)
{
switch (sink->ringbuffer->spec.type) {
case GST_BUFTYPE_AC3:
case GST_BUFTYPE_EAC3:
case GST_BUFTYPE_DTS:
case GST_BUFTYPE_MPEG:
{
/* FIXME: alloc memory from PA if possible */
gint framesize = gst_audio_iec61937_frame_size (&sink->ringbuffer->spec);
GstBuffer *out;
if (framesize <= 0)
return NULL;
out = gst_buffer_new_and_alloc (framesize);
if (!gst_audio_iec61937_payload (GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf), GST_BUFFER_DATA (out),
GST_BUFFER_SIZE (out), &sink->ringbuffer->spec)) {
gst_buffer_unref (out);
return NULL;
}
gst_buffer_copy_metadata (out, buf, GST_BUFFER_COPY_ALL);
return out;
}
default:
return gst_buffer_ref (buf);
}
}
static void
gst_pulsesink_class_init (GstPulseSinkClass * klass)
{
@ -1683,6 +1868,7 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
gstaudiosink_class->create_ringbuffer =
GST_DEBUG_FUNCPTR (gst_pulsesink_create_ringbuffer);
gstaudiosink_class->payload = GST_DEBUG_FUNCPTR (gst_pulsesink_payload);
/* Overwrite GObject fields */
g_object_class_install_property (gobject_class,
@ -1771,6 +1957,14 @@ gst_pulsesink_get_time (GstClock * clock, GstBaseAudioSink * sink)
pbuf = GST_PULSERING_BUFFER_CAST (sink->ringbuffer);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
#ifdef HAVE_PULSE_1_0
if (g_atomic_int_get (&psink->format_lost)) {
/* Stream was lost in a format change, it'll get set up again once
* upstream renegotiates */
return psink->format_lost_time;
}
#endif
pa_threaded_mainloop_lock (mainloop);
if (gst_pulsering_is_dead (psink, pbuf, TRUE))
goto server_dead;
@ -1799,6 +1993,181 @@ server_dead:
}
}
static void
gst_pulsesink_sink_info_cb (pa_context * c, const pa_sink_info * i, int eol,
void *userdata)
{
GstPulseRingBuffer *pbuf;
GstPulseSink *psink;
#ifdef HAVE_PULSE_1_0
GList *l;
guint8 j;
#endif
pbuf = GST_PULSERING_BUFFER_CAST (userdata);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
if (!i)
goto done;
g_free (psink->device_description);
psink->device_description = g_strdup (i->description);
#ifdef HAVE_PULSE_1_0
g_mutex_lock (psink->sink_formats_lock);
for (l = g_list_first (psink->sink_formats); l; l = g_list_next (l))
pa_format_info_free ((pa_format_info *) l->data);
g_list_free (psink->sink_formats);
psink->sink_formats = NULL;
for (j = 0; j < i->n_formats; j++)
psink->sink_formats = g_list_prepend (psink->sink_formats,
pa_format_info_copy (i->formats[j]));
g_mutex_unlock (psink->sink_formats_lock);
#endif
done:
pa_threaded_mainloop_signal (mainloop, 0);
}
#ifdef HAVE_PULSE_1_0
static gboolean
gst_pulsesink_pad_acceptcaps (GstPad * pad, GstCaps * caps)
{
GstPulseSink *psink = GST_PULSESINK (gst_pad_get_parent_element (pad));
GstPulseRingBuffer *pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK
(psink)->ringbuffer);
GstCaps *pad_caps;
GstStructure *st;
gboolean ret = FALSE;
GstRingBufferSpec spec = { 0 };
pa_stream *stream = NULL;
pa_operation *o = NULL;
pa_channel_map channel_map;
pa_stream_flags_t flags;
pa_format_info *format = NULL, *formats[1];
guint channels;
pad_caps = gst_pad_get_caps_reffed (pad);
if (pad_caps) {
ret = gst_caps_can_intersect (pad_caps, caps);
gst_caps_unref (pad_caps);
}
/* Either template caps didn't match, or we're still in NULL state */
if (!ret || !pbuf->context)
goto done;
/* If we've not got fixed caps, creating a stream might fail, so let's just
* return from here with default acceptcaps behaviour */
if (!gst_caps_is_fixed (caps))
goto done;
ret = FALSE;
pa_threaded_mainloop_lock (mainloop);
spec.latency_time = GST_BASE_AUDIO_SINK (psink)->latency_time;
if (!gst_ring_buffer_parse_caps (&spec, caps))
goto out;
if (!gst_pulse_fill_format_info (&spec, &format, &channels))
goto out;
/* Make sure input is framed (one frame per buffer) and can be payloaded */
if (!pa_format_info_is_pcm (format)) {
gboolean framed = FALSE, parsed = FALSE;
st = gst_caps_get_structure (caps, 0);
gst_structure_get_boolean (st, "framed", &framed);
gst_structure_get_boolean (st, "parsed", &parsed);
if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0)
goto out;
}
/* initialize the channel map */
if (pa_format_info_is_pcm (format) &&
gst_pulse_gst_to_channel_map (&channel_map, &spec))
pa_format_info_set_channel_map (format, &channel_map);
if (pbuf->stream) {
/* We're already in PAUSED or above, so just reuse this stream to query
* sink formats and use those. */
GList *i;
if (!(o = pa_context_get_sink_info_by_name (pbuf->context, psink->device,
gst_pulsesink_sink_info_cb, pbuf)))
goto info_failed;
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait (mainloop);
if (gst_pulsering_is_dead (psink, pbuf, TRUE))
goto out;
}
g_mutex_lock (psink->sink_formats_lock);
for (i = g_list_first (psink->sink_formats); i; i = g_list_next (i)) {
if (pa_format_info_is_compatible ((pa_format_info *) i->data, format)) {
ret = TRUE;
break;
}
}
g_mutex_unlock (psink->sink_formats_lock);
} else {
/* We're in READY, let's connect a stream to see if the format is
* accpeted by whatever sink we're routed to */
formats[0] = format;
if (!(stream = pa_stream_new_extended (pbuf->context, "pulsesink probe",
formats, 1, psink->proplist)))
goto out;
/* construct the flags */
flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE |
PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED;
pa_stream_set_state_callback (stream, gst_pulsering_stream_state_cb, pbuf);
if (pa_stream_connect_playback (stream, psink->device, NULL, flags, NULL,
NULL) < 0)
goto out;
ret = gst_pulsering_wait_for_stream_ready (psink, stream);
}
out:
if (format)
pa_format_info_free (format);
if (o)
pa_operation_unref (o);
if (stream) {
pa_stream_set_state_callback (stream, NULL, NULL);
pa_stream_disconnect (stream);
pa_stream_unref (stream);
}
pa_threaded_mainloop_unlock (mainloop);
done:
gst_object_unref (psink);
return ret;
info_failed:
{
GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
("pa_context_get_sink_input_info() failed: %s",
pa_strerror (pa_context_errno (pbuf->context))), (NULL));
goto out;
}
}
#endif
static void
gst_pulsesink_init (GstPulseSink * pulsesink)
{
@ -1807,6 +2176,11 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
pulsesink->device_description = NULL;
pulsesink->client_name = gst_pulse_client_name ();
#ifdef HAVE_PULSE_1_0
pulsesink->sink_formats_lock = g_mutex_new ();
pulsesink->sink_formats = NULL;
#endif
pulsesink->volume = DEFAULT_VOLUME;
pulsesink->volume_set = FALSE;
@ -1815,6 +2189,11 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
pulsesink->notify = 0;
#ifdef HAVE_PULSE_1_0
g_atomic_int_set (&pulsesink->format_lost, FALSE);
pulsesink->format_lost_time = GST_CLOCK_TIME_NONE;
#endif
pulsesink->properties = NULL;
pulsesink->proplist = NULL;
@ -1826,6 +2205,11 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
gst_audio_clock_new ("GstPulseSinkClock",
(GstAudioClockGetTimeFunc) gst_pulsesink_get_time, pulsesink);
#ifdef HAVE_PULSE_1_0
gst_pad_set_acceptcaps_function (GST_BASE_SINK (pulsesink)->sinkpad,
GST_DEBUG_FUNCPTR (gst_pulsesink_pad_acceptcaps));
#endif
/* TRUE for sinks, FALSE for sources */
pulsesink->probe = gst_pulseprobe_new (G_OBJECT (pulsesink),
G_OBJECT_GET_CLASS (pulsesink), PROP_DEVICE, pulsesink->device,
@ -1836,12 +2220,23 @@ static void
gst_pulsesink_finalize (GObject * object)
{
GstPulseSink *pulsesink = GST_PULSESINK_CAST (object);
#ifdef HAVE_PULSE_1_0
GList *i;
#endif
g_free (pulsesink->server);
g_free (pulsesink->device);
g_free (pulsesink->device_description);
g_free (pulsesink->client_name);
#ifdef HAVE_PULSE_1_0
for (i = g_list_first (pulsesink->sink_formats); i; i = g_list_next (i))
pa_format_info_free ((pa_format_info *) i->data);
g_list_free (pulsesink->sink_formats);
g_mutex_free (pulsesink->sink_formats_lock);
#endif
if (pulsesink->properties)
gst_structure_free (pulsesink->properties);
if (pulsesink->proplist)
@ -1877,7 +2272,16 @@ gst_pulsesink_set_volume (GstPulseSink * psink, gdouble volume)
if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
goto no_index;
#ifdef HAVE_PULSE_1_0
if (pa_format_info_is_pcm (pbuf->format))
gst_pulse_cvolume_from_linear (&v, pbuf->channels, volume);
else
/* FIXME: this will eventually be superceded by checks to see if the volume
* is readable/writable */
goto unlock;
#else
gst_pulse_cvolume_from_linear (&v, pbuf->sample_spec.channels, volume);
#endif
if (!(o = pa_context_set_sink_input_volume (pbuf->context, idx,
&v, NULL, NULL)))
@ -2154,26 +2558,6 @@ info_failed:
}
}
static void
gst_pulsesink_sink_info_cb (pa_context * c, const pa_sink_info * i, int eol,
void *userdata)
{
GstPulseRingBuffer *pbuf;
GstPulseSink *psink;
pbuf = GST_PULSERING_BUFFER_CAST (userdata);
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
if (!i)
goto done;
g_free (psink->device_description);
psink->device_description = g_strdup (i->description);
done:
pa_threaded_mainloop_signal (mainloop, 0);
}
static gchar *
gst_pulsesink_device_description (GstPulseSink * psink)
{
@ -2570,6 +2954,7 @@ gst_pulsesink_change_state (GstElement * element, GstStateChange transition)
gst_message_new_clock_provide (GST_OBJECT_CAST (element),
GST_BASE_AUDIO_SINK (pulsesink)->provided_clock, TRUE));
break;
default:
break;
}
@ -2580,6 +2965,7 @@ gst_pulsesink_change_state (GstElement * element, GstStateChange transition)
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
/* format_lost is reset in release() in baseaudiosink */
gst_element_post_message (element,
gst_message_new_clock_lost (GST_OBJECT_CAST (element),
GST_BASE_AUDIO_SINK (pulsesink)->provided_clock));

View file

@ -72,6 +72,13 @@ struct _GstPulseSink
GstStructure *properties;
pa_proplist *proplist;
#ifdef HAVE_PULSE_1_0
GMutex *sink_formats_lock;
GList *sink_formats;
volatile gint format_lost;
GstClockTime format_lost_time;
#endif
};
struct _GstPulseSinkClass

View file

@ -63,6 +63,7 @@ enum
PROP_DEVICE_NAME,
PROP_CLIENT,
PROP_STREAM_PROPERTIES,
PROP_SOURCE_OUTPUT_INDEX,
PROP_LAST
};
@ -153,6 +154,7 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
GstAudioSrcClass *gstaudiosrc_class = GST_AUDIO_SRC_CLASS (klass);
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
gchar *clientname;
gobject_class->finalize = gst_pulsesrc_finalize;
gobject_class->set_property = gst_pulsesrc_set_property;
@ -189,6 +191,7 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
"Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
clientname = gst_pulse_client_name ();
/**
* GstPulseSink:client
*
@ -199,9 +202,10 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
g_object_class_install_property (gobject_class,
PROP_CLIENT,
g_param_spec_string ("client", "Client",
"The PulseAudio client_name_to_use", gst_pulse_client_name (),
"The PulseAudio client_name_to_use", clientname,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_READY));
g_free (clientname);
/**
* GstPulseSrc:stream-properties
@ -225,6 +229,19 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
g_param_spec_boxed ("stream-properties", "stream properties",
"list of pulseaudio stream properties",
GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstPulseSrc:source-output-index
*
* The index of the PulseAudio source output corresponding to this element.
*
* Since: 0.10.31
*/
g_object_class_install_property (gobject_class,
PROP_SOURCE_OUTPUT_INDEX,
g_param_spec_uint ("source-output-index", "source output index",
"The index of the PulseAudio source output corresponding to this "
"record stream", 0, G_MAXUINT, PA_INVALID_INDEX,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gst_element_class_set_details_simple (gstelement_class,
"PulseAudio Audio Source",
@ -244,6 +261,7 @@ gst_pulsesrc_init (GstPulseSrc * pulsesrc)
pulsesrc->context = NULL;
pulsesrc->stream = NULL;
pulsesrc->source_output_idx = PA_INVALID_INDEX;
pulsesrc->read_buffer = NULL;
pulsesrc->read_buffer_length = 0;
@ -273,6 +291,8 @@ gst_pulsesrc_destroy_stream (GstPulseSrc * pulsesrc)
pa_stream_disconnect (pulsesrc->stream);
pa_stream_unref (pulsesrc->stream);
pulsesrc->stream = NULL;
pulsesrc->source_output_idx = PA_INVALID_INDEX;
g_object_notify (G_OBJECT (pulsesrc), "source-output-index");
}
g_free (pulsesrc->device_description);
@ -470,6 +490,9 @@ gst_pulsesrc_get_property (GObject * object,
case PROP_STREAM_PROPERTIES:
gst_value_set_structure (value, pulsesrc->properties);
break;
case PROP_SOURCE_OUTPUT_INDEX:
g_value_set_uint (value, pulsesrc->source_output_idx);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1011,6 +1034,10 @@ gst_pulsesrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
pa_threaded_mainloop_wait (pulsesrc->mainloop);
}
/* store the source output index so it can be accessed via a property */
pulsesrc->source_output_idx = pa_stream_get_index (pulsesrc->stream);
g_object_notify (G_OBJECT (pulsesrc), "source-output-index");
/* get the actual buffering properties now */
actual = pa_stream_get_buffer_attr (pulsesrc->stream);

View file

@ -61,6 +61,7 @@ struct _GstPulseSrc
pa_context *context;
pa_stream *stream;
guint32 source_output_idx;
pa_sample_spec sample_spec;

View file

@ -118,6 +118,89 @@ gst_pulse_fill_sample_spec (GstRingBufferSpec * spec, pa_sample_spec * ss)
return TRUE;
}
#ifdef HAVE_PULSE_1_0
gboolean
gst_pulse_fill_format_info (GstRingBufferSpec * spec, pa_format_info ** f,
guint * channels)
{
pa_format_info *format;
pa_sample_format_t sf = PA_SAMPLE_INVALID;
format = pa_format_info_new ();
if (spec->format == GST_MU_LAW && spec->width == 8) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_ULAW;
} else if (spec->format == GST_A_LAW && spec->width == 8) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_ALAW;
} else if (spec->format == GST_U8 && spec->width == 8) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_U8;
} else if (spec->format == GST_S16_LE && spec->width == 16) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S16LE;
} else if (spec->format == GST_S16_BE && spec->width == 16) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S16BE;
} else if (spec->format == GST_FLOAT32_LE && spec->width == 32) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_FLOAT32LE;
} else if (spec->format == GST_FLOAT32_BE && spec->width == 32) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_FLOAT32BE;
} else if (spec->format == GST_S32_LE && spec->width == 32) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S32LE;
} else if (spec->format == GST_S32_BE && spec->width == 32) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S32BE;
} else if (spec->format == GST_S24_3LE && spec->width == 24) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S24LE;
} else if (spec->format == GST_S24_3BE && spec->width == 24) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S24BE;
} else if (spec->format == GST_S24_LE && spec->width == 32) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S24_32LE;
} else if (spec->format == GST_S24_BE && spec->width == 32) {
format->encoding = PA_ENCODING_PCM;
sf = PA_SAMPLE_S24_32BE;
} else if (spec->format == GST_AC3) {
format->encoding = PA_ENCODING_AC3_IEC61937;
} else if (spec->format == GST_EAC3) {
format->encoding = PA_ENCODING_EAC3_IEC61937;
} else if (spec->format == GST_DTS) {
format->encoding = PA_ENCODING_DTS_IEC61937;
} else if (spec->format == GST_MPEG) {
format->encoding = PA_ENCODING_MPEG_IEC61937;
} else {
goto fail;
}
if (format->encoding == PA_ENCODING_PCM) {
pa_format_info_set_sample_format (format, sf);
pa_format_info_set_channels (format, spec->channels);
}
pa_format_info_set_rate (format, spec->rate);
if (!pa_format_info_valid (format))
goto fail;
*f = format;
*channels = spec->channels;
return TRUE;
fail:
if (format)
pa_format_info_free (format);
return FALSE;
}
#endif
/* PATH_MAX is not defined everywhere, e.g. on GNU Hurd */
#ifndef PATH_MAX
#define PATH_MAX 4096

View file

@ -28,6 +28,10 @@
gboolean gst_pulse_fill_sample_spec (GstRingBufferSpec * spec,
pa_sample_spec * ss);
#ifdef HAVE_PULSE_1_0
gboolean gst_pulse_fill_format_info (GstRingBufferSpec * spec,
pa_format_info ** f, guint * channels);
#endif
gchar *gst_pulse_client_name (void);

View file

@ -163,6 +163,8 @@ static gboolean gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, guint * size, gint * skipsize);
static GstFlowReturn gst_ac3_parse_parse_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
static gboolean gst_ac3_parse_src_event (GstBaseParse * parse,
GstEvent * event);
#define gst_ac3_parse_parent_class parent_class
G_DEFINE_TYPE (GstAc3Parse, gst_ac3_parse, GST_TYPE_BASE_PARSE);
@ -193,6 +195,8 @@ gst_ac3_parse_class_init (GstAc3ParseClass * klass)
parse_class->check_valid_frame =
GST_DEBUG_FUNCPTR (gst_ac3_parse_check_valid_frame);
parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_ac3_parse_parse_frame);
parse_class->src_event = GST_DEBUG_FUNCPTR (gst_ac3_parse_src_event);
}
static void
@ -202,6 +206,7 @@ gst_ac3_parse_reset (GstAc3Parse * ac3parse)
ac3parse->sample_rate = -1;
ac3parse->blocks = -1;
ac3parse->eac = FALSE;
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_NONE);
}
static void
@ -237,9 +242,58 @@ gst_ac3_parse_stop (GstBaseParse * parse)
return TRUE;
}
static void
gst_ac3_parse_set_alignment (GstAc3Parse * ac3parse, gboolean eac)
{
GstCaps *caps;
GstStructure *st;
const gchar *str = NULL;
int i;
if (G_LIKELY (!eac))
goto done;
caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (ac3parse));
if (!caps)
goto done;
for (i = 0; i < gst_caps_get_size (caps); i++) {
st = gst_caps_get_structure (caps, i);
if (!g_str_equal (gst_structure_get_name (st), "audio/x-eac3"))
continue;
if ((str = gst_structure_get_string (st, "alignment"))) {
if (g_str_equal (str, "iec61937")) {
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_IEC61937);
GST_DEBUG_OBJECT (ac3parse, "picked iec61937 alignment");
} else if (g_str_equal (str, "frame") == 0) {
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME);
GST_DEBUG_OBJECT (ac3parse, "picked frame alignment");
} else {
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME);
GST_WARNING_OBJECT (ac3parse, "unknown alignment: %s", str);
}
break;
}
}
if (caps)
gst_caps_unref (caps);
done:
/* default */
if (ac3parse->align == GST_AC3_PARSE_ALIGN_NONE) {
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME);
GST_DEBUG_OBJECT (ac3parse, "picked syncframe alignment");
}
}
static gboolean
gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid)
gint skip, guint * frame_size, guint * rate, guint * chans, guint * blks,
guint * sid)
{
GstBitReader bits;
gpointer data;
@ -251,6 +305,7 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
gst_bit_reader_init (&bits, data, size);
gst_bit_reader_skip_unchecked (&bits, skip * 8);
gst_bit_reader_skip_unchecked (&bits, 16 + 16);
fscod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2);
@ -304,7 +359,8 @@ cleanup:
static gboolean
gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid)
gint skip, guint * frame_size, guint * rate, guint * chans, guint * blks,
guint * sid)
{
GstBitReader bits;
gpointer data;
@ -317,6 +373,7 @@ gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
gst_bit_reader_init (&bits, data, size);
gst_bit_reader_skip_unchecked (&bits, skip * 8);
gst_bit_reader_skip_unchecked (&bits, 16);
strmtyp = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); /* strmtyp */
@ -367,7 +424,7 @@ cleanup:
}
static gboolean
gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf,
gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf, gint skip,
guint * framesize, guint * rate, guint * chans, guint * blocks,
guint * sid, gboolean * eac)
{
@ -383,6 +440,8 @@ gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf,
GST_MEMDUMP_OBJECT (parse, "AC3 frame sync", data, MIN (size, 16));
gst_bit_reader_skip_unchecked (&bits, skip * 8);
sync = gst_bit_reader_get_bits_uint16_unchecked (&bits, 16);
gst_bit_reader_skip_unchecked (&bits, 16 + 8);
bsid = gst_bit_reader_peek_bits_uint8_unchecked (&bits, 5);
@ -395,17 +454,18 @@ gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf,
if (bsid <= 10) {
if (eac)
*eac = FALSE;
ret = gst_ac3_parse_frame_header_ac3 (parse, buf, framesize, rate, chans,
blocks, sid);
ret = gst_ac3_parse_frame_header_ac3 (parse, buf, skip, framesize, rate,
chans, blocks, sid);
goto cleanup;
}
if (bsid <= 16) {
} else if (bsid <= 16) {
if (eac)
*eac = TRUE;
ret = gst_ac3_parse_frame_header_eac3 (parse, buf, framesize, rate, chans,
blocks, sid);
ret = gst_ac3_parse_frame_header_eac3 (parse, buf, skip, framesize, rate,
chans, blocks, sid);
goto cleanup;
} else {
GST_DEBUG_OBJECT (parse, "unexpected bsid %d", bsid);
return FALSE;
}
GST_DEBUG_OBJECT (parse, "unexpected bsid %d", bsid);
@ -424,7 +484,9 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
GstBuffer *buf = frame->buffer;
GstByteReader reader;
gint off;
gboolean lost_sync, draining;
gboolean lost_sync, draining, eac, more = FALSE;
guint frmsiz, blocks, sid;
gint have_blocks = 0;
gpointer data;
gsize size;
gboolean ret = FALSE;
@ -452,23 +514,69 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
}
/* make sure the values in the frame header look sane */
if (!gst_ac3_parse_frame_header (ac3parse, buf, framesize, NULL, NULL,
NULL, NULL, NULL)) {
if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &frmsiz, NULL, NULL,
&blocks, &sid, &eac)) {
*skipsize = off + 2;
goto cleanup;
}
*framesize = frmsiz;
if (G_UNLIKELY (g_atomic_int_get (&ac3parse->align) ==
GST_AC3_PARSE_ALIGN_NONE))
gst_ac3_parse_set_alignment (ac3parse, eac);
GST_LOG_OBJECT (parse, "got frame");
lost_sync = GST_BASE_PARSE_LOST_SYNC (parse);
draining = GST_BASE_PARSE_DRAINING (parse);
if (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937) {
/* We need 6 audio blocks from each substream, so we keep going forwards
* till we have it */
g_assert (blocks > 0);
GST_LOG_OBJECT (ac3parse, "Need %d frames before pushing", 6 / blocks);
if (sid != 0) {
/* We need the first substream to be the one with id 0 */
GST_LOG_OBJECT (ac3parse, "Skipping till we find sid 0");
*skipsize = off + 2;
return FALSE;
}
*framesize = 0;
/* Loop till we have 6 blocks per substream */
for (have_blocks = 0; !more && have_blocks < 6; have_blocks += blocks) {
/* Loop till we get one frame from each substream */
do {
*framesize += frmsiz;
if (!gst_byte_reader_skip (&reader, frmsiz) ||
GST_BUFFER_SIZE (buf) < (*framesize + 6)) {
more = TRUE;
break;
}
if (!gst_ac3_parse_frame_header (ac3parse, buf, *framesize, &frmsiz,
NULL, NULL, NULL, &sid, &eac)) {
*skipsize = off + 2;
return FALSE;
}
} while (sid);
}
/* We're now at the next frame, so no need to skip if resyncing */
frmsiz = 0;
}
if (lost_sync && !draining) {
guint16 word = 0;
GST_DEBUG_OBJECT (ac3parse, "resyncing; checking next frame syncword");
if (!gst_byte_reader_skip (&reader, *framesize) ||
if (more || !gst_byte_reader_skip (&reader, frmsiz) ||
!gst_byte_reader_get_uint16_be (&reader, &word)) {
GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data");
gst_base_parse_set_min_frame_size (parse, *framesize + 6);
@ -502,7 +610,7 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
guint fsize, rate, chans, blocks, sid;
gboolean eac, update_rate = FALSE;
if (!gst_ac3_parse_frame_header (ac3parse, buf, &fsize, &rate, &chans,
if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &fsize, &rate, &chans,
&blocks, &sid, &eac))
goto broken_header;
@ -528,6 +636,9 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3",
"framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate,
"channels", G_TYPE_INT, chans, NULL);
gst_caps_set_simple (caps, "alignment", G_TYPE_STRING,
g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937 ?
"iec61937" : "frame", NULL);
gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
gst_caps_unref (caps);
@ -557,3 +668,33 @@ broken_header:
return GST_FLOW_ERROR;
}
}
static gboolean
gst_ac3_parse_src_event (GstBaseParse * parse, GstEvent * event)
{
GstAc3Parse *ac3parse = GST_AC3_PARSE (parse);
if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) &&
gst_event_has_name (event, "ac3parse-set-alignment")) {
const GstStructure *st = gst_event_get_structure (event);
const gchar *align = gst_structure_get_string (st, "alignment");
if (g_str_equal (align, "iec61937")) {
GST_DEBUG_OBJECT (ac3parse, "Switching to iec61937 alignment");
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_IEC61937);
} else if (g_str_equal (align, "frame")) {
GST_DEBUG_OBJECT (ac3parse, "Switching to frame alignment");
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME);
} else {
g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME);
GST_WARNING_OBJECT (ac3parse, "Got unknown alignment request (%s) "
"reverting to frame alignment.",
gst_structure_get_string (st, "alignment"));
}
gst_event_unref (event);
return TRUE;
}
return GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
}

View file

@ -42,6 +42,12 @@ G_BEGIN_DECLS
typedef struct _GstAc3Parse GstAc3Parse;
typedef struct _GstAc3ParseClass GstAc3ParseClass;
enum {
GST_AC3_PARSE_ALIGN_NONE,
GST_AC3_PARSE_ALIGN_FRAME,
GST_AC3_PARSE_ALIGN_IEC61937,
};
/**
* GstAc3Parse:
*
@ -51,10 +57,11 @@ struct _GstAc3Parse {
GstBaseParse baseparse;
/*< private >*/
gint sample_rate;
gint channels;
gint blocks;
gboolean eac;
gint sample_rate;
gint channels;
gint blocks;
gboolean eac;
volatile gint align;
};
/**

View file

@ -692,6 +692,12 @@ gst_au_parse_sink_event (GstPad * pad, GstEvent * event)
gst_event_unref (event);
break;
}
case GST_EVENT_EOS:
if (!auparse->srcpad) {
GST_ELEMENT_ERROR (auparse, STREAM, WRONG_TYPE,
("No valid input found before end of stream"), (NULL));
}
/* fall-through */
default:
ret = gst_pad_event_default (pad, event);
break;

View file

@ -609,7 +609,7 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass)
* processing latency and accuracy of timestamp adjustment for telecine
* streams.
*
* Since: 0.10.29.
* Since: 0.10.31
*
*/
g_object_class_install_property (gobject_class, PROP_LOCKING,
@ -623,7 +623,7 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass)
* This selects whether to ignore obscure/rare telecine patterns.
* NTSC 2:3 pulldown variants are the only really common patterns.
*
* Since: 0.10.29.
* Since: 0.10.31
*
*/
g_object_class_install_property (gobject_class, PROP_IGNORE_OBSCURE,
@ -638,7 +638,7 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass)
* This selects whether to drop orphan fields at the beginning of telecine
* patterns in active locking mode.
*
* Since: 0.10.29.
* Since: 0.10.31
*
*/
g_object_class_install_property (gobject_class, PROP_DROP_ORPHANS,

View file

@ -35,8 +35,6 @@
#if defined (HAVE_CPU_PPC64) || defined (HAVE_CPU_PPC)
#include <sys/types.h>
#include <sys/sysctl.h>
#include "ppc_zoom_ultimate.h"
#include "ppc_drawings.h"
#endif /* HAVE_CPU_PPC64 || HAVE_CPU_PPC */

View file

@ -7474,7 +7474,7 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
/* Subtract the header size */
GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
size, qtdemux->header_size);
g_assert (size > qtdemux->header_size);
g_assert (size >= qtdemux->header_size);
size = size - qtdemux->header_size;
if (!gst_qtdemux_get_duration (qtdemux, &duration) ||

View file

@ -4873,16 +4873,20 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
/* pixel width and height are the w and h of the video in pixels */
if (videocontext->pixel_width > 0 && videocontext->pixel_height > 0) {
gint w = videocontext->pixel_width;
gint h = videocontext->pixel_height;
gst_structure_set (structure,
"width", G_TYPE_INT, w, "height", G_TYPE_INT, h, NULL);
}
if (videocontext->display_width > 0 && videocontext->display_height > 0) {
if (videocontext->display_width > 0 || videocontext->display_height > 0) {
int n, d;
if (videocontext->display_width <= 0)
videocontext->display_width = videocontext->pixel_width;
if (videocontext->display_height <= 0)
videocontext->display_height = videocontext->pixel_height;
/* calculate the pixel aspect ratio using the display and pixel w/h */
n = videocontext->display_width * videocontext->pixel_height;
d = videocontext->display_height * videocontext->pixel_width;

View file

@ -45,7 +45,7 @@
#include "ebml-read.h"
#include "matroska-read-common.h"
GST_DEBUG_CATEGORY_STATIC (matroskareadcommon_debug);
GST_DEBUG_CATEGORY (matroskareadcommon_debug);
#define GST_CAT_DEFAULT matroskareadcommon_debug
#define DEBUG_ELEMENT_START(common, ebml, element) \

View file

@ -31,6 +31,8 @@
G_BEGIN_DECLS
GST_DEBUG_CATEGORY_EXTERN(matroskareadcommon_debug);
typedef enum {
GST_MATROSKA_READ_STATE_START,
GST_MATROSKA_READ_STATE_SEGMENT,

View file

@ -25,6 +25,7 @@
#include "matroska-demux.h"
#include "matroska-parse.h"
#include "matroska-read-common.h"
#include "matroska-mux.h"
#include "matroska-ids.h"
#include "webm-mux.h"
@ -40,6 +41,9 @@ plugin_init (GstPlugin * plugin)
gst_matroska_register_tags ();
GST_DEBUG_CATEGORY_INIT (matroskareadcommon_debug, "matroskareadcommon", 0,
"Matroska demuxer/parser shared debug");
ret = gst_matroska_demux_plugin_init (plugin);
ret &= gst_matroska_parse_plugin_init (plugin);
ret &= gst_element_register (plugin, "matroskamux", GST_RANK_PRIMARY,

View file

@ -125,6 +125,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
#define DEFAULT_INDEX 0
#define DEFAULT_POST_MESSAGES FALSE
#define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
#define DEFAULT_MAX_FILES 0
enum
{
@ -133,6 +134,7 @@ enum
PROP_INDEX,
PROP_POST_MESSAGES,
PROP_NEXT_FILE,
PROP_MAX_FILES,
PROP_LAST
};
@ -148,6 +150,12 @@ static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
GstBuffer * buffer);
static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
GstCaps * caps);
static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink *
multifilesink);
static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
GstBuffer * buffer);
static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink *
multifilesink);
#define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
static GType
@ -219,6 +227,22 @@ gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
/**
* GstMultiFileSink:max-files
*
* Maximum number of files to keep on disk. Once the maximum is reached, old
* files start to be deleted to make room for new ones.
*
* Since: 0.10.31
*/
g_object_class_install_property (gobject_class, PROP_MAX_FILES,
g_param_spec_uint ("max-files", "Max files",
"Maximum number of files to keep on disk. Once the maximum is reached,"
"old files start to be deleted to make room for new ones.",
0, G_MAXUINT, DEFAULT_MAX_FILES,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gobject_class->finalize = gst_multi_file_sink_finalize;
gstbasesink_class->get_times = NULL;
@ -244,6 +268,9 @@ gst_multi_file_sink_init (GstMultiFileSink * multifilesink)
multifilesink->filename = g_strdup (DEFAULT_LOCATION);
multifilesink->index = DEFAULT_INDEX;
multifilesink->post_messages = DEFAULT_POST_MESSAGES;
multifilesink->max_files = DEFAULT_MAX_FILES;
multifilesink->files = NULL;
multifilesink->n_files = 0;
gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
@ -256,6 +283,8 @@ gst_multi_file_sink_finalize (GObject * object)
GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
g_free (sink->filename);
g_slist_foreach (sink->files, (GFunc) g_free, NULL);
g_slist_free (sink->files);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -290,6 +319,9 @@ gst_multi_file_sink_set_property (GObject * object, guint prop_id,
case PROP_NEXT_FILE:
sink->next_file = g_value_get_enum (value);
break;
case PROP_MAX_FILES:
sink->max_files = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -315,6 +347,9 @@ gst_multi_file_sink_get_property (GObject * object, guint prop_id,
case PROP_NEXT_FILE:
g_value_set_enum (value, sink->next_file);
break;
case PROP_MAX_FILES:
g_value_set_uint (value, sink->max_files);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -399,39 +434,29 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
switch (multifilesink->next_file) {
case GST_MULTI_FILE_SINK_NEXT_BUFFER:
gst_multi_file_sink_ensure_max_files (multifilesink);
filename = g_strdup_printf (multifilesink->filename,
multifilesink->index);
ret = g_file_set_contents (filename, (char *) data, size, &error);
if (!ret)
goto write_error;
multifilesink->files = g_slist_append (multifilesink->files, filename);
multifilesink->n_files += 1;
gst_multi_file_sink_post_message (multifilesink, buffer, filename);
multifilesink->index++;
g_free (filename);
break;
case GST_MULTI_FILE_SINK_NEXT_DISCONT:
if (GST_BUFFER_IS_DISCONT (buffer)) {
if (multifilesink->file) {
fclose (multifilesink->file);
multifilesink->file = NULL;
filename = g_strdup_printf (multifilesink->filename,
multifilesink->index);
gst_multi_file_sink_post_message (multifilesink, buffer, filename);
g_free (filename);
multifilesink->index++;
}
if (multifilesink->file)
gst_multi_file_sink_close_file (multifilesink, buffer);
}
if (multifilesink->file == NULL) {
filename = g_strdup_printf (multifilesink->filename,
multifilesink->index);
multifilesink->file = g_fopen (filename, "wb");
g_free (filename);
if (multifilesink->file == NULL)
if (!gst_multi_file_sink_open_next_file (multifilesink))
goto stdio_write_error;
}
@ -451,16 +476,8 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
if (multifilesink->file) {
fclose (multifilesink->file);
multifilesink->file = NULL;
filename = g_strdup_printf (multifilesink->filename,
multifilesink->index);
gst_multi_file_sink_post_message (multifilesink, buffer, filename);
g_free (filename);
multifilesink->index++;
}
if (multifilesink->file)
gst_multi_file_sink_close_file (multifilesink, buffer);
multifilesink->next_segment += 10 * GST_SECOND;
}
@ -468,12 +485,7 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
if (multifilesink->file == NULL) {
int i;
filename = g_strdup_printf (multifilesink->filename,
multifilesink->index);
multifilesink->file = g_fopen (filename, "wb");
g_free (filename);
if (multifilesink->file == NULL)
if (!gst_multi_file_sink_open_next_file (multifilesink))
goto stdio_write_error;
if (multifilesink->streamheaders) {
@ -571,3 +583,57 @@ gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
return TRUE;
}
static void
gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
{
char *filename;
while (multifilesink->max_files &&
multifilesink->n_files >= multifilesink->max_files) {
filename = multifilesink->files->data;
g_remove (filename);
multifilesink->files = g_slist_delete_link (multifilesink->files,
multifilesink->files);
multifilesink->n_files -= 1;
}
}
static gboolean
gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
{
char *filename;
g_return_val_if_fail (multifilesink->file == NULL, FALSE);
gst_multi_file_sink_ensure_max_files (multifilesink);
filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
multifilesink->file = g_fopen (filename, "wb");
if (multifilesink->file == NULL) {
g_free (filename);
return FALSE;
}
multifilesink->files = g_slist_append (multifilesink->files, filename);
multifilesink->n_files += 1;
return TRUE;
}
static void
gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
GstBuffer * buffer)
{
char *filename;
fclose (multifilesink->file);
multifilesink->file = NULL;
if (buffer) {
filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
gst_multi_file_sink_post_message (multifilesink, buffer, filename);
g_free (filename);
}
multifilesink->index++;
}

View file

@ -67,6 +67,9 @@ struct _GstMultiFileSink
gboolean post_messages;
GstMultiFileSinkNext next_file;
FILE *file;
guint max_files;
GSList *files;
guint n_files;
gint64 next_segment;

View file

@ -159,7 +159,7 @@ gst_multipart_demux_class_init (GstMultipartDemuxClass * klass)
* not change and emit no-more-pads as soon as the first boundary
* content is parsed, decoded, and pads are linked.
*
* Since: 0.10.30
* Since: 0.10.31
*/
g_object_class_install_property (gobject_class, PROP_SINGLE_STREAM,
g_param_spec_boolean ("single-stream", "Single Stream",

View file

@ -517,7 +517,7 @@ gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux)
GST_BUFFER_OFFSET_END (footerbuf) = mux->offset;
GST_BUFFER_FLAG_SET (footerbuf, GST_BUFFER_FLAG_DELTA_UNIT);
GST_DEBUG_OBJECT (mux, "pushing %" G_GSIZE_FORMAT " bytes footer buffer", 2);
GST_DEBUG_OBJECT (mux, "pushing 2 bytes footer buffer");
ret = gst_pad_push (mux->srcpad, footerbuf);
beach:

View file

@ -96,6 +96,8 @@ static GstBuffer *gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload,
GstBuffer * buf);
static gboolean gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * filter,
GstCaps * caps);
static gboolean gst_rtp_h264_depay_handle_event (GstBaseRTPDepayload * depay,
GstEvent * event);
static void
gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass)
@ -135,6 +137,7 @@ gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass)
gstbasertpdepayload_class->process = gst_rtp_h264_depay_process;
gstbasertpdepayload_class->set_caps = gst_rtp_h264_depay_setcaps;
gstbasertpdepayload_class->handle_event = gst_rtp_h264_depay_handle_event;
GST_DEBUG_CATEGORY_INIT (rtph264depay_debug, "rtph264depay", 0,
"H264 Video RTP Depayloader");
@ -149,6 +152,18 @@ gst_rtp_h264_depay_init (GstRtpH264Depay * rtph264depay)
rtph264depay->merge = DEFAULT_ACCESS_UNIT;
}
static void
gst_rtp_h264_depay_reset (GstRtpH264Depay * rtph264depay)
{
gst_adapter_clear (rtph264depay->adapter);
rtph264depay->wait_start = TRUE;
gst_adapter_clear (rtph264depay->picture_adapter);
rtph264depay->picture_start = FALSE;
rtph264depay->last_keyframe = FALSE;
rtph264depay->last_ts = 0;
rtph264depay->current_fu_type = 0;
}
static void
gst_rtp_h264_depay_finalize (GObject * object)
{
@ -461,13 +476,34 @@ incomplete_caps:
}
}
static GstBuffer *
gst_rtp_h264_complete_au (GstRtpH264Depay * rtph264depay,
GstClockTime * out_timestamp, gboolean * out_keyframe)
{
guint outsize;
GstBuffer *outbuf;
/* we had a picture in the adapter and we completed it */
GST_DEBUG_OBJECT (rtph264depay, "taking completed AU");
outsize = gst_adapter_available (rtph264depay->picture_adapter);
outbuf = gst_adapter_take_buffer (rtph264depay->picture_adapter, outsize);
*out_timestamp = rtph264depay->last_ts;
*out_keyframe = rtph264depay->last_keyframe;
rtph264depay->last_keyframe = FALSE;
rtph264depay->picture_start = FALSE;
return outbuf;
}
/* SPS/PPS/IDR considered key, all others DELTA;
* so downstream waiting for keyframe can pick up at SPS/PPS/IDR */
#define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8))
static gboolean
gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
GstClockTime in_timestamp)
GstClockTime in_timestamp, gboolean marker)
{
GstBaseRTPDepayload *depayload = GST_BASE_RTP_DEPAYLOAD (rtph264depay);
gint nal_type;
@ -511,20 +547,9 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
}
GST_DEBUG_OBJECT (depayload, "start %d, complete %d", start, complete);
if (complete && rtph264depay->picture_start) {
guint outsize;
/* we had a picture in the adapter and we completed it */
GST_DEBUG_OBJECT (depayload, "taking completed AU");
outsize = gst_adapter_available (rtph264depay->picture_adapter);
outbuf = gst_adapter_take_buffer (rtph264depay->picture_adapter, outsize);
out_timestamp = rtph264depay->last_ts;
out_keyframe = rtph264depay->last_keyframe;
rtph264depay->last_keyframe = FALSE;
rtph264depay->picture_start = FALSE;
}
if (complete && rtph264depay->picture_start)
outbuf = gst_rtp_h264_complete_au (rtph264depay, &out_timestamp,
&out_keyframe);
/* add to adapter */
GST_DEBUG_OBJECT (depayload, "adding NAL to picture adapter");
@ -532,6 +557,10 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
rtph264depay->last_ts = in_timestamp;
rtph264depay->last_keyframe |= keyframe;
rtph264depay->picture_start |= start;
if (marker)
outbuf = gst_rtp_h264_complete_au (rtph264depay, &out_timestamp,
&out_keyframe);
} else {
/* no merge, output is input nal */
GST_DEBUG_OBJECT (depayload, "using NAL as output");
@ -571,6 +600,35 @@ short_nal:
}
}
static void
gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay)
{
guint outsize;
guint8 *outdata;
GstBuffer *outbuf;
outsize = gst_adapter_available (rtph264depay->adapter);
outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize);
outdata = GST_BUFFER_DATA (outbuf);
GST_DEBUG_OBJECT (rtph264depay, "output %d bytes", outsize);
if (rtph264depay->byte_stream) {
memcpy (outdata, sync_bytes, sizeof (sync_bytes));
} else {
outsize -= 4;
outdata[0] = (outsize >> 24);
outdata[1] = (outsize >> 16);
outdata[2] = (outsize >> 8);
outdata[3] = (outsize);
}
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf,
rtph264depay->fu_timestamp, rtph264depay->fu_marker);
rtph264depay->current_fu_type = 0;
}
static GstBuffer *
gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
{
@ -585,6 +643,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
if (GST_BUFFER_IS_DISCONT (buf)) {
gst_adapter_clear (rtph264depay->adapter);
rtph264depay->wait_start = TRUE;
rtph264depay->current_fu_type = 0;
}
{
@ -595,6 +654,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
guint8 *outdata;
guint outsize, nalu_size;
GstClockTime timestamp;
gboolean marker;
timestamp = GST_BUFFER_TIMESTAMP (buf);
@ -602,6 +662,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
payload_len = gst_rtp_buffer_get_payload_len (&rtp);
payload = gst_rtp_buffer_get_payload (&rtp);
marker = gst_rtp_buffer_get_marker (&rtp);
GST_DEBUG_OBJECT (rtph264depay, "receiving %d bytes", payload_len);
@ -625,6 +686,13 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
GST_DEBUG_OBJECT (rtph264depay, "NRI %d, Type %d", nal_ref_idc,
nal_unit_type);
/* If FU unit was being processed, but the current nal is of a different
* type. Assume that the remote payloader is buggy (didn't set the end bit
* when the FU ended) and send out what we gathered thusfar */
if (G_UNLIKELY (rtph264depay->current_fu_type != 0 &&
nal_unit_type != rtph264depay->current_fu_type))
gst_rtp_h264_push_fragmentation_unit (rtph264depay);
switch (nal_unit_type) {
case 0:
case 30:
@ -686,7 +754,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
outsize = gst_adapter_available (rtph264depay->adapter);
outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize);
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp);
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker);
break;
}
case 26:
@ -725,6 +793,15 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
/* NAL unit starts here */
guint8 nal_header;
/* If a new FU unit started, while still processing an older one.
* Assume that the remote payloader is buggy (doesn't set the end
* bit) and send out what we've gathered thusfar */
if (G_UNLIKELY (rtph264depay->current_fu_type != 0))
gst_rtp_h264_push_fragmentation_unit (rtph264depay);
rtph264depay->current_fu_type = nal_unit_type;
rtph264depay->fu_timestamp = timestamp;
rtph264depay->wait_start = FALSE;
/* reconstruct NAL header */
@ -766,28 +843,11 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
gst_adapter_push (rtph264depay->adapter, outbuf);
}
rtph264depay->fu_marker = marker;
/* if NAL unit ends, flush the adapter */
if (E) {
GST_DEBUG_OBJECT (rtph264depay, "output %d bytes", outsize);
outsize = gst_adapter_available (rtph264depay->adapter);
outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize);
outdata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
if (rtph264depay->byte_stream) {
memcpy (outdata, sync_bytes, sizeof (sync_bytes));
} else {
outsize -= 4;
outdata[0] = (outsize >> 24);
outdata[1] = (outsize >> 16);
outdata[2] = (outsize >> 8);
outdata[3] = (outsize);
outsize += 4;
}
gst_buffer_unmap (outbuf, outdata, outsize);
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp);
}
if (E)
gst_rtp_h264_push_fragmentation_unit (rtph264depay);
break;
}
default:
@ -811,7 +871,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
memcpy (outdata + sizeof (sync_bytes), payload, nalu_size);
gst_buffer_unmap (outbuf, outdata, outsize);
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp);
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker);
break;
}
}
@ -843,6 +903,25 @@ not_implemented:
}
}
static gboolean
gst_rtp_h264_depay_handle_event (GstBaseRTPDepayload * depay, GstEvent * event)
{
GstRtpH264Depay *rtph264depay;
rtph264depay = GST_RTP_H264_DEPAY (depay);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
gst_rtp_h264_depay_reset (rtph264depay);
break;
default:
break;
}
return
GST_BASE_RTP_DEPAYLOAD_CLASS (parent_class)->handle_event (depay, event);
}
static GstStateChangeReturn
gst_rtp_h264_depay_change_state (GstElement * element,
GstStateChange transition)
@ -856,11 +935,7 @@ gst_rtp_h264_depay_change_state (GstElement * element,
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_adapter_clear (rtph264depay->adapter);
rtph264depay->wait_start = TRUE;
gst_adapter_clear (rtph264depay->picture_adapter);
rtph264depay->picture_start = FALSE;
rtph264depay->last_keyframe = FALSE;
gst_rtp_h264_depay_reset (rtph264depay);
break;
default:
break;

View file

@ -56,6 +56,11 @@ struct _GstRtpH264Depay
gboolean picture_start;
GstClockTime last_ts;
gboolean last_keyframe;
/* Work around broken payloaders wrt. FU-A & FU-B */
guint8 current_fu_type;
GstClockTime fu_timestamp;
gboolean fu_marker;
};
struct _GstRtpH264DepayClass

View file

@ -423,6 +423,7 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
guint8 *data, *bdata;
gsize size, bsize;
GstBuffer *buffer;
const gchar *alignment;
rtph264pay = GST_RTP_H264_PAY (basepayload);
@ -432,6 +433,12 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
* NALs */
gst_basertppayload_set_options (basepayload, "video", TRUE, "H264", 90000);
alignment = gst_structure_get_string (str, "alignment");
if (alignment && !strcmp (alignment, "au"))
rtph264pay->au_alignment = TRUE;
else
rtph264pay->au_alignment = FALSE;
/* packetized AVC video has a codec_data */
if ((value = gst_structure_get_value (str, "codec_data"))) {
guint num_sps, num_pps;
@ -777,7 +784,7 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader,
static GstFlowReturn
gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload,
const guint8 * data, guint size, GstClockTime timestamp,
GstBuffer * buffer_orig);
GstBuffer * buffer_orig, gboolean end_of_au);
static GstFlowReturn
gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload,
@ -795,7 +802,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload,
/* resend SPS */
data = gst_buffer_map (sps_buf, &size, NULL, GST_MAP_READ);
ret = gst_rtp_h264_pay_payload_nal (basepayload,
data, size, timestamp, sps_buf);
data, size, timestamp, sps_buf, FALSE);
gst_buffer_unmap (sps_buf, data, size);
/* Not critical here; but throw a warning */
if (ret != GST_FLOW_OK)
@ -808,7 +815,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload,
/* resend PPS */
data = gst_buffer_map (pps_buf, &size, NULL, GST_MAP_READ);
ret = gst_rtp_h264_pay_payload_nal (basepayload,
data, size, timestamp, pps_buf);
data, size, timestamp, pps_buf, FALSE);
gst_buffer_unmap (pps_buf, data, size);
/* Not critical here; but throw a warning */
if (ret != GST_FLOW_OK)
@ -824,7 +831,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload,
static GstFlowReturn
gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload,
const guint8 * data, guint size, GstClockTime timestamp,
GstBuffer * buffer_orig)
GstBuffer * buffer_orig, gboolean end_of_au)
{
GstRtpH264Pay *rtph264pay;
GstFlowReturn ret;
@ -910,7 +917,7 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload,
gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
/* only set the marker bit on packets containing access units */
if (IS_ACCESS_UNIT (nalType)) {
if (IS_ACCESS_UNIT (nalType) && end_of_au) {
gst_rtp_buffer_set_marker (&rtp, 1);
}
@ -1007,7 +1014,7 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload,
end = 1;
}
if (IS_ACCESS_UNIT (nalType)) {
gst_rtp_buffer_set_marker (&rtp, end);
gst_rtp_buffer_set_marker (&rtp, end && end_of_au);
}
/* FU indicator */
@ -1120,6 +1127,7 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
while (size > nal_length_size) {
gint i;
gboolean end_of_au = FALSE;
nal_len = 0;
for (i = 0; i < nal_length_size; i++) {
@ -1138,9 +1146,16 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
nal_len);
}
/* If we're at the end of the buffer, then we're at the end of the
* access unit
*/
if (rtph264pay->au_alignment && size - nal_len <= nal_length_size) {
end_of_au = TRUE;
}
ret =
gst_rtp_h264_pay_payload_nal (basepayload, data, nal_len, timestamp,
buffer);
buffer, end_of_au);
if (ret != GST_FLOW_OK)
break;
@ -1242,6 +1257,7 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
for (i = 0; i < nal_queue->len; i++) {
guint size;
gboolean end_of_au = FALSE;
nal_len = g_array_index (nal_queue, guint, i);
/* skip start code */
@ -1257,10 +1273,22 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
for (; size > 1 && data[size - 1] == 0x0; size--)
/* skip */ ;
/* If it's the last nal unit we have in non-bytestream mode, we can
* assume it's the end of an access-unit
*
* FIXME: We need to wait until the next packet or EOS to
* actually payload the NAL so we can know if the current NAL is
* the last one of an access unit or not if we are in bytestream mode
*/
if (rtph264pay->au_alignment &&
rtph264pay->scan_mode != GST_H264_SCAN_MODE_BYTESTREAM &&
i == nal_queue->len - 1)
end_of_au = TRUE;
/* put the data in one or more RTP packets */
ret =
gst_rtp_h264_pay_payload_nal (basepayload, data, size, timestamp,
buffer);
buffer, end_of_au);
if (ret != GST_FLOW_OK) {
break;
}

View file

@ -55,6 +55,7 @@ struct _GstRtpH264Pay
GList *sps, *pps;
gboolean packetized;
gboolean au_alignment;
guint nal_length_size;
GArray *queue;

View file

@ -89,6 +89,7 @@ typedef enum _RtpJpegMarker RtpJpegMarker;
* @JPEG_MARKER_SOS: Start of Scan marker
* @JPEG_MARKER_EOI: End of Image marker
* @JPEG_MARKER_DRI: Define Restart Interval marker
* @JPEG_MARKER_H264: H264 marker
*
* Identifers for markers in JPEG header
*/
@ -103,7 +104,8 @@ enum _RtpJpegMarker
JPEG_MARKER_DHT = 0xC4,
JPEG_MARKER_SOS = 0xDA,
JPEG_MARKER_EOI = 0xD9,
JPEG_MARKER_DRI = 0xDD
JPEG_MARKER_DRI = 0xDD,
JPEG_MARKER_H264 = 0xE4
};
#define DEFAULT_JPEG_QUANT 255
@ -586,7 +588,7 @@ gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset)
guint8 marker;
marker = data[*offset];
GST_LOG ("found %02x marker at offset %u", marker, *offset);
GST_LOG ("found 0x%02x marker at offset %u", marker, *offset);
(*offset)++;
return marker;
}
@ -638,6 +640,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload,
case JPEG_MARKER_JFIF:
case JPEG_MARKER_CMT:
case JPEG_MARKER_DHT:
case JPEG_MARKER_H264:
GST_LOG_OBJECT (pay, "skipping marker");
offset += gst_rtp_jpeg_pay_header_size (data, offset);
break;

View file

@ -83,8 +83,8 @@ GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d",
GST_STATIC_CAPS ("application/x-rtcp")
);
#define GST_PAD_LOCK(obj) (g_mutex_lock ((obj)->padlock))
#define GST_PAD_UNLOCK(obj) (g_mutex_unlock ((obj)->padlock))
#define GST_PAD_LOCK(obj) (g_static_rec_mutex_lock (&(obj)->padlock))
#define GST_PAD_UNLOCK(obj) (g_static_rec_mutex_unlock (&(obj)->padlock))
/* signals */
enum
@ -155,6 +155,7 @@ find_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
return NULL;
}
/* with PAD_LOCK */
static GstRtpSsrcDemuxPad *
find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
{
@ -167,11 +168,8 @@ find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc);
GST_OBJECT_LOCK (demux);
demuxpad = find_demux_pad_for_ssrc (demux, ssrc);
if (demuxpad != NULL) {
GST_OBJECT_UNLOCK (demux);
return demuxpad;
}
@ -218,8 +216,6 @@ find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
gst_rtp_ssrc_demux_iterate_internal_links_src);
gst_pad_set_active (rtcp_pad, TRUE);
GST_OBJECT_UNLOCK (demux);
gst_element_add_pad (GST_ELEMENT_CAST (demux), rtp_pad);
gst_element_add_pad (GST_ELEMENT_CAST (demux), rtcp_pad);
@ -333,7 +329,7 @@ gst_rtp_ssrc_demux_init (GstRtpSsrcDemux * demux)
gst_rtp_ssrc_demux_iterate_internal_links_sink);
gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtcp_sink);
demux->padlock = g_mutex_new ();
g_static_rec_mutex_init (&demux->padlock);
gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
}
@ -375,7 +371,7 @@ gst_rtp_ssrc_demux_finalize (GObject * object)
GstRtpSsrcDemux *demux;
demux = GST_RTP_SSRC_DEMUX (object);
g_mutex_free (demux->padlock);
g_static_rec_mutex_free (&demux->padlock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -514,6 +510,7 @@ gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf)
guint32 ssrc;
GstRtpSsrcDemuxPad *dpad;
GstRTPBuffer rtp;
GstPad *srcpad;
demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad));
@ -526,12 +523,19 @@ gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf)
GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc);
GST_PAD_LOCK (demux);
dpad = find_or_create_demux_pad_for_ssrc (demux, ssrc);
if (dpad == NULL)
if (dpad == NULL) {
GST_PAD_UNLOCK (demux);
goto create_failed;
}
srcpad = gst_object_ref (dpad->rtp_pad);
GST_PAD_UNLOCK (demux);
/* push to srcpad */
ret = gst_pad_push (dpad->rtp_pad, buf);
ret = gst_pad_push (srcpad, buf);
gst_object_unref (srcpad);
return ret;
@ -562,6 +566,7 @@ gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf)
GstRtpSsrcDemuxPad *dpad;
GstRTCPPacket packet;
GstRTCPBuffer rtcp;
GstPad *srcpad;
demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad));
@ -588,13 +593,19 @@ gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf)
GST_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc);
GST_PAD_LOCK (demux);
dpad = find_or_create_demux_pad_for_ssrc (demux, ssrc);
if (dpad == NULL)
if (dpad == NULL) {
GST_PAD_UNLOCK (demux);
goto create_failed;
}
srcpad = gst_object_ref (dpad->rtcp_pad);
GST_PAD_UNLOCK (demux);
/* push to srcpad */
ret = gst_pad_push (dpad->rtcp_pad, buf);
ret = gst_pad_push (srcpad, buf);
gst_object_unref (srcpad);
return ret;

View file

@ -41,7 +41,7 @@ struct _GstRtpSsrcDemux
GstPad *rtp_sink;
GstPad *rtcp_sink;
GMutex *padlock;
GStaticRecMutex padlock;
GSList *srcpads;
};

View file

@ -60,6 +60,7 @@ enum
#define DEFAULT_SOURCES NULL
#define DEFAULT_RTCP_MIN_INTERVAL (RTP_STATS_MIN_INTERVAL * GST_SECOND)
#define DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW (2 * GST_SECOND)
#define DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD (3)
enum
{
@ -78,6 +79,7 @@ enum
PROP_FAVOR_NEW,
PROP_RTCP_MIN_INTERVAL,
PROP_RTCP_FEEDBACK_RETENTION_WINDOW,
PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
PROP_LAST
};
@ -492,6 +494,14 @@ rtp_session_class_init (RTPSessionClass * klass)
0, G_MAXUINT64, DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
g_param_spec_uint ("rtcp-immediate-feedback-threshold",
"RTCP Immediate Feedback threshold",
"The maximum number of members of a RTP session for which immediate"
" feedback is used",
0, G_MAXUINT, DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
klass->get_source_by_ssrc =
GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
@ -534,6 +544,9 @@ rtp_session_init (RTPSession * sess)
sess->stats.active_sources++;
INIT_AVG (sess->stats.avg_rtcp_packet_size, 100);
rtp_stats_set_min_interval (&sess->stats,
(gdouble) DEFAULT_RTCP_MIN_INTERVAL / GST_SECOND);
/* default UDP header length */
sess->header_len = 28;
sess->mtu = DEFAULT_RTCP_MTU;
@ -550,6 +563,8 @@ rtp_session_init (RTPSession * sess)
sess->first_rtcp = TRUE;
sess->allow_early = TRUE;
sess->rtcp_feedback_retention_window = DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW;
sess->rtcp_immediate_feedback_threshold =
DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD;
sess->rtcp_pli_requests = g_array_new (FALSE, FALSE, sizeof (guint32));
@ -649,6 +664,9 @@ rtp_session_set_property (GObject * object, guint prop_id,
rtp_stats_set_min_interval (&sess->stats,
(gdouble) g_value_get_uint64 (value) / GST_SECOND);
break;
case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD:
sess->rtcp_immediate_feedback_threshold = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -703,6 +721,9 @@ rtp_session_get_property (GObject * object, guint prop_id,
case PROP_RTCP_MIN_INTERVAL:
g_value_set_uint64 (value, sess->stats.min_interval * GST_SECOND);
break;
case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD:
g_value_set_uint (value, sess->rtcp_immediate_feedback_threshold);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -2465,7 +2486,7 @@ calculate_rtcp_interval (RTPSession * sess, gboolean deterministic,
g_hash_table_foreach (sess->cnames, (GHFunc) add_bitrates, &bandwidth);
bandwidth /= 8.0;
}
if (bandwidth == 0)
if (bandwidth < 8000)
bandwidth = RTP_STATS_BANDWIDTH;
rtp_stats_set_bandwidths (&sess->stats, bandwidth,
@ -3083,7 +3104,8 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
/* check for outdated collisions */
GST_DEBUG ("Timing out collisions");
rtp_source_timeout (sess->source, current_time,
data.interval * RTCP_INTERVAL_COLLISION_TIMEOUT,
/* "a relatively long time" -- RFC 3550 section 8.2 */
RTP_STATS_MIN_INTERVAL * GST_SECOND * 10,
running_time - sess->rtcp_feedback_retention_window);
if (sess->change_ssrc) {
@ -3175,8 +3197,13 @@ rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time,
if (current_time + T_dither_max > sess->next_rtcp_check_time)
goto dont_send;
/* RFC 4585 section 3.5.2 step 4 */
if (sess->allow_early == FALSE)
/* RFC 4585 section 3.5.2 step 4
* Don't send if allow_early is FALSE, but not if we are in
* immediate mode, meaning we are part of a group of at most the
* application-specific threshold.
*/
if (sess->total_sources > sess->rtcp_immediate_feedback_threshold &&
sess->allow_early == FALSE)
goto dont_send;
if (T_dither_max) {

View file

@ -232,6 +232,7 @@ struct _RTPSession {
gboolean change_ssrc;
gboolean favor_new;
GstClockTime rtcp_feedback_retention_window;
guint rtcp_immediate_feedback_threshold;
GArray *rtcp_pli_requests;
GstClockTime last_keyframe_request;

View file

@ -510,6 +510,8 @@ gst_rtspsrc_init (GstRTSPSrc * src)
g_static_rec_mutex_init (src->state_rec_lock);
src->state = GST_RTSP_STATE_INVALID;
GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE);
}
static void
@ -5438,7 +5440,6 @@ gst_rtspsrc_open_from_sdp (GstRTSPSrc * src, GstSDPMessage * sdp,
}
src->state = GST_RTSP_STATE_INIT;
GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE);
/* setup streams */
if ((res = gst_rtspsrc_setup_streams (src, async)) < 0)

View file

@ -465,7 +465,7 @@ gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class,
*
* TV norm
*
* Since: 0.10.30
* Since: 0.10.31
*/
g_object_class_install_property (gobject_class, PROP_TV_NORM,
g_param_spec_enum ("norm", "TV norm",

View file

@ -110,7 +110,7 @@ GST_END_TEST;
GST_START_TEST (test_parse_detect_stream)
{
gst_parser_test_output_caps (ac3_frame, sizeof (ac3_frame),
NULL, SINK_CAPS_TMPL ",channels=1,rate=48000");
NULL, SINK_CAPS_TMPL ",channels=1,rate=48000,alignment=frame");
}
GST_END_TEST;

View file

@ -54,7 +54,7 @@ g_mkdtemp (const gchar * template)
return tmpdir;
}
GST_START_TEST (test_multifilesink)
GST_START_TEST (test_multifilesink_key_frame)
{
GstElement *pipeline;
GstElement *mfs;
@ -98,6 +98,57 @@ GST_START_TEST (test_multifilesink)
GST_END_TEST;
GST_START_TEST (test_multifilesink_max_files)
{
GstElement *pipeline;
GstElement *mfs;
int i;
const gchar *tmpdir;
gchar *my_tmpdir;
gchar *template;
gchar *mfs_pattern;
tmpdir = g_get_tmp_dir ();
template = g_build_filename (tmpdir, "multifile-test-XXXXXX", NULL);
my_tmpdir = g_mkdtemp (template);
fail_if (my_tmpdir == NULL);
pipeline =
gst_parse_launch
("videotestsrc num-buffers=10 ! video/x-raw-yuv,format=(fourcc)I420,width=320,height=240 ! multifilesink name=mfs",
NULL);
fail_if (pipeline == NULL);
mfs = gst_bin_get_by_name (GST_BIN (pipeline), "mfs");
fail_if (mfs == NULL);
mfs_pattern = g_build_filename (my_tmpdir, "%05d", NULL);
g_object_set (G_OBJECT (mfs), "location", mfs_pattern, "max-files", 3, NULL);
g_object_unref (mfs);
run_pipeline (pipeline);
gst_object_unref (pipeline);
for (i = 0; i < 7; i++) {
char *s;
s = g_strdup_printf (mfs_pattern, i);
fail_unless (g_remove (s) != 0);
g_free (s);
}
for (i = 7; i < 10; i++) {
char *s;
s = g_strdup_printf (mfs_pattern, i);
fail_if (g_remove (s) != 0);
g_free (s);
}
fail_if (g_remove (my_tmpdir) != 0);
g_free (mfs_pattern);
g_free (my_tmpdir);
g_free (template);
}
GST_END_TEST;
GST_START_TEST (test_multifilesrc)
{
GstElement *pipeline;
@ -164,7 +215,8 @@ libvisual_suite (void)
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_multifilesink);
tcase_add_test (tc_chain, test_multifilesink_key_frame);
tcase_add_test (tc_chain, test_multifilesink_max_files);
tcase_add_test (tc_chain, test_multifilesrc);
return s;