Commit 08044ab7 authored by Edward Hervey's avatar Edward Hervey Committed by Edward Hervey
Browse files

playback: New uridecodebin3 element

In the same vein as old uridecodebin except that it also
accepts a suburi and uses urisourcebin and decodebin3 internally
parent 28584006
......@@ -6,6 +6,7 @@ libgstplayback_la_SOURCES = \
gstdecodebin2.c \
gstdecodebin3.c \
gsturidecodebin.c \
gsturidecodebin3.c \
gsturisourcebin.c \
gstparsebin.c \
gstplayback.c \
......
......@@ -63,6 +63,7 @@ plugin_init (GstPlugin * plugin)
res &= gst_decode_bin_plugin_init (plugin);
res &= gst_decodebin3_plugin_init (plugin);
res &= gst_uri_decode_bin_plugin_init (plugin);
res &= gst_uri_decode_bin3_plugin_init (plugin);
res &= gst_uri_source_bin_plugin_init (plugin);
res &= gst_parse_bin_plugin_init (plugin);
......
......@@ -26,6 +26,7 @@
gboolean gst_decode_bin_plugin_init (GstPlugin * plugin);
gboolean gst_decodebin3_plugin_init (GstPlugin * plugin);
gboolean gst_uri_decode_bin_plugin_init (GstPlugin * plugin);
gboolean gst_uri_decode_bin3_plugin_init (GstPlugin * plugin);
gboolean gst_uri_source_bin_plugin_init (GstPlugin * plugin);
gboolean gst_parse_bin_plugin_init (GstPlugin * plugin);
......
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* TODO/FIXME:
*
* * BUFFERING MESSAGES
* ** How/Where do we deal with buffering messages from a new/prerolling
* source ? Ideally we want to re-use the same sourcebin ?
* ** Remember last buffering messages per source handler, if the SourceEntry
* group_id is the one being currently outputted on the source ghostpads,
* post the (last) buffering messages.
* If no group_id is being outputted (still prerolling), then output
* the messages directly
*
* * ASYNC HANDLING
* ** URIDECODEBIN3 is not async-aware.
*
* * GAPLESS HANDLING
* ** Correlate group_id and URI to know when/which stream is being outputted/started
*/
/**
* SECTION:element-uridecodebin3
* @title: uridecodebin3
*
* Decodes data from a URI into raw media. It selects a source element that can
* handle the given #GstURIDecodeBin3:uri scheme and connects it to a decodebin.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include <gst/gst-i18n-plugin.h>
#include <gst/pbutils/missing-plugins.h>
#include "gstplay-enum.h"
#include "gstrawcaps.h"
#include "gstplayback.h"
#include "gstplaybackutils.h"
#define GST_TYPE_URI_DECODE_BIN3 \
(gst_uri_decode_bin3_get_type())
#define GST_URI_DECODE_BIN3(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3))
#define GST_URI_DECODE_BIN3_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3Class))
#define GST_IS_URI_DECODE_BIN3(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN3))
#define GST_IS_URI_DECODE_BIN3_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN3))
#define GST_URI_DECODE_BIN3_CAST(obj) ((GstURIDecodeBin3 *) (obj))
typedef struct _GstSourceGroup GstSourceGroup;
typedef struct _GstURIDecodeBin3 GstURIDecodeBin3;
typedef struct _GstURIDecodeBin3Class GstURIDecodeBin3Class;
#define GST_URI_DECODE_BIN3_LOCK(dec) (g_mutex_lock(&((GstURIDecodeBin3*)(dec))->lock))
#define GST_URI_DECODE_BIN3_UNLOCK(dec) (g_mutex_unlock(&((GstURIDecodeBin3*)(dec))->lock))
typedef struct _GstPlayItem GstPlayItem;
typedef struct _GstSourceItem GstSourceItem;
typedef struct _GstSourceHandler GstSourceHandler;
typedef struct _OutputPad OutputPad;
/* A structure describing a play item, which travels through the elements
* over time. */
struct _GstPlayItem
{
GstURIDecodeBin3 *uridecodebin;
/* Main URI */
GstSourceItem *main_item;
/* Auxiliary URI */
/* FIXME : Replace by a list later */
GstSourceItem *sub_item;
/* The group_id used to identify this play item via STREAM_START events
* This is the group_id which will be used externally (i.e. rewritten
* to outgoing STREAM_START events and in emitted signals).
* The urisourcebin-specific group_id is located in GstSourceItem */
guint group_id;
/* Is this play item the one being currently outputted by decodebin3
* and on our source ghostpads */
gboolean currently_outputted;
};
struct _GstSourceItem
{
/* The GstPlayItem to which this GstSourceItem belongs to */
GstPlayItem *play_item;
gchar *uri;
/* The urisourcebin controlling this uri
* Can be NULL */
GstSourceHandler *handler;
/* Last buffering information */
gint last_perc;
GstMessage *last_buffering_message;
/* The groupid created by urisourcebin for this uri */
guint internal_groupid;
/* FIXME : Add tag lists and other uri-specific items here ? */
};
/* Structure wrapping everything related to a urisourcebin */
struct _GstSourceHandler
{
GstURIDecodeBin3 *uridecodebin;
GstElement *urisourcebin;
/* Signal handlers */
gulong pad_added_id;
gulong pad_removed_id;
gulong source_setup_id;
gulong about_to_finish_id;
/* TRUE if the controlled urisourcebin was added to uridecodebin */
gboolean active;
/* whether urisourcebin is drained or not.
* Reset if/when setting a new URI */
gboolean drained;
/* Whether urisourcebin posted EOS on all pads and
* there is no pending entry */
gboolean is_eos;
/* TRUE if the urisourcebin handles main item */
gboolean is_main_source;
/* buffering message stored for after switching */
GstMessage *pending_buffering_msg;
};
/* Controls an output source pad */
struct _OutputPad
{
GstURIDecodeBin3 *uridecodebin;
GstPad *target_pad;
GstPad *ghost_pad;
/* Downstream event probe id */
gulong probe_id;
/* TRUE if the pad saw EOS. Resetted to FALSE on STREAM_START */
gboolean is_eos;
/* The last seen (i.e. current) group_id
* Can be (guint)-1 if no group_id was seen yet */
guint current_group_id;
};
/**
* GstURIDecodeBin3
*
* uridecodebin3 element struct
*/
struct _GstURIDecodeBin3
{
GstBin parent_instance;
GMutex lock; /* lock for constructing */
/* Properties */
GstElement *source;
guint64 connection_speed; /* In bits/sec (0 = unknown) */
GstCaps *caps;
guint64 buffer_duration; /* When buffering, buffer duration (ns) */
guint buffer_size; /* When buffering, buffer size (bytes) */
gboolean download;
gboolean use_buffering;
guint64 ring_buffer_max_size;
GList *play_items; /* List of GstPlayItem ordered by time of
* creation. Head of list is therefore the
* current (or pending if initial) one being
* outputted */
GstPlayItem *current; /* Currently active GstPlayItem. Can be NULL
* if no entry is active yet (i.e. no source
* pads) */
/* sources.
* FIXME : Replace by a more modular system later on */
GstSourceHandler *main_handler;
GstSourceHandler *sub_handler;
/* URI handling
* FIXME : Switch to a playlist-based API */
gchar *uri;
gboolean uri_changed; /* TRUE if uri changed */
gchar *suburi;
gboolean suburi_changed; /* TRUE if suburi changed */
/* A global decodebin3 that's used to actually do decoding */
GstElement *decodebin;
/* db3 signals */
gulong db_pad_added_id;
gulong db_pad_removed_id;
gulong db_select_stream_id;
gulong db_about_to_finish_id;
GList *output_pads; /* List of OutputPad */
GList *source_handlers; /* List of SourceHandler */
/* Whether we already signalled about-to-finish or not
* FIXME: Track this by group-id ! */
gboolean posted_about_to_finish;
};
struct _GstURIDecodeBin3Class
{
GstBinClass parent_class;
gint (*select_stream) (GstURIDecodeBin3 * dbin,
GstStreamCollection * collection, GstStream * stream);
};
GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin3_debug);
#define GST_CAT_DEFAULT gst_uri_decode_bin3_debug
/* signals */
enum
{
SIGNAL_SELECT_STREAM,
SIGNAL_SOURCE_SETUP,
SIGNAL_ABOUT_TO_FINISH,
LAST_SIGNAL
};
#if 0
static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
#endif
/* properties */
#define DEFAULT_PROP_URI NULL
#define DEFAULT_PROP_SUBURI NULL
#define DEFAULT_PROP_SOURCE NULL
#define DEFAULT_CONNECTION_SPEED 0
#define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps))
#define DEFAULT_SUBTITLE_ENCODING NULL
#define DEFAULT_BUFFER_DURATION -1
#define DEFAULT_BUFFER_SIZE -1
#define DEFAULT_DOWNLOAD FALSE
#define DEFAULT_USE_BUFFERING FALSE
#define DEFAULT_RING_BUFFER_MAX_SIZE 0
enum
{
PROP_0,
PROP_URI,
PROP_CURRENT_URI,
PROP_SUBURI,
PROP_CURRENT_SUBURI,
PROP_SOURCE,
PROP_SUBTITLE_ENCODING,
PROP_CONNECTION_SPEED,
PROP_BUFFER_SIZE,
PROP_BUFFER_DURATION,
PROP_DOWNLOAD,
PROP_USE_BUFFERING,
PROP_RING_BUFFER_MAX_SIZE,
PROP_CAPS
};
static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 };
static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
static GstStaticPadTemplate video_src_template =
GST_STATIC_PAD_TEMPLATE ("video_%u",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate audio_src_template =
GST_STATIC_PAD_TEMPLATE ("audio_%u",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate text_src_template =
GST_STATIC_PAD_TEMPLATE ("text_%u",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS_ANY);
GType gst_uri_decode_bin3_get_type (void);
#define gst_uri_decode_bin3_parent_class parent_class
G_DEFINE_TYPE (GstURIDecodeBin3, gst_uri_decode_bin3, GST_TYPE_BIN);
#define REMOVE_SIGNAL(obj,id) \
if (id) { \
g_signal_handler_disconnect (obj, id); \
id = 0; \
}
static void gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_uri_decode_bin3_finalize (GObject * obj);
static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement *
element, GstStateChange transition);
static gboolean
_gst_int_accumulator (GSignalInvocationHint * ihint,
GValue * return_accu, const GValue * handler_return, gpointer dummy)
{
gint res = g_value_get_int (handler_return);
if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
g_value_set_int (return_accu, res);
if (res == -1)
return TRUE;
return FALSE;
}
static void
gst_uri_decode_bin3_class_init (GstURIDecodeBin3Class * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gobject_class->set_property = gst_uri_decode_bin3_set_property;
gobject_class->get_property = gst_uri_decode_bin3_get_property;
gobject_class->finalize = gst_uri_decode_bin3_finalize;
g_object_class_install_property (gobject_class, PROP_URI,
g_param_spec_string ("uri", "URI", "URI to decode",
DEFAULT_PROP_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_CURRENT_URI,
g_param_spec_string ("current-uri", "Current URI",
"The currently playing URI", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SUBURI,
g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_CURRENT_SUBURI,
g_param_spec_string ("current-suburi", "Current .sub-URI",
"The currently playing URI of a subtitle",
NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SOURCE,
g_param_spec_object ("source", "Source", "Source object used",
GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SUBTITLE_ENCODING,
g_param_spec_string ("subtitle-encoding", "subtitle encoding",
"Encoding to assume if input subtitles are not in UTF-8 encoding. "
"If not set, the GST_SUBTITLE_ENCODING environment variable will "
"be checked for an encoding to use. If that is not set either, "
"ISO-8859-15 will be assumed.", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
g_param_spec_uint64 ("connection-speed", "Connection Speed",
"Network connection speed in kbps (0 = unknown)",
0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
g_param_spec_int ("buffer-size", "Buffer size (bytes)",
"Buffer size when buffering streams (-1 default value)",
-1, G_MAXINT, DEFAULT_BUFFER_SIZE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BUFFER_DURATION,
g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
"Buffer duration when buffering streams (-1 default value)",
-1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstURIDecodeBin3::download:
*
* For certain media type, enable download buffering.
*/
g_object_class_install_property (gobject_class, PROP_DOWNLOAD,
g_param_spec_boolean ("download", "Download",
"Attempt download buffering when buffering network streams",
DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstURIDecodeBin3::use-buffering:
*
* Emit BUFFERING messages based on low-/high-percent thresholds of the
* demuxed or parsed data.
* When download buffering is activated and used for the current media
* type, this property does nothing. Otherwise perform buffering on the
* demuxed or parsed media.
*/
g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
g_param_spec_boolean ("use-buffering", "Use Buffering",
"Perform buffering on demuxed/parsed media",
DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstURIDecodeBin3::ring-buffer-max-size
*
* The maximum size of the ring buffer in kilobytes. If set to 0, the ring
* buffer is disabled. Default is 0.
*/
g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
g_param_spec_uint64 ("ring-buffer-max-size",
"Max. ring buffer size (bytes)",
"Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_CAPS,
g_param_spec_boxed ("caps", "Caps",
"The caps on which to stop decoding. (NULL = default)",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstURIDecodebin3::select-stream
* @decodebin: a #GstURIDecodebin3
* @collection: a #GstStreamCollection
* @stream: a #GstStream
*
* This signal is emitted whenever @decodebin needs to decide whether
* to expose a @stream of a given @collection.
*
* Returns: 1 if the stream should be selected, 0 if it shouldn't be selected.
* A value of -1 (default) lets @decodebin decide what to do with the stream.
* */
gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM] =
g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBin3Class, select_stream),
_gst_int_accumulator, NULL, g_cclosure_marshal_generic,
G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
/**
* GstURIDecodeBin3::source-setup:
* @bin: the uridecodebin.
* @source: source element
*
* This signal is emitted after a source element has been created, so
* it can be configured by setting additional properties (e.g. set a
* proxy server for an http source, or set the device and read speed for
* an audio cd source).
*/
gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP] =
g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
/**
* GstURIDecodeBin3::about-to-finish:
*
* This signal is emitted when the data for the selected URI is
* entirely buffered and it is safe to specify anothe URI.
*/
gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
0, G_TYPE_NONE);
gst_element_class_add_static_pad_template (gstelement_class,
&video_src_template);
gst_element_class_add_static_pad_template (gstelement_class,
&audio_src_template);
gst_element_class_add_static_pad_template (gstelement_class,
&text_src_template);
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
gst_element_class_set_static_metadata (gstelement_class,
"URI Decoder", "Generic/Bin/Decoder",
"Autoplug and decode an URI to raw media",
"Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>");
gstelement_class->change_state = gst_uri_decode_bin3_change_state;
}
static GstPadProbeReturn
db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output)
{
/* FIXME : IMPLEMENT */
/* EOS : Mark pad as EOS */
/* STREAM_START : Store group_id and check if currently active
* PlayEntry changed */
return GST_PAD_PROBE_OK;
}
static OutputPad *
add_output_pad (GstURIDecodeBin3 * dec, GstPad * target_pad)
{
OutputPad *output;
gchar *pad_name;
output = g_slice_new0 (OutputPad);
GST_LOG_OBJECT (dec, "Created output %p", output);
output->uridecodebin = dec;
output->target_pad = target_pad;
output->current_group_id = (guint) - 1;
pad_name = gst_pad_get_name (target_pad);
output->ghost_pad = gst_ghost_pad_new (pad_name, target_pad);
g_free (pad_name);
gst_pad_set_active (output->ghost_pad, TRUE);
gst_element_add_pad (GST_ELEMENT (dec), output->ghost_pad);
output->probe_id =
gst_pad_add_probe (output->target_pad,
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) db_src_probe,
output, NULL);
/* FIXME: LOCK TO PROTECT PAD LIST */
dec->output_pads = g_list_append (dec->output_pads, output);
return output;
}
static void
db_pad_added_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
{
GST_DEBUG_OBJECT (dec, "Wrapping new pad %s:%s", GST_DEBUG_PAD_NAME (pad));
if (GST_PAD_IS_SRC (pad))
add_output_pad (dec, pad);
}
static void
db_pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
{
GList *tmp;
OutputPad *output = NULL;
GST_DEBUG_OBJECT (dec, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
/* FIXME: LOCK for list access */
for (tmp = dec->output_pads; tmp; tmp = tmp->next) {
OutputPad *cand = (OutputPad *) tmp->data;
if (cand->target_pad == pad) {
output = cand;
dec->output_pads = g_list_remove_link (dec->output_pads, tmp);
break;
}
}
if (output) {
GST_LOG_OBJECT (element, "Removing output %p", output);
/* Remove source ghost pad */
gst_ghost_pad_set_target ((GstGhostPad *) output->ghost_pad, NULL);
gst_element_remove_pad ((GstElement *) dec, output->ghost_pad);
/* FIXME : Update global/current PlayEntry group_id (did we switch ?) */
/* Remove event probe */
gst_pad_remove_probe (output->target_pad, output->probe_id);
g_slice_free (OutputPad, output);
}