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 html: html-build.stamp
include $(top_srcdir)/common/upload-doc.mak 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. # The top-level SGML file. Change it if you want.
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml 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 # gtk-doc will search all .c & .h files beneath here for inline comments
# documenting functions and macros. # 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. # Extra options to supply to gtkdoc-scan.
SCAN_OPTIONS= SCAN_OPTIONS=
@ -53,14 +33,11 @@ FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
--extra-dir=$(GSTPB_PREFIX)/share/gtk-doc/html --extra-dir=$(GSTPB_PREFIX)/share/gtk-doc/html
# Used for dependencies. # Used for dependencies.
HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h HFILE_GLOB= \
CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c $(DOC_SOURCE_DIR)/*/*/*.cc $(DOC_SOURCE_DIR)/*/*/*.m $(top_srcdir)/gst/*/*.h $(top_srcdir)/ext/*/*.h $(top_srcdir)/sys/*/*.h
CFILE_GLOB= \
# this is a wingo addition $(top_srcdir)/gst/*/*.c $(top_srcdir)/ext/*/*.c $(top_srcdir)/sys/*/*.c \
# thomasvs: another nice wingo addition would be an explanation on why $(top_srcdir)/ext/*/*.cc $(top_srcdir)/sys/*/*.m
# this is useful ;)
SCANOBJ_DEPS =
# Header files to ignore when scanning. # Header files to ignore when scanning.
IGNORE_HFILES = IGNORE_HFILES =
@ -253,7 +230,7 @@ extra_files =
# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib # CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib
# contains GtkObjects/GObjects and you want to document signals and properties. # contains GtkObjects/GObjects and you want to document signals and properties.
GTKDOC_CFLAGS = $(GST_BASE_CFLAGS) -I$(top_builddir) 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_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC)
GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC) GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC)

View file

@ -7,12 +7,14 @@ libgstpulse_la_SOURCES = \
pulsemixertrack.c \ pulsemixertrack.c \
pulseprobe.c \ pulseprobe.c \
pulsesink.c \ pulsesink.c \
pulseaudiosink.c \
pulsesrc.c \ pulsesrc.c \
pulseutil.c pulseutil.c
libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS) libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS)
libgstpulse_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \ 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_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstpulse_la_LIBTOOLFLAGS = --tag=disable-static libgstpulse_la_LIBTOOLFLAGS = --tag=disable-static

View file

@ -49,6 +49,12 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_PULSESRC)) GST_TYPE_PULSESRC))
return FALSE; 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, if (!gst_element_register (plugin, "pulsemixer", GST_RANK_NONE,
GST_TYPE_PULSEMIXER)) GST_TYPE_PULSEMIXER))
return FALSE; 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)) if (idx != pa_stream_get_index (pbuf->stream))
continue; 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 /* Actually this event is also triggered when other properties of
* the stream change that are unrelated to the volume. However it is * the stream change that are unrelated to the volume. However it is
* probably cheaper to signal the change here and check for the * 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, static GstStateChangeReturn gst_pulsesink_change_state (GstElement * element,
GstStateChange transition); 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", static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, " GST_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS));
"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_IMPLEMENT_PULSEPROBE_METHODS (GstPulseSink, gst_pulsesink); GST_IMPLEMENT_PULSEPROBE_METHODS (GstPulseSink, gst_pulsesink);
@ -2002,6 +1998,8 @@ done:
} }
#ifdef HAVE_PULSE_1_0 #ifdef HAVE_PULSE_1_0
/* NOTE: If you're making changes here, see if pulseaudiosink acceptcaps also
* needs to be changed accordingly. */
static gboolean static gboolean
gst_pulsesink_pad_acceptcaps (GstPad * pad, GstCaps * caps) gst_pulsesink_pad_acceptcaps (GstPad * pad, GstCaps * caps)
{ {

View file

@ -24,6 +24,10 @@
#ifndef __GST_PULSESINK_H__ #ifndef __GST_PULSESINK_H__
#define __GST_PULSESINK_H__ #define __GST_PULSESINK_H__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/audio/gstaudiosink.h> #include <gst/audio/gstaudiosink.h>
@ -88,6 +92,60 @@ struct _GstPulseSinkClass
GType gst_pulsesink_get_type (void); 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 G_END_DECLS
#endif /* __GST_PULSESINK_H__ */ #endif /* __GST_PULSESINK_H__ */

View file

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

View file

@ -1,10 +1,10 @@
plugin_LTLIBRARIES = libgstsouphttpsrc.la 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_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_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(SOUP_LIBS)
libgstsouphttpsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstsouphttpsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstsouphttpsrc_la_LIBTOOLFLAGS = --tag=disable-static 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 <gst/gst-i18n-plugin.h>
#include "gstsouphttpsrc.h" #include "gstsouphttpsrc.h"
#include "gstsouphttpsink.h" #include "gstsouphttpclientsink.h"
static gboolean static gboolean
@ -34,8 +34,8 @@ plugin_init (GstPlugin * plugin)
gst_element_register (plugin, "souphttpsrc", GST_RANK_PRIMARY, gst_element_register (plugin, "souphttpsrc", GST_RANK_PRIMARY,
GST_TYPE_SOUP_HTTP_SRC); GST_TYPE_SOUP_HTTP_SRC);
gst_element_register (plugin, "souphttpsink", GST_RANK_NONE, gst_element_register (plugin, "souphttpclientsink", GST_RANK_NONE,
GST_TYPE_SOUP_HTTP_SINK); GST_TYPE_SOUP_HTTP_CLIENT_SINK);
return TRUE; return TRUE;
} }

View file

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

View file

@ -1,5 +1,5 @@
/* GStreamer /* 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -17,29 +17,27 @@
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*/ */
#ifndef _GST_SOUP_HTTP_SINK_H_ #ifndef _GST_SOUP_HTTP_CLIENT_SINK_H_
#define _GST_SOUP_HTTP_SINK_H_ #define _GST_SOUP_HTTP_CLIENT_SINK_H_
#include <gst/base/gstbasesink.h> #include <gst/base/gstbasesink.h>
#include <libsoup/soup.h> #include <libsoup/soup.h>
G_BEGIN_DECLS G_BEGIN_DECLS
#define GST_TYPE_SOUP_HTTP_SINK (gst_soup_http_sink_get_type()) #define GST_TYPE_SOUP_HTTP_CLIENT_SINK (gst_soup_http_client_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_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SOUP_HTTP_CLIENT_SINK,GstSoupHttpClientSink))
#define GST_SOUP_HTTP_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SOUP_HTTP_SINK,GstSoupHttpSinkClass)) #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_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_SINK)) #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_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_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 _GstSoupHttpClientSink GstSoupHttpClientSink;
typedef struct _GstSoupHttpSinkClass GstSoupHttpSinkClass; typedef struct _GstSoupHttpClientSinkClass GstSoupHttpClientSinkClass;
struct _GstSoupHttpSink struct _GstSoupHttpClientSink
{ {
GstBaseSink base_souphttpsink; GstBaseSink base_souphttpsink;
GstPad *sinkpad;
GMutex *mutex; GMutex *mutex;
GCond *cond; GCond *cond;
GMainContext *context; GMainContext *context;
@ -71,12 +69,12 @@ struct _GstSoupHttpSink
}; };
struct _GstSoupHttpSinkClass struct _GstSoupHttpClientSinkClass
{ {
GstBaseSinkClass base_souphttpsink_class; GstBaseSinkClass base_souphttpsink_class;
}; };
GType gst_soup_http_sink_get_type (void); GType gst_soup_http_client_sink_get_type (void);
G_END_DECLS 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, soup_message_headers_foreach (msg->response_headers,
gst_soup_http_src_headers_foreach, src); 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)) { if (src->automatic_redirect && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code, GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code,
soup_message_headers_get (msg->response_headers, "Location")); 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 /* Check if we have a continous stream, if not drop some samples or the buffer or
* insert some silence samples */ * insert some silence samples */
if (enc->next_ts != GST_CLOCK_TIME_NONE && if (enc->next_ts != GST_CLOCK_TIME_NONE &&
GST_BUFFER_TIMESTAMP_IS_VALID (buf) &&
GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) { GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) {
guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf); guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf);
guint64 diff_bytes; 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", static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, " GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) { 2, 4 };"));
"framed = (boolean) false, " "mpegversion = (int) { 2, 4 };"));
GST_DEBUG_CATEGORY_STATIC (aacparse_debug); GST_DEBUG_CATEGORY_STATIC (aacparse_debug);
#define GST_CAT_DEFAULT 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->channels = (data[1] & 0x78) >> 3;
aacparse->header_type = DSPAAC_HEADER_NONE; aacparse->header_type = DSPAAC_HEADER_NONE;
aacparse->mpegversion = 4; aacparse->mpegversion = 4;
aacparse->frame_samples = (data[1] & 4) ? 960 : 1024;
gst_buffer_unmap (buf, data, size); gst_buffer_unmap (buf, data, size);
GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d", GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, "
aacparse->object_type, aacparse->sample_rate, aacparse->channels); "samples=%d", aacparse->object_type, aacparse->sample_rate,
aacparse->channels, aacparse->frame_samples);
/* arrange for metadata and get out of the way */ /* arrange for metadata and get out of the way */
gst_aac_parse_set_src_caps (aacparse, caps); 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, gst_aac_parse_parse_adts_header (aacparse, data, &rate, &channels,
&aacparse->object_type, &aacparse->mpegversion); &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", GST_DEBUG ("ADTS: samplerate %d, channels %d, objtype %d, version %d",
rate, channels, aacparse->object_type, aacparse->mpegversion); 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_caps_unref (sinkcaps);
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), 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; return ret;
@ -705,6 +707,7 @@ gst_aac_parse_start (GstBaseParse * parse)
aacparse = GST_AAC_PARSE (parse); aacparse = GST_AAC_PARSE (parse);
GST_DEBUG ("start"); GST_DEBUG ("start");
aacparse->frame_samples = 1024;
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), ADTS_MAX_SIZE); gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), ADTS_MAX_SIZE);
return TRUE; return TRUE;
} }

View file

@ -63,19 +63,6 @@ typedef struct _GstAacParseClass GstAacParseClass;
/** /**
* GstAacParse: * 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. * The opaque GstAacParse data structure.
*/ */
@ -88,6 +75,7 @@ struct _GstAacParse {
gint sample_rate; gint sample_rate;
gint channels; gint channels;
gint mpegversion; gint mpegversion;
gint frame_samples;
GstAacHeaderType header_type; GstAacHeaderType header_type;
}; };

View file

@ -144,16 +144,16 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC, GST_PAD_SRC,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) true, " 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, " "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", static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) false; " GST_STATIC_CAPS ("audio/x-ac3; " "audio/x-eac3; " "audio/ac3"));
"audio/x-eac3, framed = (boolean) false; "
"audio/ac3, framed = (boolean) false "));
static void gst_ac3_parse_finalize (GObject * object); 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_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class, 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>"); "AC3 parser", "Tim-Philipp Müller <tim centricular net>");
parse_class->start = GST_DEBUG_FUNCPTR (gst_ac3_parse_start); 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; GstBitReader bits;
gpointer data; gpointer data;
gsize size; gsize size;
guint8 fscod, frmsizcod, bsid, acmod, lfe_on; guint8 fscod, frmsizcod, bsid, acmod, lfe_on, rate_scale;
gboolean ret = FALSE; gboolean ret = FALSE;
GST_LOG_OBJECT (ac3parse, "parsing ac3"); 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); 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) if (frame_size)
*frame_size = frmsizcod_table[frmsizcod].frame_size[fscod] * 2; *frame_size = frmsizcod_table[frmsizcod].frame_size[fscod] * 2;
if (rate) if (rate)
*rate = fscod_rates[fscod]; *rate = fscod_rates[fscod] >> rate_scale;
if (chans) if (chans)
*chans = acmod_chans[acmod] + lfe_on; *chans = acmod_chans[acmod] + lfe_on;
if (blks) 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 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", GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3",
"framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate, "framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate,
"channels", G_TYPE_INT, chans, NULL); "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 ]," " channels = (int) [ 1, 8 ],"
" rate = (int) [ 8000, 192000 ]," " rate = (int) [ 8000, 192000 ],"
" depth = (int) { 14, 16 }," " 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", static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, 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); 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", static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, 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); 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, " GST_STATIC_CAPS ("audio/mpeg, "
"mpegversion = (int) 1, " "mpegversion = (int) 1, "
"layer = (int) [ 1, 3 ], " "layer = (int) [ 1, 3 ], "
"rate = (int) [ 8000, 48000 ], channels = (int) [ 1, 2 ]," "mpegaudioversion = (int) [ 1, 3], "
"parsed=(boolean) true") "rate = (int) [ 8000, 48000 ], "
"channels = (int) [ 1, 2 ], " "parsed=(boolean) true")
); );
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, 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); 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); 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 static gboolean
gst_navseek_src_event (GstBaseTransform * trans, GstEvent * event) 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) { } else if (strcmp (key, "n") == 0) {
/* normal speed */ /* normal speed */
gst_navseek_change_playback_rate (navseek, 1.0); gst_navseek_change_playback_rate (navseek, 1.0);
} else if (strcmp (key, "space") == 0) {
gst_navseek_toggle_play_pause (navseek);
} }
} else { } else {
break; break;

View file

@ -107,6 +107,8 @@ static GstStateChangeReturn
gst_flv_mux_change_state (GstElement * element, GstStateChange transition); gst_flv_mux_change_state (GstElement * element, GstStateChange transition);
static void gst_flv_mux_reset (GstElement * element); static void gst_flv_mux_reset (GstElement * element);
static void gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * pad,
gboolean video);
typedef struct typedef struct
{ {
@ -228,10 +230,10 @@ gst_flv_mux_reset (GstElement * element)
GstFlvMux *mux = GST_FLV_MUX (element); GstFlvMux *mux = GST_FLV_MUX (element);
GSList *sl; 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; 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); 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; 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 * static GstPad *
gst_flv_mux_request_new_pad (GstElement * element, gst_flv_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps) GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
@ -544,18 +566,9 @@ gst_flv_mux_request_new_pad (GstElement * element,
cpad = (GstFlvPad *) cpad = (GstFlvPad *)
gst_collect_pads_add_pad (mux->collect, pad, sizeof (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->audio_codec_data = NULL;
cpad->video_codec = G_MAXUINT;
cpad->video_codec_data = NULL; cpad->video_codec_data = NULL;
gst_flv_mux_reset_pad (mux, cpad, video);
cpad->last_timestamp = 0;
/* FIXME: hacked way to override/extend the event function of /* FIXME: hacked way to override/extend the event function of
* GstCollectPads; because it sets its own event function giving the * 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)); GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad));
GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad); GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
if (cpad && cpad->audio_codec_data) gst_flv_mux_reset_pad (mux, cpad, cpad->video);
gst_buffer_unref (cpad->audio_codec_data);
if (cpad && cpad->video_codec_data)
gst_buffer_unref (cpad->video_codec_data);
gst_collect_pads_remove_pad (mux->collect, pad); gst_collect_pads_remove_pad (mux->collect, pad);
gst_element_remove_pad (element, 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->back = (guint32 *) malloc (buffsize * sizeof (guint32) + 128);
goomdata->buffsize = buffsize; goomdata->buffsize = buffsize;
goomdata->p1 = goomdata->p1 = (void *) (((guintptr) goomdata->pixel + 0x7f) & (~0x7f));
(void *) (((unsigned long) goomdata->pixel + 0x7f) & (~0x7f)); goomdata->p2 = (void *) (((guintptr) goomdata->back + 0x7f) & (~0x7f));
goomdata->p2 = (void *) (((unsigned long) goomdata->back + 0x7f) & (~0x7f));
} }
goomdata->resolx = resx; goomdata->resolx = resx;

View file

@ -1757,15 +1757,25 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
GstCollectData *cdata = (GstCollectData *) walk->data; GstCollectData *cdata = (GstCollectData *) walk->data;
GstQTPad *qtpad = (GstQTPad *) cdata; 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_DEBUG_OBJECT (qtmux, "Sending the last buffer for pad %s",
GST_PAD_NAME (qtpad->collect.pad)); GST_PAD_NAME (qtpad->collect.pad));
ret = gst_qt_mux_add_buffer (qtmux, qtpad, NULL); 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, " GST_WARNING_OBJECT (qtmux, "Failed to send last buffer for %s, "
"flow return: %s", GST_PAD_NAME (qtpad->collect.pad), "flow return: %s", GST_PAD_NAME (qtpad->collect.pad),
gst_flow_get_name (ret)); gst_flow_get_name (ret));
}
/* having flushed above, can check for buffers now */
if (!GST_CLOCK_TIME_IS_VALID (qtpad->first_ts)) { if (!GST_CLOCK_TIME_IS_VALID (qtpad->first_ts)) {
GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers", GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers",
GST_PAD_NAME (qtpad->collect.pad)); 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++; 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 */ /* takes ownership of @buf */
static GstBuffer * static GstBuffer *
gst_qt_mux_get_asc_buffer_ts (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) 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); 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: again:
last_buf = pad->last_buf; 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); buf = gst_qt_mux_get_asc_buffer_ts (qtmux, pad, buf);
if (!buf && !last_buf) { if (!buf && !last_buf) {
GST_DEBUG_OBJECT (qtmux, "no reordered buffer"); GST_DEBUG_OBJECT (qtmux, "no reordered buffer");
@ -2177,6 +2227,31 @@ again:
goto no_order; 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 /* fall back to duration if last buffer or
* out-of-order (determined previously), otherwise use input ts */ * out-of-order (determined previously), otherwise use input ts */
if (buf == NULL || if (buf == NULL ||
@ -2336,20 +2411,6 @@ again:
qtmux->longest_chunk = duration; 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 */ /* now we go and register this buffer/sample all over */
/* note that a new chunk is started each time (not fancy but works) */ /* note that a new chunk is started each time (not fancy but works) */
if (qtmux->moov_recov_file) { 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, typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux,
const char *tag, const char *tag_bis, GNode * node); const char *tag, const char *tag_bis, GNode * node);
@ -8450,7 +8485,9 @@ static const struct
* http://atomicparsley.sourceforge.net/mpeg-4files.html and * http://atomicparsley.sourceforge.net/mpeg-4files.html and
* bug #614471 * 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 static void
@ -8865,6 +8902,15 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
GST_LOG_OBJECT (qtdemux, "No udta node found."); 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); qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
return TRUE; return TRUE;

View file

@ -189,6 +189,9 @@ G_BEGIN_DECLS
#define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m') #define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m')
#define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c') #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 */ /* ISO Motion JPEG 2000 fourcc */
#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2') #define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2')
#define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h') #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; gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
guint64 total; guint64 total;
guint8 b; guint8 b;
GstFlowReturn ret;
g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR); g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (_length != 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 */ /* read element id */
needed = 2; needed = 2;
buf = peek (ctx, needed); ret = peek (ctx, needed, &buf);
if (!buf) if (ret != GST_FLOW_OK)
goto not_enough_data; goto peek_error;
b = GST_READ_UINT8 (buf); b = GST_READ_UINT8 (buf);
total = (guint64) b; total = (guint64) b;
while (read <= 4 && !(total & len_mask)) { 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 */ /* need id and at least something for subsequent length */
needed = read + 1; needed = read + 1;
buf = peek (ctx, needed); ret = peek (ctx, needed, &buf);
if (!buf) if (ret != GST_FLOW_OK)
goto not_enough_data; goto peek_error;
while (n < read) { while (n < read) {
b = GST_READ_UINT8 (buf + n); b = GST_READ_UINT8 (buf + n);
total = (total << 8) | b; total = (total << 8) | b;
@ -112,10 +111,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
num_ffs++; num_ffs++;
needed += read - 1; needed += read - 1;
buf = peek (ctx, needed); ret = peek (ctx, needed, &buf);
if (!buf) if (ret != GST_FLOW_OK)
goto not_enough_data; goto peek_error;
buf += (needed - read); buf += (needed - read);
n = 1; n = 1;
while (n < read) { while (n < read) {
@ -137,10 +135,11 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
return GST_FLOW_OK; return GST_FLOW_OK;
/* ERRORS */ /* ERRORS */
not_enough_data: peek_error:
{ {
GST_WARNING_OBJECT (el, "peek failed, ret = %d", ret);
*_needed = needed; *_needed = needed;
return GST_FLOW_UNEXPECTED; return ret;
} }
invalid_id: invalid_id:
{ {
@ -190,15 +189,13 @@ gst_ebml_read_clear (GstEbmlRead * ebml)
ebml->el = NULL; ebml->el = NULL;
} }
static const guint8 * static GstFlowReturn
gst_ebml_read_peek (GstByteReader * br, guint peek) 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 GST_FLOW_OK;
if (G_LIKELY (gst_byte_reader_peek_data (br, peek, &data)))
return data;
else else
return NULL; return GST_FLOW_UNEXPECTED;
} }
static GstFlowReturn static GstFlowReturn

View file

@ -59,7 +59,7 @@ typedef struct _GstEbmlRead {
GArray *readers; GArray *readers;
} GstEbmlRead; } 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 */ /* returns UNEXPECTED if not enough data */
GstFlowReturn gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, GstFlowReturn gst_ebml_peek_id_length (guint32 * _id, guint64 * _length,

View file

@ -81,9 +81,12 @@ enum
{ {
ARG_0, ARG_0,
ARG_METADATA, 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", static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
@ -168,6 +171,12 @@ static void gst_matroska_demux_reset (GstElement * element);
static gboolean perform_seek_to_offset (GstMatroskaDemux * demux, static gboolean perform_seek_to_offset (GstMatroskaDemux * demux,
guint64 offset); 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); GType gst_matroska_demux_get_type (void);
GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstElement, GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstElement,
GST_TYPE_ELEMENT); GST_TYPE_ELEMENT);
@ -223,6 +232,15 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
gobject_class->finalize = gst_matroska_demux_finalize; 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 = gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state); GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
gstelement_class->send_event = gstelement_class->send_event =
@ -262,6 +280,9 @@ gst_matroska_demux_init (GstMatroskaDemux * demux,
demux->common.adapter = gst_adapter_new (); demux->common.adapter = gst_adapter_new ();
/* property defaults */
demux->max_gap_time = DEFAULT_MAX_GAP_TIME;
/* finish off */ /* finish off */
gst_matroska_demux_reset (GST_ELEMENT (demux)); 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 /* handle gaps, e.g. non-zero start-time, or an cue index entry
* that landed us with timestamps not quite intended */ * that landed us with timestamps not quite intended */
GST_OBJECT_LOCK (demux); 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) { demux->common.segment.rate > 0.0) {
GstClockTimeDiff diff; GstClockTimeDiff diff;
GstEvent *event1, *event2; GstEvent *event1, *event2;
@ -3372,8 +3394,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
/* only send newsegments with increasing start times, /* only send newsegments with increasing start times,
* otherwise if these go back and forth downstream (sinks) increase * otherwise if these go back and forth downstream (sinks) increase
* accumulated time and running_time */ * accumulated time and running_time */
diff = GST_CLOCK_DIFF (demux->common.segment.last_stop, lace_time); diff = GST_CLOCK_DIFF (demux->last_stop_end, lace_time);
if (diff > 2 * GST_SECOND && lace_time > demux->common.segment.start if (diff > 0 && diff > demux->max_gap_time
&& lace_time > demux->common.segment.start
&& (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) && (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)
|| lace_time < demux->common.segment.stop)) { || lace_time < demux->common.segment.stop)) {
GST_DEBUG_OBJECT (demux, GST_DEBUG_OBJECT (demux,
@ -3386,11 +3409,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
* accum time, hence running_time */ * accum time, hence running_time */
/* close ahead of gap */ /* close ahead of gap */
event1 = gst_event_new_new_segment (TRUE, event1 = gst_event_new_new_segment (TRUE,
demux->common.segment.rate, demux->common.segment.rate, demux->common.segment.format,
demux->common.segment.format, demux->last_stop_end, demux->last_stop_end,
demux->common.segment.last_stop, demux->last_stop_end);
demux->common.segment.last_stop,
demux->common.segment.last_stop);
/* skip gap */ /* skip gap */
event2 = gst_event_new_new_segment (FALSE, event2 = gst_event_new_new_segment (FALSE,
demux->common.segment.rate, demux->common.segment.rate,
@ -5414,6 +5435,48 @@ gst_matroska_demux_change_state (GstElement * element,
return ret; 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 gboolean
gst_matroska_demux_plugin_init (GstPlugin * plugin) gst_matroska_demux_plugin_init (GstPlugin * plugin)
{ {

View file

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

View file

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

View file

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

View file

@ -1639,14 +1639,12 @@ gst_matroska_read_common_peek_bytes (GstMatroskaReadCommon * common, guint64
return GST_FLOW_OK; return GST_FLOW_OK;
} }
static const guint8 * static GstFlowReturn
gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek) gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek,
guint8 ** data)
{ {
guint8 *data = NULL; return gst_matroska_read_common_peek_bytes (common, common->offset, peek,
NULL, data);
gst_matroska_read_common_peek_bytes (common, common->offset, peek, NULL,
&data);
return data;
} }
GstFlowReturn 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 */ * so downstream waiting for keyframe can pick up at SPS/PPS/IDR */
#define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8)) #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, gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
GstClockTime in_timestamp, gboolean marker) 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); GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
else else
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
gst_base_rtp_depayload_push (depayload, outbuf);
} }
return TRUE; return outbuf;
/* ERRORS */ /* ERRORS */
short_nal: short_nal:
@ -596,12 +594,13 @@ short_nal:
GST_WARNING_OBJECT (depayload, "dropping short NAL"); GST_WARNING_OBJECT (depayload, "dropping short NAL");
gst_buffer_unmap (nal, data, size); gst_buffer_unmap (nal, data, size);
gst_buffer_unref (nal); gst_buffer_unref (nal);
return FALSE; return NULL;
} }
} }
static void static GstBuffer *
gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay) gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay,
gboolean send)
{ {
guint outsize; guint outsize;
guint8 *outdata; guint8 *outdata;
@ -624,17 +623,26 @@ gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay)
} }
gst_buffer_unmap (outbuf, outdata, -1); 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; 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 * static GstBuffer *
gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
{ {
GstRtpH264Depay *rtph264depay; GstRtpH264Depay *rtph264depay;
GstBuffer *outbuf; GstBuffer *outbuf = NULL;
guint8 nal_unit_type; guint8 nal_unit_type;
GstRTPBuffer rtp = { NULL }; 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 */ * when the FU ended) and send out what we gathered thusfar */
if (G_UNLIKELY (rtph264depay->current_fu_type != 0 && if (G_UNLIKELY (rtph264depay->current_fu_type != 0 &&
nal_unit_type != rtph264depay->current_fu_type)) 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) { switch (nal_unit_type) {
case 0: case 0:
@ -755,7 +763,8 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
outsize = gst_adapter_available (rtph264depay->adapter); outsize = gst_adapter_available (rtph264depay->adapter);
outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize); 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; break;
} }
case 26: 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 * Assume that the remote payloader is buggy (doesn't set the end
* bit) and send out what we've gathered thusfar */ * bit) and send out what we've gathered thusfar */
if (G_UNLIKELY (rtph264depay->current_fu_type != 0)) 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->current_fu_type = nal_unit_type;
rtph264depay->fu_timestamp = timestamp; rtph264depay->fu_timestamp = timestamp;
@ -844,11 +853,12 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
gst_adapter_push (rtph264depay->adapter, outbuf); gst_adapter_push (rtph264depay->adapter, outbuf);
} }
outbuf = NULL;
rtph264depay->fu_marker = marker; rtph264depay->fu_marker = marker;
/* if NAL unit ends, flush the adapter */ /* if NAL unit ends, flush the adapter */
if (E) if (E)
gst_rtp_h264_push_fragmentation_unit (rtph264depay); outbuf = gst_rtp_h264_push_fragmentation_unit (rtph264depay, FALSE);
break; break;
} }
default: default:
@ -872,14 +882,15 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
memcpy (outdata + sizeof (sync_bytes), payload, nalu_size); memcpy (outdata + sizeof (sync_bytes), payload, nalu_size);
gst_buffer_unmap (outbuf, outdata, outsize); 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; break;
} }
} }
gst_rtp_buffer_unmap (&rtp); gst_rtp_buffer_unmap (&rtp);
} }
return NULL; return outbuf;
/* ERRORS */ /* ERRORS */
undefined_type: undefined_type:

View file

@ -213,6 +213,9 @@ struct _GstRtpBinPrivate
gint shutdown; gint shutdown;
gboolean autoremove; gboolean autoremove;
/* UNIX (ntp) time of last SR sync used */
guint64 last_unix;
}; };
/* signals and args */ /* signals and args */
@ -245,6 +248,8 @@ enum
#define DEFAULT_AUTOREMOVE FALSE #define DEFAULT_AUTOREMOVE FALSE
#define DEFAULT_BUFFER_MODE RTP_JITTER_BUFFER_MODE_SLAVE #define DEFAULT_BUFFER_MODE RTP_JITTER_BUFFER_MODE_SLAVE
#define DEFAULT_USE_PIPELINE_CLOCK FALSE #define DEFAULT_USE_PIPELINE_CLOCK FALSE
#define DEFAULT_RTCP_SYNC GST_RTP_BIN_RTCP_SYNC_ALWAYS
#define DEFAULT_RTCP_SYNC_INTERVAL 0
enum enum
{ {
@ -254,12 +259,39 @@ enum
PROP_DO_LOST, PROP_DO_LOST,
PROP_IGNORE_PT, PROP_IGNORE_PT,
PROP_NTP_SYNC, PROP_NTP_SYNC,
PROP_RTCP_SYNC,
PROP_RTCP_SYNC_INTERVAL,
PROP_AUTOREMOVE, PROP_AUTOREMOVE,
PROP_BUFFER_MODE, PROP_BUFFER_MODE,
PROP_USE_PIPELINE_CLOCK, PROP_USE_PIPELINE_CLOCK,
PROP_LAST 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 */ /* helper objects */
typedef struct _GstRtpBinSession GstRtpBinSession; typedef struct _GstRtpBinSession GstRtpBinSession;
typedef struct _GstRtpBinStream GstRtpBinStream; typedef struct _GstRtpBinStream GstRtpBinStream;
@ -310,6 +342,9 @@ struct _GstRtpBinStream
gboolean have_sync; gboolean have_sync;
/* mapping to local RTP and NTP time */ /* mapping to local RTP and NTP time */
gint64 rt_delta; 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) #define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->lock)
@ -775,6 +810,8 @@ gst_rtp_bin_reset_sync (GstRtpBin * rtpbin)
* lip-sync */ * lip-sync */
stream->have_sync = FALSE; stream->have_sync = FALSE;
stream->rt_delta = 0; stream->rt_delta = 0;
stream->rtp_delta = 0;
stream->clock_base = -100 * GST_SECOND;
} }
} }
GST_RTP_BIN_UNLOCK (rtpbin); GST_RTP_BIN_UNLOCK (rtpbin);
@ -979,7 +1016,8 @@ stream_set_ts_offset (GstRtpBin * bin, GstRtpBinStream * stream,
static void static void
gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
guint8 * data, guint64 ntptime, guint64 last_extrtptime, 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; GstRtpBinClient *client;
gboolean created; gboolean created;
@ -1014,6 +1052,19 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
stream->ssrc, client, client->cname); 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 /* 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 * 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 * 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, GST_DEBUG_OBJECT (bin,
"base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT "base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT
", local RTP %" G_GUINT64_FORMAT ", clock-rate %d", base_rtptime, ", local RTP %" G_GUINT64_FORMAT ", clock-rate %d, "
last_extrtptime, local_rtp, clock_rate); "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 /* calculate local RTP time in gstreamer timestamp, we essentially perform the
* same conversion that a jitterbuffer would use to convert an rtp timestamp * 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->rt_delta = rtdiff - ntpdiff;
stream_set_ts_offset (bin, stream, stream->rt_delta); stream_set_ts_offset (bin, stream, stream->rt_delta);
} else if (client->nstreams > 1) { } else {
gint64 min; 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 /* calculate delta between server and receiver. last_unix is created by
* converting the ntptime in the last SR packet to a gstreamer timestamp. This * 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). * latencies).
* The stream that has the smallest diff is selected as the reference stream, * The stream that has the smallest diff is selected as the reference stream,
* all other streams will have a positive offset to this difference. */ * 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)) { for (walk = client->streams; walk; walk = g_slist_next (walk)) {
GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data; GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
if (!ostream->have_sync) if (!ostream->have_sync) {
all_sync = FALSE;
continue; 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) if (ostream->rt_delta < min)
min = ostream->rt_delta; 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, /* arrange to re-sync for each stream upon significant change,
min); * 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 */ /* calculate offsets for each stream */
for (walk = client->streams; walk; walk = g_slist_next (walk)) { 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 /* calculate offset to our reference stream, this should always give a
* positive number. */ * 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); 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_rtptime;
guint64 base_time; guint64 base_time;
guint clock_rate; guint clock_rate;
guint64 clock_base;
guint64 extrtptime; guint64 extrtptime;
GstBuffer *buffer; GstBuffer *buffer;
GstRTCPBuffer rtcp = { NULL }; 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")); g_value_get_uint64 (gst_structure_get_value (s, "base-rtptime"));
base_time = g_value_get_uint64 (gst_structure_get_value (s, "base-time")); 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_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 = extrtptime =
g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime")); g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime"));
buffer = gst_value_get_buffer (gst_structure_get_value (s, "sr-buffer")); 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); GST_RTP_BIN_LOCK (bin);
/* associate the stream to CNAME */ /* associate the stream to CNAME */
gst_rtp_bin_associate (bin, stream, len, data, 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); GST_RTP_BIN_UNLOCK (bin);
} }
} }
@ -1265,7 +1420,9 @@ create_stream (GstRtpBinSession * session, guint32 ssrc)
stream->have_sync = FALSE; stream->have_sync = FALSE;
stream->rt_delta = 0; stream->rt_delta = 0;
stream->rtp_delta = 0;
stream->percent = 100; stream->percent = 100;
stream->clock_base = -100 * GST_SECOND;
session->streams = g_slist_prepend (session->streams, stream); session->streams = g_slist_prepend (session->streams, stream);
/* provide clock_rate to the jitterbuffer when needed */ /* 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, "Synchronize received streams to the NTP clock", DEFAULT_NTP_SYNC,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 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->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
gstelement_class->request_new_pad = gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_rtp_bin_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->do_lost = DEFAULT_DO_LOST;
rtpbin->ignore_pt = DEFAULT_IGNORE_PT; rtpbin->ignore_pt = DEFAULT_IGNORE_PT;
rtpbin->ntp_sync = DEFAULT_NTP_SYNC; 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->priv->autoremove = DEFAULT_AUTOREMOVE;
rtpbin->buffer_mode = DEFAULT_BUFFER_MODE; rtpbin->buffer_mode = DEFAULT_BUFFER_MODE;
rtpbin->use_pipeline_clock = DEFAULT_USE_PIPELINE_CLOCK; 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: case PROP_NTP_SYNC:
rtpbin->ntp_sync = g_value_get_boolean (value); rtpbin->ntp_sync = g_value_get_boolean (value);
break; 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: case PROP_IGNORE_PT:
rtpbin->ignore_pt = g_value_get_boolean (value); rtpbin->ignore_pt = g_value_get_boolean (value);
break; break;
@ -1883,6 +2074,12 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id,
case PROP_NTP_SYNC: case PROP_NTP_SYNC:
g_value_set_boolean (value, rtpbin->ntp_sync); g_value_set_boolean (value, rtpbin->ntp_sync);
break; 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: case PROP_AUTOREMOVE:
g_value_set_boolean (value, rtpbin->priv->autoremove); g_value_set_boolean (value, rtpbin->priv->autoremove);
break; break;
@ -2020,6 +2217,7 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message)
now = gst_clock_get_time (clock); now = gst_clock_get_time (clock);
base_time = gst_element_get_base_time (GST_ELEMENT_CAST (bin)); base_time = gst_element_get_base_time (GST_ELEMENT_CAST (bin));
running_time = now - base_time; running_time = now - base_time;
gst_object_unref (clock);
} }
GST_DEBUG_OBJECT (bin, GST_DEBUG_OBJECT (bin,
"running time now %" GST_TIME_FORMAT, "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: case GST_STATE_CHANGE_NULL_TO_READY:
break; break;
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
priv->last_unix = 0;
GST_LOG_OBJECT (rtpbin, "clearing shutdown flag"); GST_LOG_OBJECT (rtpbin, "clearing shutdown flag");
g_atomic_int_set (&priv->shutdown, 0); g_atomic_int_set (&priv->shutdown, 0);
break; break;

View file

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

View file

@ -664,6 +664,11 @@ gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
JBUF_LOCK (priv); JBUF_LOCK (priv);
priv->clock_rate = -1; 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); JBUF_UNLOCK (priv);
} }
@ -1953,6 +1958,7 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
guint32 rtptime; guint32 rtptime;
gboolean drop = FALSE; gboolean drop = FALSE;
GstRTCPBuffer rtcp = { NULL }; GstRTCPBuffer rtcp = { NULL };
guint64 clock_base;
jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); 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, rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
&clock_rate, &last_rtptime); &clock_rate, &last_rtptime);
clock_base = priv->clock_base;
GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %" GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %"
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT, G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT
ext_rtptime, base_rtptime, clock_rate); ", clock-base %" G_GUINT64_FORMAT,
ext_rtptime, base_rtptime, clock_rate, clock_base);
if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) { if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) {
GST_DEBUG_OBJECT (jitterbuffer, "dropping, no RTP values"); 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; diff = ext_rtptime - last_rtptime;
/* if bigger than 1 second, we drop it */ /* if bigger than 1 second, we drop it */
if (diff > clock_rate) { if (diff > clock_rate) {
GST_DEBUG_OBJECT (jitterbuffer, "dropping, too far ahead"); GST_DEBUG_OBJECT (jitterbuffer, "too far ahead");
drop = TRUE; /* 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 %" GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
G_GUINT64_FORMAT, last_rtptime, 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-rtptime", G_TYPE_UINT64, base_rtptime,
"base-time", G_TYPE_UINT64, base_time, "base-time", G_TYPE_UINT64, base_time,
"clock-rate", G_TYPE_UINT, clock_rate, "clock-rate", G_TYPE_UINT, clock_rate,
"clock-base", G_TYPE_UINT64, clock_base,
"sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime, "sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
"sr-buffer", GST_TYPE_BUFFER, buffer, NULL); "sr-buffer", GST_TYPE_BUFFER, buffer, NULL);

View file

@ -835,6 +835,10 @@ rtcp_thread (GstRtpSession * rtpsession)
session = rtpsession->priv->session; 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) { while (!rtpsession->priv->stop_thread) {
GstClockReturn res; GstClockReturn res;

View file

@ -640,6 +640,26 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf,
} }
rtptime = gst_rtp_buffer_get_timestamp (&rtp); 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) { switch (jbuf->mode) {
case RTP_JITTER_BUFFER_MODE_NONE: case RTP_JITTER_BUFFER_MODE_NONE:
case RTP_JITTER_BUFFER_MODE_BUFFER: case RTP_JITTER_BUFFER_MODE_BUFFER:

View file

@ -543,6 +543,8 @@ rtp_session_init (RTPSession * sess)
sess->source->internal = TRUE; sess->source->internal = TRUE;
sess->stats.active_sources++; sess->stats.active_sources++;
INIT_AVG (sess->stats.avg_rtcp_packet_size, 100); 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, rtp_stats_set_min_interval (&sess->stats,
(gdouble) DEFAULT_RTCP_MIN_INTERVAL / GST_SECOND); (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: case PROP_RTCP_MIN_INTERVAL:
rtp_stats_set_min_interval (&sess->stats, rtp_stats_set_min_interval (&sess->stats,
(gdouble) g_value_get_uint64 (value) / GST_SECOND); (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; break;
case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD: case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD:
sess->rtcp_immediate_feedback_threshold = g_value_get_uint (value); 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 sendertimeout = FALSE;
gboolean is_sender, is_active; gboolean is_sender, is_active;
RTPSession *sess = data->sess; RTPSession *sess = data->sess;
GstClockTime interval; GstClockTime interval, binterval;
GstClockTime btime;
is_sender = RTP_SOURCE_IS_SENDER (source); is_sender = RTP_SOURCE_IS_SENDER (source);
is_active = RTP_SOURCE_IS_ACTIVE (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. */ /* check for our own source, we don't want to delete our own source. */
if (!(source == sess->source)) { if (!(source == sess->source)) {
if (source->received_bye) { 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 /* sources that were inactive for more than 5 times the deterministic reporting
* interval get timed out. the min timeout is 5 seconds. */ * interval get timed out. the min timeout is 5 seconds. */
if (data->current_time > source->last_activity) { /* mind old time that might pre-date last time going to PLAYING */
interval = MAX (data->interval * 5, 5 * GST_SECOND); btime = MAX (source->last_activity, sess->start_time);
if (data->current_time - source->last_activity > interval) { 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, 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; 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 /* senders that did not send for a long time become a receiver, this also
* holds for our own source. */ * holds for our own source. */
if (is_sender) { if (is_sender) {
if (data->current_time > source->last_rtp_activity) { /* mind old time that might pre-date last time going to PLAYING */
interval = MAX (data->interval * 2, 5 * GST_SECOND); btime = MAX (source->last_rtp_activity, sess->start_time);
if (data->current_time - source->last_rtp_activity > interval) { 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_DEBUG ("sender source %08x timed out and became receiver, last %"
GST_TIME_FORMAT, source->ssrc, GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime));
GST_TIME_ARGS (source->last_rtp_activity));
source->is_sender = FALSE; source->is_sender = FALSE;
sess->stats.sender_sources--; sess->stats.sender_sources--;
sendertimeout = TRUE; sendertimeout = TRUE;

View file

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

View file

@ -1368,6 +1368,9 @@ rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime,
/* make current */ /* make current */
src->stats.curr_sr = curridx; 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 LAST_SIGNAL
}; };
enum _GstRtspSrcRtcpSyncMode
{
RTCP_SYNC_ALWAYS,
RTCP_SYNC_INITIAL,
RTCP_SYNC_RTP
};
enum _GstRtspSrcBufferMode enum _GstRtspSrcBufferMode
{ {
BUFFER_MODE_NONE, BUFFER_MODE_NONE,
@ -1651,7 +1658,7 @@ cleanup:
} }
static void static void
gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush) gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush, gboolean playing)
{ {
GstEvent *event; GstEvent *event;
gint cmd, i; gint cmd, i;
@ -1667,9 +1674,12 @@ gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
state = GST_STATE_PAUSED; state = GST_STATE_PAUSED;
} else { } else {
event = gst_event_new_flush_stop (TRUE); 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; 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)); clock = gst_element_get_clock (GST_ELEMENT_CAST (src));
if (clock) { if (clock) {
base_time = gst_clock_get_time (clock); base_time = gst_clock_get_time (clock);
@ -1818,7 +1828,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
* blocking in preroll). */ * blocking in preroll). */
if (flush) { if (flush) {
GST_DEBUG_OBJECT (src, "starting flush"); GST_DEBUG_OBJECT (src, "starting flush");
gst_rtspsrc_flush (src, TRUE); gst_rtspsrc_flush (src, TRUE, FALSE);
} else { } else {
if (src->task) { if (src->task) {
gst_task_pause (src->task); gst_task_pause (src->task);
@ -1867,7 +1877,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
if (flush) { if (flush) {
/* if we started flush, we stop now */ /* if we started flush, we stop now */
GST_DEBUG_OBJECT (src, "stopping flush"); 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 */ /* 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 */ /* start new request */
gst_rtspsrc_loop_start_cmd (src, cmd); gst_rtspsrc_loop_start_cmd (src, cmd);
GST_DEBUG_OBJECT (src, "sending cmd %d", cmd);
GST_OBJECT_LOCK (src); GST_OBJECT_LOCK (src);
old = src->loop_cmd; old = src->loop_cmd;
if (old != CMD_WAIT) { if (old != CMD_WAIT) {
@ -5900,6 +5912,47 @@ gst_rtspsrc_parse_rtpinfo (GstRTSPSrc * src, gchar * rtpinfo)
return TRUE; 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 static gdouble
gst_rtspsrc_get_float (const gchar * dstr) 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) &hval, hval_idx++) == GST_RTSP_OK)
gst_rtspsrc_parse_rtpinfo (src, hval); 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); gst_rtsp_message_unset (&response);
/* early exit when we did aggregate control */ /* early exit when we did aggregate control */
@ -6401,10 +6460,6 @@ gst_rtspsrc_thread (GstRTSPSrc * src)
switch (cmd) { switch (cmd) {
case CMD_OPEN: 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); ret = gst_rtspsrc_open (src, TRUE);
break; break;
case CMD_PLAY: case CMD_PLAY:
@ -6524,6 +6579,11 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
goto start_failed; goto start_failed;
break; break;
case GST_STATE_CHANGE_READY_TO_PAUSED: 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); gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_OPEN, FALSE);
break; break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING: 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 *location, *location_end;
gchar *colptr; gchar *colptr;
/* consider no protocol to be udp:// */
protocol = gst_uri_get_protocol (uristr); protocol = gst_uri_get_protocol (uristr);
if (!protocol)
goto no_protocol;
if (strcmp (protocol, "udp") != 0) if (strcmp (protocol, "udp") != 0)
goto wrong_protocol; goto wrong_protocol;
g_free (protocol); g_free (protocol);
@ -425,6 +428,11 @@ gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri)
return 0; return 0;
/* ERRORS */ /* ERRORS */
no_protocol:
{
GST_ERROR ("error parsing uri %s: no protocol", uristr);
return -1;
}
wrong_protocol: wrong_protocol:
{ {
GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr, GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr,

View file

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

View file

@ -71,6 +71,8 @@ enum
PROP_ENDX, PROP_ENDX,
PROP_ENDY, PROP_ENDY,
PROP_REMOTE, PROP_REMOTE,
PROP_XID,
PROP_XNAME,
}; };
#define gst_ximage_src_parent_class parent_class #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 static gboolean
gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name) 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->width = s->xcontext->width;
s->height = s->xcontext->height; s->height = s->xcontext->height;
/* Always capture root window, for now */
s->xwindow = s->xcontext->root; 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 #ifdef HAVE_XFIXES
/* check if xfixes supported */ /* check if xfixes supported */
@ -603,7 +675,8 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc)
} else } else
#endif /* HAVE_XSHM */ #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) { if (ximagesrc->remote) {
XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
ximagesrc->startx, ximagesrc->starty, ximagesrc->width, ximagesrc->startx, ximagesrc->starty, ximagesrc->width,
@ -847,6 +920,21 @@ gst_ximage_src_set_property (GObject * object, guint prop_id,
case PROP_REMOTE: case PROP_REMOTE:
src->remote = g_value_get_boolean (value); src->remote = g_value_get_boolean (value);
break; 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: default:
break; break;
} }
@ -890,6 +978,12 @@ gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value,
case PROP_REMOTE: case PROP_REMOTE:
g_value_set_boolean (value, src->remote); g_value_set_boolean (value, src->remote);
break; break;
case PROP_XID:
g_value_set_uint64 (value, src->xid);
break;
case PROP_XNAME:
g_value_set_string (value, src->xname);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -928,6 +1022,7 @@ gst_ximage_src_finalize (GObject * object)
if (src->xcontext) if (src->xcontext)
ximageutil_xcontext_clear (src->xcontext); ximageutil_xcontext_clear (src->xcontext);
g_free (src->xname);
g_mutex_free (src->pool_lock); g_mutex_free (src->pool_lock);
g_mutex_free (src->x_lock); g_mutex_free (src->x_lock);
@ -952,9 +1047,16 @@ gst_ximage_src_get_caps (GstBaseSrc * bs, GstCaps * filter)
(s)->srcpad)); (s)->srcpad));
xcontext = s->xcontext; xcontext = s->xcontext;
width = s->xcontext->width;
width = xcontext->width; height = s->xcontext->height;
height = 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 /* property comments say 0 means right/bottom, means we can't capture
the top left pixel alone */ the top left pixel alone */
@ -1135,6 +1237,28 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass)
g_param_spec_boolean ("remote", "Remote dispay", g_param_spec_boolean ("remote", "Remote dispay",
"Whether the display is remote", FALSE, "Whether the display is remote", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 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", gst_element_class_set_details_simple (ec, "Ximage video source",
"Source/Video", "Source/Video",

View file

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

View file

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

View file

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

View file

@ -50,6 +50,7 @@ static void
mux_pcm_audio (guint num_buffers, guint repeat) mux_pcm_audio (guint num_buffers, guint repeat)
{ {
GstElement *src, *sink, *flvmux, *conv, *pipeline; GstElement *src, *sink, *flvmux, *conv, *pipeline;
GstPad *sinkpad, *srcpad;
gint counter; gint counter;
GST_LOG ("num_buffers = %u", num_buffers); 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 (src, conv));
fail_unless (gst_element_link (flvmux, sink)); 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 { do {
GstStateChangeReturn state_ret; GstStateChangeReturn state_ret;
GstMessage *msg; GstMessage *msg;
GstPad *sinkpad, *srcpad;
GST_LOG ("repeat=%d", repeat); 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; counter = 0;
state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED); state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);

View file

@ -43,6 +43,7 @@ static GstPad *mysrcpad, *mysinkpad;
"rate = (int) 48000" "rate = (int) 48000"
#define VIDEO_CAPS_STRING "video/mpeg, " \ #define VIDEO_CAPS_STRING "video/mpeg, " \
"mpegversion = (int) 4, " \ "mpegversion = (int) 4, " \
"systemstream = (boolean) false, " \
"width = (int) 384, " \ "width = (int) 384, " \
"height = (int) 288, " \ "height = (int) 288, " \
"framerate = (fraction) 25/1" "framerate = (fraction) 25/1"
@ -517,7 +518,7 @@ create_qtmux_profile (const gchar * variant)
return cprof; return cprof;
} }
GST_START_TEST (test_encodebin) GST_START_TEST (test_encodebin_qtmux)
{ {
GstEncodingContainerProfile *cprof; GstEncodingContainerProfile *cprof;
GstElement *enc; GstElement *enc;
@ -553,6 +554,136 @@ GST_START_TEST (test_encodebin)
GST_END_TEST; 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 * static Suite *
qtmux_suite (void) 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_audio_pad_frag_asc_streamable);
tcase_add_test (tc_chain, test_reuse); 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; return s;
} }