gstreamer/gst/playback/gstplaybasebin.c
Ronald S. Bultje 6fd179b578 ext/flac/gstflacdec.c: Only return true if we actually filled something in. Prevents player applications from showing...
Original commit message from CVS:
* ext/flac/gstflacdec.c: (gst_flacdec_src_query):
Only return true if we actually filled something in. Prevents
player applications from showing a random length for flac files.
* gst-libs/gst/riff/riff-read.c: (gst_riff_read_class_init),
(gst_riff_read_use_event), (gst_riff_read_handle_event),
(gst_riff_read_seek), (gst_riff_read_skip), (gst_riff_read_strh),
(gst_riff_read_strf_vids_with_data),
(gst_riff_read_strf_auds_with_data), (gst_riff_read_strf_iavs):
OK, ok, so I implemented event handling. Apparently it's normal
that we receive random events at random points without asking
for it.
* gst/avi/gstavidemux.c: (gst_avi_demux_reset),
(gst_avi_demux_src_convert), (gst_avi_demux_handle_src_query),
(gst_avi_demux_handle_src_event), (gst_avi_demux_stream_index),
(gst_avi_demux_sync), (gst_avi_demux_stream_scan),
(gst_avi_demux_massage_index), (gst_avi_demux_stream_header),
(gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry),
(gst_avi_demux_stream_data), (gst_avi_demux_loop):
* gst/avi/gstavidemux.h:
Implement non-lineair chunk handling and subchunk processing.
The first solves playback of AVI files where the audio and video
data of individual buffers that we read are not synchronized.
This should not happen according to the wonderful AVI specs, but
of course it does happen in reality. It is also a prerequisite for
the second. Subchunk processing allows us to cut chunks in small
pieces and process each of these pieces separately. This is
required because I've seen several AVI files with incredibly large
audio chunks, even some files with only one audio chunk for the
whole file. This allows for proper playback including seeking.
This patch is supposed to fix all AVI A/V sync issues.
* gst/flx/gstflxdec.c: (gst_flxdec_class_init),
(flx_decode_chunks), (flx_decode_color), (gst_flxdec_loop):
Work.
* gst/modplug/gstmodplug.cc:
Proper return value setting for the query() function.
* gst/playback/gstplaybasebin.c: (setup_source):
Being in non-playing state (after, e.g., EOS) is not necessarily
a bad thing. Allow for that. This fixes playback of short files.
They don't actually playback fully now, because the clock already
runs. This means that small files (<500kB) with a small length
(<2sec) will still not or barely play. Other files, such as mod
or flx, will work correctly, however.
2004-09-29 09:45:40 +00:00

785 lines
24 KiB
C

/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstplaybasebin.h"
GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug);
#define GST_CAT_DEFAULT gst_play_base_bin_debug
#define DEFAULT_QUEUE_SIZE (3 * GST_SECOND)
/* props */
enum
{
ARG_0,
ARG_URI,
ARG_THREADED,
ARG_NSTREAMS,
ARG_QUEUE_SIZE,
ARG_STREAMINFO,
};
/* signals */
enum
{
MUTE_STREAM_SIGNAL,
LINK_STREAM_SIGNAL,
UNLINK_STREAM_SIGNAL,
LAST_SIGNAL
};
static void gst_play_base_bin_class_init (GstPlayBaseBinClass * klass);
static void gst_play_base_bin_init (GstPlayBaseBin * play_base_bin);
static void gst_play_base_bin_dispose (GObject * object);
static void gst_play_base_bin_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * spec);
static void gst_play_base_bin_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * spec);
static GstElementStateReturn gst_play_base_bin_change_state (GstElement *
element);
static void gst_play_base_bin_add_element (GstBin * bin, GstElement * element);
static void gst_play_base_bin_remove_element (GstBin * bin,
GstElement * element);
extern GstElementStateReturn gst_element_set_state_func (GstElement * element,
GstElementState state);
static void gst_play_base_bin_error (GstElement * element,
GstElement * source, GError * error, gchar * debug, gpointer data);
static void gst_play_base_bin_found_tag (GstElement * element,
GstElement * source, const GstTagList * taglist, gpointer data);
static GstElementClass *element_class;
static GstElementClass *parent_class;
static guint gst_play_base_bin_signals[LAST_SIGNAL] = { 0 };
GType
gst_play_base_bin_get_type (void)
{
static GType gst_play_base_bin_type = 0;
if (!gst_play_base_bin_type) {
static const GTypeInfo gst_play_base_bin_info = {
sizeof (GstPlayBaseBinClass),
NULL,
NULL,
(GClassInitFunc) gst_play_base_bin_class_init,
NULL,
NULL,
sizeof (GstPlayBaseBin),
0,
(GInstanceInitFunc) gst_play_base_bin_init,
NULL
};
gst_play_base_bin_type = g_type_register_static (GST_TYPE_BIN,
"GstPlayBaseBin", &gst_play_base_bin_info, 0);
}
return gst_play_base_bin_type;
}
static void
gst_play_base_bin_class_init (GstPlayBaseBinClass * klass)
{
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
GstBinClass *gstbin_klass;
gobject_klass = (GObjectClass *) klass;
gstelement_klass = (GstElementClass *) klass;
gstbin_klass = (GstBinClass *) klass;
element_class = g_type_class_ref (gst_element_get_type ());
parent_class = g_type_class_ref (gst_bin_get_type ());
gobject_klass->set_property = gst_play_base_bin_set_property;
gobject_klass->get_property = gst_play_base_bin_get_property;
g_object_class_install_property (gobject_klass, ARG_URI,
g_param_spec_string ("uri", "URI", "URI of the media to play",
NULL, G_PARAM_READWRITE));
g_object_class_install_property (gobject_klass, ARG_NSTREAMS,
g_param_spec_int ("nstreams", "NStreams", "number of streams",
0, G_MAXINT, 0, G_PARAM_READABLE));
g_object_class_install_property (gobject_klass, ARG_QUEUE_SIZE,
g_param_spec_uint64 ("queue-size", "Queue size",
"Size of internal queues in nanoseconds", 0, G_MAXINT64,
DEFAULT_QUEUE_SIZE, G_PARAM_READABLE));
g_object_class_install_property (gobject_klass, ARG_STREAMINFO,
g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo",
G_PARAM_READABLE));
GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0,
"playbasebin");
gst_play_base_bin_signals[MUTE_STREAM_SIGNAL] =
g_signal_new ("mute-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPlayBaseBinClass, mute_stream),
NULL, NULL, gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2,
G_TYPE_OBJECT, G_TYPE_BOOLEAN);
gst_play_base_bin_signals[LINK_STREAM_SIGNAL] =
g_signal_new ("link-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPlayBaseBinClass, link_stream),
NULL, NULL, gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2,
G_TYPE_OBJECT, GST_TYPE_PAD);
gst_play_base_bin_signals[UNLINK_STREAM_SIGNAL] =
g_signal_new ("unlink-stream", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstPlayBaseBinClass, unlink_stream),
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 1, G_TYPE_OBJECT);
gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_base_bin_dispose);
/* we handle state changes like an element */
gstelement_klass->set_state = GST_ELEMENT_CLASS (element_class)->set_state;
gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state);
gstbin_klass->add_element = GST_DEBUG_FUNCPTR (gst_play_base_bin_add_element);
gstbin_klass->remove_element =
GST_DEBUG_FUNCPTR (gst_play_base_bin_remove_element);
klass->mute_stream = gst_play_base_bin_mute_stream;
klass->link_stream = gst_play_base_bin_link_stream;
klass->unlink_stream = gst_play_base_bin_unlink_stream;
}
static void
gst_play_base_bin_init (GstPlayBaseBin * play_base_bin)
{
play_base_bin->uri = NULL;
play_base_bin->threaded = FALSE;
play_base_bin->need_rebuild = TRUE;
play_base_bin->source = NULL;
play_base_bin->decoder = NULL;
play_base_bin->preroll_lock = g_mutex_new ();
play_base_bin->preroll_cond = g_cond_new ();
play_base_bin->preroll_elems = NULL;
play_base_bin->queue_size = DEFAULT_QUEUE_SIZE;
GST_FLAG_SET (play_base_bin, GST_BIN_SELF_SCHEDULABLE);
}
static void
gst_play_base_bin_dispose (GObject * object)
{
GstPlayBaseBin *play_base_bin;
play_base_bin = GST_PLAY_BASE_BIN (object);
g_free (play_base_bin->uri);
play_base_bin->uri = NULL;
if (G_OBJECT_CLASS (parent_class)->dispose) {
G_OBJECT_CLASS (parent_class)->dispose (object);
}
}
static void
queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin)
{
GST_DEBUG ("queue %s overrun", gst_element_get_name (element));
g_mutex_lock (play_base_bin->preroll_lock);
GST_DEBUG ("signal preroll done");
g_cond_signal (play_base_bin->preroll_cond);
GST_DEBUG ("signaled preroll done");
g_mutex_unlock (play_base_bin->preroll_lock);
}
static GstElement *
gen_preroll_element (GstPlayBaseBin * play_base_bin, GstPad * pad)
{
GstElement *element;
gchar *name;
name = g_strdup_printf ("preroll_%s", gst_pad_get_name (pad));
element = gst_element_factory_make ("queue", name);
g_object_set (G_OBJECT (element), "max-size-buffers", 0, NULL);
g_object_set (G_OBJECT (element), "max-size-bytes", 0, NULL);
g_object_set (G_OBJECT (element), "max-size-time", 3 * GST_SECOND, NULL);
g_signal_connect (G_OBJECT (element), "overrun",
G_CALLBACK (queue_overrun), play_base_bin);
g_free (name);
return element;
}
static void
remove_prerolls (GstPlayBaseBin * play_base_bin)
{
GList *prerolls, *infos;
for (prerolls = play_base_bin->preroll_elems; prerolls;
prerolls = g_list_next (prerolls)) {
GstElement *element = GST_ELEMENT (prerolls->data);
GST_LOG ("removing preroll element %s", gst_element_get_name (element));
gst_bin_remove (GST_BIN (play_base_bin->thread), element);
}
g_list_free (play_base_bin->preroll_elems);
play_base_bin->preroll_elems = NULL;
for (infos = play_base_bin->streaminfo; infos; infos = g_list_next (infos)) {
GstStreamInfo *info = GST_STREAM_INFO (infos->data);
g_object_unref (info);
}
g_list_free (play_base_bin->streaminfo);
play_base_bin->streaminfo = NULL;
play_base_bin->nstreams = 0;
play_base_bin->naudiopads = 0;
play_base_bin->nvideopads = 0;
play_base_bin->nunknownpads = 0;
}
static void
unknown_type (GstElement * element, GstCaps * caps,
GstPlayBaseBin * play_base_bin)
{
gchar *capsstr = gst_caps_to_string (caps);
g_warning ("don't know how to handle %s", capsstr);
g_free (capsstr);
}
static void
no_more_pads (GstElement * element, GstPlayBaseBin * play_base_bin)
{
GST_DEBUG ("no more pads");
g_mutex_lock (play_base_bin->preroll_lock);
GST_DEBUG ("signal preroll done");
g_cond_signal (play_base_bin->preroll_cond);
GST_DEBUG ("signaled preroll done");
g_mutex_unlock (play_base_bin->preroll_lock);
}
static void
new_decoded_pad (GstElement * element, GstPad * pad, gboolean last,
GstPlayBaseBin * play_base_bin)
{
GstStructure *structure;
const gchar *mimetype;
GstCaps *caps;
GstElement *new_element = NULL;
GstStreamInfo *info;
GstStreamType type;
GstPad *srcpad;
gboolean need_preroll;
GST_DEBUG ("play base: new decoded pad");
caps = gst_pad_get_caps (pad);
if (gst_caps_is_empty (caps)) {
g_warning ("no type on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
return;
}
structure = gst_caps_get_structure (caps, 0);
mimetype = gst_structure_get_name (structure);
play_base_bin->nstreams++;
need_preroll = FALSE;
if (g_str_has_prefix (mimetype, "audio/")) {
type = GST_STREAM_TYPE_AUDIO;
play_base_bin->naudiopads++;
/* first audio pad gets a preroll element */
if (play_base_bin->naudiopads == 1) {
need_preroll = TRUE;
}
} else if (g_str_has_prefix (mimetype, "video/")) {
type = GST_STREAM_TYPE_VIDEO;
play_base_bin->nvideopads++;
/* first video pad gets a preroll element */
if (play_base_bin->nvideopads == 1) {
need_preroll = TRUE;
}
} else {
type = GST_STREAM_TYPE_UNKNOWN;
play_base_bin->nunknownpads++;
}
if (last || !need_preroll) {
srcpad = pad;
} else {
new_element = gen_preroll_element (play_base_bin, pad);
srcpad = gst_element_get_pad (new_element, "src");
gst_bin_add (GST_BIN (play_base_bin->thread), new_element);
play_base_bin->threaded = TRUE;
play_base_bin->preroll_elems =
g_list_prepend (play_base_bin->preroll_elems, new_element);
gst_pad_link (pad, gst_element_get_pad (new_element, "sink"));
gst_element_set_state (new_element, GST_STATE_PAUSED);
}
/* add the stream to the list */
info = gst_stream_info_new (srcpad, type, NULL);
play_base_bin->streaminfo = g_list_append (play_base_bin->streaminfo, info);
/* signal the no more pads after adding the stream */
if (last)
no_more_pads (NULL, play_base_bin);
}
static void
state_change (GstElement * element,
GstElementState old_state, GstElementState new_state, gpointer data)
{
GstPlayBaseBin *play_base_bin = GST_PLAY_BASE_BIN (data);
if (old_state > new_state) {
/* EOS or error occurred */
GST_DEBUG ("state changed downwards");
g_mutex_lock (play_base_bin->preroll_lock);
GST_DEBUG ("signal preroll done");
g_cond_signal (play_base_bin->preroll_cond);
GST_DEBUG ("signaled preroll done");
g_mutex_unlock (play_base_bin->preroll_lock);
}
}
static gboolean
setup_source (GstPlayBaseBin * play_base_bin)
{
GstElement *old_src;
if (!play_base_bin->need_rebuild)
return TRUE;
old_src = play_base_bin->source;
play_base_bin->source =
gst_element_make_from_uri (GST_URI_SRC, play_base_bin->uri, "source");
if (!play_base_bin->source) {
g_warning ("don't know how to read %s", play_base_bin->uri);
play_base_bin->source = old_src;
return FALSE;
} else {
if (old_src) {
GST_LOG ("removing old src element %s", gst_element_get_name (old_src));
gst_bin_remove (GST_BIN (play_base_bin->thread), old_src);
}
gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->source);
}
{
gboolean res;
gint sig1, sig2, sig3, sig4;
GstElement *old_dec;
old_dec = play_base_bin->decoder;
play_base_bin->decoder = gst_element_factory_make ("decodebin", "decoder");
if (!play_base_bin->decoder) {
g_warning ("can't find decoder element");
play_base_bin->decoder = old_dec;
return FALSE;
} else {
if (old_dec) {
GST_LOG ("removing old decoder element %s",
gst_element_get_name (old_dec));
gst_bin_remove (GST_BIN (play_base_bin->thread), old_dec);
}
gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->decoder);
}
remove_prerolls (play_base_bin);
res =
gst_element_link_pads (play_base_bin->source, "src",
play_base_bin->decoder, "sink");
if (!res) {
g_warning ("can't link source to typefind element");
return FALSE;
}
sig1 = g_signal_connect (G_OBJECT (play_base_bin->decoder),
"new-decoded-pad", G_CALLBACK (new_decoded_pad), play_base_bin);
sig2 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "no-more-pads",
G_CALLBACK (no_more_pads), play_base_bin);
sig3 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "unknown-type",
G_CALLBACK (unknown_type), play_base_bin);
/* either when the queues are filled or when the decoder element has no more
* dynamic streams, the cond is unlocked. We can remove the signal handlers then
*/
g_mutex_lock (play_base_bin->preroll_lock);
if (gst_element_set_state (play_base_bin->thread, GST_STATE_PLAYING) ==
GST_STATE_SUCCESS) {
GST_DEBUG ("waiting for preroll...");
sig4 = g_signal_connect (G_OBJECT (play_base_bin->thread),
"state-change", G_CALLBACK (state_change), play_base_bin);
g_cond_wait (play_base_bin->preroll_cond, play_base_bin->preroll_lock);
GST_DEBUG ("preroll done !");
} else {
GST_DEBUG ("state change failed, media cannot be loaded");
sig4 = 0;
}
g_mutex_unlock (play_base_bin->preroll_lock);
if (sig4 != 0)
g_signal_handler_disconnect (G_OBJECT (play_base_bin->thread), sig4);
g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig3);
g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig2);
g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig1);
play_base_bin->need_rebuild = FALSE;
}
return TRUE;
}
static void
gst_play_base_bin_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstPlayBaseBin *play_base_bin;
g_return_if_fail (GST_IS_PLAY_BASE_BIN (object));
play_base_bin = GST_PLAY_BASE_BIN (object);
switch (prop_id) {
case ARG_URI:
{
const gchar *uri = g_value_get_string (value);
if (uri == NULL) {
g_warning ("cannot set NULL uri");
return;
}
/* if we have no previous uri, or the new uri is different from the
* old one, replug */
if (play_base_bin->uri == NULL || strcmp (play_base_bin->uri, uri) != 0) {
g_free (play_base_bin->uri);
play_base_bin->uri = g_strdup (uri);
GST_DEBUG ("setting new uri to %s", uri);
play_base_bin->need_rebuild = TRUE;
}
break;
}
case ARG_QUEUE_SIZE:
play_base_bin->queue_size = g_value_get_uint64 (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_play_base_bin_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstPlayBaseBin *play_base_bin;
g_return_if_fail (GST_IS_PLAY_BASE_BIN (object));
play_base_bin = GST_PLAY_BASE_BIN (object);
switch (prop_id) {
case ARG_URI:
g_value_set_string (value, play_base_bin->uri);
break;
case ARG_NSTREAMS:
g_value_set_int (value, play_base_bin->nstreams);
break;
case ARG_QUEUE_SIZE:
g_value_set_uint64 (value, play_base_bin->queue_size);
break;
case ARG_STREAMINFO:
g_value_set_pointer (value, play_base_bin->streaminfo);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
play_base_eos (GstBin * bin, GstPlayBaseBin * play_base_bin)
{
no_more_pads (GST_ELEMENT (bin), play_base_bin);
gst_element_set_eos (GST_ELEMENT (play_base_bin));
}
static GstElementStateReturn
gst_play_base_bin_change_state (GstElement * element)
{
GstElementStateReturn ret = GST_STATE_SUCCESS;
GstPlayBaseBin *play_base_bin;
play_base_bin = GST_PLAY_BASE_BIN (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY:
{
GstScheduler *sched;
play_base_bin->thread = gst_thread_new ("internal_thread");
sched = gst_scheduler_factory_make ("opt", play_base_bin->thread);
if (sched) {
gst_element_set_scheduler (play_base_bin->thread, sched);
//gst_object_set_parent (GST_OBJECT (play_base_bin->thread), GST_OBJECT (play_base_bin));
gst_element_set_state (play_base_bin->thread, GST_STATE_READY);
g_signal_connect (G_OBJECT (play_base_bin->thread), "eos",
G_CALLBACK (play_base_eos), play_base_bin);
g_signal_connect (play_base_bin->thread, "found_tag",
G_CALLBACK (gst_play_base_bin_found_tag), play_base_bin);
} else {
g_warning ("could not get 'opt' scheduler");
gst_object_unref (GST_OBJECT (play_base_bin->thread));
play_base_bin->thread = NULL;
ret = GST_STATE_FAILURE;
}
break;
}
case GST_STATE_READY_TO_PAUSED:
{
if (!setup_source (play_base_bin)) {
GST_ELEMENT_ERROR (GST_ELEMENT (play_base_bin), STREAM,
CODEC_NOT_FOUND,
("cannot open file \"%s\"", play_base_bin->uri), (NULL));
ret = GST_STATE_FAILURE;
} else if (!play_base_bin->streaminfo) {
GST_ELEMENT_ERROR (GST_ELEMENT (play_base_bin), STREAM,
CODEC_NOT_FOUND,
("Failed to find any supported stream in file \"%s\"",
play_base_bin->uri), (NULL));
ret = GST_STATE_FAILURE;
} else {
ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED);
}
if (ret == GST_STATE_SUCCESS) {
/* error forwarding:
* we only connect after the stream has been set up. If that failed,
* we simply emit our own error. This also prevents us from failing
* because one stream was unrecognized. */
g_signal_connect (play_base_bin->thread, "error",
G_CALLBACK (gst_play_base_bin_error), play_base_bin);
}
break;
}
case GST_STATE_PAUSED_TO_PLAYING:
ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PLAYING);
break;
case GST_STATE_PLAYING_TO_PAUSED:
ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED);
break;
case GST_STATE_PAUSED_TO_READY:
g_signal_handlers_disconnect_by_func (play_base_bin->thread,
G_CALLBACK (gst_play_base_bin_error), play_base_bin);
ret = gst_element_set_state (play_base_bin->thread, GST_STATE_READY);
play_base_bin->need_rebuild = TRUE;
break;
case GST_STATE_READY_TO_NULL:
gst_object_unref (GST_OBJECT (play_base_bin->thread));
break;
default:
break;
}
if (ret == GST_STATE_SUCCESS) {
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
}
return ret;
}
static void
gst_play_base_bin_add_element (GstBin * bin, GstElement * element)
{
GstPlayBaseBin *play_base_bin;
play_base_bin = GST_PLAY_BASE_BIN (bin);
if (play_base_bin->thread) {
GstScheduler *sched;
GstClock *clock;
if (play_base_bin->threaded) {
gchar *name;
GstElement *thread;
name = g_strdup_printf ("thread_%s", gst_element_get_name (element));
thread = gst_thread_new (name);
g_free (name);
gst_bin_add (GST_BIN (thread), element);
element = thread;
}
gst_bin_add (GST_BIN (play_base_bin->thread), element);
/* hack */
sched = gst_element_get_scheduler (GST_ELEMENT (play_base_bin->thread));
clock = gst_scheduler_get_clock (sched);
gst_scheduler_set_clock (sched, clock);
/* FIXME set element to READY so that negotiation can happen. This
* currently fails because of weird negotiation problems. */
/* gst_element_set_state (element, GST_STATE_READY); */
} else {
g_warning ("adding elements is not allowed in NULL");
}
}
static void
gst_play_base_bin_remove_element (GstBin * bin, GstElement * element)
{
GstPlayBaseBin *play_base_bin;
play_base_bin = GST_PLAY_BASE_BIN (bin);
if (play_base_bin->thread) {
if (play_base_bin->threaded) {
gchar *name;
GstElement *thread;
name = g_strdup_printf ("thread_%s", gst_element_get_name (element));
thread = gst_bin_get_by_name (GST_BIN (play_base_bin->thread), name);
g_free (name);
if (!thread) {
g_warning ("cannot find element to remove");
} else {
element = thread;
}
}
GST_LOG ("removing element %s", gst_element_get_name (element));
gst_bin_remove (GST_BIN (play_base_bin->thread), element);
} else {
g_warning ("removing elements is not allowed in NULL");
}
}
static void
gst_play_base_bin_error (GstElement * element,
GstElement * _source, GError * error, gchar * debug, gpointer data)
{
GstObject *source, *parent;
source = GST_OBJECT (_source);
parent = GST_OBJECT (data);
/* tell ourselves */
gst_object_ref (source);
gst_object_ref (parent);
GST_DEBUG ("forwarding error \"%s\" from %s to %s", error->message,
GST_ELEMENT_NAME (source), GST_OBJECT_NAME (parent));
g_signal_emit_by_name (G_OBJECT (parent), "error", source, error, debug);
GST_DEBUG ("forwarded error \"%s\" from %s to %s", error->message,
GST_ELEMENT_NAME (source), GST_OBJECT_NAME (parent));
gst_object_unref (source);
gst_object_unref (parent);
}
static void
gst_play_base_bin_found_tag (GstElement * element,
GstElement * _source, const GstTagList * taglist, gpointer data)
{
GstObject *source, *parent;
source = GST_OBJECT (_source);
parent = GST_OBJECT (data);
/* tell ourselves */
gst_object_ref (source);
gst_object_ref (parent);
GST_DEBUG ("forwarding taglist %p from %s to %s", taglist,
GST_ELEMENT_NAME (source), GST_OBJECT_NAME (parent));
g_signal_emit_by_name (G_OBJECT (parent), "found-tag", source, taglist);
GST_DEBUG ("forwarded taglist %p from %s to %s", taglist,
GST_ELEMENT_NAME (source), GST_OBJECT_NAME (parent));
gst_object_unref (source);
gst_object_unref (parent);
}
void
gst_play_base_bin_mute_stream (GstPlayBaseBin * play_base_bin,
GstStreamInfo * info, gboolean mute)
{
GST_DEBUG ("mute");
}
void
gst_play_base_bin_link_stream (GstPlayBaseBin * play_base_bin,
GstStreamInfo * info, GstPad * pad)
{
if (info == NULL) {
GList *streams;
for (streams = play_base_bin->streaminfo; streams;
streams = g_list_next (streams)) {
GstStreamInfo *sinfo = (GstStreamInfo *) streams->data;
if (gst_pad_is_linked (sinfo->pad))
continue;
if (gst_pad_can_link (sinfo->pad, pad)) {
info = sinfo;
break;
}
}
}
if (info) {
if (!gst_pad_link (info->pad, pad)) {
GST_DEBUG ("could not link");
gst_play_base_bin_mute_stream (play_base_bin, info, TRUE);
}
} else {
GST_DEBUG ("could not find pad to link");
}
}
void
gst_play_base_bin_unlink_stream (GstPlayBaseBin * play_base_bin,
GstStreamInfo * info)
{
GST_DEBUG ("unlink");
}
const GList *
gst_play_base_bin_get_streaminfo (GstPlayBaseBin * play_base_bin)
{
return play_base_bin->streaminfo;
}