summaryrefslogtreecommitdiff
path: root/gst-libs/gst/media-info/media-info-priv.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst-libs/gst/media-info/media-info-priv.c')
-rw-r--r--gst-libs/gst/media-info/media-info-priv.c641
1 files changed, 641 insertions, 0 deletions
diff --git a/gst-libs/gst/media-info/media-info-priv.c b/gst-libs/gst/media-info/media-info-priv.c
new file mode 100644
index 000000000..7c7767018
--- /dev/null
+++ b/gst-libs/gst/media-info/media-info-priv.c
@@ -0,0 +1,641 @@
+/* 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.
+ */
+
+/* media-info-priv.c - handling of internal stuff */
+
+#include <gst/gst.h>
+#include <string.h>
+#include "media-info.h"
+#include "media-info-priv.h"
+
+
+/* helper structs bits */
+GstMediaInfoStream *
+gmi_stream_new (void)
+{
+ GstMediaInfoStream *stream;
+
+ stream = (GstMediaInfoStream *) g_malloc (sizeof (GstMediaInfoStream));
+
+ stream->length_tracks = 0;
+ stream->length_time = 0;
+ stream->bitrate = 0;
+ stream->seekable = FALSE;
+ stream->path = NULL;
+ stream->mime = NULL;
+ stream->tracks = NULL;
+
+ return stream;
+}
+
+void
+gmi_stream_free (GstMediaInfoStream *stream)
+{
+ if (stream->mime) g_free (stream->mime);
+ /* FIXME: free tracks */
+ g_free (stream);
+}
+
+GstMediaInfoTrack *
+gmi_track_new (void)
+{
+ GstMediaInfoTrack *track;
+
+ track = g_malloc (sizeof (GstMediaInfoTrack));
+
+ track->metadata = NULL;
+ track->streaminfo = NULL;
+ track->format = NULL;
+ track->length_time = 0;
+ track->con_streams = NULL;
+
+ return track;
+}
+
+/**
+ * private functions
+ */
+
+/* callbacks */
+static void
+have_type_callback (GstElement *typefind, GstCaps *type, GstMediaInfoPriv *priv)
+{
+ priv->type = type;
+}
+
+void
+deep_notify_callback (GObject *object, GstObject *origin,
+ GParamSpec *pspec, GstMediaInfoPriv *priv)
+{
+ GValue value = { 0, };
+
+ if (strcmp (pspec->name, "metadata") == 0)
+ {
+ GMI_DEBUG("DEBUG: deep_notify: have metadata !\n");
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (G_OBJECT (origin), pspec->name, &value);
+ priv->metadata = g_value_peek_pointer (&value);
+ }
+ else if (strcmp (pspec->name, "caps") == 0)
+ {
+ /* check if we're getting it from the source we want it from */
+ if (GST_IS_PAD (origin) && GST_PAD (origin) == priv->decoder_pad)
+ {
+ GMI_DEBUG("DEBUG: deep_notify: have caps on decoder_pad !\n");
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (G_OBJECT (origin), pspec->name, &value);
+ priv->format = g_value_peek_pointer (&value);
+ }
+ else GMI_DEBUG("DEBUG: igoring caps on object %s:%s\n",
+ gst_object_get_name (gst_object_get_parent (origin)),
+ gst_object_get_name (origin));
+ }
+ else if (strcmp (pspec->name, "offset") == 0)
+ {
+ /* we REALLY ignore offsets, we hate them */
+ }
+ else if (strcmp (pspec->name, "streaminfo") == 0)
+ {
+ GMI_DEBUG("DEBUG: deep_notify: have streaminfo !\n");
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (G_OBJECT (origin), pspec->name, &value);
+ priv->streaminfo = g_value_peek_pointer (&value);
+ }
+ else GMI_DEBUG("DEBUG: ignoring notify of %s\n", pspec->name);
+}
+
+/* helpers */
+
+/* reset info to a state where it can be used to query for media info
+ * clear caps, metadata, and so on */
+void
+gmi_reset (GstMediaInfo *info)
+{
+ GstMediaInfoPriv *priv = info->priv;
+ /* clear out some stuff */
+ if (priv->format)
+ {
+ GMI_DEBUG ("unreffing priv->format, error before this ?\n");
+ gst_caps_unref (priv->format);
+ priv->format = NULL;
+ }
+ if (priv->metadata)
+ {
+ GMI_DEBUG ("unreffing priv->metadata, error before this ?\n");
+ gst_caps_unref (priv->metadata);
+ priv->metadata = NULL;
+ }
+ if (priv->stream)
+ {
+ GMI_DEBUG ("freeing priv->stream, error before this ?\n");
+ g_free (priv->stream);
+ priv->stream = NULL;
+ }
+ if (priv->location)
+ {
+ GMI_DEBUG ("freeing priv->location, error before this ?\n");
+ g_free (priv->location);
+ priv->location = NULL;
+ }
+ priv->flags = 0;
+ priv->state = GST_MEDIA_INFO_STATE_NULL;
+}
+
+/* seek to a track and reset metadata and streaminfo structs */
+gboolean
+gmi_seek_to_track (GstMediaInfo *info, long track)
+{
+ GstEvent *event;
+ GstFormat track_format = 0;
+ GstMediaInfoPriv *priv = info->priv;
+ gboolean res;
+
+ /* FIXME: consider more nicks as "track" */
+ track_format = gst_format_get_by_nick ("logical_stream");
+ if (track_format == 0) return FALSE;
+ GMI_DEBUG("Track format: %d\n", track_format);
+
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PLAYING)
+ == GST_STATE_FAILURE)
+ g_warning ("Couldn't set to play");
+ g_assert (GST_IS_PAD (info->priv->decoder_pad));
+ event = gst_event_new_seek (track_format |
+ GST_SEEK_METHOD_SET |
+ GST_SEEK_FLAG_FLUSH,
+ track);
+ res = gst_pad_send_event (info->priv->decoder_pad, event);
+ if (!res)
+ {
+ g_warning ("seek to logical track on pad %s:%s failed of element %s",
+ GST_DEBUG_PAD_NAME(info->priv->decoder_pad),
+ gst_element_get_factory((gst_pad_get_parent(info->priv->decoder_pad)))->details->longname);
+ return FALSE;
+ }
+ /* clear structs because of the seek */
+ if (priv->metadata)
+ {
+ gst_caps_unref (priv->metadata);
+ priv->metadata = NULL;
+ }
+ if (priv->streaminfo)
+ {
+ gst_caps_unref (priv->streaminfo);
+ priv->streaminfo = NULL;
+ }
+ return TRUE;
+}
+
+/* create a good decoder for this mime type */
+/* FIXME: maybe make this more generic with a type, so it can be used
+ * for taggers and other things as well */
+GstElement *
+gmi_get_decoder (GstMediaInfo *info, const char *mime)
+{
+ GstElement *decoder;
+ gchar *factory = NULL;
+
+ /* check if we have an active codec element in the hash table for this */
+ decoder = g_hash_table_lookup (info->priv->decoders, mime);
+ if (decoder == NULL)
+ {
+ GMI_DEBUG("DEBUG: no decoder in table, inserting one\n");
+ /* FIXME: please figure out proper mp3 mimetypes */
+ if ((strcmp (mime, "application/x-ogg") == 0) ||
+ (strcmp (mime, "application/ogg") == 0))
+ factory = g_strdup ("vorbisfile");
+ else if ((strcmp (mime, "audio/mpeg") == 0) ||
+ (strcmp (mime, "audio/x-mp3") == 0) ||
+ (strcmp (mime, "audio/mp3") == 0))
+ factory = g_strdup ("mad");
+ else if (strcmp (mime, "application/x-flac") == 0)
+ factory = g_strdup ("flacdec");
+ else if (strcmp (mime, "audio/x-wav") == 0)
+ factory = g_strdup ("wavparse");
+ else if (strcmp (mime, "audio/x-mod") == 0 ||
+ strcmp (mime, "audio/x-s3m") == 0 ||
+ strcmp (mime, "audio/x-xm") == 0 ||
+ strcmp (mime, "audio/x-it") == 0)
+ factory = g_strdup ("modplug");
+
+ if (factory == NULL)
+ return NULL;
+
+ GMI_DEBUG("DEBUG: using factory %s\n", factory);
+ decoder = gst_element_factory_make (factory, "decoder");
+ g_free (factory);
+
+ if (decoder)
+ {
+ g_hash_table_insert (info->priv->decoders, g_strdup (mime), decoder);
+ /* ref it so we don't lose it when removing from bin */
+ g_object_ref (GST_OBJECT (decoder));
+ }
+ }
+ return decoder;
+}
+
+/* set the decoder to be used for decoding
+ * install callback handlers
+ */
+void
+gmi_set_decoder (GstMediaInfo *info, GstElement *decoder)
+{
+ GstMediaInfoPriv *priv = info->priv;
+
+ /* set up pipeline and connect signal handlers */
+ priv->decoder = decoder;
+ gst_bin_add (GST_BIN (priv->pipeline), decoder);
+ if (!gst_element_link (priv->source, decoder))
+ g_warning ("Couldn't connect source and decoder\n");
+ /* FIXME: we should be connecting to ALL possible src pads */
+ if (!(priv->decoder_pad = gst_element_get_pad (decoder, "src")))
+ g_warning ("Couldn't get decoder pad\n");
+ if (!(priv->source_pad = gst_element_get_pad (priv->source, "src")))
+ g_warning ("Couldn't get source pad\n");
+}
+
+/* clear the decoder (if it was set)
+ */
+void
+gmi_clear_decoder (GstMediaInfo *info)
+{
+ if (info->priv->decoder)
+ {
+ /* clear up decoder */
+ /* FIXME: shouldn't need to set state here */
+ gst_element_set_state (info->priv->pipeline, GST_STATE_READY);
+ gst_element_unlink (info->priv->source, info->priv->decoder);
+ gst_bin_remove (GST_BIN (info->priv->pipeline), info->priv->decoder);
+ info->priv->decoder = NULL;
+ }
+}
+
+/****
+ * typefind functions
+ * find the type of a file and store it in the caps of the info
+ * FIXME: we might better return GstCaps instead of storing them
+ * internally */
+
+/* prepare for typefind, move from NULL to TYPEFIND */
+gboolean
+gmip_find_type_pre (GstMediaInfoPriv *priv)
+{
+ /* clear vars that need clearing */
+ if (priv->type)
+ {
+ /* we don't need to unref, this is done inside gsttypefind.c
+ gst_caps_unref (priv->type);
+ */
+ priv->type = NULL;
+ }
+
+ GMI_DEBUG("DEBUG: gmip_find_type_pre: start\n");
+ /* find out type */
+ /* FIXME: we could move caps for typefind out of struct and
+ * just use it through this function only */
+
+ gst_bin_add (GST_BIN (priv->pipeline), priv->typefind);
+ g_object_set (G_OBJECT (priv->source), "location", priv->location, NULL);
+ if (!gst_element_link (priv->source, priv->typefind))
+ g_warning ("Couldn't connect source and typefind\n");
+ g_signal_connect (G_OBJECT (priv->typefind), "have-type",
+ G_CALLBACK (have_type_callback), priv);
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PLAYING)
+ == GST_STATE_FAILURE)
+ {
+ g_warning ("Couldn't set to play");
+ return FALSE;
+ }
+ priv->state = GST_MEDIA_INFO_STATE_TYPEFIND;
+ return TRUE;
+}
+
+/* finish off typefind */
+gboolean
+gmip_find_type_post (GstMediaInfoPriv *priv)
+{
+ /*clear up typefind */
+ gst_element_set_state (priv->pipeline, GST_STATE_READY);
+ gst_element_unlink (priv->source, priv->typefind);
+ gst_bin_remove (GST_BIN (priv->pipeline), priv->typefind);
+
+ if (priv->type == NULL)
+ {
+ g_warning ("iteration ended, type not found !\n");
+ return FALSE;
+ }
+ priv->state = GST_MEDIA_INFO_STATE_STREAM;
+ return TRUE;
+}
+
+/* complete version */
+gboolean
+gmip_find_type (GstMediaInfoPriv *priv)
+{
+ if (!gmip_find_type_pre (priv))
+ return FALSE;
+ GMI_DEBUG("DEBUG: gmip_find_type: iterating\n");
+ while ((priv->type == NULL) &&
+ gst_bin_iterate (GST_BIN (priv->pipeline)))
+ GMI_DEBUG("+");
+ GMI_DEBUG("\n");
+ return gmip_find_type_post (priv);
+}
+
+/* FIXME: why not have these functions work on priv types ? */
+gboolean
+gmip_find_stream_pre (GstMediaInfoPriv *priv)
+{
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PLAYING)
+ == GST_STATE_FAILURE)
+ {
+ g_warning ("Couldn't set to play");
+ return FALSE;
+ }
+ priv->state = GST_MEDIA_INFO_STATE_STREAM;
+ return TRUE;
+}
+
+gboolean
+gmip_find_stream_post (GstMediaInfoPriv *priv)
+{
+ GstMediaInfoStream *stream = priv->stream;
+ const GstFormat *formats;
+ GstFormat track_format = 0;
+ GstFormat format;
+ gint64 value;
+ gboolean res;
+ glong bytes = 0;
+
+
+ GMI_DEBUG("gmip_find_stream_post: start\n");
+ /* find a format that matches the "track" concept */
+ /* FIXME: this is used in vorbis, but we might have to loop when
+ * more codecs have tracks */
+ track_format = gst_format_get_by_nick ("logical_stream");
+
+ /* get supported formats on decoder pad */
+ formats = gst_pad_get_formats (priv->decoder_pad);
+ while (*formats)
+ {
+ const GstFormatDefinition *definition;
+
+ format = *formats;
+
+ g_assert (GST_IS_PAD (priv->decoder_pad));
+ definition = gst_format_get_details (*formats);
+ GMI_DEBUG("trying to figure out length for format %s\n", definition->nick);
+
+ res = gst_pad_query (priv->decoder_pad, GST_QUERY_TOTAL,
+ &format, &value);
+
+ if (res)
+ {
+ switch (format)
+ {
+ case GST_FORMAT_TIME:
+ stream->length_time = value;
+ GMI_DEBUG(" total %s: %lld\n", definition->nick, value);
+ break;
+ case GST_FORMAT_DEFAULT:
+ case GST_FORMAT_BYTES:
+ break;
+ default:
+ /* separation is necessary because track_format doesn't resolve to
+ * int */
+ if (format == track_format)
+ {
+ stream->length_tracks = value;
+ GMI_DEBUG(" total %s: %lld\n", definition->nick, value);
+ }
+ else
+ GMI_DEBUG("unhandled format %s\n", definition->nick);
+ }
+ }
+ else
+ GMI_DEBUG("query didn't return result for %s\n", definition->nick);
+
+ formats++;
+ }
+ if (stream->length_tracks == 0) stream->length_tracks = 1;
+ /* now get number of bytes from the sink pad to get the bitrate */
+ format = GST_FORMAT_BYTES;
+ g_assert (GST_IS_PAD (priv->source_pad));
+ res = gst_pad_query (priv->source_pad, GST_QUERY_TOTAL,
+ &format, &value);
+ if (!res) g_warning ("Failed to query on sink pad !");
+ bytes = value;
+ GMI_DEBUG("bitrate calc: bytes gotten: %ld\n", bytes);
+
+ if (bytes)
+ {
+ double seconds = (double) stream->length_time / GST_SECOND;
+ double bits = bytes * 8;
+ stream->bitrate = (long) (bits / seconds);
+ }
+ priv->state = GST_MEDIA_INFO_STATE_METADATA; /* metadata of first track */
+ return TRUE;
+}
+
+/* get properties of complete physical stream
+ * and return them in pre-alloced stream struct in priv->stream */
+gboolean
+gmip_find_stream (GstMediaInfoPriv *priv)
+{
+ GMI_DEBUG("DEBUG:gmip_find_stream start\n");
+
+ gmip_find_stream_pre (priv);
+ /* iterate until caps are found */
+ /* FIXME: this should be done through the plugin sending some signal
+ * that it is ready for queries */
+ while (gst_bin_iterate (GST_BIN (priv->pipeline)) &&
+ priv->format == NULL)
+ ;
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PAUSED)
+ == GST_STATE_FAILURE)
+ g_warning ("Couldn't set to paused");
+
+ if (priv->format == NULL)
+ {
+ GMI_DEBUG("DEBUG: gmip_find_stream: couldn't get caps !");
+ return FALSE;
+ }
+ return gmip_find_stream_post (priv);
+}
+
+/* find metadata encoded in media and store in priv->metadata */
+gboolean
+gmip_find_track_metadata_pre (GstMediaInfoPriv *priv)
+{
+ /* FIXME: this is a hack to set max allowed iterations for metadata
+ * querying - we should make gst smarter by itself instead */
+ priv->metadata_iters = 0;
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PLAYING)
+ == GST_STATE_FAILURE)
+ {
+ g_warning ("Couldn't set to play");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+gmip_find_track_metadata_post (GstMediaInfoPriv *priv)
+{
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PAUSED)
+ == GST_STATE_FAILURE)
+ return FALSE;
+ priv->current_track->metadata = priv->metadata;
+ priv->metadata = NULL;
+ return TRUE;
+}
+
+gboolean
+gmip_find_track_metadata (GstMediaInfoPriv *priv)
+{
+ gmip_find_track_metadata_pre (priv);
+ GMI_DEBUG("DEBUG: gmip_find_metadata: iterating\n");
+ while ((priv->metadata == NULL) &&
+ gst_bin_iterate (GST_BIN (priv->pipeline)))
+ GMI_DEBUG("+");
+ GMI_DEBUG("\n");
+ gmip_find_track_metadata_post (priv);
+
+ return TRUE;
+}
+
+/* find streaminfo found by decoder and store in priv->streaminfo */
+/* FIXME: this is an exact copy, so reuse this functioin instead */
+gboolean
+gmip_find_track_streaminfo_pre (GstMediaInfoPriv *priv)
+{
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PLAYING)
+ == GST_STATE_FAILURE)
+ {
+ g_warning ("Couldn't set to play");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+gmip_find_track_streaminfo_post (GstMediaInfoPriv *priv)
+{
+ GstFormat format, track_format;
+
+ gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+
+ /* now add total length to this, and maybe even bitrate ? FIXME */
+ track_format = gst_format_get_by_nick ("logical_stream");
+ if (track_format == 0)
+ {
+ g_print ("FIXME: implement getting length of whole track\n");
+ }
+ else
+ {
+ /* which one are we at ? */
+ long track_num;
+ gint64 value_start, value_end;
+ gboolean res;
+
+ res = gst_pad_query (priv->decoder_pad, GST_QUERY_POSITION,
+ &track_format, &value_start);
+ if (res)
+ {
+ format = GST_FORMAT_TIME;
+ track_num = value_start;
+ GMI_DEBUG("DEBUG: we are currently at %ld\n", track_num);
+ res = gst_pad_convert (priv->decoder_pad,
+ track_format, track_num,
+ &format, &value_start);
+ res &= gst_pad_convert (priv->decoder_pad,
+ track_format, track_num + 1,
+ &format, &value_end);
+ if (res)
+ {
+ GstPropsEntry *length;
+ /* substract to get the length */
+ GMI_DEBUG("DEBUG: start %lld, end %lld\n", value_start, value_end);
+ value_end -= value_start;
+ /* FIXME: check units; this is in seconds */
+ length = gst_props_entry_new ("length",
+ GST_PROPS_INT ((int) (value_end / 1E6)));
+ gst_props_add_entry (gst_caps_get_props (priv->streaminfo), length);
+ }
+ }
+ }
+ priv->current_track->streaminfo = priv->streaminfo;
+ priv->streaminfo = NULL;
+
+ return TRUE;
+}
+
+gboolean
+gmip_find_track_streaminfo (GstMediaInfoPriv *priv)
+{
+ gmip_find_track_streaminfo_pre (priv);
+ GMI_DEBUG("DEBUG: gmip_find_streaminfo: iterating\n");
+ while ((priv->streaminfo == NULL) &&
+ gst_bin_iterate (GST_BIN (priv->pipeline)))
+ GMI_DEBUG("+");
+ GMI_DEBUG("\n");
+ gmip_find_track_streaminfo_post (priv);
+
+ return TRUE;
+}
+
+/* find format found by decoder and store in priv->format */
+gboolean
+gmip_find_track_format_pre (GstMediaInfoPriv *priv)
+{
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PLAYING)
+ == GST_STATE_FAILURE)
+ {
+ g_warning ("Couldn't set to play");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+gmip_find_track_format_post (GstMediaInfoPriv *priv)
+{
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PAUSED)
+ == GST_STATE_FAILURE)
+ return FALSE;
+ priv->current_track->format = priv->format;
+ priv->format = NULL;
+ return TRUE;
+}
+
+gboolean
+gmip_find_track_format (GstMediaInfoPriv *priv)
+{
+ gmip_find_track_format_pre (priv);
+ GMI_DEBUG("DEBUG: gmip_find_format: iterating\n");
+ while ((priv->format == NULL) &&
+ gst_bin_iterate (GST_BIN (priv->pipeline)))
+ GMI_DEBUG("+");
+ GMI_DEBUG("\n");
+ gmip_find_track_format_post (priv);
+
+ return TRUE;
+}
+
+