Merge branch 'master' into 0.11

Conflicts:
	common
	ext/pulse/pulsesink.c
	ext/soup/gstsouphttpclientsink.c
	gst/audioparsers/gstaacparse.c
	gst/audioparsers/gstac3parse.c
	gst/rtp/gstrtph264depay.c
	gst/rtpmanager/gstrtpjitterbuffer.c
	gst/rtpmanager/rtpjitterbuffer.c
	gst/rtsp/gstrtspsrc.c
	sys/ximage/gstximagesrc.c
This commit is contained in:
Wim Taymans 2011-09-28 12:44:59 +02:00
commit 87fbd1e784
50 changed files with 2174 additions and 352 deletions

View file

@ -13,33 +13,13 @@ FORMATS=html
html: html-build.stamp
include $(top_srcdir)/common/upload-doc.mak
# generated basefiles
#basefiles = \
## $(DOC_MODULE).types \
# $(DOC_MODULE)-sections.txt \
# $(DOC_MODULE)-docs.sgml
# ugly hack to make -unused.sgml work
#unused-build.stamp:
# BUILDDIR=`pwd` && \
# cd $(srcdir)/tmpl && \
# ln -sf gstreamer-libs-unused.sgml \
# $$BUILDDIR/tmpl/gstreamer-libs-@GST_MAJORMINOR@-unused.sgml
# touch unused-build.stamp
# these rules are added to create parallel docs using GST_MAJORMINOR
#$(basefiles): gstreamer-libs-@GST_MAJORMINOR@%: gstreamer-libs%
# cp $< $@
#CLEANFILES = $(basefiles)
# The top-level SGML file. Change it if you want.
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
# The directory containing the source code. Relative to $(top_srcdir).
# The directory containing the source code.
# gtk-doc will search all .c & .h files beneath here for inline comments
# documenting functions and macros.
DOC_SOURCE_DIR = $(top_srcdir)
DOC_SOURCE_DIR = $(top_srcdir)/gst $(top_srcdir)/ext $(top_srcdir)/sys
# Extra options to supply to gtkdoc-scan.
SCAN_OPTIONS=
@ -53,14 +33,11 @@ FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
--extra-dir=$(GSTPB_PREFIX)/share/gtk-doc/html
# Used for dependencies.
HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h
CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c $(DOC_SOURCE_DIR)/*/*/*.cc $(DOC_SOURCE_DIR)/*/*/*.m
# this is a wingo addition
# thomasvs: another nice wingo addition would be an explanation on why
# this is useful ;)
SCANOBJ_DEPS =
HFILE_GLOB= \
$(top_srcdir)/gst/*/*.h $(top_srcdir)/ext/*/*.h $(top_srcdir)/sys/*/*.h
CFILE_GLOB= \
$(top_srcdir)/gst/*/*.c $(top_srcdir)/ext/*/*.c $(top_srcdir)/sys/*/*.c \
$(top_srcdir)/ext/*/*.cc $(top_srcdir)/sys/*/*.m
# Header files to ignore when scanning.
IGNORE_HFILES =
@ -253,7 +230,7 @@ extra_files =
# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib
# contains GtkObjects/GObjects and you want to document signals and properties.
GTKDOC_CFLAGS = $(GST_BASE_CFLAGS) -I$(top_builddir)
GTKDOC_LIBS = $(SCANOBJ_DEPS) $(GST_BASE_LIBS)
GTKDOC_LIBS = $(GST_BASE_LIBS)
GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC)
GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC)

View file

@ -7,12 +7,14 @@ libgstpulse_la_SOURCES = \
pulsemixertrack.c \
pulseprobe.c \
pulsesink.c \
pulseaudiosink.c \
pulsesrc.c \
pulseutil.c
libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS)
libgstpulse_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \
-lgstinterfaces-$(GST_MAJORMINOR) $(GST_BASE_LIBS) $(GST_LIBS) $(PULSE_LIBS)
-lgstinterfaces-$(GST_MAJORMINOR) -lgstpbutils-$(GST_MAJORMINOR) \
$(GST_BASE_LIBS) $(GST_LIBS) $(PULSE_LIBS)
libgstpulse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstpulse_la_LIBTOOLFLAGS = --tag=disable-static

View file

@ -49,6 +49,12 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_PULSESRC))
return FALSE;
#ifdef HAVE_PULSE_1_0
if (!gst_element_register (plugin, "pulseaudiosink", GST_RANK_PRIMARY + 11,
GST_TYPE_PULSE_AUDIO_SINK))
return FALSE;
#endif
if (!gst_element_register (plugin, "pulsemixer", GST_RANK_NONE,
GST_TYPE_PULSEMIXER))
return FALSE;

927
ext/pulse/pulseaudiosink.c Normal file
View file

@ -0,0 +1,927 @@
/*-*- Mode: C; c-basic-offset: 2 -*-*/
/* GStreamer pulseaudio plugin
*
* Copyright (c) 2011 Intel Corporation
* 2011 Collabora
* 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
* 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* gst-pulse is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* gst-pulse is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with gst-pulse; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
/**
* SECTION:element-pulseaudiosink
* @see_also: pulsesink, pulsesrc, pulsemixer
*
* This element outputs audio to a
* <ulink href="http://www.pulseaudio.org">PulseAudio sound server</ulink> via
* the @pulsesink element. It transparently takes care of passing compressed
* format as-is if the sink supports it, decoding if necessary, and changes
* to supported formats at runtime.
*
* <refsect2>
* <title>Example pipelines</title>
* |[
* gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! pulseaudiosink
* ]| Decode and play an Ogg/Vorbis file.
* |[
* gst-launch -v filesrc location=test.mp3 ! mp3parse ! pulseaudiosink stream-properties="props,media.title=test"
* ]| Play an MP3 file on a sink that supports decoding directly, plug in a
* decoder if/when required.
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_PULSE_1_0
#include <gst/pbutils/pbutils.h>
#include <gst/gst-i18n-plugin.h>
#include <gst/audio/gstaudioiec61937.h>
#include "pulsesink.h"
GST_DEBUG_CATEGORY (pulseaudiosink_debug);
#define GST_CAT_DEFAULT (pulseaudiosink_debug)
#define GST_PULSE_AUDIO_SINK_LOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"locking from thread %p", \
g_thread_self ()); \
g_mutex_lock (GST_PULSE_AUDIO_SINK_CAST(obj)->lock); \
GST_LOG_OBJECT (obj, \
"locked from thread %p", \
g_thread_self ()); \
} G_STMT_END
#define GST_PULSE_AUDIO_SINK_UNLOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"unlocking from thread %p", \
g_thread_self ()); \
g_mutex_unlock (GST_PULSE_AUDIO_SINK_CAST(obj)->lock); \
} G_STMT_END
typedef struct
{
GstBin parent;
GMutex *lock;
GstPad *sinkpad;
GstPad *sink_proxypad;
GstPadEventFunction sinkpad_old_eventfunc;
GstPadEventFunction proxypad_old_eventfunc;
GstPulseSink *psink;
GstElement *dbin2;
GstSegment segment;
guint event_probe_id;
gulong pad_added_id;
gboolean format_lost;
} GstPulseAudioSink;
typedef struct
{
GstBinClass parent_class;
guint n_prop_own;
guint n_prop_total;
} GstPulseAudioSinkClass;
static void gst_pulse_audio_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_pulse_audio_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_pulse_audio_sink_dispose (GObject * object);
static gboolean gst_pulse_audio_sink_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_pulse_audio_sink_sink_event (GstPad * pad,
GstEvent * event);
static gboolean gst_pulse_audio_sink_sink_acceptcaps (GstPad * pad,
GstCaps * caps);
static gboolean gst_pulse_audio_sink_sink_setcaps (GstPad * pad,
GstCaps * caps);
static GstStateChangeReturn
gst_pulse_audio_sink_change_state (GstElement * element,
GstStateChange transition);
static void
gst_pulse_audio_sink_do_init (GType type)
{
GST_DEBUG_CATEGORY_INIT (pulseaudiosink_debug, "pulseaudiosink", 0,
"Bin that wraps pulsesink for handling compressed formats");
}
GST_BOILERPLATE_FULL (GstPulseAudioSink, gst_pulse_audio_sink, GstBin,
GST_TYPE_BIN, gst_pulse_audio_sink_do_init);
static GstStaticPadTemplate sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS));
static void
gst_pulse_audio_sink_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_set_details_simple (element_class,
"Bin wrapping pulsesink", "Sink/Audio/Bin",
"Correctly handles sink changes when streaming compressed formats to "
"pulsesink", "Arun Raghavan <arun.raghavan@collabora.co.uk>");
}
static GParamSpec *
param_spec_copy (GParamSpec * spec)
{
const char *name, *nick, *blurb;
GParamFlags flags;
name = g_param_spec_get_name (spec);
nick = g_param_spec_get_nick (spec);
blurb = g_param_spec_get_blurb (spec);
flags = spec->flags;
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_BOOLEAN) {
return g_param_spec_boolean (name, nick, blurb,
G_PARAM_SPEC_BOOLEAN (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_BOXED) {
return g_param_spec_boxed (name, nick, blurb, spec->value_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_CHAR) {
GParamSpecChar *cspec = G_PARAM_SPEC_CHAR (spec);
return g_param_spec_char (name, nick, blurb, cspec->minimum,
cspec->maximum, cspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_DOUBLE) {
GParamSpecDouble *dspec = G_PARAM_SPEC_DOUBLE (spec);
return g_param_spec_double (name, nick, blurb, dspec->minimum,
dspec->maximum, dspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_ENUM) {
return g_param_spec_enum (name, nick, blurb, spec->value_type,
G_PARAM_SPEC_ENUM (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_FLAGS) {
return g_param_spec_flags (name, nick, blurb, spec->value_type,
G_PARAM_SPEC_ENUM (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_FLOAT) {
GParamSpecFloat *fspec = G_PARAM_SPEC_FLOAT (spec);
return g_param_spec_double (name, nick, blurb, fspec->minimum,
fspec->maximum, fspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_GTYPE) {
return g_param_spec_gtype (name, nick, blurb,
G_PARAM_SPEC_GTYPE (spec)->is_a_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_INT) {
GParamSpecInt *ispec = G_PARAM_SPEC_INT (spec);
return g_param_spec_int (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_INT64) {
GParamSpecInt64 *ispec = G_PARAM_SPEC_INT64 (spec);
return g_param_spec_int64 (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_LONG) {
GParamSpecLong *lspec = G_PARAM_SPEC_LONG (spec);
return g_param_spec_long (name, nick, blurb, lspec->minimum,
lspec->maximum, lspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_OBJECT) {
return g_param_spec_object (name, nick, blurb, spec->value_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_PARAM) {
return g_param_spec_param (name, nick, blurb, spec->value_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_POINTER) {
return g_param_spec_pointer (name, nick, blurb, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_STRING) {
return g_param_spec_string (name, nick, blurb,
G_PARAM_SPEC_STRING (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UCHAR) {
GParamSpecUChar *cspec = G_PARAM_SPEC_UCHAR (spec);
return g_param_spec_uchar (name, nick, blurb, cspec->minimum,
cspec->maximum, cspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UINT) {
GParamSpecUInt *ispec = G_PARAM_SPEC_UINT (spec);
return g_param_spec_uint (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UINT64) {
GParamSpecUInt64 *ispec = G_PARAM_SPEC_UINT64 (spec);
return g_param_spec_uint64 (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_ULONG) {
GParamSpecULong *lspec = G_PARAM_SPEC_ULONG (spec);
return g_param_spec_ulong (name, nick, blurb, lspec->minimum,
lspec->maximum, lspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UNICHAR) {
return g_param_spec_unichar (name, nick, blurb,
G_PARAM_SPEC_UNICHAR (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_VARIANT) {
GParamSpecVariant *vspec = G_PARAM_SPEC_VARIANT (spec);
return g_param_spec_variant (name, nick, blurb, vspec->type,
vspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == GST_TYPE_PARAM_MINI_OBJECT) {
return gst_param_spec_mini_object (name, nick, blurb, spec->value_type,
flags);
}
g_warning ("Unknown param type %ld for '%s'",
(long) G_PARAM_SPEC_TYPE (spec), name);
g_assert_not_reached ();
}
static void
gst_pulse_audio_sink_class_init (GstPulseAudioSinkClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *element_class = (GstElementClass *) klass;
GstPulseSinkClass *psink_class =
GST_PULSESINK_CLASS (g_type_class_ref (GST_TYPE_PULSESINK));
GParamSpec **specs;
guint n, i, j;
gobject_class->get_property = gst_pulse_audio_sink_get_property;
gobject_class->set_property = gst_pulse_audio_sink_set_property;
gobject_class->dispose = gst_pulse_audio_sink_dispose;
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_change_state);
/* Find out how many properties we already have */
specs = g_object_class_list_properties (gobject_class, &klass->n_prop_own);
g_free (specs);
/* Proxy pulsesink's properties */
specs = g_object_class_list_properties (G_OBJECT_CLASS (psink_class), &n);
for (i = 0, j = klass->n_prop_own; i < n; i++) {
if (g_object_class_find_property (gobject_class,
g_param_spec_get_name (specs[i]))) {
/* We already inherited this property from a parent, skip */
j--;
} else {
g_object_class_install_property (gobject_class, i + j + 1,
param_spec_copy (specs[i]));
}
}
klass->n_prop_total = i + j;
g_free (specs);
g_type_class_unref (psink_class);
}
static GstPad *
get_proxypad (GstPad * sinkpad)
{
GstIterator *iter = NULL;
GstPad *proxypad = NULL;
iter = gst_pad_iterate_internal_links (sinkpad);
if (iter) {
if (gst_iterator_next (iter, (gpointer) & proxypad) != GST_ITERATOR_OK)
proxypad = NULL;
gst_iterator_free (iter);
}
return proxypad;
}
static void
post_missing_element_message (GstPulseAudioSink * pbin, const gchar * name)
{
GstMessage *msg;
msg = gst_missing_element_message_new (GST_ELEMENT_CAST (pbin), name);
gst_element_post_message (GST_ELEMENT_CAST (pbin), msg);
}
static void
notify_cb (GObject * selector, GParamSpec * pspec, GstPulseAudioSink * pbin)
{
g_object_notify (G_OBJECT (pbin), g_param_spec_get_name (pspec));
}
static void
gst_pulse_audio_sink_init (GstPulseAudioSink * pbin,
GstPulseAudioSinkClass * klass)
{
GstPad *pad = NULL;
GParamSpec **specs;
GString *prop;
guint i;
pbin->lock = g_mutex_new ();
gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED);
pbin->psink = GST_PULSESINK (gst_element_factory_make ("pulsesink",
"pulseaudiosink-sink"));
g_assert (pbin->psink != NULL);
if (!gst_bin_add (GST_BIN (pbin), GST_ELEMENT (pbin->psink))) {
GST_ERROR_OBJECT (pbin, "Failed to add pulsesink to bin");
goto error;
}
pad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
pbin->sinkpad = gst_ghost_pad_new_from_template ("sink", pad,
gst_static_pad_template_get (&sink_template));
pbin->sinkpad_old_eventfunc = GST_PAD_EVENTFUNC (pbin->sinkpad);
gst_pad_set_event_function (pbin->sinkpad,
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_event));
gst_pad_set_setcaps_function (pbin->sinkpad,
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_setcaps));
gst_pad_set_acceptcaps_function (pbin->sinkpad,
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_acceptcaps));
gst_element_add_pad (GST_ELEMENT (pbin), pbin->sinkpad);
if (!(pbin->sink_proxypad = get_proxypad (pbin->sinkpad)))
GST_ERROR_OBJECT (pbin, "Failed to get proxypad of srcpad");
else {
pbin->proxypad_old_eventfunc = GST_PAD_EVENTFUNC (pbin->sink_proxypad);
gst_pad_set_event_function (pbin->sink_proxypad,
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_src_event));
}
/* Now proxy all the notify::* signals */
specs = g_object_class_list_properties (G_OBJECT_CLASS (klass), &i);
prop = g_string_sized_new (30);
for (i--; i >= klass->n_prop_own; i--) {
g_string_printf (prop, "notify::%s", g_param_spec_get_name (specs[i]));
g_signal_connect (pbin->psink, prop->str, G_CALLBACK (notify_cb), pbin);
}
g_string_free (prop, TRUE);
g_free (specs);
pbin->format_lost = FALSE;
out:
if (pad)
gst_object_unref (pad);
return;
error:
if (pbin->psink)
gst_object_unref (pbin->psink);
goto out;
}
static void
gst_pulse_audio_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object);
GstPulseAudioSinkClass *klass =
GST_PULSE_AUDIO_SINK_CLASS (G_OBJECT_GET_CLASS (object));
g_return_if_fail (prop_id <= klass->n_prop_total);
g_object_set_property (G_OBJECT (pbin->psink), g_param_spec_get_name (pspec),
value);
}
static void
gst_pulse_audio_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object);
GstPulseAudioSinkClass *klass =
GST_PULSE_AUDIO_SINK_CLASS (G_OBJECT_GET_CLASS (object));
g_return_if_fail (prop_id <= klass->n_prop_total);
g_object_get_property (G_OBJECT (pbin->psink), g_param_spec_get_name (pspec),
value);
}
static void
gst_pulse_audio_sink_free_dbin2 (GstPulseAudioSink * pbin)
{
g_signal_handler_disconnect (pbin->dbin2, pbin->pad_added_id);
gst_element_set_state (pbin->dbin2, GST_STATE_NULL);
gst_bin_remove (GST_BIN (pbin), pbin->dbin2);
pbin->dbin2 = NULL;
}
static void
gst_pulse_audio_sink_dispose (GObject * object)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object);
if (pbin->lock) {
g_mutex_free (pbin->lock);
pbin->lock = NULL;
}
if (pbin->sink_proxypad) {
gst_object_unref (pbin->sink_proxypad);
pbin->sink_proxypad = NULL;
}
if (pbin->dbin2) {
g_signal_handler_disconnect (pbin->dbin2, pbin->pad_added_id);
pbin->dbin2 = NULL;
}
pbin->sinkpad = NULL;
pbin->psink = NULL;
}
static gboolean
gst_pulse_audio_sink_update_sinkpad (GstPulseAudioSink * pbin, GstPad * sinkpad)
{
gboolean ret;
ret = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (pbin->sinkpad), sinkpad);
if (!ret)
GST_WARNING_OBJECT (pbin, "Could not update ghostpad target");
return ret;
}
static void
distribute_running_time (GstElement * element, const GstSegment * segment)
{
GstEvent *event;
GstPad *pad;
pad = gst_element_get_static_pad (element, "sink");
/* FIXME: Some decoders collect newsegments and send them out at once, making
* them lose accumulator events (and thus making dbin2_event_probe() hard to
* do right if we're sending these as well. We can get away with not sending
* these at the moment, but this should be fixed! */
#if 0
if (segment->accum) {
event = gst_event_new_new_segment_full (FALSE, segment->rate,
segment->applied_rate, segment->format, 0, segment->accum, 0);
gst_pad_send_event (pad, event);
}
#endif
event = gst_event_new_new_segment_full (FALSE, segment->rate,
segment->applied_rate, segment->format,
segment->start, segment->stop, segment->time);
gst_pad_send_event (pad, event);
gst_object_unref (pad);
}
static gboolean
dbin2_event_probe (GstPad * pad, GstMiniObject * obj, gpointer data)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data);
GstEvent *event = GST_EVENT (obj);
if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
GST_DEBUG_OBJECT (pbin, "Got newsegment - dropping");
gst_pad_remove_event_probe (pad, pbin->event_probe_id);
gst_object_unref (pbin);
return FALSE;
}
return TRUE;
}
static void
pad_added_cb (GstElement * dbin2, GstPad * pad, gpointer * data)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data);
GstPad *sinkpad = NULL;
pbin = GST_PULSE_AUDIO_SINK (data);
sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
GST_ERROR_OBJECT (pbin, "Failed to link decodebin2 to pulsesink");
else
GST_DEBUG_OBJECT (pbin, "Linked new pad to pulsesink");
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
gst_object_unref (sinkpad);
}
/* Called with pbin lock held */
static void
gst_pulse_audio_sink_add_dbin2 (GstPulseAudioSink * pbin)
{
GstPad *sinkpad = NULL;
g_assert (pbin->dbin2 == NULL);
pbin->dbin2 = gst_element_factory_make ("decodebin2", "pulseaudiosink-dbin2");
if (!pbin->dbin2) {
post_missing_element_message (pbin, "decodebin2");
GST_ELEMENT_WARNING (pbin, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"decodebin2"), ("audio playback might fail"));
goto out;
}
if (!gst_bin_add (GST_BIN (pbin), pbin->dbin2)) {
GST_ERROR_OBJECT (pbin, "Failed to add decodebin2 to bin");
goto out;
}
pbin->pad_added_id = g_signal_connect (pbin->dbin2, "pad-added",
G_CALLBACK (pad_added_cb), pbin);
if (!gst_element_sync_state_with_parent (pbin->dbin2)) {
GST_ERROR_OBJECT (pbin, "Failed to set decodebin2 to parent state");
goto out;
}
/* Trap the newsegment events that we feed the decodebin and discard them */
sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
pbin->event_probe_id = gst_pad_add_event_probe (sinkpad,
G_CALLBACK (dbin2_event_probe), gst_object_ref (pbin));
gst_object_unref (sinkpad);
sinkpad = NULL;
GST_DEBUG_OBJECT (pbin, "Distributing running time to decodebin");
distribute_running_time (pbin->dbin2, &pbin->segment);
sinkpad = gst_element_get_static_pad (pbin->dbin2, "sink");
gst_pulse_audio_sink_update_sinkpad (pbin, sinkpad);
out:
if (sinkpad)
gst_object_unref (sinkpad);
}
static void
update_eac3_alignment (GstPulseAudioSink * pbin)
{
GstCaps *caps = gst_pad_peer_get_caps_reffed (pbin->sinkpad);
GstStructure *st;
if (!caps)
return;
st = gst_caps_get_structure (caps, 0);
if (g_str_equal (gst_structure_get_name (st), "audio/x-eac3")) {
GstStructure *event_st = gst_structure_new ("ac3parse-set-alignment",
"alignment", G_TYPE_STRING, pbin->dbin2 ? "frame" : "iec61937", NULL);
if (!gst_pad_push_event (pbin->sinkpad,
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, event_st)))
GST_WARNING_OBJECT (pbin->sinkpad, "Could not update alignment");
}
gst_caps_unref (caps);
}
static void
proxypad_blocked_cb (GstPad * pad, gboolean blocked, gpointer data)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data);
GstCaps *caps;
GstPad *sinkpad = NULL;
if (!blocked) {
/* Unblocked, don't need to do anything */
GST_DEBUG_OBJECT (pbin, "unblocked");
return;
}
GST_DEBUG_OBJECT (pbin, "blocked");
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (!pbin->format_lost) {
sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
caps = gst_pad_get_caps_reffed (pad);
if (gst_pad_accept_caps (sinkpad, caps)) {
if (pbin->dbin2) {
GST_DEBUG_OBJECT (pbin, "Removing decodebin");
gst_pulse_audio_sink_free_dbin2 (pbin);
gst_pulse_audio_sink_update_sinkpad (pbin, sinkpad);
} else
GST_DEBUG_OBJECT (pbin, "Doing nothing");
gst_caps_unref (caps);
gst_object_unref (sinkpad);
goto done;
}
/* pulsesink doesn't accept the incoming caps, so add a decodebin
* (potentially after removing the existing once, since decodebin2 can't
* renegotiate). */
} else {
/* Format lost, proceed to try plugging a decodebin */
pbin->format_lost = FALSE;
}
if (pbin->dbin2 != NULL) {
/* decodebin2 doesn't support reconfiguration, so throw this one away and
* create a new one. */
gst_pulse_audio_sink_free_dbin2 (pbin);
}
GST_DEBUG_OBJECT (pbin, "Adding decodebin");
gst_pulse_audio_sink_add_dbin2 (pbin);
done:
update_eac3_alignment (pbin);
gst_pad_set_blocked_async_full (pad, FALSE, proxypad_blocked_cb,
gst_object_ref (pbin), (GDestroyNotify) gst_object_unref);
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
}
static gboolean
gst_pulse_audio_sink_src_event (GstPad * pad, GstEvent * event)
{
GstPulseAudioSink *pbin = NULL;
GstPad *ghostpad = NULL;
gboolean ret = FALSE;
ghostpad = GST_PAD_CAST (gst_pad_get_parent (pad));
if (G_UNLIKELY (!ghostpad)) {
GST_WARNING_OBJECT (pad, "Could not get ghostpad");
goto out;
}
pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (ghostpad));
if (G_UNLIKELY (!pbin)) {
GST_WARNING_OBJECT (pad, "Could not get pulseaudiosink");
goto out;
}
if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) &&
(gst_event_has_name (event, "pulse-format-lost") ||
gst_event_has_name (event, "pulse-sink-changed"))) {
g_return_val_if_fail (pad->mode != GST_ACTIVATE_PULL, FALSE);
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (gst_event_has_name (event, "pulse-format-lost"))
pbin->format_lost = TRUE;
if (!gst_pad_is_blocked (pad))
gst_pad_set_blocked_async_full (pad, TRUE, proxypad_blocked_cb,
gst_object_ref (pbin), (GDestroyNotify) gst_object_unref);
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
ret = TRUE;
} else if (pbin->proxypad_old_eventfunc) {
ret = pbin->proxypad_old_eventfunc (pad, event);
event = NULL;
}
out:
if (ghostpad)
gst_object_unref (ghostpad);
if (pbin)
gst_object_unref (pbin);
if (event)
gst_event_unref (event);
return ret;
}
static gboolean
gst_pulse_audio_sink_sink_event (GstPad * pad, GstEvent * event)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (pad));
gboolean ret;
ret = pbin->sinkpad_old_eventfunc (pad, gst_event_ref (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_NEWSEGMENT:
{
GstFormat format;
gdouble rate, arate;
gint64 start, stop, time;
gboolean update;
GST_PULSE_AUDIO_SINK_LOCK (pbin);
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
&start, &stop, &time);
GST_DEBUG_OBJECT (pbin,
"newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
GST_TIME_ARGS (time));
if (format == GST_FORMAT_TIME) {
/* Store the values for feeding to sub-elements */
gst_segment_set_newsegment_full (&pbin->segment, update,
rate, arate, format, start, stop, time);
} else {
GST_WARNING_OBJECT (pbin, "Got a non-TIME format segment");
gst_segment_init (&pbin->segment, GST_FORMAT_TIME);
}
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
}
case GST_EVENT_FLUSH_STOP:
GST_PULSE_AUDIO_SINK_LOCK (pbin);
gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED);
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
default:
break;
}
gst_object_unref (pbin);
gst_event_unref (event);
return ret;
}
/* The bin's acceptcaps should be exactly equivalent to a pulsesink that is
* connected to a sink that supports all the formats in template caps. This
* means that upstream will have to have everything possibly upto a parser
* plugged and we plugin a decoder whenever required. */
static gboolean
gst_pulse_audio_sink_sink_acceptcaps (GstPad * pad, GstCaps * caps)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (pad));
GstRingBufferSpec spec = { 0 };
const GstStructure *st;
GstCaps *pad_caps = NULL;
gboolean ret = FALSE;
pad_caps = gst_pad_get_caps_reffed (pad);
if (!pad_caps || !gst_caps_can_intersect (pad_caps, caps))
goto out;
/* 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 out;
spec.latency_time = GST_BASE_AUDIO_SINK (pbin->psink)->latency_time;
if (!gst_ring_buffer_parse_caps (&spec, caps))
goto out;
/* Make sure non-raw input is framed (one frame per buffer) and can be
* payloaded */
st = gst_caps_get_structure (caps, 0);
if (!g_str_has_prefix (gst_structure_get_name (st), "audio/x-raw")) {
gboolean framed = FALSE, parsed = FALSE;
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;
}
ret = TRUE;
out:
if (pad_caps)
gst_caps_unref (pad_caps);
gst_object_unref (pbin);
return ret;
}
static gboolean
gst_pulse_audio_sink_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (pad));
gboolean ret = TRUE;
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (!gst_pad_is_blocked (pbin->sinkpad))
gst_pad_set_blocked_async_full (pbin->sink_proxypad, TRUE,
proxypad_blocked_cb, gst_object_ref (pbin),
(GDestroyNotify) gst_object_unref);
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
gst_object_unref (pbin);
return ret;
}
static GstStateChangeReturn
gst_pulse_audio_sink_change_state (GstElement * element,
GstStateChange transition)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
/* Nothing to do for upward transitions */
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (gst_pad_is_blocked (pbin->sinkpad)) {
gst_pad_set_blocked_async_full (pbin->sink_proxypad, FALSE,
proxypad_blocked_cb, gst_object_ref (pbin),
(GDestroyNotify) gst_object_unref);
}
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret != GST_STATE_CHANGE_SUCCESS) {
GST_DEBUG_OBJECT (pbin, "Base class returned %d on state change", ret);
goto out;
}
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PULSE_AUDIO_SINK_LOCK (pbin);
gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED);
if (pbin->dbin2) {
GstPad *pad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink),
"sink");
gst_pulse_audio_sink_free_dbin2 (pbin);
gst_pulse_audio_sink_update_sinkpad (pbin, pad);
gst_object_unref (pad);
}
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
default:
break;
}
out:
return ret;
}
#endif /* HAVE_PULSE_1_0 */

View file

@ -420,6 +420,27 @@ gst_pulsering_context_subscribe_cb (pa_context * c,
if (idx != pa_stream_get_index (pbuf->stream))
continue;
#ifdef HAVE_PULSE_1_0
if (psink->device && pa_format_info_is_pcm (pbuf->format) &&
!g_str_equal (psink->device,
pa_stream_get_device_name (pbuf->stream))) {
/* Underlying sink changed. And this is not a passthrough stream. Let's
* see if someone upstream wants to try to renegotiate. */
GstEvent *renego;
g_free (psink->device);
psink->device = g_strdup (pa_stream_get_device_name (pbuf->stream));
GST_INFO_OBJECT (psink, "emitting sink-changed");
renego = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new ("pulse-sink-changed", NULL));
if (!gst_pad_push_event (GST_BASE_SINK (psink)->sinkpad, renego))
GST_DEBUG_OBJECT (psink, "Emitted sink-changed - nobody was listening");
}
#endif
/* Actually this event is also triggered when other properties of
* the stream change that are unrelated to the volume. However it is
* probably cheaper to signal the change here and check for the
@ -1716,35 +1737,10 @@ static gboolean gst_pulsesink_event (GstBaseSink * sink, GstEvent * event);
static GstStateChangeReturn gst_pulsesink_change_state (GstElement * element,
GstStateChange transition);
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
# define FORMATS "{ S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, " \
"S24LE, S24BE, S24_32LE, S24_32BE, S8 }"
#else
# define FORMATS "{ S16BE, S16LE, F32BE, F32LE, S32BE, S32LE, " \
"S24BE, S24LE, S24_32BE, S24_32LE, S8 }"
#endif
static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " FORMATS ", "
"rate = (int) [ 1, MAX ], "
"channels = (int) [ 1, 32 ];"
"audio/x-alaw, "
"rate = (int) [ 1, MAX], "
"channels = (int) [ 1, 32 ];"
"audio/x-mulaw, "
"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_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS));
GST_IMPLEMENT_PULSEPROBE_METHODS (GstPulseSink, gst_pulsesink);
@ -2002,6 +1998,8 @@ done:
}
#ifdef HAVE_PULSE_1_0
/* NOTE: If you're making changes here, see if pulseaudiosink acceptcaps also
* needs to be changed accordingly. */
static gboolean
gst_pulsesink_pad_acceptcaps (GstPad * pad, GstCaps * caps)
{

View file

@ -24,6 +24,10 @@
#ifndef __GST_PULSESINK_H__
#define __GST_PULSESINK_H__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/audio/gstaudiosink.h>
@ -88,6 +92,60 @@ struct _GstPulseSinkClass
GType gst_pulsesink_get_type (void);
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
# define FORMATS "{ S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, " \
"S24LE, S24BE, S24_32LE, S24_32BE, S8 }"
#else
# define FORMATS "{ S16BE, S16LE, F32BE, F32LE, S32BE, S32LE, " \
"S24BE, S24LE, S24_32BE, S24_32LE, S8 }"
#endif
#define _PULSE_SINK_CAPS_COMMON \
"audio/x-raw, " \
"format = (string) " FORMATS ", " \
"rate = (int) [ 1, MAX ], " \
"channels = (int) [ 1, 32 ];" \
"audio/x-alaw, " \
"rate = (int) [ 1, MAX], " \
"channels = (int) [ 1, 32 ];" \
"audio/x-mulaw, " \
"rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ];"
#ifdef HAVE_PULSE_1_0
#define _PULSE_SINK_CAPS_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;"
#else
#define _PULSE_SINK_CAPS_1_0 ""
#endif
#define PULSE_SINK_TEMPLATE_CAPS \
_PULSE_SINK_CAPS_COMMON \
_PULSE_SINK_CAPS_1_0
#ifdef HAVE_PULSE_1_0
#define GST_TYPE_PULSE_AUDIO_SINK \
(gst_pulse_audio_sink_get_type())
#define GST_PULSE_AUDIO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSink))
#define GST_PULSE_AUDIO_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSinkClass))
#define GST_IS_PULSE_AUDIO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PULSE_AUDIO_SINK))
#define GST_IS_PULSE_AUDIO_SINK_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PULSE_AUDIO_SINK))
#define GST_PULSE_AUDIO_SINK_CAST(obj) \
((GstPulseAudioSink *)(obj))
GType gst_pulse_audio_sink_get_type (void);
#endif /* HAVE_PULSE_1_0 */
G_END_DECLS
#endif /* __GST_PULSESINK_H__ */

View file

@ -22,6 +22,10 @@
#ifndef __GST_PULSEUTIL_H__
#define __GST_PULSEUTIL_H__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <pulse/pulseaudio.h>
#include <gst/audio/gstaudiosink.h>

View file

@ -1,10 +1,10 @@
plugin_LTLIBRARIES = libgstsouphttpsrc.la
libgstsouphttpsrc_la_SOURCES = gstsouphttpsrc.c gstsouphttpsink.c gstsoup.c
libgstsouphttpsrc_la_SOURCES = gstsouphttpsrc.c gstsouphttpclientsink.c gstsoup.c
libgstsouphttpsrc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(SOUP_CFLAGS)
libgstsouphttpsrc_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(SOUP_LIBS)
libgstsouphttpsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstsouphttpsrc_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gstsouphttpsrc.h gstsouphttpsink.h
noinst_HEADERS = gstsouphttpsrc.h gstsouphttpclientsink.h

View file

@ -19,7 +19,7 @@
#include <gst/gst-i18n-plugin.h>
#include "gstsouphttpsrc.h"
#include "gstsouphttpsink.h"
#include "gstsouphttpclientsink.h"
static gboolean
@ -34,8 +34,8 @@ plugin_init (GstPlugin * plugin)
gst_element_register (plugin, "souphttpsrc", GST_RANK_PRIMARY,
GST_TYPE_SOUP_HTTP_SRC);
gst_element_register (plugin, "souphttpsink", GST_RANK_NONE,
GST_TYPE_SOUP_HTTP_SINK);
gst_element_register (plugin, "souphttpclientsink", GST_RANK_NONE,
GST_TYPE_SOUP_HTTP_CLIENT_SINK);
return TRUE;
}

View file

@ -17,16 +17,16 @@
* Boston, MA 02110-1335, USA.
*/
/**
* SECTION:element-gstsouphttpsink
* SECTION:element-gstsouphttpclientsink
*
* The souphttpsink element sends pipeline data to an HTTP server
* The souphttpclientsink element sends pipeline data to an HTTP server
* using HTTP PUT commands.
*
* <refsect2>
* <title>Example launch line</title>
* |[
* gst-launch -v videotestsrc num-buffers=300 ! theoraenc ! oggmux !
* souphttpsink location=http://server/filename.ogv
* souphttpclientsink location=http://server/filename.ogv
* ]|
*
* This example encodes 10 seconds of video and sends it to the HTTP
@ -40,43 +40,44 @@
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include "gstsouphttpsink.h"
#include "gstsouphttpclientsink.h"
GST_DEBUG_CATEGORY_STATIC (gst_soup_http_sink_debug_category);
#define GST_CAT_DEFAULT gst_soup_http_sink_debug_category
GST_DEBUG_CATEGORY_STATIC (souphttpclientsink_dbg);
#define GST_CAT_DEFAULT souphttpclientsink_dbg
/* prototypes */
static void gst_soup_http_sink_set_property (GObject * object,
static void gst_soup_http_client_sink_set_property (GObject * object,
guint property_id, const GValue * value, GParamSpec * pspec);
static void gst_soup_http_sink_get_property (GObject * object,
static void gst_soup_http_client_sink_get_property (GObject * object,
guint property_id, GValue * value, GParamSpec * pspec);
static void gst_soup_http_sink_dispose (GObject * object);
static void gst_soup_http_sink_finalize (GObject * object);
static void gst_soup_http_client_sink_dispose (GObject * object);
static void gst_soup_http_client_sink_finalize (GObject * object);
static gboolean gst_soup_http_sink_set_caps (GstBaseSink * sink,
static gboolean gst_soup_http_client_sink_set_caps (GstBaseSink * sink,
GstCaps * caps);
static void gst_soup_http_sink_get_times (GstBaseSink * sink,
static void gst_soup_http_client_sink_get_times (GstBaseSink * sink,
GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
static gboolean gst_soup_http_sink_start (GstBaseSink * sink);
static gboolean gst_soup_http_sink_stop (GstBaseSink * sink);
static gboolean gst_soup_http_sink_unlock (GstBaseSink * sink);
static gboolean gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event);
static GstFlowReturn
gst_soup_http_sink_preroll (GstBaseSink * sink, GstBuffer * buffer);
static GstFlowReturn
gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer);
static gboolean gst_soup_http_client_sink_start (GstBaseSink * sink);
static gboolean gst_soup_http_client_sink_stop (GstBaseSink * sink);
static gboolean gst_soup_http_client_sink_unlock (GstBaseSink * sink);
static gboolean gst_soup_http_client_sink_event (GstBaseSink * sink,
GstEvent * event);
static GstFlowReturn gst_soup_http_client_sink_preroll (GstBaseSink * sink,
GstBuffer * buffer);
static GstFlowReturn gst_soup_http_client_sink_render (GstBaseSink * sink,
GstBuffer * buffer);
static void free_buffer_list (GList * list);
static void gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink);
static void gst_soup_http_client_sink_reset (GstSoupHttpClientSink *
souphttpsink);
static void authenticate (SoupSession * session, SoupMessage * msg,
SoupAuth * auth, gboolean retrying, gpointer user_data);
static void
callback (SoupSession * session, SoupMessage * msg, gpointer user_data);
static gboolean
gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink,
const gchar * uri);
static void callback (SoupSession * session, SoupMessage * msg,
gpointer user_data);
static gboolean gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink *
souphttpsink, const gchar * uri);
enum
{
@ -93,11 +94,11 @@ enum
PROP_SESSION
};
#define DEFAULT_USER_AGENT "GStreamer souphttpsink "
#define DEFAULT_USER_AGENT "GStreamer souphttpclientsink "
/* pad templates */
static GstStaticPadTemplate gst_soup_http_sink_sink_template =
static GstStaticPadTemplate gst_soup_http_client_sink_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
@ -106,20 +107,21 @@ GST_STATIC_PAD_TEMPLATE ("sink",
/* class initialization */
#define gst_soup_http_sink_parent_class parent_class
G_DEFINE_TYPE (GstSoupHttpSink, gst_soup_http_sink, GST_TYPE_BASE_SINK);
#define gst_soup_http_client_sink_parent_class parent_class
G_DEFINE_TYPE (GstSoupHttpClientSink, gst_soup_http_client_sink,
GST_TYPE_BASE_SINK);
static void
gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass)
gst_soup_http_client_sink_class_init (GstSoupHttpClientSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass);
gobject_class->set_property = gst_soup_http_sink_set_property;
gobject_class->get_property = gst_soup_http_sink_get_property;
gobject_class->dispose = gst_soup_http_sink_dispose;
gobject_class->finalize = gst_soup_http_sink_finalize;
gobject_class->set_property = gst_soup_http_client_sink_set_property;
gobject_class->get_property = gst_soup_http_client_sink_get_property;
gobject_class->dispose = gst_soup_http_client_sink_dispose;
gobject_class->finalize = gst_soup_http_client_sink_finalize;
g_object_class_install_property (gobject_class,
PROP_LOCATION,
@ -166,37 +168,38 @@ gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass)
G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_soup_http_sink_sink_template));
gst_static_pad_template_get (&gst_soup_http_client_sink_sink_template));
gst_element_class_set_details_simple (gstelement_class, "HTTP client sink",
"Generic", "Sends streams to HTTP server via PUT",
"David Schleef <ds@entropywave.com>");
base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_soup_http_sink_set_caps);
base_sink_class->set_caps =
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_set_caps);
if (0)
base_sink_class->get_times =
GST_DEBUG_FUNCPTR (gst_soup_http_sink_get_times);
base_sink_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_sink_start);
base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_sink_stop);
base_sink_class->unlock = GST_DEBUG_FUNCPTR (gst_soup_http_sink_unlock);
base_sink_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_sink_event);
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_get_times);
base_sink_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_start);
base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_stop);
base_sink_class->unlock =
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_unlock);
base_sink_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_event);
if (0)
base_sink_class->preroll = GST_DEBUG_FUNCPTR (gst_soup_http_sink_preroll);
base_sink_class->render = GST_DEBUG_FUNCPTR (gst_soup_http_sink_render);
base_sink_class->preroll =
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_preroll);
base_sink_class->render =
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_render);
GST_DEBUG_CATEGORY_INIT (souphttpclientsink_dbg, "souphttpclientsink", 0,
"souphttpclientsink element");
GST_DEBUG_CATEGORY_INIT (gst_soup_http_sink_debug_category, "souphttpsink", 0,
"debug category for souphttpsink element");
}
static void
gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink)
gst_soup_http_client_sink_init (GstSoupHttpClientSink * souphttpsink)
{
const char *proxy;
souphttpsink->sinkpad =
gst_pad_new_from_static_template (&gst_soup_http_sink_sink_template,
"sink");
souphttpsink->mutex = g_mutex_new ();
souphttpsink->cond = g_cond_new ();
@ -210,17 +213,17 @@ gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink)
souphttpsink->prop_session = NULL;
souphttpsink->timeout = 1;
proxy = g_getenv ("http_proxy");
if (proxy && !gst_soup_http_sink_set_proxy (souphttpsink, proxy)) {
if (proxy && !gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) {
GST_WARNING_OBJECT (souphttpsink,
"The proxy in the http_proxy env var (\"%s\") cannot be parsed.",
proxy);
}
gst_soup_http_sink_reset (souphttpsink);
gst_soup_http_client_sink_reset (souphttpsink);
}
static void
gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink)
gst_soup_http_client_sink_reset (GstSoupHttpClientSink * souphttpsink)
{
g_free (souphttpsink->reason_phrase);
souphttpsink->reason_phrase = NULL;
@ -230,7 +233,8 @@ gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink)
}
static gboolean
gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, const gchar * uri)
gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink * souphttpsink,
const gchar * uri)
{
if (souphttpsink->proxy) {
soup_uri_free (souphttpsink->proxy);
@ -249,10 +253,10 @@ gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, const gchar * uri)
}
void
gst_soup_http_sink_set_property (GObject * object, guint property_id,
gst_soup_http_client_sink_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
g_mutex_lock (souphttpsink->mutex);
switch (property_id) {
@ -300,7 +304,7 @@ gst_soup_http_sink_set_property (GObject * object, guint property_id,
GST_WARNING ("proxy property cannot be NULL");
goto done;
}
if (!gst_soup_http_sink_set_proxy (souphttpsink, proxy)) {
if (!gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) {
GST_WARNING ("badly formatted proxy URI");
goto done;
}
@ -319,10 +323,10 @@ done:
}
void
gst_soup_http_sink_get_property (GObject * object, guint property_id,
gst_soup_http_client_sink_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
switch (property_id) {
case PROP_SESSION:
@ -369,9 +373,9 @@ gst_soup_http_sink_get_property (GObject * object, guint property_id,
}
void
gst_soup_http_sink_dispose (GObject * object)
gst_soup_http_client_sink_dispose (GObject * object)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
/* clean up as possible. may be called multiple times */
if (souphttpsink->prop_session)
@ -382,9 +386,9 @@ gst_soup_http_sink_dispose (GObject * object)
}
void
gst_soup_http_sink_finalize (GObject * object)
gst_soup_http_client_sink_finalize (GObject * object)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
/* clean up object here */
@ -406,9 +410,9 @@ gst_soup_http_sink_finalize (GObject * object)
static gboolean
gst_soup_http_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
gst_soup_http_client_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
GstStructure *structure;
const GValue *value_array;
int i, n;
@ -435,7 +439,7 @@ gst_soup_http_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
}
static void
gst_soup_http_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
gst_soup_http_client_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end)
{
@ -444,7 +448,7 @@ gst_soup_http_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
static gpointer
thread_func (gpointer ptr)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (ptr);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (ptr);
GST_DEBUG ("thread start");
@ -457,9 +461,9 @@ thread_func (gpointer ptr)
}
static gboolean
gst_soup_http_sink_start (GstBaseSink * sink)
gst_soup_http_client_sink_start (GstBaseSink * sink)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
if (souphttpsink->prop_session) {
souphttpsink->session = souphttpsink->prop_session;
@ -488,9 +492,9 @@ gst_soup_http_sink_start (GstBaseSink * sink)
}
static gboolean
gst_soup_http_sink_stop (GstBaseSink * sink)
gst_soup_http_client_sink_stop (GstBaseSink * sink)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
GST_DEBUG ("stop");
@ -510,13 +514,13 @@ gst_soup_http_sink_stop (GstBaseSink * sink)
souphttpsink->context = NULL;
}
gst_soup_http_sink_reset (souphttpsink);
gst_soup_http_client_sink_reset (souphttpsink);
return TRUE;
}
static gboolean
gst_soup_http_sink_unlock (GstBaseSink * sink)
gst_soup_http_client_sink_unlock (GstBaseSink * sink)
{
GST_DEBUG ("unlock");
@ -524,9 +528,9 @@ gst_soup_http_sink_unlock (GstBaseSink * sink)
}
static gboolean
gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event)
gst_soup_http_client_sink_event (GstBaseSink * sink, GstEvent * event)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
GST_DEBUG_OBJECT (souphttpsink, "event");
@ -545,7 +549,7 @@ gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event)
}
static GstFlowReturn
gst_soup_http_sink_preroll (GstBaseSink * sink, GstBuffer * buffer)
gst_soup_http_client_sink_preroll (GstBaseSink * sink, GstBuffer * buffer)
{
GST_DEBUG ("preroll");
@ -564,7 +568,7 @@ free_buffer_list (GList * list)
}
static void
send_message_locked (GstSoupHttpSink * souphttpsink)
send_message_locked (GstSoupHttpClientSink * souphttpsink)
{
GList *g;
guint64 n;
@ -643,7 +647,7 @@ send_message_locked (GstSoupHttpSink * souphttpsink)
}
static gboolean
send_message (GstSoupHttpSink * souphttpsink)
send_message (GstSoupHttpClientSink * souphttpsink)
{
g_mutex_lock (souphttpsink->mutex);
send_message_locked (souphttpsink);
@ -655,7 +659,7 @@ send_message (GstSoupHttpSink * souphttpsink)
static void
callback (SoupSession * session, SoupMessage * msg, gpointer user_data)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (user_data);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data);
GST_DEBUG_OBJECT (souphttpsink, "callback status=%d %s",
msg->status_code, msg->reason_phrase);
@ -679,9 +683,9 @@ callback (SoupSession * session, SoupMessage * msg, gpointer user_data)
}
static GstFlowReturn
gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer)
gst_soup_http_client_sink_render (GstBaseSink * sink, GstBuffer * buffer)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
GSource *source;
gboolean wake;
@ -717,7 +721,7 @@ static void
authenticate (SoupSession * session, SoupMessage * msg,
SoupAuth * auth, gboolean retrying, gpointer user_data)
{
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (user_data);
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data);
if (!retrying) {
if (souphttpsink->user_id && souphttpsink->user_pw) {

View file

@ -1,5 +1,5 @@
/* GStreamer
* Copyright (C) 2011 FIXME <fixme@example.com>
* Copyright (C) 2011 David Schleef <ds@entropywave.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -17,29 +17,27 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef _GST_SOUP_HTTP_SINK_H_
#define _GST_SOUP_HTTP_SINK_H_
#ifndef _GST_SOUP_HTTP_CLIENT_SINK_H_
#define _GST_SOUP_HTTP_CLIENT_SINK_H_
#include <gst/base/gstbasesink.h>
#include <libsoup/soup.h>
G_BEGIN_DECLS
#define GST_TYPE_SOUP_HTTP_SINK (gst_soup_http_sink_get_type())
#define GST_SOUP_HTTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SOUP_HTTP_SINK,GstSoupHttpSink))
#define GST_SOUP_HTTP_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SOUP_HTTP_SINK,GstSoupHttpSinkClass))
#define GST_IS_SOUP_HTTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_SINK))
#define GST_IS_SOUP_HTTP_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_SINK))
#define GST_TYPE_SOUP_HTTP_CLIENT_SINK (gst_soup_http_client_sink_get_type())
#define GST_SOUP_HTTP_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SOUP_HTTP_CLIENT_SINK,GstSoupHttpClientSink))
#define GST_SOUP_HTTP_CLIENT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SOUP_HTTP_CLIENT_SINK,GstSoupHttpClientSinkClass))
#define GST_IS_SOUP_HTTP_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_CLIENT_SINK))
#define GST_IS_SOUP_HTTP_CLIENT_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_CLIENT_SINK))
typedef struct _GstSoupHttpSink GstSoupHttpSink;
typedef struct _GstSoupHttpSinkClass GstSoupHttpSinkClass;
typedef struct _GstSoupHttpClientSink GstSoupHttpClientSink;
typedef struct _GstSoupHttpClientSinkClass GstSoupHttpClientSinkClass;
struct _GstSoupHttpSink
struct _GstSoupHttpClientSink
{
GstBaseSink base_souphttpsink;
GstPad *sinkpad;
GMutex *mutex;
GCond *cond;
GMainContext *context;
@ -71,12 +69,12 @@ struct _GstSoupHttpSink
};
struct _GstSoupHttpSinkClass
struct _GstSoupHttpClientSinkClass
{
GstBaseSinkClass base_souphttpsink_class;
};
GType gst_soup_http_sink_get_type (void);
GType gst_soup_http_client_sink_get_type (void);
G_END_DECLS

View file

@ -720,6 +720,9 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
soup_message_headers_foreach (msg->response_headers,
gst_soup_http_src_headers_foreach, src);
if (msg->status_code == 407 && src->proxy_id && src->proxy_pw)
return;
if (src->automatic_redirect && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code,
soup_message_headers_get (msg->response_headers, "Location"));

View file

@ -1034,6 +1034,7 @@ gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
/* Check if we have a continous stream, if not drop some samples or the buffer or
* insert some silence samples */
if (enc->next_ts != GST_CLOCK_TIME_NONE &&
GST_BUFFER_TIMESTAMP_IS_VALID (buf) &&
GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) {
guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf);
guint64 diff_bytes;

View file

@ -57,8 +57,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, "
"framed = (boolean) false, " "mpegversion = (int) { 2, 4 };"));
GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) { 2, 4 };"));
GST_DEBUG_CATEGORY_STATIC (aacparse_debug);
#define GST_CAT_DEFAULT aacparse_debug
@ -252,10 +251,12 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
aacparse->channels = (data[1] & 0x78) >> 3;
aacparse->header_type = DSPAAC_HEADER_NONE;
aacparse->mpegversion = 4;
aacparse->frame_samples = (data[1] & 4) ? 960 : 1024;
gst_buffer_unmap (buf, data, size);
GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d",
aacparse->object_type, aacparse->sample_rate, aacparse->channels);
GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, "
"samples=%d", aacparse->object_type, aacparse->sample_rate,
aacparse->channels, aacparse->frame_samples);
/* arrange for metadata and get out of the way */
gst_aac_parse_set_src_caps (aacparse, caps);
@ -451,7 +452,8 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse,
gst_aac_parse_parse_adts_header (aacparse, data, &rate, &channels,
&aacparse->object_type, &aacparse->mpegversion);
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate, 1024, 2, 2);
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate,
aacparse->frame_samples, 2, 2);
GST_DEBUG ("ADTS: samplerate %d, channels %d, objtype %d, version %d",
rate, channels, aacparse->object_type, aacparse->mpegversion);
@ -683,7 +685,7 @@ gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
gst_caps_unref (sinkcaps);
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse),
aacparse->sample_rate, 1024, 2, 2);
aacparse->sample_rate, aacparse->frame_samples, 2, 2);
}
return ret;
@ -705,6 +707,7 @@ gst_aac_parse_start (GstBaseParse * parse)
aacparse = GST_AAC_PARSE (parse);
GST_DEBUG ("start");
aacparse->frame_samples = 1024;
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), ADTS_MAX_SIZE);
return TRUE;
}

View file

@ -63,19 +63,6 @@ typedef struct _GstAacParseClass GstAacParseClass;
/**
* GstAacParse:
* @element: the parent element.
* @object_type: AAC object type of the stream.
* @bitrate: Current media bitrate.
* @sample_rate: Current media samplerate.
* @channels: Current media channel count.
* @frames_per_sec: FPS value of the current stream.
* @header_type: #GstAacHeaderType indicating the current stream type.
* @framecount: The amount of frames that has been processed this far.
* @bytecount: The amount of bytes that has been processed this far.
* @sync: Tells whether the parser is in sync (a.k.a. not searching for header)
* @eos: End-of-Stream indicator. Set when EOS event arrives.
* @duration: Duration of the current stream.
* @ts: Current stream timestamp.
*
* The opaque GstAacParse data structure.
*/
@ -88,6 +75,7 @@ struct _GstAacParse {
gint sample_rate;
gint channels;
gint mpegversion;
gint frame_samples;
GstAacHeaderType header_type;
};

View file

@ -144,16 +144,16 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) true, "
" channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ]; "
" channels = (int) [ 1, 6 ], rate = (int) [ 8000, 48000 ], "
" alignment = (string) { iec61937, frame}; "
"audio/x-eac3, framed = (boolean) true, "
" channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ] "));
" channels = (int) [ 1, 6 ], rate = (int) [ 8000, 48000 ], "
" alignment = (string) { iec61937, frame}; "));
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) false; "
"audio/x-eac3, framed = (boolean) false; "
"audio/ac3, framed = (boolean) false "));
GST_STATIC_CAPS ("audio/x-ac3; " "audio/x-eac3; " "audio/ac3"));
static void gst_ac3_parse_finalize (GObject * object);
@ -187,7 +187,7 @@ gst_ac3_parse_class_init (GstAc3ParseClass * klass)
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class,
"AC3 audio stream parser", "Codec/Parser/Audio",
"AC3 audio stream parser", "Codec/Parser/Converter/Audio",
"AC3 parser", "Tim-Philipp Müller <tim centricular net>");
parse_class->start = GST_DEBUG_FUNCPTR (gst_ac3_parse_start);
@ -298,7 +298,7 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
GstBitReader bits;
gpointer data;
gsize size;
guint8 fscod, frmsizcod, bsid, acmod, lfe_on;
guint8 fscod, frmsizcod, bsid, acmod, lfe_on, rate_scale;
gboolean ret = FALSE;
GST_LOG_OBJECT (ac3parse, "parsing ac3");
@ -338,10 +338,14 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
lfe_on = gst_bit_reader_get_bits_uint8_unchecked (&bits, 1);
/* 6/8->0, 9->1, 10->2,
see http://matroska.org/technical/specs/codecid/index.html */
rate_scale = (CLAMP (bsid, 8, 10) - 8);
if (frame_size)
*frame_size = frmsizcod_table[frmsizcod].frame_size[fscod] * 2;
if (rate)
*rate = fscod_rates[fscod];
*rate = fscod_rates[fscod] >> rate_scale;
if (chans)
*chans = acmod_chans[acmod] + lfe_on;
if (blks)
@ -631,7 +635,7 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
}
if (G_UNLIKELY (ac3parse->sample_rate != rate || ac3parse->channels != chans
|| ac3parse->eac != ac3parse->eac)) {
|| ac3parse->eac != eac)) {
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);

View file

@ -62,12 +62,13 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
" channels = (int) [ 1, 8 ],"
" rate = (int) [ 8000, 192000 ],"
" depth = (int) { 14, 16 },"
" endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }"));
" endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, "
" block-size = (int) [ 1, MAX], " " frame-size = (int) [ 1, MAX]"));
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-dts, framed = (boolean) false"));
GST_STATIC_CAPS ("audio/x-dts"));
static void gst_dca_parse_finalize (GObject * object);

View file

@ -181,7 +181,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-flac, framed = (boolean) false")
GST_STATIC_CAPS ("audio/x-flac")
);
static void gst_flac_parse_finalize (GObject * object);

View file

@ -76,14 +76,15 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_STATIC_CAPS ("audio/mpeg, "
"mpegversion = (int) 1, "
"layer = (int) [ 1, 3 ], "
"rate = (int) [ 8000, 48000 ], channels = (int) [ 1, 2 ],"
"parsed=(boolean) true")
"mpegaudioversion = (int) [ 1, 3], "
"rate = (int) [ 8000, 48000 ], "
"channels = (int) [ 1, 2 ], " "parsed=(boolean) true")
);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1, parsed=(boolean)false")
GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1")
);
static void gst_mpeg_audio_parse_finalize (GObject * object);

View file

@ -205,6 +205,23 @@ gst_navseek_segseek (GstNavSeek * navseek)
gst_object_unref (peer_pad);
}
static void
gst_navseek_toggle_play_pause (GstNavSeek * navseek)
{
GstStateChangeReturn sret;
GstState current, pending, state;
sret = gst_element_get_state (GST_ELEMENT (navseek), &current, &pending, 0);
if (sret == GST_STATE_CHANGE_FAILURE)
return;
state = (pending != GST_STATE_VOID_PENDING) ? pending : current;
gst_element_post_message (GST_ELEMENT (navseek),
gst_message_new_request_state (GST_OBJECT (navseek),
(state == GST_STATE_PLAYING) ? GST_STATE_PAUSED : GST_STATE_PLAYING));
}
static gboolean
gst_navseek_src_event (GstBaseTransform * trans, GstEvent * event)
{
@ -257,6 +274,8 @@ gst_navseek_src_event (GstBaseTransform * trans, GstEvent * event)
} else if (strcmp (key, "n") == 0) {
/* normal speed */
gst_navseek_change_playback_rate (navseek, 1.0);
} else if (strcmp (key, "space") == 0) {
gst_navseek_toggle_play_pause (navseek);
}
} else {
break;

View file

@ -107,6 +107,8 @@ static GstStateChangeReturn
gst_flv_mux_change_state (GstElement * element, GstStateChange transition);
static void gst_flv_mux_reset (GstElement * element);
static void gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * pad,
gboolean video);
typedef struct
{
@ -228,10 +230,10 @@ gst_flv_mux_reset (GstElement * element)
GstFlvMux *mux = GST_FLV_MUX (element);
GSList *sl;
while ((sl = mux->collect->data) != NULL) {
for (sl = mux->collect->data; sl != NULL; sl = g_slist_next (sl)) {
GstFlvPad *cpad = (GstFlvPad *) sl->data;
gst_element_release_request_pad (element, cpad->collect.pad);
gst_flv_mux_reset_pad (mux, cpad, cpad->video);
}
g_list_foreach (mux->index, (GFunc) gst_flv_mux_index_entry_free, NULL);
@ -503,6 +505,26 @@ gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
return ret;
}
static void
gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * cpad, gboolean video)
{
cpad->video = video;
if (cpad->audio_codec_data)
gst_buffer_unref (cpad->audio_codec_data);
cpad->audio_codec_data = NULL;
cpad->audio_codec = G_MAXUINT;
cpad->rate = G_MAXUINT;
cpad->width = G_MAXUINT;
cpad->channels = G_MAXUINT;
if (cpad->video_codec_data)
gst_buffer_unref (cpad->video_codec_data);
cpad->video_codec_data = NULL;
cpad->video_codec = G_MAXUINT;
cpad->last_timestamp = 0;
}
static GstPad *
gst_flv_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
@ -544,18 +566,9 @@ gst_flv_mux_request_new_pad (GstElement * element,
cpad = (GstFlvPad *)
gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad));
cpad->video = video;
cpad->audio_codec = G_MAXUINT;
cpad->rate = G_MAXUINT;
cpad->width = G_MAXUINT;
cpad->channels = G_MAXUINT;
cpad->audio_codec_data = NULL;
cpad->video_codec = G_MAXUINT;
cpad->video_codec_data = NULL;
cpad->last_timestamp = 0;
gst_flv_mux_reset_pad (mux, cpad, video);
/* FIXME: hacked way to override/extend the event function of
* GstCollectPads; because it sets its own event function giving the
@ -577,11 +590,7 @@ gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad));
GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
if (cpad && cpad->audio_codec_data)
gst_buffer_unref (cpad->audio_codec_data);
if (cpad && cpad->video_codec_data)
gst_buffer_unref (cpad->video_codec_data);
gst_flv_mux_reset_pad (mux, cpad, cpad->video);
gst_collect_pads_remove_pad (mux->collect, pad);
gst_element_remove_pad (element, pad);
}

View file

@ -66,9 +66,8 @@ goom_set_resolution (GoomData * goomdata, guint32 resx, guint32 resy)
goomdata->back = (guint32 *) malloc (buffsize * sizeof (guint32) + 128);
goomdata->buffsize = buffsize;
goomdata->p1 =
(void *) (((unsigned long) goomdata->pixel + 0x7f) & (~0x7f));
goomdata->p2 = (void *) (((unsigned long) goomdata->back + 0x7f) & (~0x7f));
goomdata->p1 = (void *) (((guintptr) goomdata->pixel + 0x7f) & (~0x7f));
goomdata->p2 = (void *) (((guintptr) goomdata->back + 0x7f) & (~0x7f));
}
goomdata->resolx = resx;

View file

@ -1757,15 +1757,25 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
GstCollectData *cdata = (GstCollectData *) walk->data;
GstQTPad *qtpad = (GstQTPad *) cdata;
/* send last buffer */
/* avoid add_buffer complaining if not negotiated
* in which case no buffers either, so skipping */
if (!qtpad->fourcc) {
GST_DEBUG_OBJECT (qtmux, "Pad %s has never had buffers",
GST_PAD_NAME (qtpad->collect.pad));
continue;
}
/* send last buffer; also flushes possibly queued buffers/ts */
GST_DEBUG_OBJECT (qtmux, "Sending the last buffer for pad %s",
GST_PAD_NAME (qtpad->collect.pad));
ret = gst_qt_mux_add_buffer (qtmux, qtpad, NULL);
if (ret != GST_FLOW_OK)
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (qtmux, "Failed to send last buffer for %s, "
"flow return: %s", GST_PAD_NAME (qtpad->collect.pad),
gst_flow_get_name (ret));
}
/* having flushed above, can check for buffers now */
if (!GST_CLOCK_TIME_IS_VALID (qtpad->first_ts)) {
GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers",
GST_PAD_NAME (qtpad->collect.pad));
@ -2048,6 +2058,39 @@ gst_qt_mux_push_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts)
pad->ts_n_entries++;
}
static void
check_and_subtract_ts (GstQTMux * qtmux, GstClockTime * ts_a, GstClockTime ts_b)
{
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (*ts_a))) {
if (G_LIKELY (*ts_a > ts_b)) {
*ts_a -= ts_b;
} else {
*ts_a = 0;
GST_WARNING_OBJECT (qtmux, "Subtraction would result in negative value, "
"using 0 as result");
}
}
}
/* subtract ts from all buffers enqueued on the pad */
static void
gst_qt_mux_subtract_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts)
{
gint i;
for (i = 0; (i < QTMUX_NO_OF_TS) && (i < pad->ts_n_entries); i++) {
check_and_subtract_ts (qtmux, &pad->ts_entries[i], ts);
}
for (i = 0; i < G_N_ELEMENTS (pad->buf_entries); i++) {
if (pad->buf_entries[i]) {
check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (pad->buf_entries[i]),
ts);
check_and_subtract_ts (qtmux,
&GST_BUFFER_OFFSET_END (pad->buf_entries[i]), ts);
}
}
}
/* takes ownership of @buf */
static GstBuffer *
gst_qt_mux_get_asc_buffer_ts (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
@ -2104,9 +2147,16 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
buf = pad->prepare_buf_func (pad, buf, qtmux);
}
if (G_LIKELY (buf != NULL && GST_CLOCK_TIME_IS_VALID (pad->first_ts))) {
buf = gst_buffer_make_writable (buf);
check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts);
}
/* when we obtain the first_ts we subtract from all stored buffers we have,
* after that we can subtract on input */
again:
last_buf = pad->last_buf;
if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER)) {
if (qtmux->dts_method == DTS_METHOD_REORDER) {
buf = gst_qt_mux_get_asc_buffer_ts (qtmux, pad, buf);
if (!buf && !last_buf) {
GST_DEBUG_OBJECT (qtmux, "no reordered buffer");
@ -2177,6 +2227,31 @@ again:
goto no_order;
}
/* if this is the first buffer, store the timestamp */
if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) {
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) {
pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf);
} else {
GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, "
"using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad));
pad->first_ts = 0;
}
GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %"
GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad),
GST_TIME_ARGS (pad->first_ts));
gst_qt_mux_subtract_ts (qtmux, pad, pad->first_ts);
GST_BUFFER_TIMESTAMP (last_buf) = 0;
check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (last_buf),
pad->first_ts);
if (buf) {
check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts);
check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (buf),
pad->first_ts);
}
}
/* fall back to duration if last buffer or
* out-of-order (determined previously), otherwise use input ts */
if (buf == NULL ||
@ -2336,20 +2411,6 @@ again:
qtmux->longest_chunk = duration;
}
/* if this is the first buffer, store the timestamp */
if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) {
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) {
pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf);
} else {
GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, "
"using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad));
pad->first_ts = 0;
}
GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %"
GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad),
GST_TIME_ARGS (pad->first_ts));
}
/* now we go and register this buffer/sample all over */
/* note that a new chunk is started each time (not fancy but works) */
if (qtmux->moov_recov_file) {

View file

@ -8381,6 +8381,41 @@ unknown_tag:
}
}
static void
qtdemux_tag_add_id32 (GstQTDemux * demux, const char *tag,
const char *tag_bis, GNode * node)
{
guint8 *data;
GstBuffer *buf;
guint len;
GstTagList *taglist = NULL;
GST_LOG_OBJECT (demux, "parsing ID32");
data = node->data;
len = GST_READ_UINT32_BE (data);
/* need at least full box and language tag */
if (len < 12 + 2)
return;
buf = gst_buffer_new_allocate (NULL, len - 14, 0);
gst_buffer_fill (buf, 0, data + 14, len - 14);
taglist = gst_tag_list_from_id3v2_tag (buf);
if (taglist) {
GST_LOG_OBJECT (demux, "parsing ok");
gst_tag_list_insert (demux->tag_list, taglist, GST_TAG_MERGE_KEEP);
} else {
GST_LOG_OBJECT (demux, "parsing failed");
}
if (taglist)
gst_tag_list_free (taglist);
gst_buffer_unref (buf);
}
typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux,
const char *tag, const char *tag_bis, GNode * node);
@ -8450,7 +8485,9 @@ static const struct
* http://atomicparsley.sourceforge.net/mpeg-4files.html and
* bug #614471
*/
FOURCC_____, "", NULL, qtdemux_tag_add_revdns}
FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
/* see http://www.mp4ra.org/specs.html for ID32 in meta box */
FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
};
static void
@ -8865,6 +8902,15 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
GST_LOG_OBJECT (qtdemux, "No udta node found.");
}
/* maybe also some tags in meta box */
udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
if (udta) {
GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
qtdemux_parse_udta (qtdemux, udta);
} else {
GST_LOG_OBJECT (qtdemux, "No meta node found.");
}
qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
return TRUE;

View file

@ -189,6 +189,9 @@ G_BEGIN_DECLS
#define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m')
#define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c')
/* misc tag stuff */
#define FOURCC_ID32 GST_MAKE_FOURCC('I', 'D','3','2')
/* ISO Motion JPEG 2000 fourcc */
#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2')
#define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h')

View file

@ -60,6 +60,7 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
guint64 total;
guint8 b;
GstFlowReturn ret;
g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (_length != NULL, GST_FLOW_ERROR);
@ -71,10 +72,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
/* read element id */
needed = 2;
buf = peek (ctx, needed);
if (!buf)
goto not_enough_data;
ret = peek (ctx, needed, &buf);
if (ret != GST_FLOW_OK)
goto peek_error;
b = GST_READ_UINT8 (buf);
total = (guint64) b;
while (read <= 4 && !(total & len_mask)) {
@ -86,10 +86,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
/* need id and at least something for subsequent length */
needed = read + 1;
buf = peek (ctx, needed);
if (!buf)
goto not_enough_data;
ret = peek (ctx, needed, &buf);
if (ret != GST_FLOW_OK)
goto peek_error;
while (n < read) {
b = GST_READ_UINT8 (buf + n);
total = (total << 8) | b;
@ -112,10 +111,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
num_ffs++;
needed += read - 1;
buf = peek (ctx, needed);
if (!buf)
goto not_enough_data;
ret = peek (ctx, needed, &buf);
if (ret != GST_FLOW_OK)
goto peek_error;
buf += (needed - read);
n = 1;
while (n < read) {
@ -137,10 +135,11 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
return GST_FLOW_OK;
/* ERRORS */
not_enough_data:
peek_error:
{
GST_WARNING_OBJECT (el, "peek failed, ret = %d", ret);
*_needed = needed;
return GST_FLOW_UNEXPECTED;
return ret;
}
invalid_id:
{
@ -190,15 +189,13 @@ gst_ebml_read_clear (GstEbmlRead * ebml)
ebml->el = NULL;
}
static const guint8 *
gst_ebml_read_peek (GstByteReader * br, guint peek)
static GstFlowReturn
gst_ebml_read_peek (GstByteReader * br, guint peek, const guint8 ** data)
{
const guint8 *data = NULL;
if (G_LIKELY (gst_byte_reader_peek_data (br, peek, &data)))
return data;
if (G_LIKELY (gst_byte_reader_peek_data (br, peek, data)))
return GST_FLOW_OK;
else
return NULL;
return GST_FLOW_UNEXPECTED;
}
static GstFlowReturn

View file

@ -59,7 +59,7 @@ typedef struct _GstEbmlRead {
GArray *readers;
} GstEbmlRead;
typedef const guint8 * (*GstPeekData) (gpointer * context, guint peek);
typedef GstFlowReturn (*GstPeekData) (gpointer * context, guint peek, const guint8 ** data);
/* returns UNEXPECTED if not enough data */
GstFlowReturn gst_ebml_peek_id_length (guint32 * _id, guint64 * _length,

View file

@ -81,9 +81,12 @@ enum
{
ARG_0,
ARG_METADATA,
ARG_STREAMINFO
ARG_STREAMINFO,
ARG_MAX_GAP_TIME
};
#define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND)
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
@ -168,6 +171,12 @@ static void gst_matroska_demux_reset (GstElement * element);
static gboolean perform_seek_to_offset (GstMatroskaDemux * demux,
guint64 offset);
/* gobject functions */
static void gst_matroska_demux_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_matroska_demux_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
GType gst_matroska_demux_get_type (void);
GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstElement,
GST_TYPE_ELEMENT);
@ -223,6 +232,15 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
gobject_class->finalize = gst_matroska_demux_finalize;
gobject_class->get_property = gst_matroska_demux_get_property;
gobject_class->set_property = gst_matroska_demux_set_property;
g_object_class_install_property (gobject_class, ARG_MAX_GAP_TIME,
g_param_spec_uint64 ("max-gap-time", "Maximum gap time",
"The demuxer sends out newsegment events for skipping "
"gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
gstelement_class->send_event =
@ -262,6 +280,9 @@ gst_matroska_demux_init (GstMatroskaDemux * demux,
demux->common.adapter = gst_adapter_new ();
/* property defaults */
demux->max_gap_time = DEFAULT_MAX_GAP_TIME;
/* finish off */
gst_matroska_demux_reset (GST_ELEMENT (demux));
}
@ -3364,7 +3385,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
/* handle gaps, e.g. non-zero start-time, or an cue index entry
* that landed us with timestamps not quite intended */
GST_OBJECT_LOCK (demux);
if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.last_stop) &&
if (demux->max_gap_time &&
GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) &&
demux->common.segment.rate > 0.0) {
GstClockTimeDiff diff;
GstEvent *event1, *event2;
@ -3372,8 +3394,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
/* only send newsegments with increasing start times,
* otherwise if these go back and forth downstream (sinks) increase
* accumulated time and running_time */
diff = GST_CLOCK_DIFF (demux->common.segment.last_stop, lace_time);
if (diff > 2 * GST_SECOND && lace_time > demux->common.segment.start
diff = GST_CLOCK_DIFF (demux->last_stop_end, lace_time);
if (diff > 0 && diff > demux->max_gap_time
&& lace_time > demux->common.segment.start
&& (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)
|| lace_time < demux->common.segment.stop)) {
GST_DEBUG_OBJECT (demux,
@ -3386,11 +3409,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
* accum time, hence running_time */
/* close ahead of gap */
event1 = gst_event_new_new_segment (TRUE,
demux->common.segment.rate,
demux->common.segment.format,
demux->common.segment.last_stop,
demux->common.segment.last_stop,
demux->common.segment.last_stop);
demux->common.segment.rate, demux->common.segment.format,
demux->last_stop_end, demux->last_stop_end,
demux->last_stop_end);
/* skip gap */
event2 = gst_event_new_new_segment (FALSE,
demux->common.segment.rate,
@ -5414,6 +5435,48 @@ gst_matroska_demux_change_state (GstElement * element,
return ret;
}
static void
gst_matroska_demux_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstMatroskaDemux *demux;
g_return_if_fail (GST_IS_MATROSKA_DEMUX (object));
demux = GST_MATROSKA_DEMUX (object);
switch (prop_id) {
case ARG_MAX_GAP_TIME:
GST_OBJECT_LOCK (demux);
demux->max_gap_time = g_value_get_uint64 (value);
GST_OBJECT_UNLOCK (demux);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_matroska_demux_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstMatroskaDemux *demux;
g_return_if_fail (GST_IS_MATROSKA_DEMUX (object));
demux = GST_MATROSKA_DEMUX (object);
switch (prop_id) {
case ARG_MAX_GAP_TIME:
GST_OBJECT_LOCK (demux);
g_value_set_uint64 (value, demux->max_gap_time);
GST_OBJECT_UNLOCK (demux);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
gboolean
gst_matroska_demux_plugin_init (GstPlugin * plugin)
{

View file

@ -91,6 +91,9 @@ typedef struct _GstMatroskaDemux {
/* reverse playback */
GArray *seek_index;
gint seek_entry;
/* gap handling */
guint64 max_gap_time;
} GstMatroskaDemux;
typedef struct _GstMatroskaDemuxClass {

View file

@ -391,6 +391,8 @@ gst_matroska_mux_finalize (GObject * object)
{
GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
gst_event_replace (&mux->force_key_unit_event, NULL);
gst_object_unref (mux->collect);
gst_object_unref (mux->ebml_write);
if (mux->writing_app)
@ -662,6 +664,17 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
gst_event_unref (event);
event = NULL;
break;
case GST_EVENT_CUSTOM_DOWNSTREAM:{
const GstStructure *structure;
structure = gst_event_get_structure (event);
if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
gst_event_replace (&mux->force_key_unit_event, NULL);
mux->force_key_unit_event = event;
event = NULL;
}
break;
}
default:
break;
}
@ -2692,13 +2705,20 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
}
if (mux->cluster) {
/* start a new cluster at every keyframe or when we may be reaching the
* limit of the relative timestamp */
/* start a new cluster at every keyframe, at every GstForceKeyUnit event,
* or when we may be reaching the limit of the relative timestamp */
if (mux->cluster_time +
mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
|| is_video_keyframe) {
|| is_video_keyframe || mux->force_key_unit_event) {
if (!mux->streamable)
gst_ebml_write_master_finish (ebml, mux->cluster);
/* Forward the GstForceKeyUnit event after finishing the cluster */
if (mux->force_key_unit_event) {
gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
mux->force_key_unit_event = NULL;
}
mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
mux->cluster_pos = ebml->pos;
gst_ebml_write_set_cache (ebml, 0x20);

View file

@ -126,6 +126,9 @@ typedef struct _GstMatroskaMux {
cluster_pos,
prev_cluster_size;
/* GstForceKeyUnit event */
GstEvent *force_key_unit_event;
} GstMatroskaMux;
typedef struct _GstMatroskaMuxClass {

View file

@ -1639,14 +1639,12 @@ gst_matroska_read_common_peek_bytes (GstMatroskaReadCommon * common, guint64
return GST_FLOW_OK;
}
static const guint8 *
gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek)
static GstFlowReturn
gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek,
guint8 ** data)
{
guint8 *data = NULL;
gst_matroska_read_common_peek_bytes (common, common->offset, peek, NULL,
&data);
return data;
return gst_matroska_read_common_peek_bytes (common, common->offset, peek,
NULL, data);
}
GstFlowReturn

View file

@ -501,7 +501,7 @@ gst_rtp_h264_complete_au (GstRtpH264Depay * rtph264depay,
* 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
static GstBuffer *
gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
GstClockTime in_timestamp, gboolean marker)
{
@ -584,11 +584,9 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
else
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
gst_base_rtp_depayload_push (depayload, outbuf);
}
return TRUE;
return outbuf;
/* ERRORS */
short_nal:
@ -596,12 +594,13 @@ short_nal:
GST_WARNING_OBJECT (depayload, "dropping short NAL");
gst_buffer_unmap (nal, data, size);
gst_buffer_unref (nal);
return FALSE;
return NULL;
}
}
static void
gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay)
static GstBuffer *
gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay,
gboolean send)
{
guint outsize;
guint8 *outdata;
@ -624,17 +623,26 @@ gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay)
}
gst_buffer_unmap (outbuf, outdata, -1);
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf,
rtph264depay->fu_timestamp, rtph264depay->fu_marker);
rtph264depay->current_fu_type = 0;
if (send) {
outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf,
rtph264depay->fu_timestamp, rtph264depay->fu_marker);
if (outbuf)
gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtph264depay),
outbuf);
return NULL;
} else {
return gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf,
rtph264depay->fu_timestamp, rtph264depay->fu_marker);
}
}
static GstBuffer *
gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
{
GstRtpH264Depay *rtph264depay;
GstBuffer *outbuf;
GstBuffer *outbuf = NULL;
guint8 nal_unit_type;
GstRTPBuffer rtp = { NULL };
@ -692,7 +700,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
* 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);
gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE);
switch (nal_unit_type) {
case 0:
@ -755,7 +763,8 @@ 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, marker);
outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp,
marker);
break;
}
case 26:
@ -798,7 +807,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
* 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);
gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE);
rtph264depay->current_fu_type = nal_unit_type;
rtph264depay->fu_timestamp = timestamp;
@ -844,11 +853,12 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
gst_adapter_push (rtph264depay->adapter, outbuf);
}
outbuf = NULL;
rtph264depay->fu_marker = marker;
/* if NAL unit ends, flush the adapter */
if (E)
gst_rtp_h264_push_fragmentation_unit (rtph264depay);
outbuf = gst_rtp_h264_push_fragmentation_unit (rtph264depay, FALSE);
break;
}
default:
@ -872,14 +882,15 @@ 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, marker);
outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp,
marker);
break;
}
}
gst_rtp_buffer_unmap (&rtp);
}
return NULL;
return outbuf;
/* ERRORS */
undefined_type:

View file

@ -213,6 +213,9 @@ struct _GstRtpBinPrivate
gint shutdown;
gboolean autoremove;
/* UNIX (ntp) time of last SR sync used */
guint64 last_unix;
};
/* signals and args */
@ -245,6 +248,8 @@ enum
#define DEFAULT_AUTOREMOVE FALSE
#define DEFAULT_BUFFER_MODE RTP_JITTER_BUFFER_MODE_SLAVE
#define DEFAULT_USE_PIPELINE_CLOCK FALSE
#define DEFAULT_RTCP_SYNC GST_RTP_BIN_RTCP_SYNC_ALWAYS
#define DEFAULT_RTCP_SYNC_INTERVAL 0
enum
{
@ -254,12 +259,39 @@ enum
PROP_DO_LOST,
PROP_IGNORE_PT,
PROP_NTP_SYNC,
PROP_RTCP_SYNC,
PROP_RTCP_SYNC_INTERVAL,
PROP_AUTOREMOVE,
PROP_BUFFER_MODE,
PROP_USE_PIPELINE_CLOCK,
PROP_LAST
};
enum
{
GST_RTP_BIN_RTCP_SYNC_ALWAYS,
GST_RTP_BIN_RTCP_SYNC_INITIAL,
GST_RTP_BIN_RTCP_SYNC_RTP
};
#define GST_RTP_BIN_RTCP_SYNC_TYPE (gst_rtp_bin_rtcp_sync_get_type())
static GType
gst_rtp_bin_rtcp_sync_get_type (void)
{
static GType rtcp_sync_type = 0;
static const GEnumValue rtcp_sync_types[] = {
{GST_RTP_BIN_RTCP_SYNC_ALWAYS, "always", "always"},
{GST_RTP_BIN_RTCP_SYNC_INITIAL, "initial", "initial"},
{GST_RTP_BIN_RTCP_SYNC_RTP, "rtp-info", "rtp-info"},
{0, NULL, NULL},
};
if (!rtcp_sync_type) {
rtcp_sync_type = g_enum_register_static ("GstRTCPSync", rtcp_sync_types);
}
return rtcp_sync_type;
}
/* helper objects */
typedef struct _GstRtpBinSession GstRtpBinSession;
typedef struct _GstRtpBinStream GstRtpBinStream;
@ -310,6 +342,9 @@ struct _GstRtpBinStream
gboolean have_sync;
/* mapping to local RTP and NTP time */
gint64 rt_delta;
gint64 rtp_delta;
/* base rtptime in gst time */
gint64 clock_base;
};
#define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->lock)
@ -775,6 +810,8 @@ gst_rtp_bin_reset_sync (GstRtpBin * rtpbin)
* lip-sync */
stream->have_sync = FALSE;
stream->rt_delta = 0;
stream->rtp_delta = 0;
stream->clock_base = -100 * GST_SECOND;
}
}
GST_RTP_BIN_UNLOCK (rtpbin);
@ -979,7 +1016,8 @@ stream_set_ts_offset (GstRtpBin * bin, GstRtpBinStream * stream,
static void
gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
guint8 * data, guint64 ntptime, guint64 last_extrtptime,
guint64 base_rtptime, guint64 base_time, guint clock_rate)
guint64 base_rtptime, guint64 base_time, guint clock_rate,
gint64 rtp_clock_base)
{
GstRtpBinClient *client;
gboolean created;
@ -1014,6 +1052,19 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
stream->ssrc, client, client->cname);
}
if (!GST_CLOCK_TIME_IS_VALID (last_extrtptime)) {
GST_DEBUG_OBJECT (bin, "invalidated sync data");
if (bin->rtcp_sync == GST_RTP_BIN_RTCP_SYNC_RTP) {
/* we don't need that data, so carry on,
* but make some values look saner */
last_extrtptime = base_rtptime;
} else {
/* nothing we can do with this data in this case */
GST_DEBUG_OBJECT (bin, "bailing out");
return;
}
}
/* Take the extended rtptime we found in the SR packet and map it to the
* local rtptime. The local rtp time is used to construct timestamps on the
* buffers so we will calculate what running_time corresponds to the RTP
@ -1022,8 +1073,9 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
GST_DEBUG_OBJECT (bin,
"base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT
", local RTP %" G_GUINT64_FORMAT ", clock-rate %d", base_rtptime,
last_extrtptime, local_rtp, clock_rate);
", local RTP %" G_GUINT64_FORMAT ", clock-rate %d, "
"clock-base %" G_GINT64_FORMAT, base_rtptime,
last_extrtptime, local_rtp, clock_rate, rtp_clock_base);
/* calculate local RTP time in gstreamer timestamp, we essentially perform the
* same conversion that a jitterbuffer would use to convert an rtp timestamp
@ -1070,8 +1122,10 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
stream->rt_delta = rtdiff - ntpdiff;
stream_set_ts_offset (bin, stream, stream->rt_delta);
} else if (client->nstreams > 1) {
gint64 min;
} else {
gint64 min, rtp_min, clock_base = stream->clock_base;
gboolean all_sync, use_rtp;
gboolean rtcp_sync = g_atomic_int_get (&bin->rtcp_sync);
/* calculate delta between server and receiver. last_unix is created by
* converting the ntptime in the last SR packet to a gstreamer timestamp. This
@ -1089,19 +1143,114 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
* latencies).
* The stream that has the smallest diff is selected as the reference stream,
* all other streams will have a positive offset to this difference. */
min = G_MAXINT64;
/* some alternative setting allow ignoring RTCP as much as possible,
* for servers generating bogus ntp timeline */
min = rtp_min = G_MAXINT64;
use_rtp = FALSE;
if (rtcp_sync == GST_RTP_BIN_RTCP_SYNC_RTP) {
guint64 ext_base;
use_rtp = TRUE;
/* signed version for convienience */
clock_base = base_rtptime;
/* deal with possible wrap-around */
ext_base = base_rtptime;
rtp_clock_base = gst_rtp_buffer_ext_timestamp (&ext_base, rtp_clock_base);
/* sanity check; base rtp and provided clock_base should be close */
if (rtp_clock_base >= clock_base) {
if (rtp_clock_base - clock_base < 10 * clock_rate) {
rtp_clock_base = base_time +
gst_util_uint64_scale_int (rtp_clock_base - clock_base,
GST_SECOND, clock_rate);
} else {
use_rtp = FALSE;
}
} else {
if (clock_base - rtp_clock_base < 10 * clock_rate) {
rtp_clock_base = base_time -
gst_util_uint64_scale_int (clock_base - rtp_clock_base,
GST_SECOND, clock_rate);
} else {
use_rtp = FALSE;
}
}
/* warn and bail for clarity out if no sane values */
if (!use_rtp) {
GST_WARNING_OBJECT (bin, "unable to sync to provided rtptime");
return;
}
/* store to track changes */
clock_base = rtp_clock_base;
/* generate a fake as before,
* now equating rtptime obtained from RTP-Info,
* where the large time represent the otherwise irrelevant npt/ntp time */
stream->rtp_delta = (GST_SECOND << 28) - rtp_clock_base;
}
for (walk = client->streams; walk; walk = g_slist_next (walk)) {
GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
if (!ostream->have_sync)
if (!ostream->have_sync) {
all_sync = FALSE;
continue;
}
/* change in current stream's base from previously init'ed value
* leads to reset of all stream's base */
if (stream != ostream && stream->clock_base >= 0 &&
(stream->clock_base != clock_base)) {
GST_DEBUG_OBJECT (bin, "reset upon clock base change");
ostream->clock_base = -100 * GST_SECOND;
ostream->rtp_delta = 0;
}
if (ostream->rt_delta < min)
min = ostream->rt_delta;
if (ostream->rtp_delta < rtp_min)
rtp_min = ostream->rtp_delta;
}
GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT, client,
min);
/* arrange to re-sync for each stream upon significant change,
* e.g. post-seek */
all_sync = (stream->clock_base == clock_base);
stream->clock_base = clock_base;
/* may need init performed above later on, but nothing more to do now */
if (client->nstreams <= 1)
return;
GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT
" all sync %d", client, min, all_sync);
GST_DEBUG_OBJECT (bin, "rtcp sync mode %d, use_rtp %d", rtcp_sync, use_rtp);
switch (rtcp_sync) {
case GST_RTP_BIN_RTCP_SYNC_RTP:
if (!use_rtp)
break;
GST_DEBUG_OBJECT (bin, "using rtp generated reports; "
"client %p min rtp delta %" G_GINT64_FORMAT, client, rtp_min);
/* fall-through */
case GST_RTP_BIN_RTCP_SYNC_INITIAL:
/* if all have been synced already, do not bother further */
if (all_sync) {
GST_DEBUG_OBJECT (bin, "all streams already synced; done");
return;
}
break;
default:
break;
}
/* bail out if we adjusted recently enough */
if (all_sync && (last_unix - bin->priv->last_unix) <
bin->rtcp_sync_interval * GST_MSECOND) {
GST_DEBUG_OBJECT (bin, "discarding RTCP sender packet for sync; "
"previous sender info too recent "
"(previous UNIX %" G_GUINT64_FORMAT ")", bin->priv->last_unix);
return;
}
bin->priv->last_unix = last_unix;
/* calculate offsets for each stream */
for (walk = client->streams; walk; walk = g_slist_next (walk)) {
@ -1116,7 +1265,10 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
/* calculate offset to our reference stream, this should always give a
* positive number. */
ts_offset = ostream->rt_delta - min;
if (use_rtp)
ts_offset = ostream->rtp_delta - rtp_min;
else
ts_offset = ostream->rt_delta - min;
stream_set_ts_offset (bin, ostream, ts_offset);
}
@ -1149,6 +1301,7 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
guint64 base_rtptime;
guint64 base_time;
guint clock_rate;
guint64 clock_base;
guint64 extrtptime;
GstBuffer *buffer;
GstRTCPBuffer rtcp = { NULL };
@ -1165,6 +1318,7 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
g_value_get_uint64 (gst_structure_get_value (s, "base-rtptime"));
base_time = g_value_get_uint64 (gst_structure_get_value (s, "base-time"));
clock_rate = g_value_get_uint (gst_structure_get_value (s, "clock-rate"));
clock_base = g_value_get_uint64 (gst_structure_get_value (s, "clock-base"));
extrtptime =
g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime"));
buffer = gst_value_get_buffer (gst_structure_get_value (s, "sr-buffer"));
@ -1220,7 +1374,8 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
GST_RTP_BIN_LOCK (bin);
/* associate the stream to CNAME */
gst_rtp_bin_associate (bin, stream, len, data,
ntptime, extrtptime, base_rtptime, base_time, clock_rate);
ntptime, extrtptime, base_rtptime, base_time, clock_rate,
clock_base);
GST_RTP_BIN_UNLOCK (bin);
}
}
@ -1265,7 +1420,9 @@ create_stream (GstRtpBinSession * session, guint32 ssrc)
stream->have_sync = FALSE;
stream->rt_delta = 0;
stream->rtp_delta = 0;
stream->percent = 100;
stream->clock_base = -100 * GST_SECOND;
session->streams = g_slist_prepend (session->streams, stream);
/* provide clock_rate to the jitterbuffer when needed */
@ -1656,6 +1813,32 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass)
"Synchronize received streams to the NTP clock", DEFAULT_NTP_SYNC,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpBin::rtcp-sync:
*
* If not synchronizing (directly) to the NTP clock, determines how to sync
* the various streams.
*
* Since: 0.10.31
*/
g_object_class_install_property (gobject_class, PROP_RTCP_SYNC,
g_param_spec_enum ("rtcp-sync", "RTCP Sync",
"Use of RTCP SR in synchronization", GST_RTP_BIN_RTCP_SYNC_TYPE,
DEFAULT_RTCP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpBin::rtcp-sync-interval:
*
* Determines how often to sync streams using RTCP data.
*
* Since: 0.10.31
*/
g_object_class_install_property (gobject_class, PROP_RTCP_SYNC_INTERVAL,
g_param_spec_uint ("rtcp-sync-interval", "RTCP Sync Interval",
"RTCP SR interval synchronization (ms) (0 = always)",
0, G_MAXUINT, DEFAULT_RTCP_SYNC_INTERVAL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
@ -1706,6 +1889,8 @@ gst_rtp_bin_init (GstRtpBin * rtpbin)
rtpbin->do_lost = DEFAULT_DO_LOST;
rtpbin->ignore_pt = DEFAULT_IGNORE_PT;
rtpbin->ntp_sync = DEFAULT_NTP_SYNC;
rtpbin->rtcp_sync = DEFAULT_RTCP_SYNC;
rtpbin->rtcp_sync_interval = DEFAULT_RTCP_SYNC_INTERVAL;
rtpbin->priv->autoremove = DEFAULT_AUTOREMOVE;
rtpbin->buffer_mode = DEFAULT_BUFFER_MODE;
rtpbin->use_pipeline_clock = DEFAULT_USE_PIPELINE_CLOCK;
@ -1821,6 +2006,12 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id,
case PROP_NTP_SYNC:
rtpbin->ntp_sync = g_value_get_boolean (value);
break;
case PROP_RTCP_SYNC:
g_atomic_int_set (&rtpbin->rtcp_sync, g_value_get_enum (value));
break;
case PROP_RTCP_SYNC_INTERVAL:
rtpbin->rtcp_sync_interval = g_value_get_uint (value);
break;
case PROP_IGNORE_PT:
rtpbin->ignore_pt = g_value_get_boolean (value);
break;
@ -1883,6 +2074,12 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id,
case PROP_NTP_SYNC:
g_value_set_boolean (value, rtpbin->ntp_sync);
break;
case PROP_RTCP_SYNC:
g_value_set_enum (value, g_atomic_int_get (&rtpbin->rtcp_sync));
break;
case PROP_RTCP_SYNC_INTERVAL:
g_value_set_uint (value, rtpbin->rtcp_sync_interval);
break;
case PROP_AUTOREMOVE:
g_value_set_boolean (value, rtpbin->priv->autoremove);
break;
@ -2020,6 +2217,7 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message)
now = gst_clock_get_time (clock);
base_time = gst_element_get_base_time (GST_ELEMENT_CAST (bin));
running_time = now - base_time;
gst_object_unref (clock);
}
GST_DEBUG_OBJECT (bin,
"running time now %" GST_TIME_FORMAT,
@ -2108,6 +2306,7 @@ gst_rtp_bin_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
priv->last_unix = 0;
GST_LOG_OBJECT (rtpbin, "clearing shutdown flag");
g_atomic_int_set (&priv->shutdown, 0);
break;

View file

@ -50,6 +50,8 @@ struct _GstRtpBin {
gboolean do_lost;
gboolean ignore_pt;
gboolean ntp_sync;
gint rtcp_sync;
guint rtcp_sync_interval;
RTPJitterBufferMode buffer_mode;
gboolean buffering;
gboolean use_pipeline_clock;

View file

@ -664,6 +664,11 @@ gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
JBUF_LOCK (priv);
priv->clock_rate = -1;
/* do not clear current content, but refresh state for new arrival */
GST_DEBUG_OBJECT (jitterbuffer, "reset jitterbuffer");
rtp_jitter_buffer_reset_skew (priv->jbuf);
priv->last_popped_seqnum = -1;
priv->next_seqnum = -1;
JBUF_UNLOCK (priv);
}
@ -1953,6 +1958,7 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
guint32 rtptime;
gboolean drop = FALSE;
GstRTCPBuffer rtcp = { NULL };
guint64 clock_base;
jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
@ -1989,9 +1995,12 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
&clock_rate, &last_rtptime);
clock_base = priv->clock_base;
GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %"
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT,
ext_rtptime, base_rtptime, clock_rate);
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT
", clock-base %" G_GUINT64_FORMAT,
ext_rtptime, base_rtptime, clock_rate, clock_base);
if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) {
GST_DEBUG_OBJECT (jitterbuffer, "dropping, no RTP values");
@ -2009,8 +2018,12 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
diff = ext_rtptime - last_rtptime;
/* if bigger than 1 second, we drop it */
if (diff > clock_rate) {
GST_DEBUG_OBJECT (jitterbuffer, "dropping, too far ahead");
drop = TRUE;
GST_DEBUG_OBJECT (jitterbuffer, "too far ahead");
/* should drop this, but some RTSP servers end up with bogus
* way too ahead RTCP packet when repeated PAUSE/PLAY,
* so still trigger rptbin sync but invalidate RTCP data
* (sync might use other methods) */
ext_rtptime = -1;
}
GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
G_GUINT64_FORMAT, last_rtptime, diff);
@ -2026,6 +2039,7 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
"base-rtptime", G_TYPE_UINT64, base_rtptime,
"base-time", G_TYPE_UINT64, base_time,
"clock-rate", G_TYPE_UINT, clock_rate,
"clock-base", G_TYPE_UINT64, clock_base,
"sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
"sr-buffer", GST_TYPE_BUFFER, buffer, NULL);

View file

@ -835,6 +835,10 @@ rtcp_thread (GstRtpSession * rtpsession)
session = rtpsession->priv->session;
GST_DEBUG_OBJECT (rtpsession, "starting at %" GST_TIME_FORMAT,
GST_TIME_ARGS (current_time));
session->start_time = current_time;
while (!rtpsession->priv->stop_thread) {
GstClockReturn res;

View file

@ -640,6 +640,26 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf,
}
rtptime = gst_rtp_buffer_get_timestamp (&rtp);
/* rtp time jumps are checked for during skew calculation, but bypassed
* in other mode, so mind those here and reset jb if needed.
* Only reset if valid input time, which is likely for UDP input
* where we expect this might happen due to async thread effects
* (in seek and state change cycles), but not so much for TCP input */
if (GST_CLOCK_TIME_IS_VALID (time) &&
jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE &&
jbuf->base_time != -1 && jbuf->last_rtptime != -1) {
GstClockTime ext_rtptime = jbuf->ext_rtptime;
ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
if (ext_rtptime > jbuf->last_rtptime + 3 * clock_rate ||
ext_rtptime + 3 * clock_rate < jbuf->last_rtptime) {
/* reset even if we don't have valid incoming time;
* still better than producing possibly very bogus output timestamp */
GST_WARNING ("rtp delta too big, reset skew");
rtp_jitter_buffer_reset_skew (jbuf);
}
}
switch (jbuf->mode) {
case RTP_JITTER_BUFFER_MODE_NONE:
case RTP_JITTER_BUFFER_MODE_BUFFER:

View file

@ -543,6 +543,8 @@ rtp_session_init (RTPSession * sess)
sess->source->internal = TRUE;
sess->stats.active_sources++;
INIT_AVG (sess->stats.avg_rtcp_packet_size, 100);
sess->source->stats.prev_rtcptime = 0;
sess->source->stats.last_rtcptime = 1;
rtp_stats_set_min_interval (&sess->stats,
(gdouble) DEFAULT_RTCP_MIN_INTERVAL / GST_SECOND);
@ -664,6 +666,12 @@ rtp_session_set_property (GObject * object, guint prop_id,
case PROP_RTCP_MIN_INTERVAL:
rtp_stats_set_min_interval (&sess->stats,
(gdouble) g_value_get_uint64 (value) / GST_SECOND);
/* trigger reconsideration */
RTP_SESSION_LOCK (sess);
sess->next_rtcp_check_time = 0;
RTP_SESSION_UNLOCK (sess);
if (sess->callbacks.reconsider)
sess->callbacks.reconsider (sess, sess->reconsider_user_data);
break;
case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD:
sess->rtcp_immediate_feedback_threshold = g_value_get_uint (value);
@ -2767,11 +2775,35 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
gboolean sendertimeout = FALSE;
gboolean is_sender, is_active;
RTPSession *sess = data->sess;
GstClockTime interval;
GstClockTime interval, binterval;
GstClockTime btime;
is_sender = RTP_SOURCE_IS_SENDER (source);
is_active = RTP_SOURCE_IS_ACTIVE (source);
/* our own rtcp interval may have been forced low by secondary configuration,
* while sender side may still operate with higher interval,
* so do not just take our interval to decide on timing out sender,
* but take (if data->interval <= 5 * GST_SECOND):
* interval = CLAMP (sender_interval, data->interval, 5 * GST_SECOND)
* where sender_interval is difference between last 2 received RTCP reports
*/
if (data->interval >= 5 * GST_SECOND || (source == sess->source)) {
binterval = data->interval;
} else {
GST_LOG ("prev_rtcp %" GST_TIME_FORMAT ", last_rtcp %" GST_TIME_FORMAT,
GST_TIME_ARGS (source->stats.prev_rtcptime),
GST_TIME_ARGS (source->stats.last_rtcptime));
/* if not received enough yet, fallback to larger default */
if (source->stats.last_rtcptime > source->stats.prev_rtcptime)
binterval = source->stats.last_rtcptime - source->stats.prev_rtcptime;
else
binterval = 5 * GST_SECOND;
binterval = CLAMP (binterval, data->interval, 5 * GST_SECOND);
}
GST_LOG ("timeout base interval %" GST_TIME_FORMAT,
GST_TIME_ARGS (binterval));
/* check for our own source, we don't want to delete our own source. */
if (!(source == sess->source)) {
if (source->received_bye) {
@ -2786,11 +2818,13 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
}
/* sources that were inactive for more than 5 times the deterministic reporting
* interval get timed out. the min timeout is 5 seconds. */
if (data->current_time > source->last_activity) {
interval = MAX (data->interval * 5, 5 * GST_SECOND);
if (data->current_time - source->last_activity > interval) {
/* mind old time that might pre-date last time going to PLAYING */
btime = MAX (source->last_activity, sess->start_time);
if (data->current_time > btime) {
interval = MAX (binterval * 5, 5 * GST_SECOND);
if (data->current_time - btime > interval) {
GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT,
source->ssrc, GST_TIME_ARGS (source->last_activity));
source->ssrc, GST_TIME_ARGS (btime));
remove = TRUE;
}
}
@ -2799,12 +2833,13 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
/* senders that did not send for a long time become a receiver, this also
* holds for our own source. */
if (is_sender) {
if (data->current_time > source->last_rtp_activity) {
interval = MAX (data->interval * 2, 5 * GST_SECOND);
if (data->current_time - source->last_rtp_activity > interval) {
/* mind old time that might pre-date last time going to PLAYING */
btime = MAX (source->last_rtp_activity, sess->start_time);
if (data->current_time > btime) {
interval = MAX (binterval * 2, 5 * GST_SECOND);
if (data->current_time - btime > interval) {
GST_DEBUG ("sender source %08x timed out and became receiver, last %"
GST_TIME_FORMAT, source->ssrc,
GST_TIME_ARGS (source->last_rtp_activity));
GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime));
source->is_sender = FALSE;
sess->stats.sender_sources--;
sendertimeout = TRUE;

View file

@ -209,6 +209,7 @@ struct _RTPSession {
GstClockTime next_rtcp_check_time;
GstClockTime last_rtcp_send_time;
GstClockTime start_time;
gboolean first_rtcp;
gboolean allow_early;

View file

@ -1368,6 +1368,9 @@ rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime,
/* make current */
src->stats.curr_sr = curridx;
src->stats.prev_rtcptime = src->stats.last_rtcptime;
src->stats.last_rtcptime = time;
}
/**

View file

@ -128,6 +128,13 @@ enum
LAST_SIGNAL
};
enum _GstRtspSrcRtcpSyncMode
{
RTCP_SYNC_ALWAYS,
RTCP_SYNC_INITIAL,
RTCP_SYNC_RTP
};
enum _GstRtspSrcBufferMode
{
BUFFER_MODE_NONE,
@ -1651,7 +1658,7 @@ cleanup:
}
static void
gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush, gboolean playing)
{
GstEvent *event;
gint cmd, i;
@ -1667,9 +1674,12 @@ gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
state = GST_STATE_PAUSED;
} else {
event = gst_event_new_flush_stop (TRUE);
GST_DEBUG_OBJECT (src, "stop flush");
GST_DEBUG_OBJECT (src, "stop flush; playing %d", playing);
cmd = CMD_LOOP;
state = GST_STATE_PLAYING;
if (playing)
state = GST_STATE_PLAYING;
else
state = GST_STATE_PAUSED;
clock = gst_element_get_clock (GST_ELEMENT_CAST (src));
if (clock) {
base_time = gst_clock_get_time (clock);
@ -1818,7 +1828,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
* blocking in preroll). */
if (flush) {
GST_DEBUG_OBJECT (src, "starting flush");
gst_rtspsrc_flush (src, TRUE);
gst_rtspsrc_flush (src, TRUE, FALSE);
} else {
if (src->task) {
gst_task_pause (src->task);
@ -1867,7 +1877,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
if (flush) {
/* if we started flush, we stop now */
GST_DEBUG_OBJECT (src, "stopping flush");
gst_rtspsrc_flush (src, FALSE);
gst_rtspsrc_flush (src, FALSE, playing);
}
/* now we did the seek and can activate the new segment values */
@ -4052,6 +4062,8 @@ gst_rtspsrc_loop_send_cmd (GstRTSPSrc * src, gint cmd, gboolean flush)
/* start new request */
gst_rtspsrc_loop_start_cmd (src, cmd);
GST_DEBUG_OBJECT (src, "sending cmd %d", cmd);
GST_OBJECT_LOCK (src);
old = src->loop_cmd;
if (old != CMD_WAIT) {
@ -5900,6 +5912,47 @@ gst_rtspsrc_parse_rtpinfo (GstRTSPSrc * src, gchar * rtpinfo)
return TRUE;
}
static void
gst_rtspsrc_handle_rtcp_interval (GstRTSPSrc * src, gchar * rtcp)
{
guint64 interval;
GList *walk;
interval = strtoul (rtcp, NULL, 10);
GST_DEBUG_OBJECT (src, "rtcp interval: %" G_GUINT64_FORMAT " ms", interval);
if (!interval)
return;
interval *= GST_MSECOND;
for (walk = src->streams; walk; walk = g_list_next (walk)) {
GstRTSPStream *stream = (GstRTSPStream *) walk->data;
/* already (optionally) retrieved this when configuring manager */
if (stream->session) {
GObject *rtpsession = stream->session;
GST_DEBUG_OBJECT (src, "configure rtcp interval in session %p",
rtpsession);
g_object_set (rtpsession, "rtcp-min-interval", interval, NULL);
}
}
/* now it happens that (Xenon) server sending this may also provide bogus
* RTCP SR sync data (i.e. with quite some jitter), so never mind those
* and just use RTP-Info to sync */
if (src->manager) {
GObjectClass *klass;
klass = G_OBJECT_GET_CLASS (G_OBJECT (src->manager));
if (g_object_class_find_property (klass, "rtcp-sync")) {
GST_DEBUG_OBJECT (src, "configuring rtp sync method");
g_object_set (src->manager, "rtcp-sync", RTCP_SYNC_RTP, NULL);
}
}
}
static gdouble
gst_rtspsrc_get_float (const gchar * dstr)
{
@ -6108,6 +6161,12 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment, gboolean async)
&hval, hval_idx++) == GST_RTSP_OK)
gst_rtspsrc_parse_rtpinfo (src, hval);
/* some servers indicate RTCP parameters in PLAY response,
* rather than properly in SDP */
if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RTCP_INTERVAL,
&hval, 0) == GST_RTSP_OK)
gst_rtspsrc_handle_rtcp_interval (src, hval);
gst_rtsp_message_unset (&response);
/* early exit when we did aggregate control */
@ -6401,10 +6460,6 @@ gst_rtspsrc_thread (GstRTSPSrc * src)
switch (cmd) {
case CMD_OPEN:
src->cur_protocols = src->protocols;
/* first attempt, don't ignore timeouts */
src->ignore_timeout = FALSE;
src->open_error = FALSE;
ret = gst_rtspsrc_open (src, TRUE);
break;
case CMD_PLAY:
@ -6524,6 +6579,11 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
goto start_failed;
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
/* init some state */
rtspsrc->cur_protocols = rtspsrc->protocols;
/* first attempt, don't ignore timeouts */
rtspsrc->ignore_timeout = FALSE;
rtspsrc->open_error = FALSE;
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_OPEN, FALSE);
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:

View file

@ -374,7 +374,10 @@ gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri)
gchar *location, *location_end;
gchar *colptr;
/* consider no protocol to be udp:// */
protocol = gst_uri_get_protocol (uristr);
if (!protocol)
goto no_protocol;
if (strcmp (protocol, "udp") != 0)
goto wrong_protocol;
g_free (protocol);
@ -425,6 +428,11 @@ gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri)
return 0;
/* ERRORS */
no_protocol:
{
GST_ERROR ("error parsing uri %s: no protocol", uristr);
return -1;
}
wrong_protocol:
{
GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr,

View file

@ -2358,11 +2358,20 @@ get_fmt_failed:
}
set_fmt_failed:
{
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
(_("Device '%s' cannot capture at %dx%d"),
v4l2object->videodev, width, height),
("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s",
GST_FOURCC_ARGS (pixelformat), width, height, g_strerror (errno)));
if (errno == EBUSY) {
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, BUSY,
(_("Device '%s' is busy"), v4l2object->videodev),
("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s",
GST_FOURCC_ARGS (pixelformat), width, height,
g_strerror (errno)));
} else {
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
(_("Device '%s' cannot capture at %dx%d"),
v4l2object->videodev, width, height),
("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s",
GST_FOURCC_ARGS (pixelformat), width, height,
g_strerror (errno)));
}
return FALSE;
}
invalid_dimensions:

View file

@ -71,6 +71,8 @@ enum
PROP_ENDX,
PROP_ENDY,
PROP_REMOTE,
PROP_XID,
PROP_XNAME,
};
#define gst_ximage_src_parent_class parent_class
@ -105,6 +107,35 @@ gst_ximage_src_return_buf (GstXImageSrc * ximagesrc, GstBuffer * ximage)
}
}
static Window
gst_ximage_src_find_window (GstXImageSrc * src, Window root, const char *name)
{
Window *children;
Window window = 0, root_return, parent_return;
unsigned int nchildren;
char *tmpname;
int n, status;
status = XFetchName (src->xcontext->disp, root, &tmpname);
if (status && !strcmp (name, tmpname))
return root;
status =
XQueryTree (src->xcontext->disp, root, &root_return, &parent_return,
&children, &nchildren);
if (!status || !children)
return (Window) 0;
for (n = 0; n < nchildren; ++n) {
window = gst_ximage_src_find_window (src, children[n], name);
if (window != 0)
break;
}
XFree (children);
return window;
}
static gboolean
gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name)
{
@ -125,8 +156,49 @@ gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name)
s->width = s->xcontext->width;
s->height = s->xcontext->height;
/* Always capture root window, for now */
s->xwindow = s->xcontext->root;
if (s->xid != 0 || s->xname) {
int status;
XWindowAttributes attrs;
Window window;
if (s->xid != 0) {
status = XGetWindowAttributes (s->xcontext->disp, s->xid, &attrs);
if (status) {
GST_DEBUG_OBJECT (s, "Found window XID %p", s->xid);
s->xwindow = s->xid;
goto window_found;
} else {
GST_WARNING_OBJECT (s, "Failed to get window %p attributes", s->xid);
}
}
if (s->xname) {
GST_DEBUG_OBJECT (s, "Looking for window %s", s->xname);
window = gst_ximage_src_find_window (s, s->xcontext->root, s->xname);
if (window != 0) {
GST_DEBUG_OBJECT (s, "Found window named %s as %p, ", s->xname, window);
status = XGetWindowAttributes (s->xcontext->disp, window, &attrs);
if (status) {
s->xwindow = window;
goto window_found;
} else {
GST_WARNING_OBJECT (s, "Failed to get window %p attributes", window);
}
}
}
GST_INFO_OBJECT (s, "Using root window");
goto use_root_window;
window_found:
g_assert (s->xwindow != 0);
s->width = attrs.width;
s->height = attrs.height;
GST_INFO_OBJECT (s, "Using default window %p, size of %dx%d", s->xwindow,
s->width, s->height);
}
use_root_window:
#ifdef HAVE_XFIXES
/* check if xfixes supported */
@ -603,7 +675,8 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc)
} else
#endif /* HAVE_XSHM */
{
GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XGetImage");
GST_DEBUG_OBJECT (ximagesrc,
"Retrieving screen using XGetImage, window %p", ximagesrc->xwindow);
if (ximagesrc->remote) {
XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
ximagesrc->startx, ximagesrc->starty, ximagesrc->width,
@ -847,6 +920,21 @@ gst_ximage_src_set_property (GObject * object, guint prop_id,
case PROP_REMOTE:
src->remote = g_value_get_boolean (value);
break;
case PROP_XID:
if (src->xcontext != NULL) {
g_warning ("ximagesrc window ID must be set before opening display");
break;
}
src->xid = g_value_get_uint64 (value);
break;
case PROP_XNAME:
if (src->xcontext != NULL) {
g_warning ("ximagesrc window name must be set before opening display");
break;
}
g_free (src->xname);
src->xname = g_strdup (g_value_get_string (value));
break;
default:
break;
}
@ -890,6 +978,12 @@ gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value,
case PROP_REMOTE:
g_value_set_boolean (value, src->remote);
break;
case PROP_XID:
g_value_set_uint64 (value, src->xid);
break;
case PROP_XNAME:
g_value_set_string (value, src->xname);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -928,6 +1022,7 @@ gst_ximage_src_finalize (GObject * object)
if (src->xcontext)
ximageutil_xcontext_clear (src->xcontext);
g_free (src->xname);
g_mutex_free (src->pool_lock);
g_mutex_free (src->x_lock);
@ -952,9 +1047,16 @@ gst_ximage_src_get_caps (GstBaseSrc * bs, GstCaps * filter)
(s)->srcpad));
xcontext = s->xcontext;
width = xcontext->width;
height = xcontext->height;
width = s->xcontext->width;
height = s->xcontext->height;
if (s->xwindow != 0) {
XWindowAttributes attrs;
int status = XGetWindowAttributes (s->xcontext->disp, s->xwindow, &attrs);
if (status) {
width = attrs.width;
height = attrs.height;
}
}
/* property comments say 0 means right/bottom, means we can't capture
the top left pixel alone */
@ -1135,6 +1237,28 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass)
g_param_spec_boolean ("remote", "Remote dispay",
"Whether the display is remote", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstXImageSrc:xid
*
* The XID of the window to capture. 0 for the root window (default).
*
* Since: 0.10.31
**/
g_object_class_install_property (gc, PROP_XID,
g_param_spec_uint64 ("xid", "Window XID",
"Window XID to capture from", 0, G_MAXUINT64, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstXImageSrc:xname
*
* The name of the window to capture, if any.
*
* Since: 0.10.31
**/
g_object_class_install_property (gc, PROP_XNAME,
g_param_spec_string ("xname", "Window name",
"Window name to capture from", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_set_details_simple (ec, "Ximage video source",
"Source/Video",

View file

@ -56,6 +56,10 @@ struct _GstXImageSrc
gchar *display_name;
guint screen_num;
/* Window selection */
guint64 xid;
gchar *xname;
/* Desired output framerate */
gint fps_n;
gint fps_d;

View file

@ -25,7 +25,7 @@
#include <gst/tag/tag.h>
#define SINK_CAPS "text/x-cmml"
#define SRC_CAPS "text/x-cmml"
#define SRC_CAPS "text/x-cmml, encoded=(boolean)TRUE"
#define IDENT_HEADER \
"CMML\x00\x00\x00\x00"\

View file

@ -26,7 +26,7 @@
#include <gst/tag/tag.h>
#define SINK_CAPS "text/x-cmml"
#define SRC_CAPS "text/x-cmml"
#define SRC_CAPS "text/x-cmml,encoded=(boolean)FALSE"
#define IDENT_HEADER \
"CMML\x00\x00\x00\x00"\

View file

@ -50,6 +50,7 @@ static void
mux_pcm_audio (guint num_buffers, guint repeat)
{
GstElement *src, *sink, *flvmux, *conv, *pipeline;
GstPad *sinkpad, *srcpad;
gint counter;
GST_LOG ("num_buffers = %u", num_buffers);
@ -83,25 +84,24 @@ mux_pcm_audio (guint num_buffers, guint repeat)
fail_unless (gst_element_link (src, conv));
fail_unless (gst_element_link (flvmux, sink));
/* now link the elements */
sinkpad = gst_element_get_request_pad (flvmux, "audio");
fail_unless (sinkpad != NULL, "Could not get audio request pad");
srcpad = gst_element_get_static_pad (conv, "src");
fail_unless (srcpad != NULL, "Could not get audioconvert's source pad");
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
do {
GstStateChangeReturn state_ret;
GstMessage *msg;
GstPad *sinkpad, *srcpad;
GST_LOG ("repeat=%d", repeat);
/* now link the elements */
sinkpad = gst_element_get_request_pad (flvmux, "audio");
fail_unless (sinkpad != NULL, "Could not get audio request pad");
srcpad = gst_element_get_static_pad (conv, "src");
fail_unless (srcpad != NULL, "Could not get audioconvert's source pad");
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
counter = 0;
state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);

View file

@ -43,6 +43,7 @@ static GstPad *mysrcpad, *mysinkpad;
"rate = (int) 48000"
#define VIDEO_CAPS_STRING "video/mpeg, " \
"mpegversion = (int) 4, " \
"systemstream = (boolean) false, " \
"width = (int) 384, " \
"height = (int) 288, " \
"framerate = (fraction) 25/1"
@ -517,7 +518,7 @@ create_qtmux_profile (const gchar * variant)
return cprof;
}
GST_START_TEST (test_encodebin)
GST_START_TEST (test_encodebin_qtmux)
{
GstEncodingContainerProfile *cprof;
GstElement *enc;
@ -553,6 +554,136 @@ GST_START_TEST (test_encodebin)
GST_END_TEST;
/* Fake mp3 encoder for test */
typedef GstElement TestMp3Enc;
typedef GstElementClass TestMp3EncClass;
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, layer=[1,3]")
);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw-int")
);
static GType test_mp3_enc_get_type (void);
GST_BOILERPLATE (TestMp3Enc, test_mp3_enc, GstElement, GST_TYPE_ELEMENT);
static void
test_mp3_enc_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class, "MPEG1 Audio Encoder",
"Codec/Encoder/Audio", "Pretends to encode mp3", "Foo Bar <foo@bar.com>");
}
static void
test_mp3_enc_class_init (TestMp3EncClass * klass)
{
/* doesn't actually need to do anything for this test */
}
static void
test_mp3_enc_init (TestMp3Enc * mp3enc, TestMp3EncClass * klass)
{
GstPad *pad;
pad = gst_pad_new_from_static_template (&sink_template, "sink");
gst_element_add_pad (mp3enc, pad);
pad = gst_pad_new_from_static_template (&src_template, "src");
gst_element_add_pad (mp3enc, pad);
}
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "testmp3enc", GST_RANK_NONE,
test_mp3_enc_get_type ());
}
static GstEncodingContainerProfile *
create_mp4mux_profile (void)
{
GstEncodingContainerProfile *cprof;
GstCaps *caps;
caps = gst_caps_new_simple ("video/quicktime",
"variant", G_TYPE_STRING, "iso", NULL);
cprof = gst_encoding_container_profile_new ("Name", "blah", caps, NULL);
gst_caps_unref (caps);
caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1,
"layer", G_TYPE_INT, 3, "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT,
44100, NULL);
gst_encoding_container_profile_add_profile (cprof,
GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, NULL, NULL,
1)));
gst_caps_unref (caps);
return cprof;
}
GST_START_TEST (test_encodebin_mp4mux)
{
GstEncodingContainerProfile *cprof;
GstPluginFeature *feature;
GstElement *enc, *mux;
GstPad *pad;
/* need a fake mp3 encoder because mp4 only accepts encoded formats */
gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
"fakemp3enc", "fakemp3enc", plugin_init, VERSION, "LGPL",
"gst-plugins-good", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
feature = gst_default_registry_find_feature ("testmp3enc",
GST_TYPE_ELEMENT_FACTORY);
gst_plugin_feature_set_rank (feature, GST_RANK_PRIMARY + 100);
enc = gst_element_factory_make ("encodebin", NULL);
if (enc == NULL)
return;
/* Make sure encodebin finds mp4mux even though qtmux outputs a superset */
cprof = create_mp4mux_profile ();
g_object_set (enc, "profile", cprof, NULL);
gst_encoding_profile_unref (cprof);
/* should have created a pad after setting the profile */
pad = gst_element_get_static_pad (enc, "audio_0");
fail_unless (pad != NULL);
gst_object_unref (pad);
mux = gst_bin_get_by_interface (GST_BIN (enc), GST_TYPE_TAG_SETTER);
fail_unless (mux != NULL);
{
GstElementFactory *f = gst_element_get_factory (mux);
/* make sure we got mp4mux for variant=iso */
GST_INFO ("muxer: %s", G_OBJECT_TYPE_NAME (mux));
fail_unless_equals_string (GST_PLUGIN_FEATURE_NAME (f), "mp4mux");
}
gst_object_unref (mux);
gst_object_unref (enc);
gst_plugin_feature_set_rank (feature, GST_RANK_NONE);
gst_object_unref (feature);
}
GST_END_TEST;
static Suite *
qtmux_suite (void)
{
@ -582,7 +713,8 @@ qtmux_suite (void)
tcase_add_test (tc_chain, test_audio_pad_frag_asc_streamable);
tcase_add_test (tc_chain, test_reuse);
tcase_add_test (tc_chain, test_encodebin);
tcase_add_test (tc_chain, test_encodebin_qtmux);
tcase_add_test (tc_chain, test_encodebin_mp4mux);
return s;
}