mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 09:10:36 +00:00
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:
commit
87fbd1e784
50 changed files with 2174 additions and 352 deletions
|
@ -13,33 +13,13 @@ FORMATS=html
|
|||
html: html-build.stamp
|
||||
include $(top_srcdir)/common/upload-doc.mak
|
||||
|
||||
# generated basefiles
|
||||
#basefiles = \
|
||||
## $(DOC_MODULE).types \
|
||||
# $(DOC_MODULE)-sections.txt \
|
||||
# $(DOC_MODULE)-docs.sgml
|
||||
|
||||
# ugly hack to make -unused.sgml work
|
||||
#unused-build.stamp:
|
||||
# BUILDDIR=`pwd` && \
|
||||
# cd $(srcdir)/tmpl && \
|
||||
# ln -sf gstreamer-libs-unused.sgml \
|
||||
# $$BUILDDIR/tmpl/gstreamer-libs-@GST_MAJORMINOR@-unused.sgml
|
||||
# touch unused-build.stamp
|
||||
|
||||
# these rules are added to create parallel docs using GST_MAJORMINOR
|
||||
#$(basefiles): gstreamer-libs-@GST_MAJORMINOR@%: gstreamer-libs%
|
||||
# cp $< $@
|
||||
|
||||
#CLEANFILES = $(basefiles)
|
||||
|
||||
# The top-level SGML file. Change it if you want.
|
||||
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
|
||||
|
||||
# The directory containing the source code. Relative to $(top_srcdir).
|
||||
# The directory containing the source code.
|
||||
# gtk-doc will search all .c & .h files beneath here for inline comments
|
||||
# documenting functions and macros.
|
||||
DOC_SOURCE_DIR = $(top_srcdir)
|
||||
DOC_SOURCE_DIR = $(top_srcdir)/gst $(top_srcdir)/ext $(top_srcdir)/sys
|
||||
|
||||
# Extra options to supply to gtkdoc-scan.
|
||||
SCAN_OPTIONS=
|
||||
|
@ -53,14 +33,11 @@ FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
|
|||
--extra-dir=$(GSTPB_PREFIX)/share/gtk-doc/html
|
||||
|
||||
# Used for dependencies.
|
||||
HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h
|
||||
CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c $(DOC_SOURCE_DIR)/*/*/*.cc $(DOC_SOURCE_DIR)/*/*/*.m
|
||||
|
||||
# this is a wingo addition
|
||||
# thomasvs: another nice wingo addition would be an explanation on why
|
||||
# this is useful ;)
|
||||
|
||||
SCANOBJ_DEPS =
|
||||
HFILE_GLOB= \
|
||||
$(top_srcdir)/gst/*/*.h $(top_srcdir)/ext/*/*.h $(top_srcdir)/sys/*/*.h
|
||||
CFILE_GLOB= \
|
||||
$(top_srcdir)/gst/*/*.c $(top_srcdir)/ext/*/*.c $(top_srcdir)/sys/*/*.c \
|
||||
$(top_srcdir)/ext/*/*.cc $(top_srcdir)/sys/*/*.m
|
||||
|
||||
# Header files to ignore when scanning.
|
||||
IGNORE_HFILES =
|
||||
|
@ -253,7 +230,7 @@ extra_files =
|
|||
# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib
|
||||
# contains GtkObjects/GObjects and you want to document signals and properties.
|
||||
GTKDOC_CFLAGS = $(GST_BASE_CFLAGS) -I$(top_builddir)
|
||||
GTKDOC_LIBS = $(SCANOBJ_DEPS) $(GST_BASE_LIBS)
|
||||
GTKDOC_LIBS = $(GST_BASE_LIBS)
|
||||
|
||||
GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC)
|
||||
GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC)
|
||||
|
|
|
@ -7,12 +7,14 @@ libgstpulse_la_SOURCES = \
|
|||
pulsemixertrack.c \
|
||||
pulseprobe.c \
|
||||
pulsesink.c \
|
||||
pulseaudiosink.c \
|
||||
pulsesrc.c \
|
||||
pulseutil.c
|
||||
|
||||
libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS)
|
||||
libgstpulse_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \
|
||||
-lgstinterfaces-$(GST_MAJORMINOR) $(GST_BASE_LIBS) $(GST_LIBS) $(PULSE_LIBS)
|
||||
-lgstinterfaces-$(GST_MAJORMINOR) -lgstpbutils-$(GST_MAJORMINOR) \
|
||||
$(GST_BASE_LIBS) $(GST_LIBS) $(PULSE_LIBS)
|
||||
libgstpulse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
libgstpulse_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
|
||||
|
|
|
@ -49,6 +49,12 @@ plugin_init (GstPlugin * plugin)
|
|||
GST_TYPE_PULSESRC))
|
||||
return FALSE;
|
||||
|
||||
#ifdef HAVE_PULSE_1_0
|
||||
if (!gst_element_register (plugin, "pulseaudiosink", GST_RANK_PRIMARY + 11,
|
||||
GST_TYPE_PULSE_AUDIO_SINK))
|
||||
return FALSE;
|
||||
#endif
|
||||
|
||||
if (!gst_element_register (plugin, "pulsemixer", GST_RANK_NONE,
|
||||
GST_TYPE_PULSEMIXER))
|
||||
return FALSE;
|
||||
|
|
927
ext/pulse/pulseaudiosink.c
Normal file
927
ext/pulse/pulseaudiosink.c
Normal 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 */
|
|
@ -420,6 +420,27 @@ gst_pulsering_context_subscribe_cb (pa_context * c,
|
|||
if (idx != pa_stream_get_index (pbuf->stream))
|
||||
continue;
|
||||
|
||||
#ifdef HAVE_PULSE_1_0
|
||||
if (psink->device && pa_format_info_is_pcm (pbuf->format) &&
|
||||
!g_str_equal (psink->device,
|
||||
pa_stream_get_device_name (pbuf->stream))) {
|
||||
/* Underlying sink changed. And this is not a passthrough stream. Let's
|
||||
* see if someone upstream wants to try to renegotiate. */
|
||||
GstEvent *renego;
|
||||
|
||||
g_free (psink->device);
|
||||
psink->device = g_strdup (pa_stream_get_device_name (pbuf->stream));
|
||||
|
||||
GST_INFO_OBJECT (psink, "emitting sink-changed");
|
||||
|
||||
renego = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
|
||||
gst_structure_new ("pulse-sink-changed", NULL));
|
||||
|
||||
if (!gst_pad_push_event (GST_BASE_SINK (psink)->sinkpad, renego))
|
||||
GST_DEBUG_OBJECT (psink, "Emitted sink-changed - nobody was listening");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Actually this event is also triggered when other properties of
|
||||
* the stream change that are unrelated to the volume. However it is
|
||||
* probably cheaper to signal the change here and check for the
|
||||
|
@ -1716,35 +1737,10 @@ static gboolean gst_pulsesink_event (GstBaseSink * sink, GstEvent * event);
|
|||
static GstStateChangeReturn gst_pulsesink_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
||||
# define FORMATS "{ S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, " \
|
||||
"S24LE, S24BE, S24_32LE, S24_32BE, S8 }"
|
||||
#else
|
||||
# define FORMATS "{ S16BE, S16LE, F32BE, F32LE, S32BE, S32LE, " \
|
||||
"S24BE, S24LE, S24_32BE, S24_32LE, S8 }"
|
||||
#endif
|
||||
|
||||
static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/x-raw, "
|
||||
"format = (string) " FORMATS ", "
|
||||
"rate = (int) [ 1, MAX ], "
|
||||
"channels = (int) [ 1, 32 ];"
|
||||
"audio/x-alaw, "
|
||||
"rate = (int) [ 1, MAX], "
|
||||
"channels = (int) [ 1, 32 ];"
|
||||
"audio/x-mulaw, "
|
||||
"rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ];"
|
||||
#ifdef HAVE_PULSE_1_0
|
||||
"audio/x-ac3, framed = (boolean) true;"
|
||||
"audio/x-eac3, framed = (boolean) true; "
|
||||
"audio/x-dts, framed = (boolean) true, "
|
||||
" block_size = (int) { 512, 1024, 2048 }; "
|
||||
"audio/mpeg, mpegversion = (int)1, "
|
||||
" mpegaudioversion = (int) [ 1, 2 ], parsed = (boolean) true; "
|
||||
#endif
|
||||
));
|
||||
GST_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS));
|
||||
|
||||
GST_IMPLEMENT_PULSEPROBE_METHODS (GstPulseSink, gst_pulsesink);
|
||||
|
||||
|
@ -2002,6 +1998,8 @@ done:
|
|||
}
|
||||
|
||||
#ifdef HAVE_PULSE_1_0
|
||||
/* NOTE: If you're making changes here, see if pulseaudiosink acceptcaps also
|
||||
* needs to be changed accordingly. */
|
||||
static gboolean
|
||||
gst_pulsesink_pad_acceptcaps (GstPad * pad, GstCaps * caps)
|
||||
{
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#ifndef __GST_PULSESINK_H__
|
||||
#define __GST_PULSESINK_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/gstaudiosink.h>
|
||||
|
||||
|
@ -88,6 +92,60 @@ struct _GstPulseSinkClass
|
|||
|
||||
GType gst_pulsesink_get_type (void);
|
||||
|
||||
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
||||
# define FORMATS "{ S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, " \
|
||||
"S24LE, S24BE, S24_32LE, S24_32BE, S8 }"
|
||||
#else
|
||||
# define FORMATS "{ S16BE, S16LE, F32BE, F32LE, S32BE, S32LE, " \
|
||||
"S24BE, S24LE, S24_32BE, S24_32LE, S8 }"
|
||||
#endif
|
||||
|
||||
#define _PULSE_SINK_CAPS_COMMON \
|
||||
"audio/x-raw, " \
|
||||
"format = (string) " FORMATS ", " \
|
||||
"rate = (int) [ 1, MAX ], " \
|
||||
"channels = (int) [ 1, 32 ];" \
|
||||
"audio/x-alaw, " \
|
||||
"rate = (int) [ 1, MAX], " \
|
||||
"channels = (int) [ 1, 32 ];" \
|
||||
"audio/x-mulaw, " \
|
||||
"rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ];"
|
||||
|
||||
#ifdef HAVE_PULSE_1_0
|
||||
#define _PULSE_SINK_CAPS_1_0 \
|
||||
"audio/x-ac3, framed = (boolean) true;" \
|
||||
"audio/x-eac3, framed = (boolean) true; " \
|
||||
"audio/x-dts, framed = (boolean) true, " \
|
||||
"block-size = (int) { 512, 1024, 2048 }; " \
|
||||
"audio/mpeg, mpegversion = (int) 1, " \
|
||||
"mpegaudioversion = (int) [ 1, 2 ], parsed = (boolean) true;"
|
||||
#else
|
||||
#define _PULSE_SINK_CAPS_1_0 ""
|
||||
#endif
|
||||
|
||||
#define PULSE_SINK_TEMPLATE_CAPS \
|
||||
_PULSE_SINK_CAPS_COMMON \
|
||||
_PULSE_SINK_CAPS_1_0
|
||||
|
||||
#ifdef HAVE_PULSE_1_0
|
||||
|
||||
#define GST_TYPE_PULSE_AUDIO_SINK \
|
||||
(gst_pulse_audio_sink_get_type())
|
||||
#define GST_PULSE_AUDIO_SINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSink))
|
||||
#define GST_PULSE_AUDIO_SINK_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSinkClass))
|
||||
#define GST_IS_PULSE_AUDIO_SINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PULSE_AUDIO_SINK))
|
||||
#define GST_IS_PULSE_AUDIO_SINK_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PULSE_AUDIO_SINK))
|
||||
#define GST_PULSE_AUDIO_SINK_CAST(obj) \
|
||||
((GstPulseAudioSink *)(obj))
|
||||
|
||||
GType gst_pulse_audio_sink_get_type (void);
|
||||
|
||||
#endif /* HAVE_PULSE_1_0 */
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_PULSESINK_H__ */
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
#ifndef __GST_PULSEUTIL_H__
|
||||
#define __GST_PULSEUTIL_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <gst/audio/gstaudiosink.h>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
plugin_LTLIBRARIES = libgstsouphttpsrc.la
|
||||
|
||||
libgstsouphttpsrc_la_SOURCES = gstsouphttpsrc.c gstsouphttpsink.c gstsoup.c
|
||||
libgstsouphttpsrc_la_SOURCES = gstsouphttpsrc.c gstsouphttpclientsink.c gstsoup.c
|
||||
|
||||
libgstsouphttpsrc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(SOUP_CFLAGS)
|
||||
libgstsouphttpsrc_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(SOUP_LIBS)
|
||||
libgstsouphttpsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
libgstsouphttpsrc_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
|
||||
noinst_HEADERS = gstsouphttpsrc.h gstsouphttpsink.h
|
||||
noinst_HEADERS = gstsouphttpsrc.h gstsouphttpclientsink.h
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <gst/gst-i18n-plugin.h>
|
||||
|
||||
#include "gstsouphttpsrc.h"
|
||||
#include "gstsouphttpsink.h"
|
||||
#include "gstsouphttpclientsink.h"
|
||||
|
||||
|
||||
static gboolean
|
||||
|
@ -34,8 +34,8 @@ plugin_init (GstPlugin * plugin)
|
|||
|
||||
gst_element_register (plugin, "souphttpsrc", GST_RANK_PRIMARY,
|
||||
GST_TYPE_SOUP_HTTP_SRC);
|
||||
gst_element_register (plugin, "souphttpsink", GST_RANK_NONE,
|
||||
GST_TYPE_SOUP_HTTP_SINK);
|
||||
gst_element_register (plugin, "souphttpclientsink", GST_RANK_NONE,
|
||||
GST_TYPE_SOUP_HTTP_CLIENT_SINK);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -17,16 +17,16 @@
|
|||
* Boston, MA 02110-1335, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:element-gstsouphttpsink
|
||||
* SECTION:element-gstsouphttpclientsink
|
||||
*
|
||||
* The souphttpsink element sends pipeline data to an HTTP server
|
||||
* The souphttpclientsink element sends pipeline data to an HTTP server
|
||||
* using HTTP PUT commands.
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* |[
|
||||
* gst-launch -v videotestsrc num-buffers=300 ! theoraenc ! oggmux !
|
||||
* souphttpsink location=http://server/filename.ogv
|
||||
* souphttpclientsink location=http://server/filename.ogv
|
||||
* ]|
|
||||
*
|
||||
* This example encodes 10 seconds of video and sends it to the HTTP
|
||||
|
@ -40,43 +40,44 @@
|
|||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasesink.h>
|
||||
#include "gstsouphttpsink.h"
|
||||
#include "gstsouphttpclientsink.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_soup_http_sink_debug_category);
|
||||
#define GST_CAT_DEFAULT gst_soup_http_sink_debug_category
|
||||
GST_DEBUG_CATEGORY_STATIC (souphttpclientsink_dbg);
|
||||
#define GST_CAT_DEFAULT souphttpclientsink_dbg
|
||||
|
||||
/* prototypes */
|
||||
|
||||
|
||||
static void gst_soup_http_sink_set_property (GObject * object,
|
||||
static void gst_soup_http_client_sink_set_property (GObject * object,
|
||||
guint property_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_soup_http_sink_get_property (GObject * object,
|
||||
static void gst_soup_http_client_sink_get_property (GObject * object,
|
||||
guint property_id, GValue * value, GParamSpec * pspec);
|
||||
static void gst_soup_http_sink_dispose (GObject * object);
|
||||
static void gst_soup_http_sink_finalize (GObject * object);
|
||||
static void gst_soup_http_client_sink_dispose (GObject * object);
|
||||
static void gst_soup_http_client_sink_finalize (GObject * object);
|
||||
|
||||
static gboolean gst_soup_http_sink_set_caps (GstBaseSink * sink,
|
||||
static gboolean gst_soup_http_client_sink_set_caps (GstBaseSink * sink,
|
||||
GstCaps * caps);
|
||||
static void gst_soup_http_sink_get_times (GstBaseSink * sink,
|
||||
static void gst_soup_http_client_sink_get_times (GstBaseSink * sink,
|
||||
GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
|
||||
static gboolean gst_soup_http_sink_start (GstBaseSink * sink);
|
||||
static gboolean gst_soup_http_sink_stop (GstBaseSink * sink);
|
||||
static gboolean gst_soup_http_sink_unlock (GstBaseSink * sink);
|
||||
static gboolean gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event);
|
||||
static GstFlowReturn
|
||||
gst_soup_http_sink_preroll (GstBaseSink * sink, GstBuffer * buffer);
|
||||
static GstFlowReturn
|
||||
gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer);
|
||||
static gboolean gst_soup_http_client_sink_start (GstBaseSink * sink);
|
||||
static gboolean gst_soup_http_client_sink_stop (GstBaseSink * sink);
|
||||
static gboolean gst_soup_http_client_sink_unlock (GstBaseSink * sink);
|
||||
static gboolean gst_soup_http_client_sink_event (GstBaseSink * sink,
|
||||
GstEvent * event);
|
||||
static GstFlowReturn gst_soup_http_client_sink_preroll (GstBaseSink * sink,
|
||||
GstBuffer * buffer);
|
||||
static GstFlowReturn gst_soup_http_client_sink_render (GstBaseSink * sink,
|
||||
GstBuffer * buffer);
|
||||
|
||||
static void free_buffer_list (GList * list);
|
||||
static void gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink);
|
||||
static void gst_soup_http_client_sink_reset (GstSoupHttpClientSink *
|
||||
souphttpsink);
|
||||
static void authenticate (SoupSession * session, SoupMessage * msg,
|
||||
SoupAuth * auth, gboolean retrying, gpointer user_data);
|
||||
static void
|
||||
callback (SoupSession * session, SoupMessage * msg, gpointer user_data);
|
||||
static gboolean
|
||||
gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink,
|
||||
const gchar * uri);
|
||||
static void callback (SoupSession * session, SoupMessage * msg,
|
||||
gpointer user_data);
|
||||
static gboolean gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink *
|
||||
souphttpsink, const gchar * uri);
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -93,11 +94,11 @@ enum
|
|||
PROP_SESSION
|
||||
};
|
||||
|
||||
#define DEFAULT_USER_AGENT "GStreamer souphttpsink "
|
||||
#define DEFAULT_USER_AGENT "GStreamer souphttpclientsink "
|
||||
|
||||
/* pad templates */
|
||||
|
||||
static GstStaticPadTemplate gst_soup_http_sink_sink_template =
|
||||
static GstStaticPadTemplate gst_soup_http_client_sink_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
|
@ -106,20 +107,21 @@ GST_STATIC_PAD_TEMPLATE ("sink",
|
|||
|
||||
/* class initialization */
|
||||
|
||||
#define gst_soup_http_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstSoupHttpSink, gst_soup_http_sink, GST_TYPE_BASE_SINK);
|
||||
#define gst_soup_http_client_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstSoupHttpClientSink, gst_soup_http_client_sink,
|
||||
GST_TYPE_BASE_SINK);
|
||||
|
||||
static void
|
||||
gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass)
|
||||
gst_soup_http_client_sink_class_init (GstSoupHttpClientSinkClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
||||
GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = gst_soup_http_sink_set_property;
|
||||
gobject_class->get_property = gst_soup_http_sink_get_property;
|
||||
gobject_class->dispose = gst_soup_http_sink_dispose;
|
||||
gobject_class->finalize = gst_soup_http_sink_finalize;
|
||||
gobject_class->set_property = gst_soup_http_client_sink_set_property;
|
||||
gobject_class->get_property = gst_soup_http_client_sink_get_property;
|
||||
gobject_class->dispose = gst_soup_http_client_sink_dispose;
|
||||
gobject_class->finalize = gst_soup_http_client_sink_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_LOCATION,
|
||||
|
@ -166,37 +168,38 @@ gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass)
|
|||
G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_soup_http_sink_sink_template));
|
||||
gst_static_pad_template_get (&gst_soup_http_client_sink_sink_template));
|
||||
|
||||
gst_element_class_set_details_simple (gstelement_class, "HTTP client sink",
|
||||
"Generic", "Sends streams to HTTP server via PUT",
|
||||
"David Schleef <ds@entropywave.com>");
|
||||
|
||||
base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_soup_http_sink_set_caps);
|
||||
base_sink_class->set_caps =
|
||||
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_set_caps);
|
||||
if (0)
|
||||
base_sink_class->get_times =
|
||||
GST_DEBUG_FUNCPTR (gst_soup_http_sink_get_times);
|
||||
base_sink_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_sink_start);
|
||||
base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_sink_stop);
|
||||
base_sink_class->unlock = GST_DEBUG_FUNCPTR (gst_soup_http_sink_unlock);
|
||||
base_sink_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_sink_event);
|
||||
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_get_times);
|
||||
base_sink_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_start);
|
||||
base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_stop);
|
||||
base_sink_class->unlock =
|
||||
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_unlock);
|
||||
base_sink_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_event);
|
||||
if (0)
|
||||
base_sink_class->preroll = GST_DEBUG_FUNCPTR (gst_soup_http_sink_preroll);
|
||||
base_sink_class->render = GST_DEBUG_FUNCPTR (gst_soup_http_sink_render);
|
||||
base_sink_class->preroll =
|
||||
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_preroll);
|
||||
base_sink_class->render =
|
||||
GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_render);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (souphttpclientsink_dbg, "souphttpclientsink", 0,
|
||||
"souphttpclientsink element");
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_soup_http_sink_debug_category, "souphttpsink", 0,
|
||||
"debug category for souphttpsink element");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink)
|
||||
gst_soup_http_client_sink_init (GstSoupHttpClientSink * souphttpsink)
|
||||
{
|
||||
const char *proxy;
|
||||
|
||||
souphttpsink->sinkpad =
|
||||
gst_pad_new_from_static_template (&gst_soup_http_sink_sink_template,
|
||||
"sink");
|
||||
|
||||
souphttpsink->mutex = g_mutex_new ();
|
||||
souphttpsink->cond = g_cond_new ();
|
||||
|
||||
|
@ -210,17 +213,17 @@ gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink)
|
|||
souphttpsink->prop_session = NULL;
|
||||
souphttpsink->timeout = 1;
|
||||
proxy = g_getenv ("http_proxy");
|
||||
if (proxy && !gst_soup_http_sink_set_proxy (souphttpsink, proxy)) {
|
||||
if (proxy && !gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) {
|
||||
GST_WARNING_OBJECT (souphttpsink,
|
||||
"The proxy in the http_proxy env var (\"%s\") cannot be parsed.",
|
||||
proxy);
|
||||
}
|
||||
|
||||
gst_soup_http_sink_reset (souphttpsink);
|
||||
gst_soup_http_client_sink_reset (souphttpsink);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink)
|
||||
gst_soup_http_client_sink_reset (GstSoupHttpClientSink * souphttpsink)
|
||||
{
|
||||
g_free (souphttpsink->reason_phrase);
|
||||
souphttpsink->reason_phrase = NULL;
|
||||
|
@ -230,7 +233,8 @@ gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, const gchar * uri)
|
||||
gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink * souphttpsink,
|
||||
const gchar * uri)
|
||||
{
|
||||
if (souphttpsink->proxy) {
|
||||
soup_uri_free (souphttpsink->proxy);
|
||||
|
@ -249,10 +253,10 @@ gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, const gchar * uri)
|
|||
}
|
||||
|
||||
void
|
||||
gst_soup_http_sink_set_property (GObject * object, guint property_id,
|
||||
gst_soup_http_client_sink_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
|
||||
|
||||
g_mutex_lock (souphttpsink->mutex);
|
||||
switch (property_id) {
|
||||
|
@ -300,7 +304,7 @@ gst_soup_http_sink_set_property (GObject * object, guint property_id,
|
|||
GST_WARNING ("proxy property cannot be NULL");
|
||||
goto done;
|
||||
}
|
||||
if (!gst_soup_http_sink_set_proxy (souphttpsink, proxy)) {
|
||||
if (!gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) {
|
||||
GST_WARNING ("badly formatted proxy URI");
|
||||
goto done;
|
||||
}
|
||||
|
@ -319,10 +323,10 @@ done:
|
|||
}
|
||||
|
||||
void
|
||||
gst_soup_http_sink_get_property (GObject * object, guint property_id,
|
||||
gst_soup_http_client_sink_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_SESSION:
|
||||
|
@ -369,9 +373,9 @@ gst_soup_http_sink_get_property (GObject * object, guint property_id,
|
|||
}
|
||||
|
||||
void
|
||||
gst_soup_http_sink_dispose (GObject * object)
|
||||
gst_soup_http_client_sink_dispose (GObject * object)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
|
||||
|
||||
/* clean up as possible. may be called multiple times */
|
||||
if (souphttpsink->prop_session)
|
||||
|
@ -382,9 +386,9 @@ gst_soup_http_sink_dispose (GObject * object)
|
|||
}
|
||||
|
||||
void
|
||||
gst_soup_http_sink_finalize (GObject * object)
|
||||
gst_soup_http_client_sink_finalize (GObject * object)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
|
||||
|
||||
/* clean up object here */
|
||||
|
||||
|
@ -406,9 +410,9 @@ gst_soup_http_sink_finalize (GObject * object)
|
|||
|
||||
|
||||
static gboolean
|
||||
gst_soup_http_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
||||
gst_soup_http_client_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
|
||||
GstStructure *structure;
|
||||
const GValue *value_array;
|
||||
int i, n;
|
||||
|
@ -435,7 +439,7 @@ gst_soup_http_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
|||
}
|
||||
|
||||
static void
|
||||
gst_soup_http_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
|
||||
gst_soup_http_client_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
|
||||
GstClockTime * start, GstClockTime * end)
|
||||
{
|
||||
|
||||
|
@ -444,7 +448,7 @@ gst_soup_http_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
|
|||
static gpointer
|
||||
thread_func (gpointer ptr)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (ptr);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (ptr);
|
||||
|
||||
GST_DEBUG ("thread start");
|
||||
|
||||
|
@ -457,9 +461,9 @@ thread_func (gpointer ptr)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
gst_soup_http_sink_start (GstBaseSink * sink)
|
||||
gst_soup_http_client_sink_start (GstBaseSink * sink)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
|
||||
|
||||
if (souphttpsink->prop_session) {
|
||||
souphttpsink->session = souphttpsink->prop_session;
|
||||
|
@ -488,9 +492,9 @@ gst_soup_http_sink_start (GstBaseSink * sink)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
gst_soup_http_sink_stop (GstBaseSink * sink)
|
||||
gst_soup_http_client_sink_stop (GstBaseSink * sink)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
|
||||
|
||||
GST_DEBUG ("stop");
|
||||
|
||||
|
@ -510,13 +514,13 @@ gst_soup_http_sink_stop (GstBaseSink * sink)
|
|||
souphttpsink->context = NULL;
|
||||
}
|
||||
|
||||
gst_soup_http_sink_reset (souphttpsink);
|
||||
gst_soup_http_client_sink_reset (souphttpsink);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_soup_http_sink_unlock (GstBaseSink * sink)
|
||||
gst_soup_http_client_sink_unlock (GstBaseSink * sink)
|
||||
{
|
||||
GST_DEBUG ("unlock");
|
||||
|
||||
|
@ -524,9 +528,9 @@ gst_soup_http_sink_unlock (GstBaseSink * sink)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event)
|
||||
gst_soup_http_client_sink_event (GstBaseSink * sink, GstEvent * event)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
|
||||
|
||||
GST_DEBUG_OBJECT (souphttpsink, "event");
|
||||
|
||||
|
@ -545,7 +549,7 @@ gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event)
|
|||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_soup_http_sink_preroll (GstBaseSink * sink, GstBuffer * buffer)
|
||||
gst_soup_http_client_sink_preroll (GstBaseSink * sink, GstBuffer * buffer)
|
||||
{
|
||||
GST_DEBUG ("preroll");
|
||||
|
||||
|
@ -564,7 +568,7 @@ free_buffer_list (GList * list)
|
|||
}
|
||||
|
||||
static void
|
||||
send_message_locked (GstSoupHttpSink * souphttpsink)
|
||||
send_message_locked (GstSoupHttpClientSink * souphttpsink)
|
||||
{
|
||||
GList *g;
|
||||
guint64 n;
|
||||
|
@ -643,7 +647,7 @@ send_message_locked (GstSoupHttpSink * souphttpsink)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
send_message (GstSoupHttpSink * souphttpsink)
|
||||
send_message (GstSoupHttpClientSink * souphttpsink)
|
||||
{
|
||||
g_mutex_lock (souphttpsink->mutex);
|
||||
send_message_locked (souphttpsink);
|
||||
|
@ -655,7 +659,7 @@ send_message (GstSoupHttpSink * souphttpsink)
|
|||
static void
|
||||
callback (SoupSession * session, SoupMessage * msg, gpointer user_data)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (user_data);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data);
|
||||
|
||||
GST_DEBUG_OBJECT (souphttpsink, "callback status=%d %s",
|
||||
msg->status_code, msg->reason_phrase);
|
||||
|
@ -679,9 +683,9 @@ callback (SoupSession * session, SoupMessage * msg, gpointer user_data)
|
|||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer)
|
||||
gst_soup_http_client_sink_render (GstBaseSink * sink, GstBuffer * buffer)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
|
||||
GSource *source;
|
||||
gboolean wake;
|
||||
|
||||
|
@ -717,7 +721,7 @@ static void
|
|||
authenticate (SoupSession * session, SoupMessage * msg,
|
||||
SoupAuth * auth, gboolean retrying, gpointer user_data)
|
||||
{
|
||||
GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (user_data);
|
||||
GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data);
|
||||
|
||||
if (!retrying) {
|
||||
if (souphttpsink->user_id && souphttpsink->user_pw) {
|
|
@ -1,5 +1,5 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2011 FIXME <fixme@example.com>
|
||||
* Copyright (C) 2011 David Schleef <ds@entropywave.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -17,29 +17,27 @@
|
|||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _GST_SOUP_HTTP_SINK_H_
|
||||
#define _GST_SOUP_HTTP_SINK_H_
|
||||
#ifndef _GST_SOUP_HTTP_CLIENT_SINK_H_
|
||||
#define _GST_SOUP_HTTP_CLIENT_SINK_H_
|
||||
|
||||
#include <gst/base/gstbasesink.h>
|
||||
#include <libsoup/soup.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_SOUP_HTTP_SINK (gst_soup_http_sink_get_type())
|
||||
#define GST_SOUP_HTTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SOUP_HTTP_SINK,GstSoupHttpSink))
|
||||
#define GST_SOUP_HTTP_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SOUP_HTTP_SINK,GstSoupHttpSinkClass))
|
||||
#define GST_IS_SOUP_HTTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_SINK))
|
||||
#define GST_IS_SOUP_HTTP_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_SINK))
|
||||
#define GST_TYPE_SOUP_HTTP_CLIENT_SINK (gst_soup_http_client_sink_get_type())
|
||||
#define GST_SOUP_HTTP_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SOUP_HTTP_CLIENT_SINK,GstSoupHttpClientSink))
|
||||
#define GST_SOUP_HTTP_CLIENT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SOUP_HTTP_CLIENT_SINK,GstSoupHttpClientSinkClass))
|
||||
#define GST_IS_SOUP_HTTP_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_CLIENT_SINK))
|
||||
#define GST_IS_SOUP_HTTP_CLIENT_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_CLIENT_SINK))
|
||||
|
||||
typedef struct _GstSoupHttpSink GstSoupHttpSink;
|
||||
typedef struct _GstSoupHttpSinkClass GstSoupHttpSinkClass;
|
||||
typedef struct _GstSoupHttpClientSink GstSoupHttpClientSink;
|
||||
typedef struct _GstSoupHttpClientSinkClass GstSoupHttpClientSinkClass;
|
||||
|
||||
struct _GstSoupHttpSink
|
||||
struct _GstSoupHttpClientSink
|
||||
{
|
||||
GstBaseSink base_souphttpsink;
|
||||
|
||||
GstPad *sinkpad;
|
||||
|
||||
GMutex *mutex;
|
||||
GCond *cond;
|
||||
GMainContext *context;
|
||||
|
@ -71,12 +69,12 @@ struct _GstSoupHttpSink
|
|||
|
||||
};
|
||||
|
||||
struct _GstSoupHttpSinkClass
|
||||
struct _GstSoupHttpClientSinkClass
|
||||
{
|
||||
GstBaseSinkClass base_souphttpsink_class;
|
||||
};
|
||||
|
||||
GType gst_soup_http_sink_get_type (void);
|
||||
GType gst_soup_http_client_sink_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
@ -720,6 +720,9 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
|
|||
soup_message_headers_foreach (msg->response_headers,
|
||||
gst_soup_http_src_headers_foreach, src);
|
||||
|
||||
if (msg->status_code == 407 && src->proxy_id && src->proxy_pw)
|
||||
return;
|
||||
|
||||
if (src->automatic_redirect && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
|
||||
GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code,
|
||||
soup_message_headers_get (msg->response_headers, "Location"));
|
||||
|
|
|
@ -1034,6 +1034,7 @@ gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
|
|||
/* Check if we have a continous stream, if not drop some samples or the buffer or
|
||||
* insert some silence samples */
|
||||
if (enc->next_ts != GST_CLOCK_TIME_NONE &&
|
||||
GST_BUFFER_TIMESTAMP_IS_VALID (buf) &&
|
||||
GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) {
|
||||
guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf);
|
||||
guint64 diff_bytes;
|
||||
|
|
|
@ -57,8 +57,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/mpeg, "
|
||||
"framed = (boolean) false, " "mpegversion = (int) { 2, 4 };"));
|
||||
GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) { 2, 4 };"));
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (aacparse_debug);
|
||||
#define GST_CAT_DEFAULT aacparse_debug
|
||||
|
@ -252,10 +251,12 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
|
|||
aacparse->channels = (data[1] & 0x78) >> 3;
|
||||
aacparse->header_type = DSPAAC_HEADER_NONE;
|
||||
aacparse->mpegversion = 4;
|
||||
aacparse->frame_samples = (data[1] & 4) ? 960 : 1024;
|
||||
gst_buffer_unmap (buf, data, size);
|
||||
|
||||
GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d",
|
||||
aacparse->object_type, aacparse->sample_rate, aacparse->channels);
|
||||
GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, "
|
||||
"samples=%d", aacparse->object_type, aacparse->sample_rate,
|
||||
aacparse->channels, aacparse->frame_samples);
|
||||
|
||||
/* arrange for metadata and get out of the way */
|
||||
gst_aac_parse_set_src_caps (aacparse, caps);
|
||||
|
@ -451,7 +452,8 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse,
|
|||
gst_aac_parse_parse_adts_header (aacparse, data, &rate, &channels,
|
||||
&aacparse->object_type, &aacparse->mpegversion);
|
||||
|
||||
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate, 1024, 2, 2);
|
||||
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate,
|
||||
aacparse->frame_samples, 2, 2);
|
||||
|
||||
GST_DEBUG ("ADTS: samplerate %d, channels %d, objtype %d, version %d",
|
||||
rate, channels, aacparse->object_type, aacparse->mpegversion);
|
||||
|
@ -683,7 +685,7 @@ gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
|
|||
gst_caps_unref (sinkcaps);
|
||||
|
||||
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse),
|
||||
aacparse->sample_rate, 1024, 2, 2);
|
||||
aacparse->sample_rate, aacparse->frame_samples, 2, 2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -705,6 +707,7 @@ gst_aac_parse_start (GstBaseParse * parse)
|
|||
|
||||
aacparse = GST_AAC_PARSE (parse);
|
||||
GST_DEBUG ("start");
|
||||
aacparse->frame_samples = 1024;
|
||||
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), ADTS_MAX_SIZE);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -63,19 +63,6 @@ typedef struct _GstAacParseClass GstAacParseClass;
|
|||
|
||||
/**
|
||||
* GstAacParse:
|
||||
* @element: the parent element.
|
||||
* @object_type: AAC object type of the stream.
|
||||
* @bitrate: Current media bitrate.
|
||||
* @sample_rate: Current media samplerate.
|
||||
* @channels: Current media channel count.
|
||||
* @frames_per_sec: FPS value of the current stream.
|
||||
* @header_type: #GstAacHeaderType indicating the current stream type.
|
||||
* @framecount: The amount of frames that has been processed this far.
|
||||
* @bytecount: The amount of bytes that has been processed this far.
|
||||
* @sync: Tells whether the parser is in sync (a.k.a. not searching for header)
|
||||
* @eos: End-of-Stream indicator. Set when EOS event arrives.
|
||||
* @duration: Duration of the current stream.
|
||||
* @ts: Current stream timestamp.
|
||||
*
|
||||
* The opaque GstAacParse data structure.
|
||||
*/
|
||||
|
@ -88,6 +75,7 @@ struct _GstAacParse {
|
|||
gint sample_rate;
|
||||
gint channels;
|
||||
gint mpegversion;
|
||||
gint frame_samples;
|
||||
|
||||
GstAacHeaderType header_type;
|
||||
};
|
||||
|
|
|
@ -144,16 +144,16 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) true, "
|
||||
" channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ]; "
|
||||
" channels = (int) [ 1, 6 ], rate = (int) [ 8000, 48000 ], "
|
||||
" alignment = (string) { iec61937, frame}; "
|
||||
"audio/x-eac3, framed = (boolean) true, "
|
||||
" channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ] "));
|
||||
" channels = (int) [ 1, 6 ], rate = (int) [ 8000, 48000 ], "
|
||||
" alignment = (string) { iec61937, frame}; "));
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) false; "
|
||||
"audio/x-eac3, framed = (boolean) false; "
|
||||
"audio/ac3, framed = (boolean) false "));
|
||||
GST_STATIC_CAPS ("audio/x-ac3; " "audio/x-eac3; " "audio/ac3"));
|
||||
|
||||
static void gst_ac3_parse_finalize (GObject * object);
|
||||
|
||||
|
@ -187,7 +187,7 @@ gst_ac3_parse_class_init (GstAc3ParseClass * klass)
|
|||
gst_static_pad_template_get (&src_template));
|
||||
|
||||
gst_element_class_set_details_simple (element_class,
|
||||
"AC3 audio stream parser", "Codec/Parser/Audio",
|
||||
"AC3 audio stream parser", "Codec/Parser/Converter/Audio",
|
||||
"AC3 parser", "Tim-Philipp Müller <tim centricular net>");
|
||||
|
||||
parse_class->start = GST_DEBUG_FUNCPTR (gst_ac3_parse_start);
|
||||
|
@ -298,7 +298,7 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
|
|||
GstBitReader bits;
|
||||
gpointer data;
|
||||
gsize size;
|
||||
guint8 fscod, frmsizcod, bsid, acmod, lfe_on;
|
||||
guint8 fscod, frmsizcod, bsid, acmod, lfe_on, rate_scale;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
GST_LOG_OBJECT (ac3parse, "parsing ac3");
|
||||
|
@ -338,10 +338,14 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
|
|||
|
||||
lfe_on = gst_bit_reader_get_bits_uint8_unchecked (&bits, 1);
|
||||
|
||||
/* 6/8->0, 9->1, 10->2,
|
||||
see http://matroska.org/technical/specs/codecid/index.html */
|
||||
rate_scale = (CLAMP (bsid, 8, 10) - 8);
|
||||
|
||||
if (frame_size)
|
||||
*frame_size = frmsizcod_table[frmsizcod].frame_size[fscod] * 2;
|
||||
if (rate)
|
||||
*rate = fscod_rates[fscod];
|
||||
*rate = fscod_rates[fscod] >> rate_scale;
|
||||
if (chans)
|
||||
*chans = acmod_chans[acmod] + lfe_on;
|
||||
if (blks)
|
||||
|
@ -631,7 +635,7 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
|
|||
}
|
||||
|
||||
if (G_UNLIKELY (ac3parse->sample_rate != rate || ac3parse->channels != chans
|
||||
|| ac3parse->eac != ac3parse->eac)) {
|
||||
|| ac3parse->eac != eac)) {
|
||||
GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3",
|
||||
"framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate,
|
||||
"channels", G_TYPE_INT, chans, NULL);
|
||||
|
|
|
@ -62,12 +62,13 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|||
" channels = (int) [ 1, 8 ],"
|
||||
" rate = (int) [ 8000, 192000 ],"
|
||||
" depth = (int) { 14, 16 },"
|
||||
" endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }"));
|
||||
" endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, "
|
||||
" block-size = (int) [ 1, MAX], " " frame-size = (int) [ 1, MAX]"));
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/x-dts, framed = (boolean) false"));
|
||||
GST_STATIC_CAPS ("audio/x-dts"));
|
||||
|
||||
static void gst_dca_parse_finalize (GObject * object);
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
|||
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/x-flac, framed = (boolean) false")
|
||||
GST_STATIC_CAPS ("audio/x-flac")
|
||||
);
|
||||
|
||||
static void gst_flac_parse_finalize (GObject * object);
|
||||
|
|
|
@ -76,14 +76,15 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|||
GST_STATIC_CAPS ("audio/mpeg, "
|
||||
"mpegversion = (int) 1, "
|
||||
"layer = (int) [ 1, 3 ], "
|
||||
"rate = (int) [ 8000, 48000 ], channels = (int) [ 1, 2 ],"
|
||||
"parsed=(boolean) true")
|
||||
"mpegaudioversion = (int) [ 1, 3], "
|
||||
"rate = (int) [ 8000, 48000 ], "
|
||||
"channels = (int) [ 1, 2 ], " "parsed=(boolean) true")
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1, parsed=(boolean)false")
|
||||
GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1")
|
||||
);
|
||||
|
||||
static void gst_mpeg_audio_parse_finalize (GObject * object);
|
||||
|
|
|
@ -205,6 +205,23 @@ gst_navseek_segseek (GstNavSeek * navseek)
|
|||
gst_object_unref (peer_pad);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_navseek_toggle_play_pause (GstNavSeek * navseek)
|
||||
{
|
||||
GstStateChangeReturn sret;
|
||||
GstState current, pending, state;
|
||||
|
||||
sret = gst_element_get_state (GST_ELEMENT (navseek), ¤t, &pending, 0);
|
||||
if (sret == GST_STATE_CHANGE_FAILURE)
|
||||
return;
|
||||
|
||||
state = (pending != GST_STATE_VOID_PENDING) ? pending : current;
|
||||
|
||||
gst_element_post_message (GST_ELEMENT (navseek),
|
||||
gst_message_new_request_state (GST_OBJECT (navseek),
|
||||
(state == GST_STATE_PLAYING) ? GST_STATE_PAUSED : GST_STATE_PLAYING));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_navseek_src_event (GstBaseTransform * trans, GstEvent * event)
|
||||
{
|
||||
|
@ -257,6 +274,8 @@ gst_navseek_src_event (GstBaseTransform * trans, GstEvent * event)
|
|||
} else if (strcmp (key, "n") == 0) {
|
||||
/* normal speed */
|
||||
gst_navseek_change_playback_rate (navseek, 1.0);
|
||||
} else if (strcmp (key, "space") == 0) {
|
||||
gst_navseek_toggle_play_pause (navseek);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
|
|
@ -107,6 +107,8 @@ static GstStateChangeReturn
|
|||
gst_flv_mux_change_state (GstElement * element, GstStateChange transition);
|
||||
|
||||
static void gst_flv_mux_reset (GstElement * element);
|
||||
static void gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * pad,
|
||||
gboolean video);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -228,10 +230,10 @@ gst_flv_mux_reset (GstElement * element)
|
|||
GstFlvMux *mux = GST_FLV_MUX (element);
|
||||
GSList *sl;
|
||||
|
||||
while ((sl = mux->collect->data) != NULL) {
|
||||
for (sl = mux->collect->data; sl != NULL; sl = g_slist_next (sl)) {
|
||||
GstFlvPad *cpad = (GstFlvPad *) sl->data;
|
||||
|
||||
gst_element_release_request_pad (element, cpad->collect.pad);
|
||||
gst_flv_mux_reset_pad (mux, cpad, cpad->video);
|
||||
}
|
||||
|
||||
g_list_foreach (mux->index, (GFunc) gst_flv_mux_index_entry_free, NULL);
|
||||
|
@ -503,6 +505,26 @@ gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * cpad, gboolean video)
|
||||
{
|
||||
cpad->video = video;
|
||||
|
||||
if (cpad->audio_codec_data)
|
||||
gst_buffer_unref (cpad->audio_codec_data);
|
||||
cpad->audio_codec_data = NULL;
|
||||
cpad->audio_codec = G_MAXUINT;
|
||||
cpad->rate = G_MAXUINT;
|
||||
cpad->width = G_MAXUINT;
|
||||
cpad->channels = G_MAXUINT;
|
||||
|
||||
if (cpad->video_codec_data)
|
||||
gst_buffer_unref (cpad->video_codec_data);
|
||||
cpad->video_codec_data = NULL;
|
||||
cpad->video_codec = G_MAXUINT;
|
||||
cpad->last_timestamp = 0;
|
||||
}
|
||||
|
||||
static GstPad *
|
||||
gst_flv_mux_request_new_pad (GstElement * element,
|
||||
GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
|
||||
|
@ -544,18 +566,9 @@ gst_flv_mux_request_new_pad (GstElement * element,
|
|||
cpad = (GstFlvPad *)
|
||||
gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad));
|
||||
|
||||
cpad->video = video;
|
||||
|
||||
cpad->audio_codec = G_MAXUINT;
|
||||
cpad->rate = G_MAXUINT;
|
||||
cpad->width = G_MAXUINT;
|
||||
cpad->channels = G_MAXUINT;
|
||||
cpad->audio_codec_data = NULL;
|
||||
|
||||
cpad->video_codec = G_MAXUINT;
|
||||
cpad->video_codec_data = NULL;
|
||||
|
||||
cpad->last_timestamp = 0;
|
||||
gst_flv_mux_reset_pad (mux, cpad, video);
|
||||
|
||||
/* FIXME: hacked way to override/extend the event function of
|
||||
* GstCollectPads; because it sets its own event function giving the
|
||||
|
@ -577,11 +590,7 @@ gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
|
|||
GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad));
|
||||
GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
|
||||
|
||||
if (cpad && cpad->audio_codec_data)
|
||||
gst_buffer_unref (cpad->audio_codec_data);
|
||||
if (cpad && cpad->video_codec_data)
|
||||
gst_buffer_unref (cpad->video_codec_data);
|
||||
|
||||
gst_flv_mux_reset_pad (mux, cpad, cpad->video);
|
||||
gst_collect_pads_remove_pad (mux->collect, pad);
|
||||
gst_element_remove_pad (element, pad);
|
||||
}
|
||||
|
|
|
@ -66,9 +66,8 @@ goom_set_resolution (GoomData * goomdata, guint32 resx, guint32 resy)
|
|||
goomdata->back = (guint32 *) malloc (buffsize * sizeof (guint32) + 128);
|
||||
goomdata->buffsize = buffsize;
|
||||
|
||||
goomdata->p1 =
|
||||
(void *) (((unsigned long) goomdata->pixel + 0x7f) & (~0x7f));
|
||||
goomdata->p2 = (void *) (((unsigned long) goomdata->back + 0x7f) & (~0x7f));
|
||||
goomdata->p1 = (void *) (((guintptr) goomdata->pixel + 0x7f) & (~0x7f));
|
||||
goomdata->p2 = (void *) (((guintptr) goomdata->back + 0x7f) & (~0x7f));
|
||||
}
|
||||
|
||||
goomdata->resolx = resx;
|
||||
|
|
|
@ -1757,15 +1757,25 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
|
|||
GstCollectData *cdata = (GstCollectData *) walk->data;
|
||||
GstQTPad *qtpad = (GstQTPad *) cdata;
|
||||
|
||||
/* send last buffer */
|
||||
/* avoid add_buffer complaining if not negotiated
|
||||
* in which case no buffers either, so skipping */
|
||||
if (!qtpad->fourcc) {
|
||||
GST_DEBUG_OBJECT (qtmux, "Pad %s has never had buffers",
|
||||
GST_PAD_NAME (qtpad->collect.pad));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* send last buffer; also flushes possibly queued buffers/ts */
|
||||
GST_DEBUG_OBJECT (qtmux, "Sending the last buffer for pad %s",
|
||||
GST_PAD_NAME (qtpad->collect.pad));
|
||||
ret = gst_qt_mux_add_buffer (qtmux, qtpad, NULL);
|
||||
if (ret != GST_FLOW_OK)
|
||||
if (ret != GST_FLOW_OK) {
|
||||
GST_WARNING_OBJECT (qtmux, "Failed to send last buffer for %s, "
|
||||
"flow return: %s", GST_PAD_NAME (qtpad->collect.pad),
|
||||
gst_flow_get_name (ret));
|
||||
}
|
||||
|
||||
/* having flushed above, can check for buffers now */
|
||||
if (!GST_CLOCK_TIME_IS_VALID (qtpad->first_ts)) {
|
||||
GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers",
|
||||
GST_PAD_NAME (qtpad->collect.pad));
|
||||
|
@ -2048,6 +2058,39 @@ gst_qt_mux_push_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts)
|
|||
pad->ts_n_entries++;
|
||||
}
|
||||
|
||||
static void
|
||||
check_and_subtract_ts (GstQTMux * qtmux, GstClockTime * ts_a, GstClockTime ts_b)
|
||||
{
|
||||
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (*ts_a))) {
|
||||
if (G_LIKELY (*ts_a > ts_b)) {
|
||||
*ts_a -= ts_b;
|
||||
} else {
|
||||
*ts_a = 0;
|
||||
GST_WARNING_OBJECT (qtmux, "Subtraction would result in negative value, "
|
||||
"using 0 as result");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* subtract ts from all buffers enqueued on the pad */
|
||||
static void
|
||||
gst_qt_mux_subtract_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; (i < QTMUX_NO_OF_TS) && (i < pad->ts_n_entries); i++) {
|
||||
check_and_subtract_ts (qtmux, &pad->ts_entries[i], ts);
|
||||
}
|
||||
for (i = 0; i < G_N_ELEMENTS (pad->buf_entries); i++) {
|
||||
if (pad->buf_entries[i]) {
|
||||
check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (pad->buf_entries[i]),
|
||||
ts);
|
||||
check_and_subtract_ts (qtmux,
|
||||
&GST_BUFFER_OFFSET_END (pad->buf_entries[i]), ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* takes ownership of @buf */
|
||||
static GstBuffer *
|
||||
gst_qt_mux_get_asc_buffer_ts (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
|
||||
|
@ -2104,9 +2147,16 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
|
|||
buf = pad->prepare_buf_func (pad, buf, qtmux);
|
||||
}
|
||||
|
||||
if (G_LIKELY (buf != NULL && GST_CLOCK_TIME_IS_VALID (pad->first_ts))) {
|
||||
buf = gst_buffer_make_writable (buf);
|
||||
check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts);
|
||||
}
|
||||
/* when we obtain the first_ts we subtract from all stored buffers we have,
|
||||
* after that we can subtract on input */
|
||||
|
||||
again:
|
||||
last_buf = pad->last_buf;
|
||||
if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER)) {
|
||||
if (qtmux->dts_method == DTS_METHOD_REORDER) {
|
||||
buf = gst_qt_mux_get_asc_buffer_ts (qtmux, pad, buf);
|
||||
if (!buf && !last_buf) {
|
||||
GST_DEBUG_OBJECT (qtmux, "no reordered buffer");
|
||||
|
@ -2177,6 +2227,31 @@ again:
|
|||
goto no_order;
|
||||
}
|
||||
|
||||
/* if this is the first buffer, store the timestamp */
|
||||
if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) {
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) {
|
||||
pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, "
|
||||
"using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad));
|
||||
pad->first_ts = 0;
|
||||
}
|
||||
GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %"
|
||||
GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad),
|
||||
GST_TIME_ARGS (pad->first_ts));
|
||||
|
||||
gst_qt_mux_subtract_ts (qtmux, pad, pad->first_ts);
|
||||
|
||||
GST_BUFFER_TIMESTAMP (last_buf) = 0;
|
||||
check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (last_buf),
|
||||
pad->first_ts);
|
||||
if (buf) {
|
||||
check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts);
|
||||
check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (buf),
|
||||
pad->first_ts);
|
||||
}
|
||||
}
|
||||
|
||||
/* fall back to duration if last buffer or
|
||||
* out-of-order (determined previously), otherwise use input ts */
|
||||
if (buf == NULL ||
|
||||
|
@ -2336,20 +2411,6 @@ again:
|
|||
qtmux->longest_chunk = duration;
|
||||
}
|
||||
|
||||
/* if this is the first buffer, store the timestamp */
|
||||
if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) {
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) {
|
||||
pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, "
|
||||
"using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad));
|
||||
pad->first_ts = 0;
|
||||
}
|
||||
GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %"
|
||||
GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad),
|
||||
GST_TIME_ARGS (pad->first_ts));
|
||||
}
|
||||
|
||||
/* now we go and register this buffer/sample all over */
|
||||
/* note that a new chunk is started each time (not fancy but works) */
|
||||
if (qtmux->moov_recov_file) {
|
||||
|
|
|
@ -8381,6 +8381,41 @@ unknown_tag:
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
qtdemux_tag_add_id32 (GstQTDemux * demux, const char *tag,
|
||||
const char *tag_bis, GNode * node)
|
||||
{
|
||||
guint8 *data;
|
||||
GstBuffer *buf;
|
||||
guint len;
|
||||
GstTagList *taglist = NULL;
|
||||
|
||||
GST_LOG_OBJECT (demux, "parsing ID32");
|
||||
|
||||
data = node->data;
|
||||
len = GST_READ_UINT32_BE (data);
|
||||
|
||||
/* need at least full box and language tag */
|
||||
if (len < 12 + 2)
|
||||
return;
|
||||
|
||||
buf = gst_buffer_new_allocate (NULL, len - 14, 0);
|
||||
gst_buffer_fill (buf, 0, data + 14, len - 14);
|
||||
|
||||
taglist = gst_tag_list_from_id3v2_tag (buf);
|
||||
if (taglist) {
|
||||
GST_LOG_OBJECT (demux, "parsing ok");
|
||||
gst_tag_list_insert (demux->tag_list, taglist, GST_TAG_MERGE_KEEP);
|
||||
} else {
|
||||
GST_LOG_OBJECT (demux, "parsing failed");
|
||||
}
|
||||
|
||||
if (taglist)
|
||||
gst_tag_list_free (taglist);
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux,
|
||||
const char *tag, const char *tag_bis, GNode * node);
|
||||
|
||||
|
@ -8450,7 +8485,9 @@ static const struct
|
|||
* http://atomicparsley.sourceforge.net/mpeg-4files.html and
|
||||
* bug #614471
|
||||
*/
|
||||
FOURCC_____, "", NULL, qtdemux_tag_add_revdns}
|
||||
FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
|
||||
/* see http://www.mp4ra.org/specs.html for ID32 in meta box */
|
||||
FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -8865,6 +8902,15 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
|
|||
GST_LOG_OBJECT (qtdemux, "No udta node found.");
|
||||
}
|
||||
|
||||
/* maybe also some tags in meta box */
|
||||
udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
|
||||
if (udta) {
|
||||
GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
|
||||
qtdemux_parse_udta (qtdemux, udta);
|
||||
} else {
|
||||
GST_LOG_OBJECT (qtdemux, "No meta node found.");
|
||||
}
|
||||
|
||||
qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
|
||||
|
||||
return TRUE;
|
||||
|
|
|
@ -189,6 +189,9 @@ G_BEGIN_DECLS
|
|||
#define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m')
|
||||
#define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c')
|
||||
|
||||
/* misc tag stuff */
|
||||
#define FOURCC_ID32 GST_MAKE_FOURCC('I', 'D','3','2')
|
||||
|
||||
/* ISO Motion JPEG 2000 fourcc */
|
||||
#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2')
|
||||
#define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h')
|
||||
|
|
|
@ -60,6 +60,7 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
|
|||
gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
|
||||
guint64 total;
|
||||
guint8 b;
|
||||
GstFlowReturn ret;
|
||||
|
||||
g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (_length != NULL, GST_FLOW_ERROR);
|
||||
|
@ -71,10 +72,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
|
|||
|
||||
/* read element id */
|
||||
needed = 2;
|
||||
buf = peek (ctx, needed);
|
||||
if (!buf)
|
||||
goto not_enough_data;
|
||||
|
||||
ret = peek (ctx, needed, &buf);
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto peek_error;
|
||||
b = GST_READ_UINT8 (buf);
|
||||
total = (guint64) b;
|
||||
while (read <= 4 && !(total & len_mask)) {
|
||||
|
@ -86,10 +86,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
|
|||
|
||||
/* need id and at least something for subsequent length */
|
||||
needed = read + 1;
|
||||
buf = peek (ctx, needed);
|
||||
if (!buf)
|
||||
goto not_enough_data;
|
||||
|
||||
ret = peek (ctx, needed, &buf);
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto peek_error;
|
||||
while (n < read) {
|
||||
b = GST_READ_UINT8 (buf + n);
|
||||
total = (total << 8) | b;
|
||||
|
@ -112,10 +111,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
|
|||
num_ffs++;
|
||||
|
||||
needed += read - 1;
|
||||
buf = peek (ctx, needed);
|
||||
if (!buf)
|
||||
goto not_enough_data;
|
||||
|
||||
ret = peek (ctx, needed, &buf);
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto peek_error;
|
||||
buf += (needed - read);
|
||||
n = 1;
|
||||
while (n < read) {
|
||||
|
@ -137,10 +135,11 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
|
|||
return GST_FLOW_OK;
|
||||
|
||||
/* ERRORS */
|
||||
not_enough_data:
|
||||
peek_error:
|
||||
{
|
||||
GST_WARNING_OBJECT (el, "peek failed, ret = %d", ret);
|
||||
*_needed = needed;
|
||||
return GST_FLOW_UNEXPECTED;
|
||||
return ret;
|
||||
}
|
||||
invalid_id:
|
||||
{
|
||||
|
@ -190,15 +189,13 @@ gst_ebml_read_clear (GstEbmlRead * ebml)
|
|||
ebml->el = NULL;
|
||||
}
|
||||
|
||||
static const guint8 *
|
||||
gst_ebml_read_peek (GstByteReader * br, guint peek)
|
||||
static GstFlowReturn
|
||||
gst_ebml_read_peek (GstByteReader * br, guint peek, const guint8 ** data)
|
||||
{
|
||||
const guint8 *data = NULL;
|
||||
|
||||
if (G_LIKELY (gst_byte_reader_peek_data (br, peek, &data)))
|
||||
return data;
|
||||
if (G_LIKELY (gst_byte_reader_peek_data (br, peek, data)))
|
||||
return GST_FLOW_OK;
|
||||
else
|
||||
return NULL;
|
||||
return GST_FLOW_UNEXPECTED;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
|
|
|
@ -59,7 +59,7 @@ typedef struct _GstEbmlRead {
|
|||
GArray *readers;
|
||||
} GstEbmlRead;
|
||||
|
||||
typedef const guint8 * (*GstPeekData) (gpointer * context, guint peek);
|
||||
typedef GstFlowReturn (*GstPeekData) (gpointer * context, guint peek, const guint8 ** data);
|
||||
|
||||
/* returns UNEXPECTED if not enough data */
|
||||
GstFlowReturn gst_ebml_peek_id_length (guint32 * _id, guint64 * _length,
|
||||
|
|
|
@ -81,9 +81,12 @@ enum
|
|||
{
|
||||
ARG_0,
|
||||
ARG_METADATA,
|
||||
ARG_STREAMINFO
|
||||
ARG_STREAMINFO,
|
||||
ARG_MAX_GAP_TIME
|
||||
};
|
||||
|
||||
#define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND)
|
||||
|
||||
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
|
@ -168,6 +171,12 @@ static void gst_matroska_demux_reset (GstElement * element);
|
|||
static gboolean perform_seek_to_offset (GstMatroskaDemux * demux,
|
||||
guint64 offset);
|
||||
|
||||
/* gobject functions */
|
||||
static void gst_matroska_demux_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_matroska_demux_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
|
||||
GType gst_matroska_demux_get_type (void);
|
||||
GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstElement,
|
||||
GST_TYPE_ELEMENT);
|
||||
|
@ -223,6 +232,15 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
|
|||
|
||||
gobject_class->finalize = gst_matroska_demux_finalize;
|
||||
|
||||
gobject_class->get_property = gst_matroska_demux_get_property;
|
||||
gobject_class->set_property = gst_matroska_demux_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class, ARG_MAX_GAP_TIME,
|
||||
g_param_spec_uint64 ("max-gap-time", "Maximum gap time",
|
||||
"The demuxer sends out newsegment events for skipping "
|
||||
"gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
|
||||
DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gstelement_class->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
|
||||
gstelement_class->send_event =
|
||||
|
@ -262,6 +280,9 @@ gst_matroska_demux_init (GstMatroskaDemux * demux,
|
|||
|
||||
demux->common.adapter = gst_adapter_new ();
|
||||
|
||||
/* property defaults */
|
||||
demux->max_gap_time = DEFAULT_MAX_GAP_TIME;
|
||||
|
||||
/* finish off */
|
||||
gst_matroska_demux_reset (GST_ELEMENT (demux));
|
||||
}
|
||||
|
@ -3364,7 +3385,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
|
|||
/* handle gaps, e.g. non-zero start-time, or an cue index entry
|
||||
* that landed us with timestamps not quite intended */
|
||||
GST_OBJECT_LOCK (demux);
|
||||
if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.last_stop) &&
|
||||
if (demux->max_gap_time &&
|
||||
GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) &&
|
||||
demux->common.segment.rate > 0.0) {
|
||||
GstClockTimeDiff diff;
|
||||
GstEvent *event1, *event2;
|
||||
|
@ -3372,8 +3394,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
|
|||
/* only send newsegments with increasing start times,
|
||||
* otherwise if these go back and forth downstream (sinks) increase
|
||||
* accumulated time and running_time */
|
||||
diff = GST_CLOCK_DIFF (demux->common.segment.last_stop, lace_time);
|
||||
if (diff > 2 * GST_SECOND && lace_time > demux->common.segment.start
|
||||
diff = GST_CLOCK_DIFF (demux->last_stop_end, lace_time);
|
||||
if (diff > 0 && diff > demux->max_gap_time
|
||||
&& lace_time > demux->common.segment.start
|
||||
&& (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)
|
||||
|| lace_time < demux->common.segment.stop)) {
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
|
@ -3386,11 +3409,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
|
|||
* accum time, hence running_time */
|
||||
/* close ahead of gap */
|
||||
event1 = gst_event_new_new_segment (TRUE,
|
||||
demux->common.segment.rate,
|
||||
demux->common.segment.format,
|
||||
demux->common.segment.last_stop,
|
||||
demux->common.segment.last_stop,
|
||||
demux->common.segment.last_stop);
|
||||
demux->common.segment.rate, demux->common.segment.format,
|
||||
demux->last_stop_end, demux->last_stop_end,
|
||||
demux->last_stop_end);
|
||||
/* skip gap */
|
||||
event2 = gst_event_new_new_segment (FALSE,
|
||||
demux->common.segment.rate,
|
||||
|
@ -5414,6 +5435,48 @@ gst_matroska_demux_change_state (GstElement * element,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_matroska_demux_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMatroskaDemux *demux;
|
||||
|
||||
g_return_if_fail (GST_IS_MATROSKA_DEMUX (object));
|
||||
demux = GST_MATROSKA_DEMUX (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_MAX_GAP_TIME:
|
||||
GST_OBJECT_LOCK (demux);
|
||||
demux->max_gap_time = g_value_get_uint64 (value);
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_matroska_demux_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMatroskaDemux *demux;
|
||||
|
||||
g_return_if_fail (GST_IS_MATROSKA_DEMUX (object));
|
||||
demux = GST_MATROSKA_DEMUX (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_MAX_GAP_TIME:
|
||||
GST_OBJECT_LOCK (demux);
|
||||
g_value_set_uint64 (value, demux->max_gap_time);
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_matroska_demux_plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
|
|
|
@ -91,6 +91,9 @@ typedef struct _GstMatroskaDemux {
|
|||
/* reverse playback */
|
||||
GArray *seek_index;
|
||||
gint seek_entry;
|
||||
|
||||
/* gap handling */
|
||||
guint64 max_gap_time;
|
||||
} GstMatroskaDemux;
|
||||
|
||||
typedef struct _GstMatroskaDemuxClass {
|
||||
|
|
|
@ -391,6 +391,8 @@ gst_matroska_mux_finalize (GObject * object)
|
|||
{
|
||||
GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
|
||||
|
||||
gst_event_replace (&mux->force_key_unit_event, NULL);
|
||||
|
||||
gst_object_unref (mux->collect);
|
||||
gst_object_unref (mux->ebml_write);
|
||||
if (mux->writing_app)
|
||||
|
@ -662,6 +664,17 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
|
|||
gst_event_unref (event);
|
||||
event = NULL;
|
||||
break;
|
||||
case GST_EVENT_CUSTOM_DOWNSTREAM:{
|
||||
const GstStructure *structure;
|
||||
|
||||
structure = gst_event_get_structure (event);
|
||||
if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
|
||||
gst_event_replace (&mux->force_key_unit_event, NULL);
|
||||
mux->force_key_unit_event = event;
|
||||
event = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2692,13 +2705,20 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
|
|||
}
|
||||
|
||||
if (mux->cluster) {
|
||||
/* start a new cluster at every keyframe or when we may be reaching the
|
||||
* limit of the relative timestamp */
|
||||
/* start a new cluster at every keyframe, at every GstForceKeyUnit event,
|
||||
* or when we may be reaching the limit of the relative timestamp */
|
||||
if (mux->cluster_time +
|
||||
mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
|
||||
|| is_video_keyframe) {
|
||||
|| is_video_keyframe || mux->force_key_unit_event) {
|
||||
if (!mux->streamable)
|
||||
gst_ebml_write_master_finish (ebml, mux->cluster);
|
||||
|
||||
/* Forward the GstForceKeyUnit event after finishing the cluster */
|
||||
if (mux->force_key_unit_event) {
|
||||
gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
|
||||
mux->force_key_unit_event = NULL;
|
||||
}
|
||||
|
||||
mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
|
||||
mux->cluster_pos = ebml->pos;
|
||||
gst_ebml_write_set_cache (ebml, 0x20);
|
||||
|
|
|
@ -126,6 +126,9 @@ typedef struct _GstMatroskaMux {
|
|||
cluster_pos,
|
||||
prev_cluster_size;
|
||||
|
||||
/* GstForceKeyUnit event */
|
||||
GstEvent *force_key_unit_event;
|
||||
|
||||
} GstMatroskaMux;
|
||||
|
||||
typedef struct _GstMatroskaMuxClass {
|
||||
|
|
|
@ -1639,14 +1639,12 @@ gst_matroska_read_common_peek_bytes (GstMatroskaReadCommon * common, guint64
|
|||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static const guint8 *
|
||||
gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek)
|
||||
static GstFlowReturn
|
||||
gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek,
|
||||
guint8 ** data)
|
||||
{
|
||||
guint8 *data = NULL;
|
||||
|
||||
gst_matroska_read_common_peek_bytes (common, common->offset, peek, NULL,
|
||||
&data);
|
||||
return data;
|
||||
return gst_matroska_read_common_peek_bytes (common, common->offset, peek,
|
||||
NULL, data);
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
|
|
|
@ -501,7 +501,7 @@ gst_rtp_h264_complete_au (GstRtpH264Depay * rtph264depay,
|
|||
* so downstream waiting for keyframe can pick up at SPS/PPS/IDR */
|
||||
#define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8))
|
||||
|
||||
static gboolean
|
||||
static GstBuffer *
|
||||
gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
|
||||
GstClockTime in_timestamp, gboolean marker)
|
||||
{
|
||||
|
@ -584,11 +584,9 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
|
|||
GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
else
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
|
||||
gst_base_rtp_depayload_push (depayload, outbuf);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return outbuf;
|
||||
|
||||
/* ERRORS */
|
||||
short_nal:
|
||||
|
@ -596,12 +594,13 @@ short_nal:
|
|||
GST_WARNING_OBJECT (depayload, "dropping short NAL");
|
||||
gst_buffer_unmap (nal, data, size);
|
||||
gst_buffer_unref (nal);
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay)
|
||||
static GstBuffer *
|
||||
gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay,
|
||||
gboolean send)
|
||||
{
|
||||
guint outsize;
|
||||
guint8 *outdata;
|
||||
|
@ -624,17 +623,26 @@ gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay)
|
|||
}
|
||||
gst_buffer_unmap (outbuf, outdata, -1);
|
||||
|
||||
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf,
|
||||
rtph264depay->fu_timestamp, rtph264depay->fu_marker);
|
||||
|
||||
rtph264depay->current_fu_type = 0;
|
||||
|
||||
if (send) {
|
||||
outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf,
|
||||
rtph264depay->fu_timestamp, rtph264depay->fu_marker);
|
||||
if (outbuf)
|
||||
gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtph264depay),
|
||||
outbuf);
|
||||
return NULL;
|
||||
} else {
|
||||
return gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf,
|
||||
rtph264depay->fu_timestamp, rtph264depay->fu_marker);
|
||||
}
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
|
||||
{
|
||||
GstRtpH264Depay *rtph264depay;
|
||||
GstBuffer *outbuf;
|
||||
GstBuffer *outbuf = NULL;
|
||||
guint8 nal_unit_type;
|
||||
GstRTPBuffer rtp = { NULL };
|
||||
|
||||
|
@ -692,7 +700,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
|
|||
* when the FU ended) and send out what we gathered thusfar */
|
||||
if (G_UNLIKELY (rtph264depay->current_fu_type != 0 &&
|
||||
nal_unit_type != rtph264depay->current_fu_type))
|
||||
gst_rtp_h264_push_fragmentation_unit (rtph264depay);
|
||||
gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE);
|
||||
|
||||
switch (nal_unit_type) {
|
||||
case 0:
|
||||
|
@ -755,7 +763,8 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
|
|||
outsize = gst_adapter_available (rtph264depay->adapter);
|
||||
outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize);
|
||||
|
||||
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker);
|
||||
outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp,
|
||||
marker);
|
||||
break;
|
||||
}
|
||||
case 26:
|
||||
|
@ -798,7 +807,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
|
|||
* Assume that the remote payloader is buggy (doesn't set the end
|
||||
* bit) and send out what we've gathered thusfar */
|
||||
if (G_UNLIKELY (rtph264depay->current_fu_type != 0))
|
||||
gst_rtp_h264_push_fragmentation_unit (rtph264depay);
|
||||
gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE);
|
||||
|
||||
rtph264depay->current_fu_type = nal_unit_type;
|
||||
rtph264depay->fu_timestamp = timestamp;
|
||||
|
@ -844,11 +853,12 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
|
|||
gst_adapter_push (rtph264depay->adapter, outbuf);
|
||||
}
|
||||
|
||||
outbuf = NULL;
|
||||
rtph264depay->fu_marker = marker;
|
||||
|
||||
/* if NAL unit ends, flush the adapter */
|
||||
if (E)
|
||||
gst_rtp_h264_push_fragmentation_unit (rtph264depay);
|
||||
outbuf = gst_rtp_h264_push_fragmentation_unit (rtph264depay, FALSE);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -872,14 +882,15 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
|
|||
memcpy (outdata + sizeof (sync_bytes), payload, nalu_size);
|
||||
gst_buffer_unmap (outbuf, outdata, outsize);
|
||||
|
||||
gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker);
|
||||
outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp,
|
||||
marker);
|
||||
break;
|
||||
}
|
||||
}
|
||||
gst_rtp_buffer_unmap (&rtp);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return outbuf;
|
||||
|
||||
/* ERRORS */
|
||||
undefined_type:
|
||||
|
|
|
@ -213,6 +213,9 @@ struct _GstRtpBinPrivate
|
|||
gint shutdown;
|
||||
|
||||
gboolean autoremove;
|
||||
|
||||
/* UNIX (ntp) time of last SR sync used */
|
||||
guint64 last_unix;
|
||||
};
|
||||
|
||||
/* signals and args */
|
||||
|
@ -245,6 +248,8 @@ enum
|
|||
#define DEFAULT_AUTOREMOVE FALSE
|
||||
#define DEFAULT_BUFFER_MODE RTP_JITTER_BUFFER_MODE_SLAVE
|
||||
#define DEFAULT_USE_PIPELINE_CLOCK FALSE
|
||||
#define DEFAULT_RTCP_SYNC GST_RTP_BIN_RTCP_SYNC_ALWAYS
|
||||
#define DEFAULT_RTCP_SYNC_INTERVAL 0
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -254,12 +259,39 @@ enum
|
|||
PROP_DO_LOST,
|
||||
PROP_IGNORE_PT,
|
||||
PROP_NTP_SYNC,
|
||||
PROP_RTCP_SYNC,
|
||||
PROP_RTCP_SYNC_INTERVAL,
|
||||
PROP_AUTOREMOVE,
|
||||
PROP_BUFFER_MODE,
|
||||
PROP_USE_PIPELINE_CLOCK,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
GST_RTP_BIN_RTCP_SYNC_ALWAYS,
|
||||
GST_RTP_BIN_RTCP_SYNC_INITIAL,
|
||||
GST_RTP_BIN_RTCP_SYNC_RTP
|
||||
};
|
||||
|
||||
#define GST_RTP_BIN_RTCP_SYNC_TYPE (gst_rtp_bin_rtcp_sync_get_type())
|
||||
static GType
|
||||
gst_rtp_bin_rtcp_sync_get_type (void)
|
||||
{
|
||||
static GType rtcp_sync_type = 0;
|
||||
static const GEnumValue rtcp_sync_types[] = {
|
||||
{GST_RTP_BIN_RTCP_SYNC_ALWAYS, "always", "always"},
|
||||
{GST_RTP_BIN_RTCP_SYNC_INITIAL, "initial", "initial"},
|
||||
{GST_RTP_BIN_RTCP_SYNC_RTP, "rtp-info", "rtp-info"},
|
||||
{0, NULL, NULL},
|
||||
};
|
||||
|
||||
if (!rtcp_sync_type) {
|
||||
rtcp_sync_type = g_enum_register_static ("GstRTCPSync", rtcp_sync_types);
|
||||
}
|
||||
return rtcp_sync_type;
|
||||
}
|
||||
|
||||
/* helper objects */
|
||||
typedef struct _GstRtpBinSession GstRtpBinSession;
|
||||
typedef struct _GstRtpBinStream GstRtpBinStream;
|
||||
|
@ -310,6 +342,9 @@ struct _GstRtpBinStream
|
|||
gboolean have_sync;
|
||||
/* mapping to local RTP and NTP time */
|
||||
gint64 rt_delta;
|
||||
gint64 rtp_delta;
|
||||
/* base rtptime in gst time */
|
||||
gint64 clock_base;
|
||||
};
|
||||
|
||||
#define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->lock)
|
||||
|
@ -775,6 +810,8 @@ gst_rtp_bin_reset_sync (GstRtpBin * rtpbin)
|
|||
* lip-sync */
|
||||
stream->have_sync = FALSE;
|
||||
stream->rt_delta = 0;
|
||||
stream->rtp_delta = 0;
|
||||
stream->clock_base = -100 * GST_SECOND;
|
||||
}
|
||||
}
|
||||
GST_RTP_BIN_UNLOCK (rtpbin);
|
||||
|
@ -979,7 +1016,8 @@ stream_set_ts_offset (GstRtpBin * bin, GstRtpBinStream * stream,
|
|||
static void
|
||||
gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
|
||||
guint8 * data, guint64 ntptime, guint64 last_extrtptime,
|
||||
guint64 base_rtptime, guint64 base_time, guint clock_rate)
|
||||
guint64 base_rtptime, guint64 base_time, guint clock_rate,
|
||||
gint64 rtp_clock_base)
|
||||
{
|
||||
GstRtpBinClient *client;
|
||||
gboolean created;
|
||||
|
@ -1014,6 +1052,19 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
|
|||
stream->ssrc, client, client->cname);
|
||||
}
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (last_extrtptime)) {
|
||||
GST_DEBUG_OBJECT (bin, "invalidated sync data");
|
||||
if (bin->rtcp_sync == GST_RTP_BIN_RTCP_SYNC_RTP) {
|
||||
/* we don't need that data, so carry on,
|
||||
* but make some values look saner */
|
||||
last_extrtptime = base_rtptime;
|
||||
} else {
|
||||
/* nothing we can do with this data in this case */
|
||||
GST_DEBUG_OBJECT (bin, "bailing out");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take the extended rtptime we found in the SR packet and map it to the
|
||||
* local rtptime. The local rtp time is used to construct timestamps on the
|
||||
* buffers so we will calculate what running_time corresponds to the RTP
|
||||
|
@ -1022,8 +1073,9 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
|
|||
|
||||
GST_DEBUG_OBJECT (bin,
|
||||
"base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT
|
||||
", local RTP %" G_GUINT64_FORMAT ", clock-rate %d", base_rtptime,
|
||||
last_extrtptime, local_rtp, clock_rate);
|
||||
", local RTP %" G_GUINT64_FORMAT ", clock-rate %d, "
|
||||
"clock-base %" G_GINT64_FORMAT, base_rtptime,
|
||||
last_extrtptime, local_rtp, clock_rate, rtp_clock_base);
|
||||
|
||||
/* calculate local RTP time in gstreamer timestamp, we essentially perform the
|
||||
* same conversion that a jitterbuffer would use to convert an rtp timestamp
|
||||
|
@ -1070,8 +1122,10 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
|
|||
stream->rt_delta = rtdiff - ntpdiff;
|
||||
|
||||
stream_set_ts_offset (bin, stream, stream->rt_delta);
|
||||
} else if (client->nstreams > 1) {
|
||||
gint64 min;
|
||||
} else {
|
||||
gint64 min, rtp_min, clock_base = stream->clock_base;
|
||||
gboolean all_sync, use_rtp;
|
||||
gboolean rtcp_sync = g_atomic_int_get (&bin->rtcp_sync);
|
||||
|
||||
/* calculate delta between server and receiver. last_unix is created by
|
||||
* converting the ntptime in the last SR packet to a gstreamer timestamp. This
|
||||
|
@ -1089,19 +1143,114 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
|
|||
* latencies).
|
||||
* The stream that has the smallest diff is selected as the reference stream,
|
||||
* all other streams will have a positive offset to this difference. */
|
||||
min = G_MAXINT64;
|
||||
|
||||
/* some alternative setting allow ignoring RTCP as much as possible,
|
||||
* for servers generating bogus ntp timeline */
|
||||
min = rtp_min = G_MAXINT64;
|
||||
use_rtp = FALSE;
|
||||
if (rtcp_sync == GST_RTP_BIN_RTCP_SYNC_RTP) {
|
||||
guint64 ext_base;
|
||||
|
||||
use_rtp = TRUE;
|
||||
/* signed version for convienience */
|
||||
clock_base = base_rtptime;
|
||||
/* deal with possible wrap-around */
|
||||
ext_base = base_rtptime;
|
||||
rtp_clock_base = gst_rtp_buffer_ext_timestamp (&ext_base, rtp_clock_base);
|
||||
/* sanity check; base rtp and provided clock_base should be close */
|
||||
if (rtp_clock_base >= clock_base) {
|
||||
if (rtp_clock_base - clock_base < 10 * clock_rate) {
|
||||
rtp_clock_base = base_time +
|
||||
gst_util_uint64_scale_int (rtp_clock_base - clock_base,
|
||||
GST_SECOND, clock_rate);
|
||||
} else {
|
||||
use_rtp = FALSE;
|
||||
}
|
||||
} else {
|
||||
if (clock_base - rtp_clock_base < 10 * clock_rate) {
|
||||
rtp_clock_base = base_time -
|
||||
gst_util_uint64_scale_int (clock_base - rtp_clock_base,
|
||||
GST_SECOND, clock_rate);
|
||||
} else {
|
||||
use_rtp = FALSE;
|
||||
}
|
||||
}
|
||||
/* warn and bail for clarity out if no sane values */
|
||||
if (!use_rtp) {
|
||||
GST_WARNING_OBJECT (bin, "unable to sync to provided rtptime");
|
||||
return;
|
||||
}
|
||||
/* store to track changes */
|
||||
clock_base = rtp_clock_base;
|
||||
/* generate a fake as before,
|
||||
* now equating rtptime obtained from RTP-Info,
|
||||
* where the large time represent the otherwise irrelevant npt/ntp time */
|
||||
stream->rtp_delta = (GST_SECOND << 28) - rtp_clock_base;
|
||||
}
|
||||
|
||||
for (walk = client->streams; walk; walk = g_slist_next (walk)) {
|
||||
GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
|
||||
|
||||
if (!ostream->have_sync)
|
||||
if (!ostream->have_sync) {
|
||||
all_sync = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* change in current stream's base from previously init'ed value
|
||||
* leads to reset of all stream's base */
|
||||
if (stream != ostream && stream->clock_base >= 0 &&
|
||||
(stream->clock_base != clock_base)) {
|
||||
GST_DEBUG_OBJECT (bin, "reset upon clock base change");
|
||||
ostream->clock_base = -100 * GST_SECOND;
|
||||
ostream->rtp_delta = 0;
|
||||
}
|
||||
|
||||
if (ostream->rt_delta < min)
|
||||
min = ostream->rt_delta;
|
||||
if (ostream->rtp_delta < rtp_min)
|
||||
rtp_min = ostream->rtp_delta;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT, client,
|
||||
min);
|
||||
/* arrange to re-sync for each stream upon significant change,
|
||||
* e.g. post-seek */
|
||||
all_sync = (stream->clock_base == clock_base);
|
||||
stream->clock_base = clock_base;
|
||||
|
||||
/* may need init performed above later on, but nothing more to do now */
|
||||
if (client->nstreams <= 1)
|
||||
return;
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT
|
||||
" all sync %d", client, min, all_sync);
|
||||
GST_DEBUG_OBJECT (bin, "rtcp sync mode %d, use_rtp %d", rtcp_sync, use_rtp);
|
||||
|
||||
switch (rtcp_sync) {
|
||||
case GST_RTP_BIN_RTCP_SYNC_RTP:
|
||||
if (!use_rtp)
|
||||
break;
|
||||
GST_DEBUG_OBJECT (bin, "using rtp generated reports; "
|
||||
"client %p min rtp delta %" G_GINT64_FORMAT, client, rtp_min);
|
||||
/* fall-through */
|
||||
case GST_RTP_BIN_RTCP_SYNC_INITIAL:
|
||||
/* if all have been synced already, do not bother further */
|
||||
if (all_sync) {
|
||||
GST_DEBUG_OBJECT (bin, "all streams already synced; done");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* bail out if we adjusted recently enough */
|
||||
if (all_sync && (last_unix - bin->priv->last_unix) <
|
||||
bin->rtcp_sync_interval * GST_MSECOND) {
|
||||
GST_DEBUG_OBJECT (bin, "discarding RTCP sender packet for sync; "
|
||||
"previous sender info too recent "
|
||||
"(previous UNIX %" G_GUINT64_FORMAT ")", bin->priv->last_unix);
|
||||
return;
|
||||
}
|
||||
bin->priv->last_unix = last_unix;
|
||||
|
||||
/* calculate offsets for each stream */
|
||||
for (walk = client->streams; walk; walk = g_slist_next (walk)) {
|
||||
|
@ -1116,7 +1265,10 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
|
|||
|
||||
/* calculate offset to our reference stream, this should always give a
|
||||
* positive number. */
|
||||
ts_offset = ostream->rt_delta - min;
|
||||
if (use_rtp)
|
||||
ts_offset = ostream->rtp_delta - rtp_min;
|
||||
else
|
||||
ts_offset = ostream->rt_delta - min;
|
||||
|
||||
stream_set_ts_offset (bin, ostream, ts_offset);
|
||||
}
|
||||
|
@ -1149,6 +1301,7 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
|
|||
guint64 base_rtptime;
|
||||
guint64 base_time;
|
||||
guint clock_rate;
|
||||
guint64 clock_base;
|
||||
guint64 extrtptime;
|
||||
GstBuffer *buffer;
|
||||
GstRTCPBuffer rtcp = { NULL };
|
||||
|
@ -1165,6 +1318,7 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
|
|||
g_value_get_uint64 (gst_structure_get_value (s, "base-rtptime"));
|
||||
base_time = g_value_get_uint64 (gst_structure_get_value (s, "base-time"));
|
||||
clock_rate = g_value_get_uint (gst_structure_get_value (s, "clock-rate"));
|
||||
clock_base = g_value_get_uint64 (gst_structure_get_value (s, "clock-base"));
|
||||
extrtptime =
|
||||
g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime"));
|
||||
buffer = gst_value_get_buffer (gst_structure_get_value (s, "sr-buffer"));
|
||||
|
@ -1220,7 +1374,8 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
|
|||
GST_RTP_BIN_LOCK (bin);
|
||||
/* associate the stream to CNAME */
|
||||
gst_rtp_bin_associate (bin, stream, len, data,
|
||||
ntptime, extrtptime, base_rtptime, base_time, clock_rate);
|
||||
ntptime, extrtptime, base_rtptime, base_time, clock_rate,
|
||||
clock_base);
|
||||
GST_RTP_BIN_UNLOCK (bin);
|
||||
}
|
||||
}
|
||||
|
@ -1265,7 +1420,9 @@ create_stream (GstRtpBinSession * session, guint32 ssrc)
|
|||
|
||||
stream->have_sync = FALSE;
|
||||
stream->rt_delta = 0;
|
||||
stream->rtp_delta = 0;
|
||||
stream->percent = 100;
|
||||
stream->clock_base = -100 * GST_SECOND;
|
||||
session->streams = g_slist_prepend (session->streams, stream);
|
||||
|
||||
/* provide clock_rate to the jitterbuffer when needed */
|
||||
|
@ -1656,6 +1813,32 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass)
|
|||
"Synchronize received streams to the NTP clock", DEFAULT_NTP_SYNC,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstRtpBin::rtcp-sync:
|
||||
*
|
||||
* If not synchronizing (directly) to the NTP clock, determines how to sync
|
||||
* the various streams.
|
||||
*
|
||||
* Since: 0.10.31
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_RTCP_SYNC,
|
||||
g_param_spec_enum ("rtcp-sync", "RTCP Sync",
|
||||
"Use of RTCP SR in synchronization", GST_RTP_BIN_RTCP_SYNC_TYPE,
|
||||
DEFAULT_RTCP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstRtpBin::rtcp-sync-interval:
|
||||
*
|
||||
* Determines how often to sync streams using RTCP data.
|
||||
*
|
||||
* Since: 0.10.31
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_RTCP_SYNC_INTERVAL,
|
||||
g_param_spec_uint ("rtcp-sync-interval", "RTCP Sync Interval",
|
||||
"RTCP SR interval synchronization (ms) (0 = always)",
|
||||
0, G_MAXUINT, DEFAULT_RTCP_SYNC_INTERVAL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
|
||||
gstelement_class->request_new_pad =
|
||||
GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
|
||||
|
@ -1706,6 +1889,8 @@ gst_rtp_bin_init (GstRtpBin * rtpbin)
|
|||
rtpbin->do_lost = DEFAULT_DO_LOST;
|
||||
rtpbin->ignore_pt = DEFAULT_IGNORE_PT;
|
||||
rtpbin->ntp_sync = DEFAULT_NTP_SYNC;
|
||||
rtpbin->rtcp_sync = DEFAULT_RTCP_SYNC;
|
||||
rtpbin->rtcp_sync_interval = DEFAULT_RTCP_SYNC_INTERVAL;
|
||||
rtpbin->priv->autoremove = DEFAULT_AUTOREMOVE;
|
||||
rtpbin->buffer_mode = DEFAULT_BUFFER_MODE;
|
||||
rtpbin->use_pipeline_clock = DEFAULT_USE_PIPELINE_CLOCK;
|
||||
|
@ -1821,6 +2006,12 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id,
|
|||
case PROP_NTP_SYNC:
|
||||
rtpbin->ntp_sync = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_RTCP_SYNC:
|
||||
g_atomic_int_set (&rtpbin->rtcp_sync, g_value_get_enum (value));
|
||||
break;
|
||||
case PROP_RTCP_SYNC_INTERVAL:
|
||||
rtpbin->rtcp_sync_interval = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_IGNORE_PT:
|
||||
rtpbin->ignore_pt = g_value_get_boolean (value);
|
||||
break;
|
||||
|
@ -1883,6 +2074,12 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id,
|
|||
case PROP_NTP_SYNC:
|
||||
g_value_set_boolean (value, rtpbin->ntp_sync);
|
||||
break;
|
||||
case PROP_RTCP_SYNC:
|
||||
g_value_set_enum (value, g_atomic_int_get (&rtpbin->rtcp_sync));
|
||||
break;
|
||||
case PROP_RTCP_SYNC_INTERVAL:
|
||||
g_value_set_uint (value, rtpbin->rtcp_sync_interval);
|
||||
break;
|
||||
case PROP_AUTOREMOVE:
|
||||
g_value_set_boolean (value, rtpbin->priv->autoremove);
|
||||
break;
|
||||
|
@ -2020,6 +2217,7 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message)
|
|||
now = gst_clock_get_time (clock);
|
||||
base_time = gst_element_get_base_time (GST_ELEMENT_CAST (bin));
|
||||
running_time = now - base_time;
|
||||
gst_object_unref (clock);
|
||||
}
|
||||
GST_DEBUG_OBJECT (bin,
|
||||
"running time now %" GST_TIME_FORMAT,
|
||||
|
@ -2108,6 +2306,7 @@ gst_rtp_bin_change_state (GstElement * element, GstStateChange transition)
|
|||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
priv->last_unix = 0;
|
||||
GST_LOG_OBJECT (rtpbin, "clearing shutdown flag");
|
||||
g_atomic_int_set (&priv->shutdown, 0);
|
||||
break;
|
||||
|
|
|
@ -50,6 +50,8 @@ struct _GstRtpBin {
|
|||
gboolean do_lost;
|
||||
gboolean ignore_pt;
|
||||
gboolean ntp_sync;
|
||||
gint rtcp_sync;
|
||||
guint rtcp_sync_interval;
|
||||
RTPJitterBufferMode buffer_mode;
|
||||
gboolean buffering;
|
||||
gboolean use_pipeline_clock;
|
||||
|
|
|
@ -664,6 +664,11 @@ gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
|
|||
|
||||
JBUF_LOCK (priv);
|
||||
priv->clock_rate = -1;
|
||||
/* do not clear current content, but refresh state for new arrival */
|
||||
GST_DEBUG_OBJECT (jitterbuffer, "reset jitterbuffer");
|
||||
rtp_jitter_buffer_reset_skew (priv->jbuf);
|
||||
priv->last_popped_seqnum = -1;
|
||||
priv->next_seqnum = -1;
|
||||
JBUF_UNLOCK (priv);
|
||||
}
|
||||
|
||||
|
@ -1953,6 +1958,7 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
|
|||
guint32 rtptime;
|
||||
gboolean drop = FALSE;
|
||||
GstRTCPBuffer rtcp = { NULL };
|
||||
guint64 clock_base;
|
||||
|
||||
jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
|
||||
|
||||
|
@ -1989,9 +1995,12 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
|
|||
rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
|
||||
&clock_rate, &last_rtptime);
|
||||
|
||||
clock_base = priv->clock_base;
|
||||
|
||||
GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %"
|
||||
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT,
|
||||
ext_rtptime, base_rtptime, clock_rate);
|
||||
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT
|
||||
", clock-base %" G_GUINT64_FORMAT,
|
||||
ext_rtptime, base_rtptime, clock_rate, clock_base);
|
||||
|
||||
if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) {
|
||||
GST_DEBUG_OBJECT (jitterbuffer, "dropping, no RTP values");
|
||||
|
@ -2009,8 +2018,12 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
|
|||
diff = ext_rtptime - last_rtptime;
|
||||
/* if bigger than 1 second, we drop it */
|
||||
if (diff > clock_rate) {
|
||||
GST_DEBUG_OBJECT (jitterbuffer, "dropping, too far ahead");
|
||||
drop = TRUE;
|
||||
GST_DEBUG_OBJECT (jitterbuffer, "too far ahead");
|
||||
/* should drop this, but some RTSP servers end up with bogus
|
||||
* way too ahead RTCP packet when repeated PAUSE/PLAY,
|
||||
* so still trigger rptbin sync but invalidate RTCP data
|
||||
* (sync might use other methods) */
|
||||
ext_rtptime = -1;
|
||||
}
|
||||
GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
|
||||
G_GUINT64_FORMAT, last_rtptime, diff);
|
||||
|
@ -2026,6 +2039,7 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
|
|||
"base-rtptime", G_TYPE_UINT64, base_rtptime,
|
||||
"base-time", G_TYPE_UINT64, base_time,
|
||||
"clock-rate", G_TYPE_UINT, clock_rate,
|
||||
"clock-base", G_TYPE_UINT64, clock_base,
|
||||
"sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
|
||||
"sr-buffer", GST_TYPE_BUFFER, buffer, NULL);
|
||||
|
||||
|
|
|
@ -835,6 +835,10 @@ rtcp_thread (GstRtpSession * rtpsession)
|
|||
|
||||
session = rtpsession->priv->session;
|
||||
|
||||
GST_DEBUG_OBJECT (rtpsession, "starting at %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (current_time));
|
||||
session->start_time = current_time;
|
||||
|
||||
while (!rtpsession->priv->stop_thread) {
|
||||
GstClockReturn res;
|
||||
|
||||
|
|
|
@ -640,6 +640,26 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf,
|
|||
}
|
||||
|
||||
rtptime = gst_rtp_buffer_get_timestamp (&rtp);
|
||||
/* rtp time jumps are checked for during skew calculation, but bypassed
|
||||
* in other mode, so mind those here and reset jb if needed.
|
||||
* Only reset if valid input time, which is likely for UDP input
|
||||
* where we expect this might happen due to async thread effects
|
||||
* (in seek and state change cycles), but not so much for TCP input */
|
||||
if (GST_CLOCK_TIME_IS_VALID (time) &&
|
||||
jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE &&
|
||||
jbuf->base_time != -1 && jbuf->last_rtptime != -1) {
|
||||
GstClockTime ext_rtptime = jbuf->ext_rtptime;
|
||||
|
||||
ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
|
||||
if (ext_rtptime > jbuf->last_rtptime + 3 * clock_rate ||
|
||||
ext_rtptime + 3 * clock_rate < jbuf->last_rtptime) {
|
||||
/* reset even if we don't have valid incoming time;
|
||||
* still better than producing possibly very bogus output timestamp */
|
||||
GST_WARNING ("rtp delta too big, reset skew");
|
||||
rtp_jitter_buffer_reset_skew (jbuf);
|
||||
}
|
||||
}
|
||||
|
||||
switch (jbuf->mode) {
|
||||
case RTP_JITTER_BUFFER_MODE_NONE:
|
||||
case RTP_JITTER_BUFFER_MODE_BUFFER:
|
||||
|
|
|
@ -543,6 +543,8 @@ rtp_session_init (RTPSession * sess)
|
|||
sess->source->internal = TRUE;
|
||||
sess->stats.active_sources++;
|
||||
INIT_AVG (sess->stats.avg_rtcp_packet_size, 100);
|
||||
sess->source->stats.prev_rtcptime = 0;
|
||||
sess->source->stats.last_rtcptime = 1;
|
||||
|
||||
rtp_stats_set_min_interval (&sess->stats,
|
||||
(gdouble) DEFAULT_RTCP_MIN_INTERVAL / GST_SECOND);
|
||||
|
@ -664,6 +666,12 @@ rtp_session_set_property (GObject * object, guint prop_id,
|
|||
case PROP_RTCP_MIN_INTERVAL:
|
||||
rtp_stats_set_min_interval (&sess->stats,
|
||||
(gdouble) g_value_get_uint64 (value) / GST_SECOND);
|
||||
/* trigger reconsideration */
|
||||
RTP_SESSION_LOCK (sess);
|
||||
sess->next_rtcp_check_time = 0;
|
||||
RTP_SESSION_UNLOCK (sess);
|
||||
if (sess->callbacks.reconsider)
|
||||
sess->callbacks.reconsider (sess, sess->reconsider_user_data);
|
||||
break;
|
||||
case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD:
|
||||
sess->rtcp_immediate_feedback_threshold = g_value_get_uint (value);
|
||||
|
@ -2767,11 +2775,35 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
|
|||
gboolean sendertimeout = FALSE;
|
||||
gboolean is_sender, is_active;
|
||||
RTPSession *sess = data->sess;
|
||||
GstClockTime interval;
|
||||
GstClockTime interval, binterval;
|
||||
GstClockTime btime;
|
||||
|
||||
is_sender = RTP_SOURCE_IS_SENDER (source);
|
||||
is_active = RTP_SOURCE_IS_ACTIVE (source);
|
||||
|
||||
/* our own rtcp interval may have been forced low by secondary configuration,
|
||||
* while sender side may still operate with higher interval,
|
||||
* so do not just take our interval to decide on timing out sender,
|
||||
* but take (if data->interval <= 5 * GST_SECOND):
|
||||
* interval = CLAMP (sender_interval, data->interval, 5 * GST_SECOND)
|
||||
* where sender_interval is difference between last 2 received RTCP reports
|
||||
*/
|
||||
if (data->interval >= 5 * GST_SECOND || (source == sess->source)) {
|
||||
binterval = data->interval;
|
||||
} else {
|
||||
GST_LOG ("prev_rtcp %" GST_TIME_FORMAT ", last_rtcp %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (source->stats.prev_rtcptime),
|
||||
GST_TIME_ARGS (source->stats.last_rtcptime));
|
||||
/* if not received enough yet, fallback to larger default */
|
||||
if (source->stats.last_rtcptime > source->stats.prev_rtcptime)
|
||||
binterval = source->stats.last_rtcptime - source->stats.prev_rtcptime;
|
||||
else
|
||||
binterval = 5 * GST_SECOND;
|
||||
binterval = CLAMP (binterval, data->interval, 5 * GST_SECOND);
|
||||
}
|
||||
GST_LOG ("timeout base interval %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (binterval));
|
||||
|
||||
/* check for our own source, we don't want to delete our own source. */
|
||||
if (!(source == sess->source)) {
|
||||
if (source->received_bye) {
|
||||
|
@ -2786,11 +2818,13 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
|
|||
}
|
||||
/* sources that were inactive for more than 5 times the deterministic reporting
|
||||
* interval get timed out. the min timeout is 5 seconds. */
|
||||
if (data->current_time > source->last_activity) {
|
||||
interval = MAX (data->interval * 5, 5 * GST_SECOND);
|
||||
if (data->current_time - source->last_activity > interval) {
|
||||
/* mind old time that might pre-date last time going to PLAYING */
|
||||
btime = MAX (source->last_activity, sess->start_time);
|
||||
if (data->current_time > btime) {
|
||||
interval = MAX (binterval * 5, 5 * GST_SECOND);
|
||||
if (data->current_time - btime > interval) {
|
||||
GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT,
|
||||
source->ssrc, GST_TIME_ARGS (source->last_activity));
|
||||
source->ssrc, GST_TIME_ARGS (btime));
|
||||
remove = TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -2799,12 +2833,13 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
|
|||
/* senders that did not send for a long time become a receiver, this also
|
||||
* holds for our own source. */
|
||||
if (is_sender) {
|
||||
if (data->current_time > source->last_rtp_activity) {
|
||||
interval = MAX (data->interval * 2, 5 * GST_SECOND);
|
||||
if (data->current_time - source->last_rtp_activity > interval) {
|
||||
/* mind old time that might pre-date last time going to PLAYING */
|
||||
btime = MAX (source->last_rtp_activity, sess->start_time);
|
||||
if (data->current_time > btime) {
|
||||
interval = MAX (binterval * 2, 5 * GST_SECOND);
|
||||
if (data->current_time - btime > interval) {
|
||||
GST_DEBUG ("sender source %08x timed out and became receiver, last %"
|
||||
GST_TIME_FORMAT, source->ssrc,
|
||||
GST_TIME_ARGS (source->last_rtp_activity));
|
||||
GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime));
|
||||
source->is_sender = FALSE;
|
||||
sess->stats.sender_sources--;
|
||||
sendertimeout = TRUE;
|
||||
|
|
|
@ -209,6 +209,7 @@ struct _RTPSession {
|
|||
|
||||
GstClockTime next_rtcp_check_time;
|
||||
GstClockTime last_rtcp_send_time;
|
||||
GstClockTime start_time;
|
||||
gboolean first_rtcp;
|
||||
gboolean allow_early;
|
||||
|
||||
|
|
|
@ -1368,6 +1368,9 @@ rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime,
|
|||
|
||||
/* make current */
|
||||
src->stats.curr_sr = curridx;
|
||||
|
||||
src->stats.prev_rtcptime = src->stats.last_rtcptime;
|
||||
src->stats.last_rtcptime = time;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -128,6 +128,13 @@ enum
|
|||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum _GstRtspSrcRtcpSyncMode
|
||||
{
|
||||
RTCP_SYNC_ALWAYS,
|
||||
RTCP_SYNC_INITIAL,
|
||||
RTCP_SYNC_RTP
|
||||
};
|
||||
|
||||
enum _GstRtspSrcBufferMode
|
||||
{
|
||||
BUFFER_MODE_NONE,
|
||||
|
@ -1651,7 +1658,7 @@ cleanup:
|
|||
}
|
||||
|
||||
static void
|
||||
gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
|
||||
gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush, gboolean playing)
|
||||
{
|
||||
GstEvent *event;
|
||||
gint cmd, i;
|
||||
|
@ -1667,9 +1674,12 @@ gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
|
|||
state = GST_STATE_PAUSED;
|
||||
} else {
|
||||
event = gst_event_new_flush_stop (TRUE);
|
||||
GST_DEBUG_OBJECT (src, "stop flush");
|
||||
GST_DEBUG_OBJECT (src, "stop flush; playing %d", playing);
|
||||
cmd = CMD_LOOP;
|
||||
state = GST_STATE_PLAYING;
|
||||
if (playing)
|
||||
state = GST_STATE_PLAYING;
|
||||
else
|
||||
state = GST_STATE_PAUSED;
|
||||
clock = gst_element_get_clock (GST_ELEMENT_CAST (src));
|
||||
if (clock) {
|
||||
base_time = gst_clock_get_time (clock);
|
||||
|
@ -1818,7 +1828,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
|
|||
* blocking in preroll). */
|
||||
if (flush) {
|
||||
GST_DEBUG_OBJECT (src, "starting flush");
|
||||
gst_rtspsrc_flush (src, TRUE);
|
||||
gst_rtspsrc_flush (src, TRUE, FALSE);
|
||||
} else {
|
||||
if (src->task) {
|
||||
gst_task_pause (src->task);
|
||||
|
@ -1867,7 +1877,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
|
|||
if (flush) {
|
||||
/* if we started flush, we stop now */
|
||||
GST_DEBUG_OBJECT (src, "stopping flush");
|
||||
gst_rtspsrc_flush (src, FALSE);
|
||||
gst_rtspsrc_flush (src, FALSE, playing);
|
||||
}
|
||||
|
||||
/* now we did the seek and can activate the new segment values */
|
||||
|
@ -4052,6 +4062,8 @@ gst_rtspsrc_loop_send_cmd (GstRTSPSrc * src, gint cmd, gboolean flush)
|
|||
/* start new request */
|
||||
gst_rtspsrc_loop_start_cmd (src, cmd);
|
||||
|
||||
GST_DEBUG_OBJECT (src, "sending cmd %d", cmd);
|
||||
|
||||
GST_OBJECT_LOCK (src);
|
||||
old = src->loop_cmd;
|
||||
if (old != CMD_WAIT) {
|
||||
|
@ -5900,6 +5912,47 @@ gst_rtspsrc_parse_rtpinfo (GstRTSPSrc * src, gchar * rtpinfo)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtspsrc_handle_rtcp_interval (GstRTSPSrc * src, gchar * rtcp)
|
||||
{
|
||||
guint64 interval;
|
||||
GList *walk;
|
||||
|
||||
interval = strtoul (rtcp, NULL, 10);
|
||||
GST_DEBUG_OBJECT (src, "rtcp interval: %" G_GUINT64_FORMAT " ms", interval);
|
||||
|
||||
if (!interval)
|
||||
return;
|
||||
|
||||
interval *= GST_MSECOND;
|
||||
|
||||
for (walk = src->streams; walk; walk = g_list_next (walk)) {
|
||||
GstRTSPStream *stream = (GstRTSPStream *) walk->data;
|
||||
|
||||
/* already (optionally) retrieved this when configuring manager */
|
||||
if (stream->session) {
|
||||
GObject *rtpsession = stream->session;
|
||||
|
||||
GST_DEBUG_OBJECT (src, "configure rtcp interval in session %p",
|
||||
rtpsession);
|
||||
g_object_set (rtpsession, "rtcp-min-interval", interval, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* now it happens that (Xenon) server sending this may also provide bogus
|
||||
* RTCP SR sync data (i.e. with quite some jitter), so never mind those
|
||||
* and just use RTP-Info to sync */
|
||||
if (src->manager) {
|
||||
GObjectClass *klass;
|
||||
|
||||
klass = G_OBJECT_GET_CLASS (G_OBJECT (src->manager));
|
||||
if (g_object_class_find_property (klass, "rtcp-sync")) {
|
||||
GST_DEBUG_OBJECT (src, "configuring rtp sync method");
|
||||
g_object_set (src->manager, "rtcp-sync", RTCP_SYNC_RTP, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gdouble
|
||||
gst_rtspsrc_get_float (const gchar * dstr)
|
||||
{
|
||||
|
@ -6108,6 +6161,12 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment, gboolean async)
|
|||
&hval, hval_idx++) == GST_RTSP_OK)
|
||||
gst_rtspsrc_parse_rtpinfo (src, hval);
|
||||
|
||||
/* some servers indicate RTCP parameters in PLAY response,
|
||||
* rather than properly in SDP */
|
||||
if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RTCP_INTERVAL,
|
||||
&hval, 0) == GST_RTSP_OK)
|
||||
gst_rtspsrc_handle_rtcp_interval (src, hval);
|
||||
|
||||
gst_rtsp_message_unset (&response);
|
||||
|
||||
/* early exit when we did aggregate control */
|
||||
|
@ -6401,10 +6460,6 @@ gst_rtspsrc_thread (GstRTSPSrc * src)
|
|||
|
||||
switch (cmd) {
|
||||
case CMD_OPEN:
|
||||
src->cur_protocols = src->protocols;
|
||||
/* first attempt, don't ignore timeouts */
|
||||
src->ignore_timeout = FALSE;
|
||||
src->open_error = FALSE;
|
||||
ret = gst_rtspsrc_open (src, TRUE);
|
||||
break;
|
||||
case CMD_PLAY:
|
||||
|
@ -6524,6 +6579,11 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
|
|||
goto start_failed;
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
/* init some state */
|
||||
rtspsrc->cur_protocols = rtspsrc->protocols;
|
||||
/* first attempt, don't ignore timeouts */
|
||||
rtspsrc->ignore_timeout = FALSE;
|
||||
rtspsrc->open_error = FALSE;
|
||||
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_OPEN, FALSE);
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
|
|
|
@ -374,7 +374,10 @@ gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri)
|
|||
gchar *location, *location_end;
|
||||
gchar *colptr;
|
||||
|
||||
/* consider no protocol to be udp:// */
|
||||
protocol = gst_uri_get_protocol (uristr);
|
||||
if (!protocol)
|
||||
goto no_protocol;
|
||||
if (strcmp (protocol, "udp") != 0)
|
||||
goto wrong_protocol;
|
||||
g_free (protocol);
|
||||
|
@ -425,6 +428,11 @@ gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri)
|
|||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
no_protocol:
|
||||
{
|
||||
GST_ERROR ("error parsing uri %s: no protocol", uristr);
|
||||
return -1;
|
||||
}
|
||||
wrong_protocol:
|
||||
{
|
||||
GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr,
|
||||
|
|
|
@ -2358,11 +2358,20 @@ get_fmt_failed:
|
|||
}
|
||||
set_fmt_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
|
||||
(_("Device '%s' cannot capture at %dx%d"),
|
||||
v4l2object->videodev, width, height),
|
||||
("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s",
|
||||
GST_FOURCC_ARGS (pixelformat), width, height, g_strerror (errno)));
|
||||
if (errno == EBUSY) {
|
||||
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, BUSY,
|
||||
(_("Device '%s' is busy"), v4l2object->videodev),
|
||||
("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s",
|
||||
GST_FOURCC_ARGS (pixelformat), width, height,
|
||||
g_strerror (errno)));
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
|
||||
(_("Device '%s' cannot capture at %dx%d"),
|
||||
v4l2object->videodev, width, height),
|
||||
("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s",
|
||||
GST_FOURCC_ARGS (pixelformat), width, height,
|
||||
g_strerror (errno)));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
invalid_dimensions:
|
||||
|
|
|
@ -71,6 +71,8 @@ enum
|
|||
PROP_ENDX,
|
||||
PROP_ENDY,
|
||||
PROP_REMOTE,
|
||||
PROP_XID,
|
||||
PROP_XNAME,
|
||||
};
|
||||
|
||||
#define gst_ximage_src_parent_class parent_class
|
||||
|
@ -105,6 +107,35 @@ gst_ximage_src_return_buf (GstXImageSrc * ximagesrc, GstBuffer * ximage)
|
|||
}
|
||||
}
|
||||
|
||||
static Window
|
||||
gst_ximage_src_find_window (GstXImageSrc * src, Window root, const char *name)
|
||||
{
|
||||
Window *children;
|
||||
Window window = 0, root_return, parent_return;
|
||||
unsigned int nchildren;
|
||||
char *tmpname;
|
||||
int n, status;
|
||||
|
||||
status = XFetchName (src->xcontext->disp, root, &tmpname);
|
||||
if (status && !strcmp (name, tmpname))
|
||||
return root;
|
||||
|
||||
status =
|
||||
XQueryTree (src->xcontext->disp, root, &root_return, &parent_return,
|
||||
&children, &nchildren);
|
||||
if (!status || !children)
|
||||
return (Window) 0;
|
||||
|
||||
for (n = 0; n < nchildren; ++n) {
|
||||
window = gst_ximage_src_find_window (src, children[n], name);
|
||||
if (window != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
XFree (children);
|
||||
return window;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name)
|
||||
{
|
||||
|
@ -125,8 +156,49 @@ gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name)
|
|||
s->width = s->xcontext->width;
|
||||
s->height = s->xcontext->height;
|
||||
|
||||
/* Always capture root window, for now */
|
||||
s->xwindow = s->xcontext->root;
|
||||
if (s->xid != 0 || s->xname) {
|
||||
int status;
|
||||
XWindowAttributes attrs;
|
||||
Window window;
|
||||
|
||||
if (s->xid != 0) {
|
||||
status = XGetWindowAttributes (s->xcontext->disp, s->xid, &attrs);
|
||||
if (status) {
|
||||
GST_DEBUG_OBJECT (s, "Found window XID %p", s->xid);
|
||||
s->xwindow = s->xid;
|
||||
goto window_found;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (s, "Failed to get window %p attributes", s->xid);
|
||||
}
|
||||
}
|
||||
|
||||
if (s->xname) {
|
||||
GST_DEBUG_OBJECT (s, "Looking for window %s", s->xname);
|
||||
window = gst_ximage_src_find_window (s, s->xcontext->root, s->xname);
|
||||
if (window != 0) {
|
||||
GST_DEBUG_OBJECT (s, "Found window named %s as %p, ", s->xname, window);
|
||||
status = XGetWindowAttributes (s->xcontext->disp, window, &attrs);
|
||||
if (status) {
|
||||
s->xwindow = window;
|
||||
goto window_found;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (s, "Failed to get window %p attributes", window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GST_INFO_OBJECT (s, "Using root window");
|
||||
goto use_root_window;
|
||||
|
||||
window_found:
|
||||
g_assert (s->xwindow != 0);
|
||||
s->width = attrs.width;
|
||||
s->height = attrs.height;
|
||||
GST_INFO_OBJECT (s, "Using default window %p, size of %dx%d", s->xwindow,
|
||||
s->width, s->height);
|
||||
}
|
||||
use_root_window:
|
||||
|
||||
#ifdef HAVE_XFIXES
|
||||
/* check if xfixes supported */
|
||||
|
@ -603,7 +675,8 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc)
|
|||
} else
|
||||
#endif /* HAVE_XSHM */
|
||||
{
|
||||
GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XGetImage");
|
||||
GST_DEBUG_OBJECT (ximagesrc,
|
||||
"Retrieving screen using XGetImage, window %p", ximagesrc->xwindow);
|
||||
if (ximagesrc->remote) {
|
||||
XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
|
||||
ximagesrc->startx, ximagesrc->starty, ximagesrc->width,
|
||||
|
@ -847,6 +920,21 @@ gst_ximage_src_set_property (GObject * object, guint prop_id,
|
|||
case PROP_REMOTE:
|
||||
src->remote = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_XID:
|
||||
if (src->xcontext != NULL) {
|
||||
g_warning ("ximagesrc window ID must be set before opening display");
|
||||
break;
|
||||
}
|
||||
src->xid = g_value_get_uint64 (value);
|
||||
break;
|
||||
case PROP_XNAME:
|
||||
if (src->xcontext != NULL) {
|
||||
g_warning ("ximagesrc window name must be set before opening display");
|
||||
break;
|
||||
}
|
||||
g_free (src->xname);
|
||||
src->xname = g_strdup (g_value_get_string (value));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -890,6 +978,12 @@ gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
case PROP_REMOTE:
|
||||
g_value_set_boolean (value, src->remote);
|
||||
break;
|
||||
case PROP_XID:
|
||||
g_value_set_uint64 (value, src->xid);
|
||||
break;
|
||||
case PROP_XNAME:
|
||||
g_value_set_string (value, src->xname);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -928,6 +1022,7 @@ gst_ximage_src_finalize (GObject * object)
|
|||
if (src->xcontext)
|
||||
ximageutil_xcontext_clear (src->xcontext);
|
||||
|
||||
g_free (src->xname);
|
||||
g_mutex_free (src->pool_lock);
|
||||
g_mutex_free (src->x_lock);
|
||||
|
||||
|
@ -952,9 +1047,16 @@ gst_ximage_src_get_caps (GstBaseSrc * bs, GstCaps * filter)
|
|||
(s)->srcpad));
|
||||
|
||||
xcontext = s->xcontext;
|
||||
|
||||
width = xcontext->width;
|
||||
height = xcontext->height;
|
||||
width = s->xcontext->width;
|
||||
height = s->xcontext->height;
|
||||
if (s->xwindow != 0) {
|
||||
XWindowAttributes attrs;
|
||||
int status = XGetWindowAttributes (s->xcontext->disp, s->xwindow, &attrs);
|
||||
if (status) {
|
||||
width = attrs.width;
|
||||
height = attrs.height;
|
||||
}
|
||||
}
|
||||
|
||||
/* property comments say 0 means right/bottom, means we can't capture
|
||||
the top left pixel alone */
|
||||
|
@ -1135,6 +1237,28 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass)
|
|||
g_param_spec_boolean ("remote", "Remote dispay",
|
||||
"Whether the display is remote", FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* GstXImageSrc:xid
|
||||
*
|
||||
* The XID of the window to capture. 0 for the root window (default).
|
||||
*
|
||||
* Since: 0.10.31
|
||||
**/
|
||||
g_object_class_install_property (gc, PROP_XID,
|
||||
g_param_spec_uint64 ("xid", "Window XID",
|
||||
"Window XID to capture from", 0, G_MAXUINT64, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* GstXImageSrc:xname
|
||||
*
|
||||
* The name of the window to capture, if any.
|
||||
*
|
||||
* Since: 0.10.31
|
||||
**/
|
||||
g_object_class_install_property (gc, PROP_XNAME,
|
||||
g_param_spec_string ("xname", "Window name",
|
||||
"Window name to capture from", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gst_element_class_set_details_simple (ec, "Ximage video source",
|
||||
"Source/Video",
|
||||
|
|
|
@ -56,6 +56,10 @@ struct _GstXImageSrc
|
|||
gchar *display_name;
|
||||
guint screen_num;
|
||||
|
||||
/* Window selection */
|
||||
guint64 xid;
|
||||
gchar *xname;
|
||||
|
||||
/* Desired output framerate */
|
||||
gint fps_n;
|
||||
gint fps_d;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <gst/tag/tag.h>
|
||||
|
||||
#define SINK_CAPS "text/x-cmml"
|
||||
#define SRC_CAPS "text/x-cmml"
|
||||
#define SRC_CAPS "text/x-cmml, encoded=(boolean)TRUE"
|
||||
|
||||
#define IDENT_HEADER \
|
||||
"CMML\x00\x00\x00\x00"\
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <gst/tag/tag.h>
|
||||
|
||||
#define SINK_CAPS "text/x-cmml"
|
||||
#define SRC_CAPS "text/x-cmml"
|
||||
#define SRC_CAPS "text/x-cmml,encoded=(boolean)FALSE"
|
||||
|
||||
#define IDENT_HEADER \
|
||||
"CMML\x00\x00\x00\x00"\
|
||||
|
|
|
@ -50,6 +50,7 @@ static void
|
|||
mux_pcm_audio (guint num_buffers, guint repeat)
|
||||
{
|
||||
GstElement *src, *sink, *flvmux, *conv, *pipeline;
|
||||
GstPad *sinkpad, *srcpad;
|
||||
gint counter;
|
||||
|
||||
GST_LOG ("num_buffers = %u", num_buffers);
|
||||
|
@ -83,25 +84,24 @@ mux_pcm_audio (guint num_buffers, guint repeat)
|
|||
fail_unless (gst_element_link (src, conv));
|
||||
fail_unless (gst_element_link (flvmux, sink));
|
||||
|
||||
/* now link the elements */
|
||||
sinkpad = gst_element_get_request_pad (flvmux, "audio");
|
||||
fail_unless (sinkpad != NULL, "Could not get audio request pad");
|
||||
|
||||
srcpad = gst_element_get_static_pad (conv, "src");
|
||||
fail_unless (srcpad != NULL, "Could not get audioconvert's source pad");
|
||||
|
||||
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
|
||||
|
||||
gst_object_unref (srcpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
do {
|
||||
GstStateChangeReturn state_ret;
|
||||
GstMessage *msg;
|
||||
GstPad *sinkpad, *srcpad;
|
||||
|
||||
GST_LOG ("repeat=%d", repeat);
|
||||
|
||||
/* now link the elements */
|
||||
sinkpad = gst_element_get_request_pad (flvmux, "audio");
|
||||
fail_unless (sinkpad != NULL, "Could not get audio request pad");
|
||||
|
||||
srcpad = gst_element_get_static_pad (conv, "src");
|
||||
fail_unless (srcpad != NULL, "Could not get audioconvert's source pad");
|
||||
|
||||
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
|
||||
|
||||
gst_object_unref (srcpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
counter = 0;
|
||||
|
||||
state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
|
|
|
@ -43,6 +43,7 @@ static GstPad *mysrcpad, *mysinkpad;
|
|||
"rate = (int) 48000"
|
||||
#define VIDEO_CAPS_STRING "video/mpeg, " \
|
||||
"mpegversion = (int) 4, " \
|
||||
"systemstream = (boolean) false, " \
|
||||
"width = (int) 384, " \
|
||||
"height = (int) 288, " \
|
||||
"framerate = (fraction) 25/1"
|
||||
|
@ -517,7 +518,7 @@ create_qtmux_profile (const gchar * variant)
|
|||
return cprof;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_encodebin)
|
||||
GST_START_TEST (test_encodebin_qtmux)
|
||||
{
|
||||
GstEncodingContainerProfile *cprof;
|
||||
GstElement *enc;
|
||||
|
@ -553,6 +554,136 @@ GST_START_TEST (test_encodebin)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
/* Fake mp3 encoder for test */
|
||||
typedef GstElement TestMp3Enc;
|
||||
typedef GstElementClass TestMp3EncClass;
|
||||
|
||||
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, layer=[1,3]")
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/x-raw-int")
|
||||
);
|
||||
|
||||
static GType test_mp3_enc_get_type (void);
|
||||
|
||||
GST_BOILERPLATE (TestMp3Enc, test_mp3_enc, GstElement, GST_TYPE_ELEMENT);
|
||||
|
||||
static void
|
||||
test_mp3_enc_base_init (gpointer klass)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&sink_template));
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&src_template));
|
||||
|
||||
gst_element_class_set_details_simple (element_class, "MPEG1 Audio Encoder",
|
||||
"Codec/Encoder/Audio", "Pretends to encode mp3", "Foo Bar <foo@bar.com>");
|
||||
}
|
||||
|
||||
static void
|
||||
test_mp3_enc_class_init (TestMp3EncClass * klass)
|
||||
{
|
||||
/* doesn't actually need to do anything for this test */
|
||||
}
|
||||
|
||||
static void
|
||||
test_mp3_enc_init (TestMp3Enc * mp3enc, TestMp3EncClass * klass)
|
||||
{
|
||||
GstPad *pad;
|
||||
|
||||
pad = gst_pad_new_from_static_template (&sink_template, "sink");
|
||||
gst_element_add_pad (mp3enc, pad);
|
||||
|
||||
pad = gst_pad_new_from_static_template (&src_template, "src");
|
||||
gst_element_add_pad (mp3enc, pad);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
return gst_element_register (plugin, "testmp3enc", GST_RANK_NONE,
|
||||
test_mp3_enc_get_type ());
|
||||
}
|
||||
|
||||
static GstEncodingContainerProfile *
|
||||
create_mp4mux_profile (void)
|
||||
{
|
||||
GstEncodingContainerProfile *cprof;
|
||||
GstCaps *caps;
|
||||
|
||||
caps = gst_caps_new_simple ("video/quicktime",
|
||||
"variant", G_TYPE_STRING, "iso", NULL);
|
||||
|
||||
cprof = gst_encoding_container_profile_new ("Name", "blah", caps, NULL);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1,
|
||||
"layer", G_TYPE_INT, 3, "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT,
|
||||
44100, NULL);
|
||||
gst_encoding_container_profile_add_profile (cprof,
|
||||
GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, NULL, NULL,
|
||||
1)));
|
||||
gst_caps_unref (caps);
|
||||
|
||||
return cprof;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_encodebin_mp4mux)
|
||||
{
|
||||
GstEncodingContainerProfile *cprof;
|
||||
GstPluginFeature *feature;
|
||||
GstElement *enc, *mux;
|
||||
GstPad *pad;
|
||||
|
||||
/* need a fake mp3 encoder because mp4 only accepts encoded formats */
|
||||
gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
|
||||
"fakemp3enc", "fakemp3enc", plugin_init, VERSION, "LGPL",
|
||||
"gst-plugins-good", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
|
||||
|
||||
feature = gst_default_registry_find_feature ("testmp3enc",
|
||||
GST_TYPE_ELEMENT_FACTORY);
|
||||
gst_plugin_feature_set_rank (feature, GST_RANK_PRIMARY + 100);
|
||||
|
||||
enc = gst_element_factory_make ("encodebin", NULL);
|
||||
if (enc == NULL)
|
||||
return;
|
||||
|
||||
/* Make sure encodebin finds mp4mux even though qtmux outputs a superset */
|
||||
cprof = create_mp4mux_profile ();
|
||||
g_object_set (enc, "profile", cprof, NULL);
|
||||
gst_encoding_profile_unref (cprof);
|
||||
|
||||
/* should have created a pad after setting the profile */
|
||||
pad = gst_element_get_static_pad (enc, "audio_0");
|
||||
fail_unless (pad != NULL);
|
||||
gst_object_unref (pad);
|
||||
|
||||
mux = gst_bin_get_by_interface (GST_BIN (enc), GST_TYPE_TAG_SETTER);
|
||||
fail_unless (mux != NULL);
|
||||
{
|
||||
GstElementFactory *f = gst_element_get_factory (mux);
|
||||
|
||||
/* make sure we got mp4mux for variant=iso */
|
||||
GST_INFO ("muxer: %s", G_OBJECT_TYPE_NAME (mux));
|
||||
fail_unless_equals_string (GST_PLUGIN_FEATURE_NAME (f), "mp4mux");
|
||||
}
|
||||
gst_object_unref (mux);
|
||||
gst_object_unref (enc);
|
||||
|
||||
gst_plugin_feature_set_rank (feature, GST_RANK_NONE);
|
||||
gst_object_unref (feature);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
qtmux_suite (void)
|
||||
{
|
||||
|
@ -582,7 +713,8 @@ qtmux_suite (void)
|
|||
tcase_add_test (tc_chain, test_audio_pad_frag_asc_streamable);
|
||||
|
||||
tcase_add_test (tc_chain, test_reuse);
|
||||
tcase_add_test (tc_chain, test_encodebin);
|
||||
tcase_add_test (tc_chain, test_encodebin_qtmux);
|
||||
tcase_add_test (tc_chain, test_encodebin_mp4mux);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue