Merge branch '0.10'

Conflicts:
	docs/gst/gstreamer-sections.txt
	gst/Makefile.am
	gst/gst.c
	gst/gst.h
	gst/gstevent.c
	gst/gstevent.h
	gst/gstmessage.h
	gst/gstquark.c
	gst/gstquark.h
	gst/gstquery.c
	gst/gstquery.h
	tests/check/Makefile.am
This commit is contained in:
Stefan Sauer 2012-04-02 21:15:09 +02:00
commit 1074a4e99a
24 changed files with 2807 additions and 3 deletions

View file

@ -42,6 +42,7 @@ EXTRA_DIST = \
part-stream-status.txt \
part-streams.txt \
part-synchronisation.txt \
part-toc.txt \
part-TODO.txt \
part-trickmodes.txt

89
docs/design/part-toc.txt Normal file
View file

@ -0,0 +1,89 @@
Implementing GstToc support in GStreamer elements
1. General info about GstToc structure
GstToc introduces a general way to handle chapters within multimedia
formats. GstToc can be represented as tree structure with arbitrary
hierarchy. Tree item can be either of two types: chapter or edition.
Chapter acts like a part of the media data, for example audio track
in CUE sheet, or part of the movie. Edition acts like some kind of
alternative way to process media content, for example DVD angles.
GstToc has one limitation on tree structure: on the same level of
hierarchy there couldn't be items of different type, i.e. you shouldn't
have editions and chapters mixed together. Here is an example of right TOC:
------- TOC -------
/ \
edition1 edition2
| |
-chapter1 -chapter3
-chapter2
Here are two editions, the first contains two chapters, and the second
has only one chapter. And here is an example of invalid TOC:
------- TOC -------
/ \
edition1 chapter1
|
-chapter1
-chapter2
Here you have edition1 and chapter1 mixed on the same level of hierarchy,
and such TOC will be considered broken.
GstToc has 'entries' field of GList type which consists of children items.
Each item is of type GstTocEntry. Also GstToc has list of tags and
GstStructure called 'info'. Please, use GstToc.info and GstTocEntry.info
fields this way: create a GstStructure, put all info related to your element
there and put this structure into the 'info' field under the name of your
element. Some fields in the 'info' structure can be used for internal
purposes, so you should use it in the way described above to not to
overwrite already existent fields.
Let's look at GstTocEntry a bit closer. One of the most important fields
is 'uid', which must be unique for each item within the TOC. This is used
to identify each item inside TOC, especially when element receives TOC
select event with UID to seek on. Field 'subentries' of type GList contains
children items of type GstTocEntry. Thus you can achieve arbitrary hierarchy
level. Field 'type' can be either GST_TOC_ENTRY_TYPE_CHAPTER or
GST_TOC_ENTRY_TYPE_EDITION which corresponds to chapter or edition type of
item respectively. Field 'pads' of type GList contains list of GStreamer
pads related to the item. It can be used for example to link a TOC with
specific pad. Field 'tags' is a list of tags related to the item. And field
'info' is similar to GstToc.info described above.
So, a little more about managing GstToc. Use gst_toc_new() and gst_toc_free()
to create/free it. GstTocEntry can be created using gst_toc_entry_new() and
gst_toc_entry_new_with_pad(). The latter method used to create GstTocEntry
linked to particular pad. While building GstToc you can set start and stop
timestamps for each item using gst_toc_entry_set_start_stop().
The best way to process already created GstToc is to recursively go through
the 'entries' and 'subentries' fields.
2. Working with GstQuery
GstQuery with GstToc can be created using gst_query_new_toc(). Use
gst_query_set_toc() to set TOC into the query and parse it with
gst_query_parse_toc(). The 'extend_uid' parameter (0 for root level) in two
last methods should be used for TOC extending: get GstTocEntry with
gst_toc_find_entry() by given UID and use it to add your own chapters/editions.
The common action on such query is to set TOC for it.
3. Working with GstMessage
GstMessage with GstToc can be created using gst_message_new_toc() and parsed
with gst_message_parse_toc(). The 'updated' parameter in these methods indicates
whether the TOC was just discovered (set to false) or TOC was already found and
have been updated (set to true). The common usage for such message is to post it
to pipeline in case you have discovered TOC data within your element.
4. Working with GstEvent
GstToc supports select event through GstEvent infrastructure. The idea is the
following: when you receive TOC select event, parse it with
gst_event_parse_toc_select() and seek stream (if it is not streamable) for
specified TOC UID (you can use gst_toc_find_entry() to find entry in TOC by UID).
To create TOC select event use gst_event_new_toc_select(). The common action on
such event is to seek to specified UID within your element.

View file

@ -101,6 +101,8 @@ Windows. It is released under the GNU Library General Public License
<xi:include href="xml/gsttagsetter.xml" />
<xi:include href="xml/gsttask.xml" />
<xi:include href="xml/gsttaskpool.xml" />
<xi:include href="xml/gsttoc.xml" />
<xi:include href="xml/gsttocsetter.xml" />
<xi:include href="xml/gsttypefind.xml" />
<xi:include href="xml/gsttypefindfactory.xml" />
<xi:include href="xml/gsturihandler.xml" />

View file

@ -1005,6 +1005,12 @@ gst_event_parse_stream_config_setup_data
gst_event_add_stream_config_header
gst_event_get_n_stream_config_headers
gst_event_parse_nth_stream_config_header
gst_event_new_toc
gst_event_parse_toc
gst_event_new_toc_select
gst_event_parse_toc_select
<SUBSECTION Standard>
GstEventClass
GST_EVENT
@ -1413,6 +1419,8 @@ gst_message_set_qos_stats
gst_message_parse_qos
gst_message_parse_qos_values
gst_message_parse_qos_stats
gst_message_new_toc
gst_message_parse_toc
GstStructureChangeType
gst_message_new_structure_change
@ -2225,6 +2233,9 @@ gst_query_has_scheduling_mode
gst_query_new_drain
gst_query_new_toc
gst_query_parse_toc
gst_query_set_toc
<SUBSECTION Standard>
GstQueryClass
GST_QUERY
@ -2673,6 +2684,51 @@ gst_task_state_get_type
</SECTION>
<SECTION>
<FILE>gsttoc</FILE>
<TITLE>GstToc</TITLE>
GstToc
GstTocEntry
GstTocEntryType
gst_toc_entry_new
gst_toc_entry_new_with_pad
gst_toc_entry_free
gst_toc_new
gst_toc_free
gst_toc_entry_copy
gst_toc_copy
gst_toc_find_entry
gst_toc_entry_get_start_stop
gst_toc_entry_set_start_stop
<SUBSECTION Standard>
GST_TYPE_TOC_ENTRY_TYPE
<SUBSECTION Private>
gst_toc_entry_type_get_type
</SECTION>
<SECTION>
<FILE>gsttocsetter</FILE>
<TITLE>GstTocSetter</TITLE>
GstTocSetter
GstTocSetterIFace
gst_toc_setter_get_toc
gst_toc_setter_get_toc_copy
gst_toc_setter_reset_toc
gst_toc_setter_set_toc
gst_toc_setter_get_toc_entry
gst_toc_setter_get_toc_entry_copy
gst_toc_setter_add_toc_entry
<SUBSECTION Standard>
GST_IS_TOC_SETTER
GST_TOC_SETTER
GST_TOC_SETTER_GET_IFACE
GST_TYPE_TOC_SETTER
<SUBSECTION Private>
gst_toc_setter_get_type
</SECTION>
<SECTION>
<FILE>gsttypefind</FILE>
<TITLE>GstTypeFind</TITLE>

View file

@ -94,6 +94,8 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
gsttagsetter.c \
gsttask.c \
gsttaskpool.c \
gsttoc.c \
gsttocsetter.c \
$(GST_TRACE_SRC) \
gsttypefind.c \
gsttypefindfactory.c \
@ -185,6 +187,8 @@ gst_headers = \
gsttagsetter.h \
gsttask.h \
gsttaskpool.h \
gsttoc.h \
gsttocsetter.h \
gsttypefind.h \
gsttypefindfactory.h \
gsturi.h \

View file

@ -741,6 +741,7 @@ init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
_priv_gst_value_initialize ();
g_type_class_ref (gst_param_spec_fraction_get_type ());
_priv_gst_tag_initialize ();
_priv_gst_toc_initialize ();
gst_parse_context_get_type ();
_priv_gst_plugin_initialize ();

View file

@ -74,6 +74,8 @@
#include <gst/gsttagsetter.h>
#include <gst/gsttask.h>
#include <gst/gsttaskpool.h>
#include <gst/gsttoc.h>
#include <gst/gsttocsetter.h>
#include <gst/gsttypefind.h>
#include <gst/gsttypefindfactory.h>
#include <gst/gsturi.h>

View file

@ -54,6 +54,9 @@ extern const char g_log_domain_gstreamer[];
/* for GstElement */
#include "gstelement.h"
/* for GstToc */
#include "gsttoc.h"
G_BEGIN_DECLS
/* used by gstparse.c and grammar.y */
@ -110,6 +113,16 @@ void _priv_gst_sample_initialize (void);
void _priv_gst_tag_initialize (void);
void _priv_gst_value_initialize (void);
void _priv_gst_debug_init (void);
void _priv_gst_toc_initialize (void);
/* TOC functions */
/* These functions are used to parse TOC messages, events and queries */
GstToc* _gst_toc_from_structure (const GstStructure *toc);
GstStructure* _gst_toc_to_structure (const GstToc *toc);
gboolean _gst_toc_structure_get_updated (const GstStructure * toc);
void _gst_toc_structure_set_updated (GstStructure * toc, gboolean updated);
gchar* _gst_toc_structure_get_extend_uid (const GstStructure * toc);
void _gst_toc_structure_set_extend_uid (GstStructure * toc, const gchar * extend_uid);
/* Private registry functions */
gboolean _priv_gst_registry_remove_cache_plugins (GstRegistry *registry);

View file

@ -113,6 +113,7 @@ static GstEventQuarks event_quarks[] = {
{GST_EVENT_STREAM_CONFIG, "stream-config", 0},
{GST_EVENT_SEGMENT, "segment", 0},
{GST_EVENT_TAG, "tag", 0},
{GST_EVENT_TOC, "toc", 0},
{GST_EVENT_BUFFERSIZE, "buffersize", 0},
{GST_EVENT_SINK_MESSAGE, "sink-message", 0},
{GST_EVENT_EOS, "eos", 0},
@ -124,6 +125,7 @@ static GstEventQuarks event_quarks[] = {
{GST_EVENT_LATENCY, "latency", 0},
{GST_EVENT_STEP, "step", 0},
{GST_EVENT_RECONFIGURE, "reconfigure", 0},
{GST_EVENT_TOC_SELECT, "toc-select", 0},
{GST_EVENT_CUSTOM_UPSTREAM, "custom-upstream", 0},
{GST_EVENT_CUSTOM_DOWNSTREAM, "custom-downstream", 0},
{GST_EVENT_CUSTOM_DOWNSTREAM_OOB, "custom-downstream-oob", 0},
@ -1610,3 +1612,112 @@ gst_event_new_stream_start (void)
{
return gst_event_new_custom (GST_EVENT_STREAM_START, NULL);
}
/**
* gst_event_new_toc:
* @toc: #GstToc structure.
* @updated: whether @toc was updated or not.
*
* Generate a TOC event from the given @toc. The purpose of the TOC event is to
* inform elements that some kind of the TOC was found.
*
* Returns: a new #GstEvent.
*
* Since: 0.10.37
*/
GstEvent *
gst_event_new_toc (GstToc * toc, gboolean updated)
{
GstStructure *toc_struct;
g_return_val_if_fail (toc != NULL, NULL);
GST_CAT_INFO (GST_CAT_EVENT, "creating toc event");
toc_struct = _gst_toc_to_structure (toc);
if (G_LIKELY (toc_struct != NULL)) {
_gst_toc_structure_set_updated (toc_struct, updated);
return gst_event_new_custom (GST_EVENT_TOC, toc_struct);
} else
return NULL;
}
/**
* gst_event_parse_toc:
* @event: a TOC event.
* @toc: (out): pointer to #GstToc structure.
* @updated: (out): pointer to store TOC updated flag.
*
* Parse a TOC @event and store the results in the given @toc and @updated locations.
*
* Since: 0.10.37
*/
void
gst_event_parse_toc (GstEvent * event, GstToc ** toc, gboolean * updated)
{
const GstStructure *structure;
g_return_if_fail (event != NULL);
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_TOC);
g_return_if_fail (toc != NULL);
structure = gst_event_get_structure (event);
*toc = _gst_toc_from_structure (structure);
if (updated != NULL)
*updated = _gst_toc_structure_get_updated (structure);
}
/**
* gst_event_new_toc_select:
* @uid: UID in the TOC to start playback from.
*
* Generate a TOC select event with the given @uid. The purpose of the
* TOC select event is to start playback based on the TOC's entry with the
* given @uid.
*
* Returns: a new #GstEvent.
*
* Since: 0.10.37
*/
GstEvent *
gst_event_new_toc_select (const gchar * uid)
{
GstStructure *structure;
g_return_val_if_fail (uid != NULL, NULL);
GST_CAT_INFO (GST_CAT_EVENT, "creating toc select event for UID: %s", uid);
structure = gst_structure_id_new (GST_QUARK (EVENT_TOC_SELECT),
GST_QUARK (UID), G_TYPE_STRING, uid, NULL);
return gst_event_new_custom (GST_EVENT_TOC_SELECT, structure);
}
/**
* gst_event_parse_toc_select:
* @event: a TOC select event.
* @uid: (out): storage for the selection UID.
*
* Parse a TOC select @event and store the results in the given @uid location.
*
* Since: 0.10.37
*/
void
gst_event_parse_toc_select (GstEvent * event, gchar ** uid)
{
const GstStructure *structure;
const GValue *val;
g_return_if_fail (event != NULL);
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_TOC_SELECT);
structure = gst_event_get_structure (event);
val = gst_structure_id_get_value (structure, GST_QUARK (UID));
if (uid != NULL)
*uid = g_strdup (g_value_get_string (val));
}

View file

@ -100,6 +100,8 @@ typedef enum {
* without a SEGMENT event.
* @GST_EVENT_SEGMENT_DONE: (unimplemented) Marks the end of a segment playback.
* @GST_EVENT_GAP: (unimplemented) Marks a gap in the datastream.
* @GST_EVENT_TOC: An event which indicates that a new table of contents (TOC)
* was found or updated. Since: 0.10.37
* @GST_EVENT_QOS: A quality message. Used to indicate to upstream elements
* that the downstream elements should adjust their processing
* rate.
@ -114,6 +116,8 @@ typedef enum {
* execute the step operation. Since: 0.10.24
* @GST_EVENT_RECONFIGURE: A request for upstream renegotiating caps and reconfiguring.
* Since: 0.11.0
* @GST_EVENT_TOC_SELECT: A request for a new playback position based on TOC
* entry's UID. Since 0.10.37
* @GST_EVENT_CUSTOM_UPSTREAM: Upstream custom event
* @GST_EVENT_CUSTOM_DOWNSTREAM: Downstream custom event that travels in the
* data flow.
@ -148,6 +152,7 @@ typedef enum {
GST_EVENT_BUFFERSIZE = GST_EVENT_MAKE_TYPE (90, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
GST_EVENT_SINK_MESSAGE = GST_EVENT_MAKE_TYPE (100, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY) | FLAG(STICKY_MULTI)),
GST_EVENT_EOS = GST_EVENT_MAKE_TYPE (110, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
GST_EVENT_TOC = GST_EVENT_MAKE_TYPE (120, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY) | FLAG(STICKY_MULTI)),
/* non-sticky downstream serialized */
GST_EVENT_SEGMENT_DONE = GST_EVENT_MAKE_TYPE (150, FLAG(DOWNSTREAM) | FLAG(SERIALIZED)),
@ -160,6 +165,7 @@ typedef enum {
GST_EVENT_LATENCY = GST_EVENT_MAKE_TYPE (220, FLAG(UPSTREAM)),
GST_EVENT_STEP = GST_EVENT_MAKE_TYPE (230, FLAG(UPSTREAM)),
GST_EVENT_RECONFIGURE = GST_EVENT_MAKE_TYPE (240, FLAG(UPSTREAM)),
GST_EVENT_TOC_SELECT = GST_EVENT_MAKE_TYPE (250, FLAG(UPSTREAM)),
/* custom events start here */
GST_EVENT_CUSTOM_UPSTREAM = GST_EVENT_MAKE_TYPE (270, FLAG(UPSTREAM)),
@ -520,6 +526,11 @@ void gst_event_copy_segment (GstEvent *event, GstSegment *se
GstEvent* gst_event_new_tag (GstTagList *taglist) G_GNUC_MALLOC;
void gst_event_parse_tag (GstEvent *event, GstTagList **taglist);
/* TOC event */
GstEvent* gst_event_new_toc (GstToc *toc, gboolean updated);
void gst_event_parse_toc (GstEvent *event, GstToc **toc, gboolean *updated);
/* buffer */
GstEvent * gst_event_new_buffer_size (GstFormat format, gint64 minsize, gint64 maxsize,
gboolean async) G_GNUC_MALLOC;
@ -561,6 +572,10 @@ void gst_event_parse_step (GstEvent *event, GstFormat *for
/* renegotiate event */
GstEvent* gst_event_new_reconfigure (void) G_GNUC_MALLOC;
/* TOC select event */
GstEvent* gst_event_new_toc_select (const gchar *uid) G_GNUC_MALLOC;
void gst_event_parse_toc_select (GstEvent *event, gchar **uid);
G_END_DECLS
#endif /* __GST_EVENT_H__ */

View file

@ -104,6 +104,7 @@ static GstMessageQuarks message_quarks[] = {
{GST_MESSAGE_STEP_START, "step-start", 0},
{GST_MESSAGE_QOS, "qos", 0},
{GST_MESSAGE_PROGRESS, "progress", 0},
{GST_MESSAGE_TOC, "toc", 0},
{0, NULL, 0}
};
@ -2171,3 +2172,61 @@ gst_message_parse_progress (GstMessage * message, GstProgressType * type,
GST_QUARK (CODE), G_TYPE_STRING, code,
GST_QUARK (TEXT), G_TYPE_STRING, text, NULL);
}
/**
* gst_message_new_toc:
* @src: the object originating the message.
* @toc: #GstToc structure for the message.
* @updated: whether TOC was updated or not.
*
* Create a new TOC message. The message is posted by elements
* that discovered or updated a TOC.
*
* Returns: a new TOC message.
*
* MT safe.
*
* Since: 0.10.37
*/
GstMessage *
gst_message_new_toc (GstObject * src, GstToc * toc, gboolean updated)
{
GstStructure *toc_struct;
g_return_val_if_fail (toc != NULL, NULL);
toc_struct = _gst_toc_to_structure (toc);
if (G_LIKELY (toc_struct != NULL)) {
_gst_toc_structure_set_updated (toc_struct, updated);
return gst_message_new_custom (GST_MESSAGE_TOC, src, toc_struct);
} else
return NULL;
}
/**
* gst_message_parse_toc:
* @message: a valid #GstMessage of type GST_MESSAGE_TOC.
* @toc: (out): return location for the TOC.
* @updated: (out): return location for the updated flag.
*
* Extract the TOC from the #GstMessage. The TOC returned in the
* output argument is a copy; the caller must free it with
* gst_toc_free() when done.
*
* MT safe.
*
* Since: 0.10.37
*/
void
gst_message_parse_toc (GstMessage * message, GstToc ** toc, gboolean * updated)
{
g_return_if_fail (GST_IS_MESSAGE (message));
g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_TOC);
g_return_if_fail (toc != NULL);
*toc = _gst_toc_from_structure (message->structure);
if (updated != NULL)
*updated = _gst_toc_structure_get_updated (message->structure);
}

View file

@ -88,6 +88,8 @@ typedef struct _GstMessage GstMessage;
* @GST_MESSAGE_QOS: A buffer was dropped or an element changed its processing
* strategy for Quality of Service reasons. Since: 0.10.29
* @GST_MESSAGE_PROGRESS: A progress message. Since: 0.10.33
* @GST_MESSAGE_TOC: A new table of contents (TOC) was found or previously found TOC
* was updated. Since: 0.10.37
* @GST_MESSAGE_ANY: mask for all of the above messages.
*
* The different message types that are available.
@ -124,6 +126,7 @@ typedef enum
GST_MESSAGE_STEP_START = (1 << 23),
GST_MESSAGE_QOS = (1 << 24),
GST_MESSAGE_PROGRESS = (1 << 25),
GST_MESSAGE_TOC = (1 << 26),
GST_MESSAGE_ANY = ~0
} GstMessageType;
@ -133,6 +136,7 @@ typedef enum
#include <gst/gsttaglist.h>
#include <gst/gststructure.h>
#include <gst/gstquery.h>
#include <gst/gsttoc.h>
#define GST_TYPE_MESSAGE (gst_message_get_type())
#define GST_IS_MESSAGE(obj) (GST_IS_MINI_OBJECT_TYPE (obj, GST_TYPE_MESSAGE))
@ -545,6 +549,9 @@ GstMessage * gst_message_new_progress (GstObject * src, GstProgress
void gst_message_parse_progress (GstMessage * message, GstProgressType * type, gchar ** code,
gchar ** text);
/* TOC */
GstMessage * gst_message_new_toc (GstObject *src, GstToc *toc, gboolean updated);
void gst_message_parse_toc (GstMessage *message, GstToc **toc, gboolean *updated);
G_END_DECLS

View file

@ -56,7 +56,8 @@ static const gchar *_quark_strings[] = {
"GstEventReconfigure", "segment", "GstQueryScheduling", "pull-mode",
"allocator", "GstEventFlushStop", "options", "GstQueryAcceptCaps",
"result", "GstQueryCaps", "filter", "modes", "GstEventStreamConfig",
"setup-data", "stream-headers", "GstEventGap", "GstQueryDrain", "params"
"setup-data", "stream-headers", "GstEventGap", "GstQueryDrain", "params",
"toc-select", "uid", "toc"
};
GQuark _priv_gst_quark_table[GST_QUARK_MAX];

View file

@ -164,7 +164,10 @@ typedef enum _GstQuarkId
GST_QUARK_EVENT_GAP = 135,
GST_QUARK_QUERY_DRAIN = 136,
GST_QUARK_PARAMS = 137,
GST_QUARK_MAX = 138
GST_QUARK_EVENT_TOC_SELECT = 138,
GST_QUARK_UID = 139,
GST_QUARK_QUERY_TOC = 140,
GST_QUARK_MAX = 141
} GstQuarkId;
extern GQuark _priv_gst_quark_table[GST_QUARK_MAX];

View file

@ -110,6 +110,7 @@ static GstQueryQuarks query_quarks[] = {
{GST_QUERY_ACCEPT_CAPS, "accept-caps", 0},
{GST_QUERY_CAPS, "caps", 0},
{GST_QUERY_DRAIN, "drain", 0},
{GST_QUERY_TOC, "toc", 0},
{0, NULL, 0}
};
@ -2320,3 +2321,88 @@ gst_query_new_drain (void)
return query;
}
/**
* gst_query_new_toc:
*
* Constructs a new query TOC query object. Use gst_query_unref()
* when done with it. A TOC query is used to query the full TOC with
* the UID marker for TOC extending (to insert some new entries).
*
* Returns: A #GstQuery.
*/
GstQuery *
gst_query_new_toc (void)
{
GstQuery *query;
query = gst_query_new (GST_QUERY_TOC, NULL);
return query;
}
/**
* gst_query_set_toc:
* @query: a #GstQuery with query type GST_QUERY_TOC.
* @toc: the GstToc to set.
* @extend_uid: UID which can be used for TOC extending (may be NULL),
* 0 means root TOC level.
*
* Answer a TOC query by setting appropriate #GstToc structure.
*/
void
gst_query_set_toc (GstQuery * query, GstToc * toc, const gchar * extend_uid)
{
GstStructure *structure;
g_return_if_fail (query != NULL);
g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_TOC);
g_return_if_fail (toc != NULL);
structure = _gst_toc_to_structure (toc);
g_return_if_fail (structure != NULL);
/* that shouldn't be happen in normal usage */
if (query->structure != NULL)
gst_structure_free (query->structure);
if (extend_uid != NULL)
_gst_toc_structure_set_extend_uid (structure, extend_uid);
query->structure = structure;
gst_structure_set_parent_refcount (query->structure,
&(query->mini_object.refcount));
}
/**
* gst_query_parse_toc:
* @query: a #GstQuery.
* @toc: (out): the storage for the received TOC (may be NULL).
* @extend_uid: (out): the storage for the received extend UID marker (may be NULL),
* 0 means root TOC level.
*
* Parse a TOC query, writing the TOC into @toc as a newly
* allocated #GstToc and extend UID into @extend_uid, if the respective parameters
* are non-NULL. Use @extend_uid value to insert new entries into the TOC (@extend_uid will
* act as root entry for newly inserted entries).
* Free @toc with gst_toc_free() and @extend_uid with g_free() after usage.
*/
void
gst_query_parse_toc (GstQuery * query, GstToc ** toc, gchar ** extend_uid)
{
const GstStructure *structure;
g_return_if_fail (query != NULL);
g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_TOC);
structure = gst_query_get_structure (query);
g_return_if_fail (structure != NULL);
if (toc != NULL)
*toc = _gst_toc_from_structure (structure);
if (extend_uid != NULL)
*extend_uid = _gst_toc_structure_get_extend_uid (structure);
}

View file

@ -33,6 +33,7 @@
#include <gst/gststructure.h>
#include <gst/gstformat.h>
#include <gst/gstpad.h>
#include <gst/gsttoc.h>
G_BEGIN_DECLS
@ -101,6 +102,8 @@ typedef enum {
* @GST_QUERY_ACCEPT_CAPS: the accept caps query
* @GST_QUERY_CAPS: the caps query
* @GST_QUERY_DRAIN: wait till all serialized data is consumed downstream
* @GST_QUERY_TOC: query the full table of contents (TOC) with the marker
* for an entry which can be used to extend received TOC. Since 0.10.37.
*
* Standard predefined Query types
*/
@ -124,7 +127,8 @@ typedef enum {
GST_QUERY_SCHEDULING = GST_QUERY_MAKE_TYPE (150, FLAG(UPSTREAM)),
GST_QUERY_ACCEPT_CAPS = GST_QUERY_MAKE_TYPE (160, FLAG(BOTH)),
GST_QUERY_CAPS = GST_QUERY_MAKE_TYPE (170, FLAG(BOTH)),
GST_QUERY_DRAIN = GST_QUERY_MAKE_TYPE (180, FLAG(DOWNSTREAM) | FLAG(SERIALIZED))
GST_QUERY_DRAIN = GST_QUERY_MAKE_TYPE (180, FLAG(DOWNSTREAM) | FLAG(SERIALIZED)),
GST_QUERY_TOC = GST_QUERY_MAKE_TYPE (190, FLAG(BOTH))
} GstQueryType;
#undef FLAG
@ -476,6 +480,11 @@ void gst_query_intersect_caps_result (GstQuery *query, GstCaps *fi
/* drain query */
GstQuery * gst_query_new_drain (void) G_GNUC_MALLOC;
/* TOC query */
GstQuery * gst_query_new_toc (void);
void gst_query_set_toc (GstQuery *query, GstToc *toc, const gchar *extend_uid);
void gst_query_parse_toc (GstQuery *query, GstToc **toc, gchar **extend_uid);
G_END_DECLS
#endif /* __GST_QUERY_H__ */

1010
gst/gsttoc.c Normal file

File diff suppressed because it is too large Load diff

111
gst/gsttoc.h Normal file
View file

@ -0,0 +1,111 @@
/* GStreamer
* (c) 2010, 2012 Alexander Saprykin <xelfium@gmail.com>
*
* gsttoc.h: generic TOC API declaration
*
* 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.
*/
#ifndef __GST_TOC_H__
#define __GST_TOC_H__
#include <gst/gstconfig.h>
#include <gst/gsttaglist.h>
#include <gst/gstformat.h>
G_BEGIN_DECLS
typedef struct _GstTocEntry GstTocEntry;
typedef struct _GstToc GstToc;
/**
* GstTocEntryType:
* @GST_TOC_ENTRY_TYPE_CHAPTER: a chapter type entry.
* @GST_TOC_ENTRY_TYPE_EDITION: an edition entry (angle or alternative in other terms).
*
* The different types of TOC entry.
*/
typedef enum {
GST_TOC_ENTRY_TYPE_CHAPTER = 0,
GST_TOC_ENTRY_TYPE_EDITION = 1
} GstTocEntryType;
/**
* GstTocEntry:
* @uid: unique (for a whole TOC) id of the entry. This value should be persistent and
* should not be changed while updating TOC. @uid should be handled as "opaque" value
* without meaning (e.g. applications should not assume the /editionX/chapterY/chapter/Z structure,
* other demuxers could do something else), it should help to track updates of certain entries.
* @type: #GstTocEntryType of this entry.
* @subentries: list of #GstTocEntry children.
* @pads: list of #GstPad objects, related to this #GstTocEntry.
* @tags: tags related to this entry.
* @info: extra information related to this entry.
*
* Definition of TOC entry structure.
*/
struct _GstTocEntry {
gchar *uid;
GstTocEntryType type;
GList *subentries;
GList *pads;
GstTagList *tags;
GstStructure *info;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
/* FIXME: pad member should be GstPad type, but that's
* impossible due to recursive includes */
/**
* GstToc:
* @entries: list of #GstTocEntry entries of the TOC.
* @tags: tags related to the whole TOC.
* @info: extra information related to the TOC.
*
* Definition of TOC structure.
*/
struct _GstToc {
GList *entries;
GstTagList *tags;
GstStructure *info;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
/* functions to create new structures */
GstToc * gst_toc_new (void);
GstTocEntry * gst_toc_entry_new (GstTocEntryType type, const gchar *uid);
GstTocEntry * gst_toc_entry_new_with_pad (GstTocEntryType type, const gchar *uid, gpointer pad);
/* functions to free structures */
void gst_toc_entry_free (GstTocEntry *entry);
void gst_toc_free (GstToc *toc);
GstTocEntry * gst_toc_find_entry (const GstToc *toc, const gchar *uid);
GstTocEntry * gst_toc_entry_copy (const GstTocEntry *entry);
GstToc * gst_toc_copy (const GstToc *toc);
void gst_toc_entry_set_start_stop (GstTocEntry *entry, gint64 start, gint64 stop);
gboolean gst_toc_entry_get_start_stop (const GstTocEntry *entry, gint64 *start, gint64 *stop);
G_END_DECLS
#endif /* __GST_TOC_H__ */

362
gst/gsttocsetter.c Normal file
View file

@ -0,0 +1,362 @@
/* GStreamer
* Copyright (C) 2010, 2012 Alexander Saprykin <xelfium@gmail.com>
*
* gsttocsetter.c: interface for TOC setting on elements
*
* 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.
*/
/**
* SECTION:gsttocsetter
* @short_description: Element interface that allows setting and retrieval
* of the TOC
*
* Element interface that allows setting of the TOC.
*
* Elements that support some kind of chapters or editions (or tracks like in
* the FLAC cue sheet) will implement this interface.
*
* If you just want to retrieve the TOC in your application then all you
* need to do is watch for TOC messages on your pipeline's bus (or you can
* perform TOC query). This interface is only for setting TOC data, not for
* extracting it. To set TOC from the application, find proper tocsetter element
* and set TOC using gst_toc_setter_set_toc().
*
* Elements implementing the #GstTocSetter interface can extend existing TOC
* by getting extend UID for that (you can use gst_toc_find_entry() to retrieve it)
* with any TOC entries received from downstream.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gst_private.h"
#include "gsttocsetter.h"
#include <gobject/gvaluecollector.h>
#include <string.h>
GST_DEBUG_CATEGORY_STATIC (gst_toc_interface_debug);
#define GST_CAT_DEFAULT tag_toc_interface_debug
static GQuark gst_toc_key;
typedef struct
{
GstToc *toc;
GStaticMutex lock;
} GstTocData;
GType
gst_toc_setter_get_type (void)
{
static volatile gsize toc_setter_type = 0;
if (g_once_init_enter (&toc_setter_type)) {
GType _type;
static const GTypeInfo toc_setter_info = {
sizeof (GstTocSetterIFace), /* class_size */
NULL, /* base_init */
NULL, /* base_finalize */
NULL,
NULL, /* class_finalize */
NULL, /* class_data */
0,
0,
NULL
};
GST_DEBUG_CATEGORY_INIT (gst_toc_interface_debug, "GstTocInterface", 0,
"interfaces for the TOC");
_type = g_type_register_static (G_TYPE_INTERFACE, "GstTocSetter",
&toc_setter_info, 0);
g_type_interface_add_prerequisite (_type, GST_TYPE_ELEMENT);
gst_toc_key = g_quark_from_static_string ("GST_TOC_SETTER");
g_once_init_leave (&toc_setter_type, _type);
}
return toc_setter_type;
}
static void
gst_toc_data_free (gpointer p)
{
GstTocData *data = (GstTocData *) p;
if (data->toc)
gst_toc_free (data->toc);
g_static_mutex_free (&data->lock);
g_slice_free (GstTocData, data);
}
static GstTocData *
gst_toc_setter_get_data (GstTocSetter * setter)
{
GstTocData *data;
data = g_object_get_qdata (G_OBJECT (setter), gst_toc_key);
if (!data) {
static GStaticMutex create_mutex = G_STATIC_MUTEX_INIT;
/* make sure no other thread is creating a GstTocData at the same time */
g_static_mutex_lock (&create_mutex);
data = g_object_get_qdata (G_OBJECT (setter), gst_toc_key);
if (!data) {
data = g_slice_new (GstTocData);
g_static_mutex_init (&data->lock);
data->toc = NULL;
g_object_set_qdata_full (G_OBJECT (setter), gst_toc_key, data,
gst_toc_data_free);
}
g_static_mutex_unlock (&create_mutex);
}
return data;
}
/**
* gst_toc_setter_reset_toc:
* @setter: a #GstTocSetter.
*
* Reset the internal TOC. Elements should call this from within the
* state-change handler.
*
* Since: 0.10.37
*/
void
gst_toc_setter_reset_toc (GstTocSetter * setter)
{
GstTocData *data;
g_return_if_fail (GST_IS_TOC_SETTER (setter));
data = gst_toc_setter_get_data (setter);
g_static_mutex_lock (&data->lock);
if (data->toc) {
gst_toc_free (data->toc);
data->toc = NULL;
}
g_static_mutex_unlock (&data->lock);
}
/**
* gst_toc_setter_get_toc:
* @setter: a #GstTocSetter.
*
* Return current TOC the setter uses. The TOC should not be
* modified or freed.
*
* This function is not thread-safe. Use gst_toc_setter_get_toc_copy() instead.
*
* Returns: a current snapshot of the TOC used in the setter
* or NULL if none is used.
*
* Since: 0.10.37
*/
const GstToc *
gst_toc_setter_get_toc (GstTocSetter * setter)
{
g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL);
return gst_toc_setter_get_data (setter)->toc;
}
/**
* gst_toc_setter_get_toc_copy:
* @setter: a #GstTocSetter.
*
* Return current TOC the setter uses. The difference between this
* function and gst_toc_setter_get_toc() is that this function returns deep
* copy of the TOC, so you can modify it in any way. This function is thread-safe.
* Free it when done with gst_toc_free().
*
* Returns: a copy of the current snapshot of the TOC used in the setter
* or NULL if none is used.
*
* Since: 0.10.37
*/
GstToc *
gst_toc_setter_get_toc_copy (GstTocSetter * setter)
{
GstTocData *data;
GstToc *ret = NULL;
g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL);
data = gst_toc_setter_get_data (setter);
g_static_mutex_lock (&data->lock);
if (data->toc != NULL)
ret = gst_toc_copy (data->toc);
g_static_mutex_unlock (&data->lock);
return ret;
}
/**
* gst_toc_setter_set_toc:
* @setter: a #GstTocSetter.
* @toc: a #GstToc to set.
*
* Set the given TOC on the setter. Previously setted TOC will be
* freed before setting a new one.
*
* Since: 0.10.37
*/
void
gst_toc_setter_set_toc (GstTocSetter * setter, const GstToc * toc)
{
GstTocData *data;
g_return_if_fail (GST_IS_TOC_SETTER (setter));
data = gst_toc_setter_get_data (setter);
g_static_mutex_lock (&data->lock);
if (data->toc)
gst_toc_free (data->toc);
data->toc = gst_toc_copy (toc);
g_static_mutex_unlock (&data->lock);
}
/**
* gst_toc_setter_get_toc_entry:
* @setter: a #GstTocSetter.
* @uid: UID to find entry with.
*
* Return #GstTocEntry (if any) with given @uid. Returned entry should
* not be modified or freed.
*
* This function is not thread-safe. Use gst_toc_setter_get_toc_entry_copy() instead.
*
* Returns: a TOC entry with given @uid from the TOC in the setter
* or NULL if none entry with such @uid was found.
*
* Since: 0.10.37
*/
const GstTocEntry *
gst_toc_setter_get_toc_entry (GstTocSetter * setter, const gchar * uid)
{
GstTocData *data;
const GstTocEntry *ret;
g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL);
g_return_val_if_fail (uid != NULL, NULL);
data = gst_toc_setter_get_data (setter);
g_static_mutex_lock (&data->lock);
ret = gst_toc_find_entry (data->toc, uid);
g_static_mutex_unlock (&data->lock);
return ret;
}
/**
* gst_toc_setter_get_toc_entry_copy:
* @setter: a #GstTocSetter.
* @uid: UID to find entry with.
*
* Return #GstTocEntry (if any) with given @uid. It perform a deep copying,
* so you can modify returned value. Free it when done with gst_toc_entry_free().
* This function is thread-safe.
*
* Returns: a TOC entry with given @uid from the TOC in the setter
* or NULL if none entry with such @uid was found.
*
* Since: 0.10.37
*/
GstTocEntry *
gst_toc_setter_get_toc_entry_copy (GstTocSetter * setter, const gchar * uid)
{
GstTocData *data;
GstTocEntry *ret = NULL;
const GstTocEntry *search;
g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL);
g_return_val_if_fail (uid != NULL, NULL);
data = gst_toc_setter_get_data (setter);
g_static_mutex_lock (&data->lock);
search = gst_toc_find_entry (data->toc, uid);
if (search != NULL)
ret = gst_toc_entry_copy (search);
g_static_mutex_unlock (&data->lock);
return ret;
}
/**
* gst_toc_setter_add_toc_entry:
* @setter: a #GstTocSetter.
* @parent_uid: UID of the parent entry to append given @entry. Use 0 for the TOC root level.
* @entry: #GstTocEntry to append.
*
* Try to find entry with given @parent_uid and append an @entry to that #GstTocEntry.
*
* Returns: TRUE if entry with @parent_uid was found, FALSE otherwise.
*
* Since: 0.10.37
*/
gboolean
gst_toc_setter_add_toc_entry (GstTocSetter * setter, const gchar * parent_uid,
const GstTocEntry * entry)
{
GstTocData *data;
GstTocEntry *parent;
GstTocEntry *copy_entry;
gboolean ret = FALSE;
g_return_val_if_fail (GST_IS_TOC_SETTER (setter), FALSE);
g_return_val_if_fail (parent_uid != NULL, FALSE);
g_return_val_if_fail (entry != NULL, FALSE);
data = gst_toc_setter_get_data (setter);
g_static_mutex_lock (&data->lock);
copy_entry = gst_toc_entry_copy (entry);
if (g_strcmp0 (parent_uid, "0") == 0)
data->toc->entries = g_list_append (data->toc->entries, copy_entry);
else {
parent = gst_toc_find_entry (data->toc, parent_uid);
if (parent != NULL) {
parent->subentries = g_list_append (parent->subentries, copy_entry);
ret = TRUE;
}
}
g_static_mutex_unlock (&data->lock);
return ret;
}

67
gst/gsttocsetter.h Normal file
View file

@ -0,0 +1,67 @@
/* GStreamer
* Copyright (C) 2010, 2012 Alexander Saprykin <xelfium@gmail.com>
*
* gsttocsetter.h: Interfaces for TOC
*
* 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.
*/
#ifndef __GST_TOC_SETTER_H__
#define __GST_TOC_SETTER_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_TOC_SETTER (gst_toc_setter_get_type ())
#define GST_TOC_SETTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TOC_SETTER, GstTocSetter))
#define GST_IS_TOC_SETTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TOC_SETTER))
#define GST_TOC_SETTER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_TOC_SETTER, GstTocSetterIFace))
/**
* GstTocSetter:
*
* Opaque #GstTocSetter data structure.
*/
typedef struct _GstTocSetter GstTocSetter;
typedef struct _GstTocSetterIFace GstTocSetterIFace;
/**
* GstTocSetterIFace:
* @g_iface: parent interface type.
*
* #GstTocSetterIFace interface.
*/
struct _GstTocSetterIFace
{
GTypeInterface g_iface;
/* signals */
/* virtual table */
};
GType gst_toc_setter_get_type (void);
void gst_toc_setter_reset_toc (GstTocSetter *setter);
const GstToc * gst_toc_setter_get_toc (GstTocSetter *setter);
GstToc * gst_toc_setter_get_toc_copy (GstTocSetter *setter);
void gst_toc_setter_set_toc (GstTocSetter *setter, const GstToc *toc);
const GstTocEntry * gst_toc_setter_get_toc_entry (GstTocSetter *setter, const gchar *uid);
GstTocEntry * gst_toc_setter_get_toc_entry_copy (GstTocSetter *setter, const gchar *uid);
gboolean gst_toc_setter_add_toc_entry (GstTocSetter *setter, const gchar *parent_uid, const GstTocEntry *entry);
G_END_DECLS
#endif /* __GST_TOC_SETTER_H__ */

View file

@ -126,6 +126,8 @@ check_PROGRAMS = \
gst/gsttag \
gst/gsttagsetter \
gst/gsttask \
gst/gsttoc \
gst/gsttocsetter \
gst/gstvalue \
generic/states \
$(PARSE_CHECKS) \

338
tests/check/gst/gsttoc.c Normal file
View file

@ -0,0 +1,338 @@
/* GStreamer
*
* unit test for GstToc
*
* Copyright (C) 2010, 2012 Alexander Saprykin <xelfium@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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* ------- TOC -------
* / \
* edition1 edition2
* | |
* -chapter1 -chapter3
* -chapter2 |
* -subchapter1
*/
#include <gst/check/gstcheck.h>
#define ENTRY_ED1 "/edition1"
#define ENTRY_ED2 "/edition2"
#define ENTRY_ED3 "test-edition"
#define ENTRY_CH1 "/edition1/chapter1"
#define ENTRY_CH2 "/edition1/chapter2"
#define ENTRY_CH3 "/edition2/chapter3"
#define ENTRY_CH4 "/test-chapter"
#define ENTRY_SUB1 "/edition2/chapter3/subchapter1"
#define ENTRY_TAG "EntryTag"
#define TOC_TAG "TocTag"
#define TEST_UID "129537542"
#define INFO_NAME "info"
#define INFO_FIELD "info-test"
#define INFO_TEXT_EN "info-text-entry"
#define INFO_TEXT_TOC "info-text-toc"
#define CHECK_TOC_ENTRY(entry_c,type_c,uid_c) \
{ \
gchar *tag_c; \
const GValue *val; \
\
fail_unless_equals_string (entry_c->uid, uid_c); \
fail_unless (entry_c->type == type_c); \
fail_unless (entry_c->tags != NULL); \
fail_unless (entry_c->pads == NULL); \
\
fail_unless (entry_c->info != NULL); \
val = gst_structure_get_value (entry_c->info, INFO_FIELD); \
fail_unless (val != NULL); \
fail_unless_equals_string (g_value_get_string (val), INFO_TEXT_EN); \
\
fail_unless (gst_tag_list_get_string (entry_c->tags, \
GST_TAG_TITLE, &tag_c)); \
fail_unless_equals_string (tag_c, ENTRY_TAG); \
}
#define CHECK_TOC(toc_t) \
{ \
GstTocEntry *entry_t, *subentry_t; \
gchar *tag_t; \
const GValue *val; \
/* check TOC */ \
fail_unless (g_list_length (toc_t->entries) == 2); \
fail_unless (toc_t->tags != NULL); \
fail_unless (gst_tag_list_get_string (toc_t->tags, \
GST_TAG_TITLE, &tag_t)); \
fail_unless_equals_string (tag_t, TOC_TAG); \
\
fail_unless (toc_t->info != NULL); \
val = gst_structure_get_value (toc_t->info, INFO_FIELD); \
fail_unless (val != NULL); \
fail_unless_equals_string (g_value_get_string (val), INFO_TEXT_TOC); \
\
/* check edition1 */ \
entry_t = g_list_nth_data (toc_t->entries, 0); \
fail_if (entry_t == NULL); \
fail_unless (g_list_length (entry_t->subentries) == 2); \
CHECK_TOC_ENTRY (entry_t, GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED1); \
/* check chapter1 */ \
subentry_t = g_list_nth_data (entry_t->subentries, 0); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 0); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH1); \
/* check chapter2 */ \
subentry_t = g_list_nth_data (entry_t->subentries, 1); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 0); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH2); \
/* check edition2 */ \
entry_t = g_list_nth_data (toc_t->entries, 1); \
fail_if (entry_t == NULL); \
fail_unless (g_list_length (entry_t->subentries) == 1); \
CHECK_TOC_ENTRY (entry_t, GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED2); \
/* check chapter3 */ \
subentry_t = g_list_nth_data (entry_t->subentries, 0); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 1); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH3); \
/* check subchapter1 */ \
subentry_t = g_list_nth_data (subentry_t->subentries, 0); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 0); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_SUB1); \
}
GST_START_TEST (test_serializing)
{
GstToc *toc, *test_toc = NULL;
GstTocEntry *ed, *ch, *subch;
GstEvent *event;
GstMessage *message;
GstQuery *query;
gboolean updated;
gchar *uid;
gint64 start = -1, stop = -1;
toc = gst_toc_new ();
fail_if (toc == NULL);
gst_tag_list_add (toc->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
TOC_TAG, NULL);
toc->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_TOC,
NULL);
/* create edition1 */
ed = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED1);
fail_if (ed == NULL);
gst_tag_list_add (ed->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ed->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
CHECK_TOC_ENTRY (ed, GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED1);
/* append chapter1 to edition1 */
ch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH1);
fail_if (ch == NULL);
gst_tag_list_add (ch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
CHECK_TOC_ENTRY (ch, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH1);
ed->subentries = g_list_append (ed->subentries, ch);
fail_unless (g_list_length (ed->subentries) == 1);
/* append chapter2 to edition1 */
ch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH2);
fail_if (ch == NULL);
gst_tag_list_add (ch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
CHECK_TOC_ENTRY (ch, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH2);
/* append edition1 to the TOC */
toc->entries = g_list_append (toc->entries, ed);
fail_unless (g_list_length (toc->entries) == 1);
/* test gst_toc_entry_find() */
ed = NULL;
ed = gst_toc_find_entry (toc, ENTRY_ED1);
fail_if (ed == NULL);
ed->subentries = g_list_append (ed->subentries, ch);
fail_unless (g_list_length (ed->subentries) == 2);
/* test info GstStructure */
gst_toc_entry_set_start_stop (ch, 100, 1000);
fail_if (!gst_toc_entry_get_start_stop (ch, &start, &stop));
fail_unless (start == 100);
fail_unless (stop == 1000);
/* create edition2 */
ed = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED2);
fail_if (ed == NULL);
gst_tag_list_add (ed->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ed->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
CHECK_TOC_ENTRY (ed, GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED2);
/* create chapter3 */
ch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH3);
fail_if (ch == NULL);
gst_tag_list_add (ch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
CHECK_TOC_ENTRY (ch, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH3);
/* create subchapter1 */
subch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_SUB1);
fail_if (subch == NULL);
gst_tag_list_add (subch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
subch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
CHECK_TOC_ENTRY (subch, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_SUB1);
/* append subchapter1 to chapter3 */
ch->subentries = g_list_append (ch->subentries, subch);
fail_unless (g_list_length (ch->subentries) == 1);
/* append chapter3 to edition2 */
ed->subentries = g_list_append (ed->subentries, ch);
fail_unless (g_list_length (ed->subentries) == 1);
/* finally append edition2 to the TOC */
toc->entries = g_list_append (toc->entries, ed);
fail_unless (g_list_length (toc->entries) == 2);
/* test gst_toc_copy() */
test_toc = gst_toc_copy (toc);
fail_if (test_toc == NULL);
CHECK_TOC (test_toc);
gst_toc_free (test_toc);
test_toc = NULL;
/* check TOC event handling */
event = gst_event_new_toc (toc, TRUE);
fail_if (event == NULL);
fail_if (event->structure == NULL);
fail_unless (event->type == GST_EVENT_TOC);
ASSERT_MINI_OBJECT_REFCOUNT (GST_MINI_OBJECT (event), "GstEvent", 1);
gst_event_parse_toc (event, &test_toc, &updated);
fail_unless (updated == TRUE);
fail_if (test_toc == NULL);
CHECK_TOC (test_toc);
gst_toc_free (test_toc);
gst_event_unref (event);
updated = FALSE;
test_toc = NULL;
/* check TOC message handling */
message = gst_message_new_toc (NULL, toc, TRUE);
fail_if (message == NULL);
fail_if (event->structure == NULL);
fail_unless (message->type == GST_MESSAGE_TOC);
ASSERT_MINI_OBJECT_REFCOUNT (GST_MINI_OBJECT (message), "GstMessage", 1);
gst_message_parse_toc (message, &test_toc, &updated);
fail_unless (updated == TRUE);
fail_if (test_toc == NULL);
CHECK_TOC (test_toc);
gst_toc_free (test_toc);
gst_message_unref (message);
test_toc = NULL;
/* check TOC select event handling */
event = gst_event_new_toc_select (TEST_UID);
fail_if (event == NULL);
fail_if (event->structure == NULL);
fail_unless (event->type == GST_EVENT_TOC_SELECT);
ASSERT_MINI_OBJECT_REFCOUNT (GST_MINI_OBJECT (event), "GstEvent", 1);
gst_event_parse_toc_select (event, &uid);
fail_unless_equals_string (uid, TEST_UID);
gst_event_unref (event);
g_free (uid);
/* check TOC query handling */
query = gst_query_new_toc ();
fail_if (query == NULL);
gst_query_set_toc (query, toc, TEST_UID);
fail_if (query->structure == NULL);
fail_unless (query->type == GST_QUERY_TOC);
ASSERT_MINI_OBJECT_REFCOUNT (GST_MINI_OBJECT (query), "GstQuery", 1);
gst_query_parse_toc (query, &test_toc, &uid);
fail_unless_equals_string (uid, TEST_UID);
fail_if (test_toc == NULL);
CHECK_TOC (test_toc);
gst_toc_free (test_toc);
gst_query_unref (query);
g_free (uid);
/* that's wrong code, we should fail */
ch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH4);
toc->entries = g_list_prepend (toc->entries, ch);
ASSERT_CRITICAL (message = gst_message_new_toc (NULL, toc, TRUE));
/* and yet another one */
toc->entries = g_list_remove (toc->entries, ch);
gst_toc_entry_free (ch);
ed = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED3);
ch = (GstTocEntry *) (toc->entries->data);
ch->subentries = g_list_prepend (ch->subentries, ed);
ASSERT_WARNING (message = gst_message_new_toc (NULL, toc, TRUE));
gst_toc_free (toc);
}
GST_END_TEST;
static Suite *
gst_toc_suite (void)
{
Suite *s = suite_create ("GstToc");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_serializing);
return s;
}
GST_CHECK_MAIN (gst_toc);

View file

@ -0,0 +1,401 @@
/* GStreamer GstTocSetter interface unit tests
* Copyright (C) 2010, 2012 Alexander Saprykin <xelfium@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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gst/check/gstcheck.h>
#include <gst/gst.h>
#include <string.h>
#define ENTRY_ED1 "/edition1"
#define ENTRY_ED2 "/edition2"
#define ENTRY_ED3 "test-edition"
#define ENTRY_CH1 "/edition1/chapter1"
#define ENTRY_CH2 "/edition1/chapter2"
#define ENTRY_CH3 "/edition2/chapter3"
#define ENTRY_CH4 "/test-chapter"
#define ENTRY_SUB1 "/edition2/chapter3/subchapter1"
#define ENTRY_TAG "EntryTag"
#define TOC_TAG "TocTag"
#define INFO_NAME "info"
#define INFO_FIELD "info-test"
#define INFO_TEXT_EN "info-text-entry"
#define INFO_TEXT_TOC "info-text-toc"
#define CHECK_TOC_ENTRY(entry_c,type_c,uid_c) \
{ \
gchar *tag_c; \
const GValue *val; \
\
fail_unless_equals_string (entry_c->uid, uid_c); \
fail_unless (entry_c->type == type_c); \
fail_unless (entry_c->tags != NULL); \
fail_unless (entry_c->pads == NULL); \
\
fail_unless (entry_c->info != NULL); \
val = gst_structure_get_value (entry_c->info, INFO_FIELD); \
fail_unless (val != NULL); \
fail_unless_equals_string (g_value_get_string (val), INFO_TEXT_EN); \
\
fail_unless (gst_tag_list_get_string (entry_c->tags, \
GST_TAG_TITLE, &tag_c)); \
fail_unless_equals_string (tag_c, ENTRY_TAG); \
}
#define CHECK_TOC(toc_t) \
{ \
GstTocEntry *entry_t, *subentry_t; \
gchar *tag_t; \
const GValue *val; \
/* check TOC */ \
fail_unless (g_list_length (toc_t->entries) == 2); \
fail_unless (toc_t->tags != NULL); \
fail_unless (gst_tag_list_get_string (toc_t->tags, \
GST_TAG_TITLE, &tag_t)); \
fail_unless_equals_string (tag_t, TOC_TAG); \
\
fail_unless (toc_t->info != NULL); \
val = gst_structure_get_value (toc_t->info, INFO_FIELD); \
fail_unless (val != NULL); \
fail_unless_equals_string (g_value_get_string (val), INFO_TEXT_TOC); \
\
/* check edition1 */ \
entry_t = g_list_nth_data (toc_t->entries, 0); \
fail_if (entry_t == NULL); \
fail_unless (g_list_length (entry_t->subentries) == 2); \
CHECK_TOC_ENTRY (entry_t, GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED1); \
/* check chapter1 */ \
subentry_t = g_list_nth_data (entry_t->subentries, 0); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 0); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH1); \
/* check chapter2 */ \
subentry_t = g_list_nth_data (entry_t->subentries, 1); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 0); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH2); \
/* check edition2 */ \
entry_t = g_list_nth_data (toc_t->entries, 1); \
fail_if (entry_t == NULL); \
fail_unless (g_list_length (entry_t->subentries) == 1); \
CHECK_TOC_ENTRY (entry_t, GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED2); \
/* check chapter3 */ \
subentry_t = g_list_nth_data (entry_t->subentries, 0); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 1); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH3); \
/* check subchapter1 */ \
subentry_t = g_list_nth_data (subentry_t->subentries, 0); \
fail_if (subentry_t == NULL); \
fail_unless (g_list_length (subentry_t->subentries) == 0); \
CHECK_TOC_ENTRY (subentry_t, GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_SUB1); \
}
/* some minimal GstTocSetter object */
#define GST_TYPE_DUMMY_ENC gst_dummy_enc_get_type()
typedef GstElement GstDummyEnc;
typedef GstElementClass GstDummyEncClass;
static void gst_dummy_enc_add_interfaces (GType enc_type);
GType gst_dummy_enc_get_type (void);
GST_BOILERPLATE_FULL (GstDummyEnc, gst_dummy_enc, GstElement,
GST_TYPE_ELEMENT, gst_dummy_enc_add_interfaces);
static void
gst_dummy_enc_add_interfaces (GType enc_type)
{
static const GInterfaceInfo toc_setter_info = { NULL, NULL, NULL };
g_type_add_interface_static (enc_type, GST_TYPE_TOC_SETTER, &toc_setter_info);
}
static void
gst_dummy_enc_base_init (gpointer g_class)
{
}
static void
gst_dummy_enc_class_init (GstDummyEncClass * klass)
{
}
static void
gst_dummy_enc_init (GstDummyEnc * enc, GstDummyEncClass * klass)
{
}
static GstToc *
create_toc (void)
{
GstToc *toc;
GstTocEntry *ed, *ch, *subch;
toc = gst_toc_new ();
gst_tag_list_add (toc->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
TOC_TAG, NULL);
toc->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_TOC,
NULL);
/* create edition1 */
ed = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED1);
gst_tag_list_add (ed->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ed->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
/* append chapter1 to edition1 */
ch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH1);
gst_tag_list_add (ch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
ed->subentries = g_list_append (ed->subentries, ch);
/* append chapter2 to edition1 */
ch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH2);
gst_tag_list_add (ch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
ed->subentries = g_list_append (ed->subentries, ch);
/* append edition1 to the TOC */
toc->entries = g_list_append (toc->entries, ed);
/* create edition2 */
ed = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, ENTRY_ED2);
gst_tag_list_add (ed->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ed->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
/* create chapter3 */
ch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_CH3);
gst_tag_list_add (ch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
ch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
/* create subchapter1 */
subch = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, ENTRY_SUB1);
gst_tag_list_add (subch->tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE,
ENTRY_TAG, NULL);
subch->info =
gst_structure_new (INFO_NAME, INFO_FIELD, G_TYPE_STRING, INFO_TEXT_EN,
NULL);
/* append subchapter1 to chapter3 */
ch->subentries = g_list_append (ch->subentries, subch);
/* append chapter3 to edition2 */
ed->subentries = g_list_append (ed->subentries, ch);
/* finally append edition2 to the TOC */
toc->entries = g_list_append (toc->entries, ed);
return toc;
}
GST_START_TEST (test_set)
{
GstToc *toc;
GstTocEntry *entry, *ed;
GstTocSetter *setter;
GstElement *enc;
enc = g_object_new (GST_TYPE_DUMMY_ENC, NULL);
fail_unless (enc != NULL);
setter = GST_TOC_SETTER (enc);
toc = create_toc ();
fail_unless (toc != NULL);
gst_toc_setter_set_toc (setter, toc);
gst_toc_free (toc);
toc = gst_toc_setter_get_toc_copy (setter);
CHECK_TOC (toc);
/* test entry adding into the root TOC */
entry = g_list_last (toc->entries)->data;
toc->entries = g_list_remove (toc->entries, entry);
gst_toc_setter_set_toc (setter, toc);
gst_toc_setter_add_toc_entry (setter, "0", entry);
gst_toc_free (toc);
toc = gst_toc_setter_get_toc_copy (setter);
CHECK_TOC (toc);
/* test entry adding into the arbitrary entry */
entry = gst_toc_find_entry (toc, ENTRY_CH2);
fail_if (entry == NULL);
ed = toc->entries->data;
ed->subentries = g_list_remove (ed->subentries, entry);
gst_toc_setter_add_toc_entry (setter, ed->uid, entry);
CHECK_TOC (toc);
gst_toc_free (toc);
gst_toc_setter_reset_toc (setter);
toc = gst_toc_setter_get_toc_copy (setter);
fail_unless (toc == NULL);
g_object_unref (enc);
}
GST_END_TEST static int spin_and_wait = 1;
static int threads_running = 0;
#define THREADS_TEST_SECONDS 1.5
static gpointer
test_threads_thread_func1 (gpointer data)
{
GstToc *toc;
GstTocSetter *setter = GST_TOC_SETTER (data);
GTimer *timer;
toc = create_toc ();
timer = g_timer_new ();
g_atomic_int_inc (&threads_running);
while (g_atomic_int_get (&spin_and_wait))
g_usleep (0);
GST_INFO ("Go!");
g_timer_start (timer);
while (g_timer_elapsed (timer, NULL) < THREADS_TEST_SECONDS)
gst_toc_setter_set_toc (setter, toc);
gst_toc_free (toc);
g_timer_destroy (timer);
GST_INFO ("Done");
return NULL;
}
static gpointer
test_threads_thread_func2 (gpointer data)
{
GstToc *toc;
GstTocSetter *setter = GST_TOC_SETTER (data);
GTimer *timer;
toc = create_toc ();
timer = g_timer_new ();
g_atomic_int_inc (&threads_running);
while (g_atomic_int_get (&spin_and_wait))
g_usleep (0);
GST_INFO ("Go!");
g_timer_start (timer);
while (g_timer_elapsed (timer, NULL) < THREADS_TEST_SECONDS)
gst_toc_setter_set_toc (setter, toc);
gst_toc_free (toc);
g_timer_destroy (timer);
GST_INFO ("Done");
return NULL;
}
static gpointer
test_threads_thread_func3 (gpointer data)
{
GstTocSetter *setter = GST_TOC_SETTER (data);
GTimer *timer;
timer = g_timer_new ();
g_atomic_int_inc (&threads_running);
while (g_atomic_int_get (&spin_and_wait))
g_usleep (0);
GST_INFO ("Go!");
g_timer_start (timer);
while (g_timer_elapsed (timer, NULL) < THREADS_TEST_SECONDS) {
gst_toc_setter_reset_toc (setter);
}
g_timer_destroy (timer);
GST_INFO ("Done");
return NULL;
}
GST_START_TEST (test_threads)
{
GstTocSetter *setter;
GThread *threads[3];
setter = GST_TOC_SETTER (g_object_new (GST_TYPE_DUMMY_ENC, NULL));
spin_and_wait = TRUE;
threads[0] = g_thread_create (test_threads_thread_func1, setter, TRUE, NULL);
threads[1] = g_thread_create (test_threads_thread_func2, setter, TRUE, NULL);
threads[2] = g_thread_create (test_threads_thread_func3, setter, TRUE, NULL);
while (g_atomic_int_get (&threads_running) < 3)
g_usleep (10);
g_atomic_int_set (&spin_and_wait, FALSE);
g_thread_join (threads[0]);
g_thread_join (threads[1]);
g_thread_join (threads[2]);
g_object_unref (G_OBJECT (setter));
}
GST_END_TEST static Suite *
gst_toc_setter_suite (void)
{
Suite *s = suite_create ("GstTocSetter");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_set);
tcase_add_test (tc_chain, test_threads);
return s;
}
GST_CHECK_MAIN (gst_toc_setter);

View file

@ -69,6 +69,7 @@ static GstElement *pipeline;
static EventLoopResult caught_error = ELR_NO_ERROR;
static gboolean quiet = FALSE;
static gboolean tags = FALSE;
static gboolean toc = FALSE;
static gboolean messages = FALSE;
static gboolean is_live = FALSE;
static gboolean waiting_eos = FALSE;
@ -437,6 +438,35 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer unused)
}
}
static void
print_toc_entry (gpointer data, gpointer user_data)
{
GstTocEntry *entry = (GstTocEntry *) data;
const guint max_indent = 40;
const gchar spc[max_indent + 1] = " ";
const gchar *entry_types[] = { "chapter", "edition" };
guint indent = MIN (GPOINTER_TO_UINT (user_data), max_indent);
gint64 start, stop;
gst_toc_entry_get_start_stop (entry, &start, &stop);
PRINT ("%s%s:", &spc[max_indent - indent], entry_types[entry->type]);
if (GST_CLOCK_TIME_IS_VALID (start)) {
PRINT (" start: %" GST_TIME_FORMAT, GST_TIME_ARGS (start));
}
if (GST_CLOCK_TIME_IS_VALID (stop)) {
PRINT (" stop: %" GST_TIME_FORMAT, GST_TIME_ARGS (stop));
}
PRINT ("\n");
indent += 2;
/* TODO: print tags */
/* loop over sub-toc entries */
g_list_foreach (entry->subentries, print_toc_entry,
GUINT_TO_POINTER (indent));
}
#ifndef DISABLE_FAULT_HANDLER
/* we only use sighandler here because the registers are not important */
static void
@ -612,6 +642,28 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
gst_tag_list_free (tag_list);
}
break;
case GST_MESSAGE_TOC:
if (toc) {
GstToc *toc_msg;
gboolean updated;
if (GST_IS_ELEMENT (GST_MESSAGE_SRC (message))) {
PRINT (_("FOUND TOC : found by element \"%s\".\n"),
GST_MESSAGE_SRC_NAME (message));
} else if (GST_IS_OBJECT (GST_MESSAGE_SRC (message))) {
PRINT (_("FOUND TOC : found by object \"%s\".\n"),
GST_MESSAGE_SRC_NAME (message));
} else {
PRINT (_("FOUND TOC\n"));
}
gst_message_parse_toc (message, &toc_msg, &updated);
/* recursively loop over toc entries */
g_list_foreach (toc_msg->entries, print_toc_entry,
GUINT_TO_POINTER (0));
gst_toc_free (toc_msg);
}
break;
case GST_MESSAGE_INFO:{
GError *gerror;
gchar *debug;
@ -832,6 +884,8 @@ main (int argc, char *argv[])
GOptionEntry options[] = {
{"tags", 't', 0, G_OPTION_ARG_NONE, &tags,
N_("Output tags (also known as metadata)"), NULL},
{"toc", 'c', 0, G_OPTION_ARG_NONE, &toc,
N_("Ouput TOC (chapters and editions)"), NULL},
{"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
N_("Output status information and property notifications"), NULL},
{"quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet,