*
* 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.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* asks for the discovery to begin (through gst_discoverer_start()).
*
* All the information is returned in a #GstDiscovererInfo structure.
- *
- * Since: 0.10.31
*/
#ifdef HAVE_CONFIG_H
#endif
#include <gst/video/video.h>
+#include <gst/audio/audio.h>
#include "pbutils.h"
-#include "pbutils-marshal.h"
#include "pbutils-private.h"
GST_DEBUG_CATEGORY_STATIC (discoverer_debug);
static GQuark _CAPS_QUARK;
static GQuark _TAGS_QUARK;
+static GQuark _TOC_QUARK;
static GQuark _MISSING_PLUGIN_QUARK;
static GQuark _STREAM_TOPOLOGY_QUARK;
static GQuark _TOPOLOGY_PAD_QUARK;
GstElement *queue;
GstElement *sink;
GstTagList *tags;
+ GstToc *toc;
} PrivateStream;
struct _GstDiscovererPrivate
/* list of pending URI to process (current excluded) */
GList *pending_uris;
- GMutex *lock;
+ GMutex lock;
/* TRUE if processing a URI */
gboolean processing;
/* TRUE if discoverer has been started */
gboolean running;
+ /* TRUE if ASYNC_DONE has been received (need to check for subtitle tags) */
+ gboolean async_done;
+
/* current items */
GstDiscovererInfo *current_info;
GError *current_error;
/* List of private streams */
GList *streams;
+ /* List of these sinks and their handler IDs (to remove the probe) */
+ guint pending_subtitle_pads;
+
/* Global elements */
GstBin *pipeline;
GstElement *uridecodebin;
/* Handler ids for various callbacks */
gulong pad_added_id;
gulong pad_remove_id;
+ gulong source_chg_id;
gulong element_added_id;
gulong bus_cb_id;
};
-#define DISCO_LOCK(dc) g_mutex_lock (dc->priv->lock);
-#define DISCO_UNLOCK(dc) g_mutex_unlock (dc->priv->lock);
+#define DISCO_LOCK(dc) g_mutex_lock (&dc->priv->lock);
+#define DISCO_UNLOCK(dc) g_mutex_unlock (&dc->priv->lock);
static void
_do_init (void)
_CAPS_QUARK = g_quark_from_static_string ("caps");
_TAGS_QUARK = g_quark_from_static_string ("tags");
+ _TOC_QUARK = g_quark_from_static_string ("toc");
_MISSING_PLUGIN_QUARK = g_quark_from_static_string ("missing-plugin");
_STREAM_TOPOLOGY_QUARK = g_quark_from_static_string ("stream-topology");
_TOPOLOGY_PAD_QUARK = g_quark_from_static_string ("pad");
SIGNAL_FINISHED,
SIGNAL_STARTING,
SIGNAL_DISCOVERED,
+ SIGNAL_SOURCE_SETUP,
LAST_SIGNAL
};
GstDiscoverer * dc);
static void uridecodebin_pad_removed_cb (GstElement * uridecodebin,
GstPad * pad, GstDiscoverer * dc);
+static void uridecodebin_source_changed_cb (GstElement * uridecodebin,
+ GParamSpec * pspec, GstDiscoverer * dc);
static void gst_discoverer_dispose (GObject * dc);
+static void gst_discoverer_finalize (GObject * dc);
static void gst_discoverer_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_discoverer_get_property (GObject * object, guint prop_id,
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->dispose = gst_discoverer_dispose;
+ gobject_class->finalize = gst_discoverer_finalize;
gobject_class->set_property = gst_discoverer_set_property;
gobject_class->get_property = gst_discoverer_get_property;
/* properties */
/**
- * GstDiscoverer:timeout
+ * GstDiscoverer:timeout:
*
* The duration (in nanoseconds) after which the discovery of an individual
* URI will timeout.
* @discoverer: the #GstDiscoverer
* @info: the results #GstDiscovererInfo
* @error: (type GLib.Error): #GError, which will be non-NULL if an error
- * occurred during discovery
+ * occurred during discovery. You must not
+ * free this #GError, it will be freed by
+ * the discoverer.
+ *
+ * Will be emitted when all information on a URI could be discovered, or
+ * an error ocurred.
*
- * Will be emitted when all information on a URI could be discovered.
+ * When an error occurs, @info might still contain some partial information,
+ * depending on the circumstances of the error.
*/
gst_discoverer_signals[SIGNAL_DISCOVERED] =
g_signal_new ("discovered", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstDiscovererClass, discovered),
- NULL, NULL, pbutils_marshal_VOID__POINTER_BOXED,
- G_TYPE_NONE, 2, GST_TYPE_DISCOVERER_INFO, GST_TYPE_G_ERROR);
+ NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 2, GST_TYPE_DISCOVERER_INFO,
+ G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ /**
+ * GstDiscoverer::source-setup:
+ * @discoverer: the #GstDiscoverer
+ * @source: source element
+ *
+ * This signal is emitted after the source element has been created for, so
+ * the URI being discovered, 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).
+ *
+ * This signal is usually emitted from the context of a GStreamer streaming
+ * thread.
+ */
+ gst_discoverer_signals[SIGNAL_SOURCE_SETUP] =
+ g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDiscovererClass, source_setup),
+ NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
}
static void
dc->priv->timeout = DEFAULT_PROP_TIMEOUT;
dc->priv->async = FALSE;
+ dc->priv->async_done = FALSE;
+
+ g_mutex_init (&dc->priv->lock);
- dc->priv->lock = g_mutex_new ();
+ dc->priv->pending_subtitle_pads = 0;
GST_LOG ("Creating pipeline");
dc->priv->pipeline = (GstBin *) gst_pipeline_new ("Discoverer");
dc->priv->pad_remove_id =
g_signal_connect_object (dc->priv->uridecodebin, "pad-removed",
G_CALLBACK (uridecodebin_pad_removed_cb), dc, 0);
+ dc->priv->source_chg_id =
+ g_signal_connect_object (dc->priv->uridecodebin, "notify::source",
+ G_CALLBACK (uridecodebin_source_changed_cb), dc, 0);
GST_LOG_OBJECT (dc, "Getting pipeline bus");
dc->priv->bus = gst_pipeline_get_bus ((GstPipeline *) dc->priv->pipeline);
/* Workaround for bug #118536 */
DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->pad_added_id);
DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->pad_remove_id);
+ DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->source_chg_id);
DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->element_added_id);
DISCONNECT_SIGNAL (dc->priv->bus, dc->priv->bus_cb_id);
gst_discoverer_stop (dc);
- if (dc->priv->lock) {
- g_mutex_free (dc->priv->lock);
- dc->priv->lock = NULL;
- }
-
if (dc->priv->seeking_query) {
gst_query_unref (dc->priv->seeking_query);
dc->priv->seeking_query = NULL;
}
static void
+gst_discoverer_finalize (GObject * obj)
+{
+ GstDiscoverer *dc = (GstDiscoverer *) obj;
+
+ g_mutex_clear (&dc->priv->lock);
+
+ G_OBJECT_CLASS (gst_discoverer_parent_class)->finalize (obj);
+}
+
+static void
gst_discoverer_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
}
static GstPadProbeReturn
-_event_probe (GstPad * pad, GstPadProbeType type, GstEvent * event,
- PrivateStream * ps)
+_event_probe (GstPad * pad, GstPadProbeInfo * info, PrivateStream * ps)
{
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+
if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
GstTagList *tl = NULL, *tmp;
ps->tags);
tmp = gst_tag_list_merge (ps->tags, tl, GST_TAG_MERGE_APPEND);
if (ps->tags)
- gst_tag_list_free (ps->tags);
+ gst_tag_list_unref (ps->tags);
ps->tags = tmp;
GST_DEBUG_OBJECT (pad, "private stream %p new tags %" GST_PTR_FORMAT, ps,
tmp);
DISCO_UNLOCK (ps->dc);
}
+ if (GST_EVENT_TYPE (event) == GST_EVENT_TOC) {
+ GstToc *tmp;
+
+ gst_event_parse_toc (event, &tmp, NULL);
+ GST_DEBUG_OBJECT (pad, "toc %" GST_PTR_FORMAT, tmp);
+ DISCO_LOCK (ps->dc);
+ ps->toc = tmp;
+ if (G_LIKELY (ps->dc->priv->processing)) {
+ GST_DEBUG_OBJECT (pad, "private stream %p toc %" GST_PTR_FORMAT, ps, tmp);
+ } else
+ GST_DEBUG_OBJECT (pad, "Dropping toc since preroll is done");
+ DISCO_UNLOCK (ps->dc);
+ }
+
return GST_PAD_PROBE_OK;
}
+static GstStaticCaps subtitle_caps = GST_STATIC_CAPS ("text/x-raw; "
+ "subpicture/x-pgs; subpicture/x-dvb; subpicture/x-dvd; "
+ "application/x-subtitle-unknown; application/x-ssa; application/x-ass; "
+ "subtitle/x-kate; application/x-kate");
+
static gboolean
is_subtitle_caps (const GstCaps * caps)
{
- static GstCaps *subs_caps = NULL;
+ GstCaps *subs_caps;
+ gboolean ret;
+
+ subs_caps = gst_static_caps_get (&subtitle_caps);
+ ret = gst_caps_can_intersect (caps, subs_caps);
+ gst_caps_unref (subs_caps);
+
+ return ret;
+}
+
+static GstPadProbeReturn
+got_subtitle_data (GstPad * pad, GstPadProbeInfo * info, GstDiscoverer * dc)
+{
+
+ if (!(GST_IS_BUFFER (info->data) || (GST_IS_EVENT (info->data)
+ && GST_EVENT_TYPE ((GstEvent *) info->data) == GST_EVENT_GAP)))
+ return GST_PAD_PROBE_OK;
- if (!subs_caps) {
- subs_caps = gst_caps_from_string ("text/plain; text/x-pango-markup; "
- "subpicture/x-pgs; subpicture/x-dvb; application/x-subtitle-unknown; "
- "application/x-ssa; application/x-ass; subtitle/x-kate; "
- "application/x-kate; video/x-dvd-subpicture; ");
+
+ DISCO_LOCK (dc);
+
+ dc->priv->pending_subtitle_pads--;
+
+ if (dc->priv->pending_subtitle_pads == 0) {
+ GstMessage *msg = gst_message_new_application (NULL,
+ gst_structure_new_empty ("DiscovererDone"));
+ gst_element_post_message ((GstElement *) dc->priv->pipeline, msg);
}
+ DISCO_UNLOCK (dc);
- return gst_caps_can_intersect (caps, subs_caps);
+ return GST_PAD_PROBE_REMOVE;
+
+}
+
+static void
+uridecodebin_source_changed_cb (GstElement * uridecodebin,
+ GParamSpec * pspec, GstDiscoverer * dc)
+{
+ GstElement *src;
+ /* get a handle to the source */
+ g_object_get (uridecodebin, pspec->name, &src, NULL);
+
+ GST_DEBUG_OBJECT (dc, "got a new source %p", src);
+
+ g_signal_emit (dc, gst_discoverer_signals[SIGNAL_SOURCE_SETUP], 0, src);
+ gst_object_unref (src);
}
static void
g_object_set (ps->sink, "silent", TRUE, NULL);
g_object_set (ps->queue, "max-size-buffers", 1, "silent", TRUE, NULL);
- caps = gst_pad_get_caps (pad, NULL);
+ caps = gst_pad_query_caps (pad, NULL);
+
+ sinkpad = gst_element_get_static_pad (ps->queue, "sink");
+ if (sinkpad == NULL)
+ goto error;
if (is_subtitle_caps (caps)) {
/* Subtitle streams are sparse and may not provide any information - don't
* wait for data to preroll */
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ (GstPadProbeCallback) got_subtitle_data, dc, NULL);
g_object_set (ps->sink, "async", FALSE, NULL);
+ DISCO_LOCK (dc);
+ dc->priv->pending_subtitle_pads++;
+ DISCO_UNLOCK (dc);
}
gst_caps_unref (caps);
if (!gst_element_sync_state_with_parent (ps->queue))
goto error;
- sinkpad = gst_element_get_static_pad (ps->queue, "sink");
- if (sinkpad == NULL)
- goto error;
if (gst_pad_link_full (pad, sinkpad,
GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)
goto error;
gst_bin_remove_many (dc->priv->pipeline, ps->sink, ps->queue, NULL);
if (ps->tags) {
- gst_tag_list_free (ps->tags);
+ gst_tag_list_unref (ps->tags);
+ }
+ if (ps->toc) {
+ gst_toc_unref (ps->toc);
}
g_slice_free (PrivateStream, ps);
if (!caps) {
GST_WARNING ("Couldn't get negotiated caps from %s:%s",
GST_DEBUG_PAD_NAME (ps->pad));
- caps = gst_pad_get_caps (ps->pad, NULL);
+ caps = gst_pad_query_caps (ps->pad, NULL);
}
if (caps) {
GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps);
gst_caps_unref (caps);
}
if (ps->tags)
- gst_structure_id_set (st, _TAGS_QUARK, GST_TYPE_STRUCTURE, ps->tags, NULL);
+ gst_structure_id_set (st, _TAGS_QUARK, GST_TYPE_TAG_LIST, ps->tags, NULL);
+ if (ps->toc)
+ gst_structure_id_set (st, _TOC_QUARK, GST_TYPE_TOC, ps->toc, NULL);
return st;
}
+/* takes ownership of new_tags, may replace *taglist with a new one */
+static void
+gst_discoverer_merge_and_replace_tags (GstTagList ** taglist,
+ GstTagList * new_tags)
+{
+ if (new_tags == NULL)
+ return;
+
+ if (*taglist == NULL) {
+ *taglist = new_tags;
+ return;
+ }
+
+ gst_tag_list_insert (*taglist, new_tags, GST_TAG_MERGE_REPLACE);
+ gst_tag_list_unref (new_tags);
+}
+
/* Parses a set of caps and tags in st and populates a GstDiscovererStreamInfo
* structure (parent, if !NULL, otherwise it allocates one)
*/
GstDiscovererStreamInfo * parent)
{
GstCaps *caps;
- GstStructure *caps_st, *tags_st;
+ GstStructure *caps_st;
+ GstTagList *tags_st;
+ GstToc *toc_st;
const gchar *name;
int tmp;
guint utmp;
if (!st || !gst_structure_id_has_field (st, _CAPS_QUARK)) {
GST_WARNING ("Couldn't find caps !");
if (parent)
- return parent;
+ return gst_discoverer_stream_info_ref (parent);
else
return (GstDiscovererStreamInfo *)
g_object_new (GST_TYPE_DISCOVERER_STREAM_INFO, NULL);
if (g_str_has_prefix (name, "audio/")) {
GstDiscovererAudioInfo *info;
+ const gchar *format_str;
if (parent)
- info = (GstDiscovererAudioInfo *) parent;
+ info = (GstDiscovererAudioInfo *) gst_discoverer_stream_info_ref (parent);
else {
info = (GstDiscovererAudioInfo *)
g_object_new (GST_TYPE_DISCOVERER_AUDIO_INFO, NULL);
- info->parent.caps = caps;
+ info->parent.caps = gst_caps_ref (caps);
}
if (gst_structure_get_int (caps_st, "rate", &tmp))
if (gst_structure_get_int (caps_st, "channels", &tmp))
info->channels = (guint) tmp;
- if (gst_structure_get_int (caps_st, "depth", &tmp))
- info->depth = (guint) tmp;
+ /* FIXME: we only want to extract depth if raw audio is what's in the
+ * container (i.e. not if there is a decoder involved) */
+ format_str = gst_structure_get_string (caps_st, "format");
+ if (format_str != NULL) {
+ const GstAudioFormatInfo *finfo;
+ GstAudioFormat format;
+
+ format = gst_audio_format_from_string (format_str);
+ finfo = gst_audio_format_get_info (format);
+ info->depth = GST_AUDIO_FORMAT_INFO_DEPTH (finfo);
+ }
if (gst_structure_id_has_field (st, _TAGS_QUARK)) {
- gst_structure_id_get (st, _TAGS_QUARK,
- GST_TYPE_STRUCTURE, &tags_st, NULL);
- if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp) ||
- gst_structure_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp))
+ gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_TAG_LIST, &tags_st, NULL);
+ if (gst_tag_list_get_uint (tags_st, GST_TAG_BITRATE, &utmp) ||
+ gst_tag_list_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp))
info->bitrate = utmp;
- if (gst_structure_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp))
+ if (gst_tag_list_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp))
info->max_bitrate = utmp;
/* FIXME: Is it worth it to remove the tags we've parsed? */
- info->parent.tags = gst_tag_list_merge (info->parent.tags,
- (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE);
+ gst_discoverer_merge_and_replace_tags (&info->parent.tags, tags_st);
+ }
- gst_structure_free (tags_st);
+ if (gst_structure_id_has_field (st, _TOC_QUARK)) {
+ gst_structure_id_get (st, _TOC_QUARK, GST_TYPE_TOC, &toc_st, NULL);
+ info->parent.toc = toc_st;
}
if (!info->language && ((GstDiscovererStreamInfo *) info)->tags) {
}
}
+ gst_caps_unref (caps);
return (GstDiscovererStreamInfo *) info;
} else if (g_str_has_prefix (name, "video/") ||
GstVideoInfo vinfo;
if (parent)
- info = (GstDiscovererVideoInfo *) parent;
+ info = (GstDiscovererVideoInfo *) gst_discoverer_stream_info_ref (parent);
else {
info = (GstDiscovererVideoInfo *)
g_object_new (GST_TYPE_DISCOVERER_VIDEO_INFO, NULL);
- info->parent.caps = caps;
+ info->parent.caps = gst_caps_ref (caps);
}
- /* FIXME : gst_video_info_from_caps only works with raw caps,
- * wouldn't we want to get all the info below for non-raw caps ?
- */
- if (g_str_has_prefix (name, "video/x-raw") &&
- gst_video_info_from_caps (&vinfo, caps)) {
+ if (gst_video_info_from_caps (&vinfo, caps)) {
info->width = (guint) vinfo.width;
info->height = (guint) vinfo.height;
info->framerate_num = vinfo.fps_n;
info->framerate_denom = vinfo.fps_d;
- info->interlaced = (vinfo.flags & GST_VIDEO_FLAG_INTERLACED) != 0;
+ info->interlaced =
+ vinfo.interlace_mode != GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
}
if (gst_structure_id_has_field (st, _TAGS_QUARK)) {
- gst_structure_id_get (st, _TAGS_QUARK,
- GST_TYPE_STRUCTURE, &tags_st, NULL);
- if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp) ||
- gst_structure_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp))
+ gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_TAG_LIST, &tags_st, NULL);
+ if (gst_tag_list_get_uint (tags_st, GST_TAG_BITRATE, &utmp) ||
+ gst_tag_list_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp))
info->bitrate = utmp;
- if (gst_structure_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp))
+ if (gst_tag_list_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp))
info->max_bitrate = utmp;
/* FIXME: Is it worth it to remove the tags we've parsed? */
- info->parent.tags = gst_tag_list_merge (info->parent.tags,
- (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE);
- gst_structure_free (tags_st);
+ gst_discoverer_merge_and_replace_tags (&info->parent.tags,
+ (GstTagList *) tags_st);
}
+ if (gst_structure_id_has_field (st, _TOC_QUARK)) {
+ gst_structure_id_get (st, _TOC_QUARK, GST_TYPE_TOC, &toc_st, NULL);
+ info->parent.toc = toc_st;
+ }
+
+ gst_caps_unref (caps);
return (GstDiscovererStreamInfo *) info;
} else if (is_subtitle_caps (caps)) {
GstDiscovererSubtitleInfo *info;
if (parent)
- info = (GstDiscovererSubtitleInfo *) parent;
+ info =
+ (GstDiscovererSubtitleInfo *) gst_discoverer_stream_info_ref (parent);
else {
info = (GstDiscovererSubtitleInfo *)
g_object_new (GST_TYPE_DISCOVERER_SUBTITLE_INFO, NULL);
- info->parent.caps = caps;
+ info->parent.caps = gst_caps_ref (caps);
}
if (gst_structure_id_has_field (st, _TAGS_QUARK)) {
const gchar *language;
- gst_structure_id_get (st, _TAGS_QUARK,
- GST_TYPE_STRUCTURE, &tags_st, NULL);
+ gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_TAG_LIST, &tags_st, NULL);
language = gst_structure_get_string (caps_st, GST_TAG_LANGUAGE_CODE);
if (language)
info->language = g_strdup (language);
/* FIXME: Is it worth it to remove the tags we've parsed? */
- info->parent.tags = gst_tag_list_merge (info->parent.tags,
- (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE);
- gst_structure_free (tags_st);
+ gst_discoverer_merge_and_replace_tags (&info->parent.tags, tags_st);
+ }
+ if (gst_structure_id_has_field (st, _TOC_QUARK)) {
+ gst_structure_id_get (st, _TOC_QUARK, GST_TYPE_TOC, &toc_st, NULL);
+ info->parent.toc = toc_st;
}
if (!info->language && ((GstDiscovererStreamInfo *) info)->tags) {
}
}
+ gst_caps_unref (caps);
return (GstDiscovererStreamInfo *) info;
} else {
GstDiscovererStreamInfo *info;
if (parent)
- info = parent;
+ info = gst_discoverer_stream_info_ref (parent);
else {
info = (GstDiscovererStreamInfo *)
g_object_new (GST_TYPE_DISCOVERER_STREAM_INFO, NULL);
- info->caps = caps;
+ info->caps = gst_caps_ref (caps);
+ }
+
+ if (gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_TAG_LIST, &tags_st,
+ NULL)) {
+ gst_discoverer_merge_and_replace_tags (&info->tags, tags_st);
}
- if (gst_structure_id_get (st, _TAGS_QUARK,
- GST_TYPE_STRUCTURE, &tags_st, NULL)) {
- info->tags = gst_tag_list_merge (info->tags, (GstTagList *) tags_st,
- GST_TAG_MERGE_REPLACE);
- gst_structure_free (tags_st);
+ if (gst_structure_id_get (st, _TOC_QUARK, GST_TYPE_TOC, &toc_st, NULL)) {
+ info->toc = toc_st;
}
+ gst_caps_unref (caps);
return info;
}
GList *tmp;
if (!gst_structure_id_has_field (topology, _TOPOLOGY_PAD_QUARK)) {
- GST_DEBUG ("Could not find pad for node %" GST_PTR_FORMAT "\n", topology);
+ GST_DEBUG ("Could not find pad for node %" GST_PTR_FORMAT, topology);
return NULL;
}
gst_structure_id_get (topology, _TOPOLOGY_PAD_QUARK,
GST_TYPE_PAD, &pad, NULL);
- if (!dc->priv->streams)
+ if (!dc->priv->streams) {
+ gst_object_unref (pad);
return NULL;
+ }
for (i = 0, tmp = dc->priv->streams; tmp; tmp = tmp->next, i++) {
ps = (PrivateStream *) tmp->data;
}
static gboolean
-child_is_raw_stream (GstCaps * parent, GstCaps * child)
+child_is_raw_stream (const GstCaps * parent, const GstCaps * child)
{
- GstStructure *st1, *st2;
+ const GstStructure *st1, *st2;
const gchar *name1, *name2;
st1 = gst_caps_get_structure (parent, 0);
/* We sometimes get an extra sub-stream from the parser. If this is
* the case, we just replace the parent caps with this stream's caps
* since they might contain more information */
- gst_caps_unref (parent->caps);
- parent->caps = caps;
+ gst_caps_replace (&parent->caps, caps);
parse_stream_topology (dc, st, parent);
add_to_list = FALSE;
-
} else if (child_is_raw_stream (parent->caps, caps)) {
/* This is the "raw" stream corresponding to the parent. This
* contains more information than the parent, tags etc. */
parse_stream_topology (dc, st, parent);
add_to_list = FALSE;
- gst_caps_unref (caps);
-
} else {
GstDiscovererStreamInfo *next = parse_stream_topology (dc, st, NULL);
res->next = next;
next->previous = res;
}
+ gst_caps_unref (caps);
}
}
if (add_to_list) {
dc->priv->current_info->stream_list =
g_list_append (dc->priv->current_info->stream_list, res);
+ } else {
+ gst_discoverer_stream_info_unref (res);
}
} else if (GST_VALUE_HOLDS_LIST (nval)) {
GstTagList *tmp;
gst_structure_id_get (topology, _TAGS_QUARK,
- GST_TYPE_STRUCTURE, &tags, NULL);
+ GST_TYPE_TAG_LIST, &tags, NULL);
GST_DEBUG ("Merge tags %" GST_PTR_FORMAT, tags);
tmp =
gst_tag_list_merge (cont->parent.tags, (GstTagList *) tags,
GST_TAG_MERGE_APPEND);
- gst_tag_list_free (tags);
+ gst_tag_list_unref (tags);
if (cont->parent.tags)
- gst_tag_list_free (cont->parent.tags);
+ gst_tag_list_unref (cont->parent.tags);
cont->parent.tags = tmp;
GST_DEBUG ("Container info tags %" GST_PTR_FORMAT, tmp);
}
if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur)) {
GST_DEBUG ("Got duration %" GST_TIME_FORMAT, GST_TIME_ARGS (dur));
dc->priv->current_info->duration = (guint64) dur;
+ } else {
+ GstStateChangeReturn sret;
+
+ /* Some parsers may not even return a rough estimate right away, e.g.
+ * because they've only processed a single frame so far, so if we
+ * didn't get a duration the first time, spin a bit and try again.
+ * Ugly, but still better than making parsers or other elements return
+ * completely bogus values. We need some API extensions to solve this
+ * better. */
+ GST_INFO ("No duration yet, try a bit harder..");
+ sret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ if (sret != GST_STATE_CHANGE_FAILURE) {
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ g_usleep (G_USEC_PER_SEC / 20);
+ if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur)
+ && dur > 0) {
+ GST_DEBUG ("Got duration %" GST_TIME_FORMAT, GST_TIME_ARGS (dur));
+ dc->priv->current_info->duration = (guint64) dur;
+ break;
+ }
+ }
+ gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ }
}
if (dc->priv->seeking_query) {
if (dc->priv->current_info->duration == 0 &&
dc->priv->current_info->stream_info != NULL &&
dc->priv->current_info->stream_info->next == NULL) {
- GstStructure *st =
- gst_caps_get_structure (dc->priv->current_info->stream_info->caps, 0);
+ GstDiscovererStreamInfo *stream_info;
+ GstStructure *st;
+
+ stream_info = dc->priv->current_info->stream_info;
+ st = gst_caps_get_structure (stream_info->caps, 0);
if (g_str_has_prefix (gst_structure_get_name (st), "image/"))
- ((GstDiscovererVideoInfo *) dc->priv->current_info->stream_info)->
- is_image = TRUE;
+ ((GstDiscovererVideoInfo *) stream_info)->is_image = TRUE;
}
}
dc->priv->current_info, dc->priv->current_error);
/* Clients get a copy of current_info since it is a boxed type */
gst_discoverer_info_unref (dc->priv->current_info);
+ dc->priv->current_info = NULL;
}
}
/* We need to stop */
done = TRUE;
- GST_DEBUG ("Setting result to ERROR");
- dc->priv->current_info->result = GST_DISCOVERER_ERROR;
+ /* Don't override missing plugin result code for missing plugin errors */
+ if (dc->priv->current_info->result != GST_DISCOVERER_MISSING_PLUGINS ||
+ (!g_error_matches (gerr, GST_CORE_ERROR,
+ GST_CORE_ERROR_MISSING_PLUGIN) &&
+ !g_error_matches (gerr, GST_STREAM_ERROR,
+ GST_STREAM_ERROR_CODEC_NOT_FOUND))) {
+ GST_DEBUG ("Setting result to ERROR");
+ dc->priv->current_info->result = GST_DISCOVERER_ERROR;
+ }
}
break;
done = TRUE;
break;
+ case GST_MESSAGE_APPLICATION:{
+ const gchar *name;
+ gboolean async_done;
+ name = gst_structure_get_name (gst_message_get_structure (msg));
+ /* Maybe ASYNC_DONE is received & we're just waiting for subtitle tags */
+ DISCO_LOCK (dc);
+ async_done = dc->priv->async_done;
+ DISCO_UNLOCK (dc);
+ if (g_str_equal (name, "DiscovererDone") && async_done)
+ return TRUE;
+ break;
+ }
+
case GST_MESSAGE_ASYNC_DONE:
if (GST_MESSAGE_SRC (msg) == (GstObject *) dc->priv->pipeline) {
GST_DEBUG ("Finished changing state asynchronously");
- done = TRUE;
+ DISCO_LOCK (dc);
+ if (dc->priv->pending_subtitle_pads == 0) {
+ done = TRUE;
+ } else {
+ /* Remember that ASYNC_DONE has been received, wait for subtitles */
+ dc->priv->async_done = TRUE;
+ }
+ DISCO_UNLOCK (dc);
}
break;
GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg),
"Setting result to MISSING_PLUGINS");
dc->priv->current_info->result = GST_DISCOVERER_MISSING_PLUGINS;
+ if (dc->priv->current_info->misc)
+ gst_structure_free (dc->priv->current_info->misc);
dc->priv->current_info->misc = gst_structure_copy (structure);
} else if (sttype == _STREAM_TOPOLOGY_QUARK) {
+ if (dc->priv->current_topology)
+ gst_structure_free (dc->priv->current_topology);
dc->priv->current_topology = gst_structure_copy (structure);
}
}
tmp =
gst_tag_list_merge (dc->priv->current_info->tags, tl,
GST_TAG_MERGE_APPEND);
- gst_tag_list_free (tl);
+ gst_tag_list_unref (tl);
if (dc->priv->current_info->tags)
- gst_tag_list_free (dc->priv->current_info->tags);
+ gst_tag_list_unref (dc->priv->current_info->tags);
dc->priv->current_info->tags = tmp;
GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Current info %p, tags %"
GST_PTR_FORMAT, dc->priv->current_info, tmp);
}
break;
+ case GST_MESSAGE_TOC:
+ {
+ GstToc *tmp;
+
+ gst_message_parse_toc (msg, &tmp, NULL);
+ GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Got toc %" GST_PTR_FORMAT, tmp);
+ dc->priv->current_info->toc = tmp;
+ GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Current info %p, toc %"
+ GST_PTR_FORMAT, dc->priv->current_info, tmp);
+ }
+ break;
+
default:
break;
}
return done;
}
-
static void
handle_current_sync (GstDiscoverer * dc)
{
done = handle_message (dc, msg);
gst_message_unref (msg);
}
-
} while (!done && (g_timer_elapsed (timer, NULL) < deadline));
/* return result */
dc->priv->current_info = NULL;
+ dc->priv->pending_subtitle_pads = 0;
+ dc->priv->async_done = FALSE;
+
/* Try popping the next uri */
if (dc->priv->async) {
if (dc->priv->pending_uris != NULL) {
* Allow asynchronous discovering of URIs to take place.
* A #GMainLoop must be available for #GstDiscoverer to properly work in
* asynchronous mode.
- *
- * Since: 0.10.31
*/
void
gst_discoverer_start (GstDiscoverer * discoverer)
*
* Stop the discovery of any pending URIs and clears the list of
* pending URIS (if any).
- *
- * Since: 0.10.31
*/
void
gst_discoverer_stop (GstDiscoverer * discoverer)
* A copy of @uri will be made internally, so the caller can safely g_free()
* afterwards.
*
- * Returns: %TRUE if the @uri was succesfully appended to the list of pending
+ * Returns: %TRUE if the @uri was successfully appended to the list of pending
* uris, else %FALSE
- *
- * Since: 0.10.31
*/
gboolean
gst_discoverer_discover_uri_async (GstDiscoverer * discoverer,
*
* Returns: (transfer full): the result of the scanning. Can be %NULL if an
* error occurred.
- *
- * Since: 0.10.31
*/
GstDiscovererInfo *
gst_discoverer_discover_uri (GstDiscoverer * discoverer, const gchar * uri,
* If an error occurred when creating the discoverer, @err will be set
* accordingly and %NULL will be returned. If @err is set, the caller must
* free it when no longer needed using g_error_free().
- *
- * Since: 0.10.31
*/
GstDiscoverer *
gst_discoverer_new (GstClockTime timeout, GError ** err)