diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index c2e90336a6..41a1d001cb 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -2271,6 +2271,7 @@ gst_discoverer_info_get_result gst_discoverer_info_get_stream_info gst_discoverer_info_get_stream_list gst_discoverer_info_get_tags +gst_discoverer_info_get_toc gst_discoverer_info_get_uri gst_discoverer_info_get_seekable gst_discoverer_info_ref @@ -2286,6 +2287,7 @@ gst_discoverer_stream_info_get_misc gst_discoverer_stream_info_get_next gst_discoverer_stream_info_get_previous gst_discoverer_stream_info_get_tags +gst_discoverer_stream_info_get_toc gst_discoverer_stream_info_ref gst_discoverer_stream_info_unref gst_discoverer_stream_info_list_free diff --git a/gst-libs/gst/pbutils/gstdiscoverer-types.c b/gst-libs/gst/pbutils/gstdiscoverer-types.c index 5b63305565..098899ff03 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer-types.c +++ b/gst-libs/gst/pbutils/gstdiscoverer-types.c @@ -67,6 +67,9 @@ gst_discoverer_stream_info_finalize (GObject * object) if (info->tags) gst_tag_list_free (info->tags); + if (info->toc) + gst_toc_free (info->toc); + if (info->misc) gst_structure_free (info->misc); } @@ -126,6 +129,9 @@ gst_discoverer_info_copy_int (GstDiscovererStreamInfo * info, if (info->tags) ret->tags = gst_tag_list_copy (info->tags); + if (info->toc) + ret->toc = gst_toc_copy (info->toc); + if (info->misc) ret->misc = gst_structure_copy (info->misc); @@ -361,6 +367,9 @@ gst_discoverer_info_finalize (GObject * object) if (info->tags) gst_tag_list_free (info->tags); + + if (info->toc) + gst_toc_free (info->toc); } static GstDiscovererInfo * @@ -412,6 +421,9 @@ gst_discoverer_info_copy (GstDiscovererInfo * ptr) if (ptr->tags) ret->tags = gst_tag_list_copy (ptr->tags); + if (ptr->toc) + ret->toc = gst_toc_copy (ptr->toc); + g_hash_table_destroy (stream_map); return ret; } @@ -648,7 +660,6 @@ gst_discoverer_stream_info_get_caps (GstDiscovererStreamInfo * info) * * Since: 0.10.31 */ - const GstTagList * gst_discoverer_stream_info_get_tags (GstDiscovererStreamInfo * info) { @@ -657,6 +668,23 @@ gst_discoverer_stream_info_get_tags (GstDiscovererStreamInfo * info) return info->tags; } +/** + * gst_discoverer_stream_info_get_toc: + * @info: a #GstDiscovererStreamInfo + * + * Returns: (transfer none): the TOC contained in this stream. If you wish to + * use the TOC after the life-time of @info you will need to copy it. + * + * Since: 0.11.92 + */ +const GstToc * +gst_discoverer_stream_info_get_toc (GstDiscovererStreamInfo * info) +{ + g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL); + + return info->toc; +} + /** * gst_discoverer_stream_info_get_misc: * @info: a #GstDiscovererStreamInfo @@ -1057,6 +1085,18 @@ DISCOVERER_INFO_ACCESSOR_CODE (misc, const GstStructure *, NULL); DISCOVERER_INFO_ACCESSOR_CODE (tags, const GstTagList *, NULL); +/** + * gst_discoverer_info_get_toc: + * @info: a #GstDiscovererInfo + * + * Returns: (transfer none): TOC contained in the URI. If you wish to use + * the TOC after the life-time of @info, you will need to copy it. + * + * Since: 0.11.92 + */ + +DISCOVERER_INFO_ACCESSOR_CODE (toc, const GstToc *, NULL); + /** * gst_discoverer_info_ref: * @info: a #GstDiscovererInfo diff --git a/gst-libs/gst/pbutils/gstdiscoverer.c b/gst-libs/gst/pbutils/gstdiscoverer.c index 94d18234f4..16e8089e9b 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer.c +++ b/gst-libs/gst/pbutils/gstdiscoverer.c @@ -57,6 +57,7 @@ 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; @@ -69,6 +70,7 @@ typedef struct GstElement *queue; GstElement *sink; GstTagList *tags; + GstToc *toc; } PrivateStream; struct _GstDiscovererPrivate @@ -135,6 +137,7 @@ _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"); @@ -449,6 +452,20 @@ _event_probe (GstPad * pad, GstPadProbeInfo * info, PrivateStream * ps) 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; } @@ -616,6 +633,9 @@ uridecodebin_pad_removed_cb (GstElement * uridecodebin, GstPad * pad, if (ps->tags) { gst_tag_list_free (ps->tags); } + if (ps->toc) { + gst_toc_free (ps->toc); + } g_slice_free (PrivateStream, ps); @@ -648,6 +668,8 @@ collect_stream_information (GstDiscoverer * dc, PrivateStream * ps, guint idx) } if (ps->tags) 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; } @@ -679,6 +701,7 @@ collect_information (GstDiscoverer * dc, const GstStructure * st, GstCaps *caps; GstStructure *caps_st; GstTagList *tags_st; + GstToc *toc_st; const gchar *name; int tmp; guint utmp; @@ -729,6 +752,11 @@ collect_information (GstDiscoverer * dc, const GstStructure * 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) { gchar *language; if (gst_tag_list_get_string (((GstDiscovererStreamInfo *) info)->tags, @@ -787,6 +815,11 @@ collect_information (GstDiscoverer * dc, const GstStructure * st, (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; @@ -815,6 +848,11 @@ collect_information (GstDiscoverer * dc, const GstStructure * 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) { gchar *language; if (gst_tag_list_get_string (((GstDiscovererStreamInfo *) info)->tags, @@ -843,6 +881,10 @@ collect_information (GstDiscoverer * dc, const GstStructure * st, gst_discoverer_merge_and_replace_tags (&info->tags, 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; } @@ -1285,6 +1327,18 @@ handle_message (GstDiscoverer * dc, GstMessage * msg) } 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; } diff --git a/gst-libs/gst/pbutils/gstdiscoverer.h b/gst-libs/gst/pbutils/gstdiscoverer.h index 611bc89419..28d3ae48e0 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer.h +++ b/gst-libs/gst/pbutils/gstdiscoverer.h @@ -50,7 +50,6 @@ GType gst_discoverer_stream_info_get_type (void); * * As a simple example, if you run #GstDiscoverer on an AVI file with one audio * and one video stream, you will get a #GstDiscovererContainerInfo - * corresponding to the AVI container, which in turn will have a * #GstDiscovererAudioInfo sub-stream and a #GstDiscovererVideoInfo sub-stream * for the audio and video streams respectively. * @@ -63,6 +62,7 @@ GstDiscovererStreamInfo* gst_discoverer_stream_info_get_previous(GstDiscovererSt GstDiscovererStreamInfo* gst_discoverer_stream_info_get_next(GstDiscovererStreamInfo* info); GstCaps* gst_discoverer_stream_info_get_caps(GstDiscovererStreamInfo* info); const GstTagList* gst_discoverer_stream_info_get_tags(GstDiscovererStreamInfo* info); +const GstToc* gst_discoverer_stream_info_get_toc(GstDiscovererStreamInfo* info); const GstStructure* gst_discoverer_stream_info_get_misc(GstDiscovererStreamInfo* info); const gchar * gst_discoverer_stream_info_get_stream_type_nick(GstDiscovererStreamInfo* info); @@ -214,7 +214,8 @@ GList* gst_discoverer_info_get_stream_list(GstDiscovererInfo* GstClockTime gst_discoverer_info_get_duration(const GstDiscovererInfo* info); gboolean gst_discoverer_info_get_seekable(const GstDiscovererInfo* info); const GstStructure* gst_discoverer_info_get_misc(const GstDiscovererInfo* info); -const GstTagList* gst_discoverer_info_get_tags(const GstDiscovererInfo* info); +const GstTagList* gst_discoverer_info_get_tags(const GstDiscovererInfo* info); +const GstToc* gst_discoverer_info_get_toc(const GstDiscovererInfo* info); GList * gst_discoverer_info_get_streams (GstDiscovererInfo *info, GType streamtype); diff --git a/gst-libs/gst/pbutils/pbutils-private.h b/gst-libs/gst/pbutils/pbutils-private.h index 7efa6abe35..48eea55d19 100644 --- a/gst-libs/gst/pbutils/pbutils-private.h +++ b/gst-libs/gst/pbutils/pbutils-private.h @@ -26,6 +26,7 @@ struct _GstDiscovererStreamInfo { GstCaps *caps; GstTagList *tags; + GstToc *toc; GstStructure *misc; gpointer _gst_reserved[GST_PADDING]; @@ -96,6 +97,7 @@ struct _GstDiscovererInfo { GstClockTime duration; GstStructure *misc; GstTagList *tags; + GstToc *toc; gboolean seekable; gpointer _gst_reserved[GST_PADDING]; diff --git a/tools/gst-discoverer.c b/tools/gst-discoverer.c index e1a4cd8c93..ee07b4241d 100644 --- a/tools/gst-discoverer.c +++ b/tools/gst-discoverer.c @@ -28,6 +28,7 @@ static gboolean async = FALSE; static gboolean silent = FALSE; +static gboolean show_toc = FALSE; static gboolean verbose = FALSE; typedef struct @@ -339,10 +340,59 @@ print_tag_each (GQuark field_id, const GValue * value, gpointer user_data) return TRUE; } +static void +print_tag_foreach (const GstTagList * tags, const gchar * tag, + gpointer user_data) +{ + GValue val = { 0, }; + gchar *str; + gint depth = GPOINTER_TO_INT (user_data); + + gst_tag_list_copy_value (&val, tags, tag); + + if (G_VALUE_HOLDS_STRING (&val)) + str = g_value_dup_string (&val); + else + str = gst_value_serialize (&val); + + g_print ("%*s%s: %s\n", 2 * depth, " ", gst_tag_get_nick (tag), str); + g_free (str); + + g_value_unset (&val); +} + +#define MAX_INDENT 40 + +static void +print_toc_entry (gpointer data, gpointer user_data) +{ + GstTocEntry *entry = (GstTocEntry *) data; + gint depth = GPOINTER_TO_INT (user_data); + guint indent = MIN (GPOINTER_TO_UINT (user_data), MAX_INDENT); + gint64 start, stop; + + gst_toc_entry_get_start_stop (entry, &start, &stop); + g_print ("%*s%s: start: %" GST_TIME_FORMAT " stop: %" GST_TIME_FORMAT "\n", + depth, " ", gst_toc_entry_type_get_nick (entry->type), + GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + indent += 2; + + /* print tags */ + if (entry->type == GST_TOC_ENTRY_TYPE_CHAPTER) + g_print ("%*sTags:\n", 2 * depth, " "); + gst_tag_list_foreach (entry->tags, print_tag_foreach, + GUINT_TO_POINTER (indent)); + + /* loop over sub-toc entries */ + g_list_foreach (entry->subentries, print_toc_entry, + GUINT_TO_POINTER (indent)); +} + static void print_properties (GstDiscovererInfo * info, gint tab) { const GstTagList *tags; + const GstToc *toc; g_print ("%*sDuration: %" GST_TIME_FORMAT "\n", tab + 1, " ", GST_TIME_ARGS (gst_discoverer_info_get_duration (info))); @@ -353,6 +403,10 @@ print_properties (GstDiscovererInfo * info, gint tab) gst_structure_foreach ((const GstStructure *) tags, print_tag_each, GINT_TO_POINTER (tab + 5)); } + if (show_toc && (toc = gst_discoverer_info_get_toc (info))) { + g_print ("%*sTOC: \n", tab + 1, " "); + g_list_foreach (toc->entries, print_toc_entry, GUINT_TO_POINTER (tab + 5)); + } } static void @@ -511,6 +565,8 @@ main (int argc, char **argv) "Specify timeout (in seconds, default 10)", "T"}, /* {"elem", 'e', 0, G_OPTION_ARG_NONE, &elem_seek, */ /* "Seek on elements instead of pads", NULL}, */ + {"toc", 'c', 0, G_OPTION_ARG_NONE, &show_toc, + "Output TOC (chapters and editions)", NULL}, {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Verbose properties", NULL}, {NULL}