mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 01:30:38 +00:00
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:
commit
4121021bb2
36 changed files with 1142 additions and 192 deletions
|
@ -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 ***
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -65,6 +65,7 @@ struct _GstJackAudioSink {
|
|||
/* our ports */
|
||||
jack_port_t **ports;
|
||||
int port_count;
|
||||
sample_t **buffers;
|
||||
};
|
||||
|
||||
struct _GstJackAudioSinkClass {
|
||||
|
|
|
@ -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;
|
||||
|
||||
state = pa_stream_get_state (pbuf->stream);
|
||||
|
||||
GST_LOG_OBJECT (psink, "stream state is now %d", state);
|
||||
|
||||
if (!PA_STREAM_IS_GOOD (state))
|
||||
if (!gst_pulsering_wait_for_stream_ready (psink, pbuf->stream))
|
||||
goto connect_failed;
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
break;
|
||||
#ifdef HAVE_PULSE_1_0
|
||||
g_free (psink->device);
|
||||
psink->device = g_strdup (pa_stream_get_device_name (pbuf->stream));
|
||||
|
||||
/* 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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ struct _GstPulseSrc
|
|||
|
||||
pa_context *context;
|
||||
pa_stream *stream;
|
||||
guint32 source_output_idx;
|
||||
|
||||
pa_sample_spec sample_spec;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
*
|
||||
|
@ -55,6 +61,7 @@ struct _GstAc3Parse {
|
|||
gint channels;
|
||||
gint blocks;
|
||||
gboolean eac;
|
||||
volatile gint align;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) ||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ struct _GstRtpH264Pay
|
|||
GList *sps, *pps;
|
||||
|
||||
gboolean packetized;
|
||||
gboolean au_alignment;
|
||||
guint nal_length_size;
|
||||
GArray *queue;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ struct _GstRtpSsrcDemux
|
|||
GstPad *rtp_sink;
|
||||
GstPad *rtcp_sink;
|
||||
|
||||
GMutex *padlock;
|
||||
GStaticRecMutex padlock;
|
||||
GSList *srcpads;
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue