mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-20 23:36:38 +00:00
652 lines
18 KiB
C
652 lines
18 KiB
C
/*
|
|
* gstcmmlparser.c - GStreamer CMML document parser
|
|
* Copyright (C) 2005 Alessandro Decina
|
|
*
|
|
* Authors:
|
|
* Alessandro Decina <alessandro@nnva.org>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
|
|
* with newer GLib versions (>= 2.31.0) */
|
|
#define GLIB_DISABLE_DEPRECATION_WARNINGS
|
|
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <gst/gst.h>
|
|
|
|
#include "gstcmmlparser.h"
|
|
#include "gstannodex.h"
|
|
#include "gstcmmlutils.h"
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (cmmlparser);
|
|
#define GST_CAT_DEFAULT cmmlparser
|
|
|
|
static void gst_cmml_parser_generic_error (void *ctx, const char *msg, ...);
|
|
static xmlNodePtr gst_cmml_parser_new_node (GstCmmlParser * parser,
|
|
const gchar * name, ...);
|
|
static void
|
|
gst_cmml_parser_parse_start_element_ns (xmlParserCtxt * ctxt,
|
|
const xmlChar * name, const xmlChar * prefix, const xmlChar * URI,
|
|
int nb_preferences, const xmlChar ** namespaces,
|
|
int nb_attributes, int nb_defaulted, const xmlChar ** attributes);
|
|
static void gst_cmml_parser_parse_end_element_ns (xmlParserCtxt * ctxt,
|
|
const xmlChar * name, const xmlChar * prefix, const xmlChar * URI);
|
|
static void gst_cmml_parser_parse_processing_instruction (xmlParserCtxtPtr ctxt,
|
|
const xmlChar * target, const xmlChar * data);
|
|
static void gst_cmml_parser_meta_to_string (GstCmmlParser * parser,
|
|
xmlNodePtr parent, GValueArray * meta);
|
|
|
|
/* initialize the parser */
|
|
void
|
|
gst_cmml_parser_init (void)
|
|
{
|
|
GST_DEBUG_CATEGORY_INIT (cmmlparser, "cmmlparser", 0, "annodex CMML parser");
|
|
|
|
xmlGenericError = gst_cmml_parser_generic_error;
|
|
}
|
|
|
|
/* create a new CMML parser
|
|
*/
|
|
GstCmmlParser *
|
|
gst_cmml_parser_new (GstCmmlParserMode mode)
|
|
{
|
|
GstCmmlParser *parser = g_malloc (sizeof (GstCmmlParser));
|
|
|
|
parser->mode = mode;
|
|
parser->context = xmlCreatePushParserCtxt (NULL, NULL,
|
|
NULL, 0, "cmml-bitstream");
|
|
xmlCtxtUseOptions (parser->context, XML_PARSE_NONET | XML_PARSE_NOERROR);
|
|
parser->context->_private = parser;
|
|
parser->context->sax->startElementNs =
|
|
(startElementNsSAX2Func) gst_cmml_parser_parse_start_element_ns;
|
|
parser->context->sax->endElementNs =
|
|
(endElementNsSAX2Func) gst_cmml_parser_parse_end_element_ns;
|
|
parser->context->sax->processingInstruction = (processingInstructionSAXFunc)
|
|
gst_cmml_parser_parse_processing_instruction;
|
|
parser->preamble_callback = NULL;
|
|
parser->cmml_end_callback = NULL;
|
|
parser->stream_callback = NULL;
|
|
parser->head_callback = NULL;
|
|
parser->clip_callback = NULL;
|
|
parser->user_data = NULL;
|
|
|
|
return parser;
|
|
}
|
|
|
|
/* free a CMML parser instance
|
|
*/
|
|
void
|
|
gst_cmml_parser_free (GstCmmlParser * parser)
|
|
{
|
|
if (parser) {
|
|
xmlFreeDoc (parser->context->myDoc);
|
|
xmlFreeParserCtxt (parser->context);
|
|
g_free (parser);
|
|
}
|
|
}
|
|
|
|
/* parse an xml chunk
|
|
*
|
|
* returns false if the xml is invalid
|
|
*/
|
|
gboolean
|
|
gst_cmml_parser_parse_chunk (GstCmmlParser * parser,
|
|
const gchar * data, guint size, GError ** err)
|
|
{
|
|
gint xmlres;
|
|
|
|
xmlres = xmlParseChunk (parser->context, data, size, 0);
|
|
if (xmlres != XML_ERR_OK) {
|
|
xmlErrorPtr xml_error = xmlCtxtGetLastError (parser->context);
|
|
|
|
GST_DEBUG ("Error occurred decoding chunk %s", data);
|
|
g_set_error (err,
|
|
GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "%s", xml_error->message);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* convert an xmlNodePtr to a string
|
|
*/
|
|
static guchar *
|
|
gst_cmml_parser_node_to_string (GstCmmlParser * parser, xmlNodePtr node)
|
|
{
|
|
xmlBufferPtr xml_buffer;
|
|
xmlDocPtr doc;
|
|
guchar *str;
|
|
|
|
if (parser)
|
|
doc = parser->context->myDoc;
|
|
else
|
|
doc = NULL;
|
|
|
|
xml_buffer = xmlBufferCreate ();
|
|
xmlNodeDump (xml_buffer, doc, node, 0, 0);
|
|
str = xmlStrndup (xml_buffer->content, xml_buffer->use);
|
|
xmlBufferFree (xml_buffer);
|
|
|
|
return str;
|
|
}
|
|
|
|
guchar *
|
|
gst_cmml_parser_tag_stream_to_string (GstCmmlParser * parser,
|
|
GstCmmlTagStream * stream)
|
|
{
|
|
xmlNodePtr node;
|
|
xmlNodePtr import;
|
|
guchar *ret;
|
|
|
|
node = gst_cmml_parser_new_node (parser, "stream", NULL);
|
|
if (stream->timebase)
|
|
xmlSetProp (node, (xmlChar *) "timebase", stream->timebase);
|
|
|
|
if (stream->utc)
|
|
xmlSetProp (node, (xmlChar *) "utc", stream->utc);
|
|
|
|
if (stream->imports) {
|
|
gint i;
|
|
GValue *val;
|
|
|
|
for (i = 0; i < stream->imports->n_values; ++i) {
|
|
val = g_value_array_get_nth (stream->imports, i);
|
|
import = gst_cmml_parser_new_node (parser, "import",
|
|
"src", g_value_get_string (val), NULL);
|
|
xmlAddChild (node, import);
|
|
}
|
|
}
|
|
|
|
ret = gst_cmml_parser_node_to_string (parser, node);
|
|
|
|
xmlUnlinkNode (node);
|
|
xmlFreeNode (node);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* convert a GstCmmlTagHead to its string representation
|
|
*/
|
|
guchar *
|
|
gst_cmml_parser_tag_head_to_string (GstCmmlParser * parser,
|
|
GstCmmlTagHead * head)
|
|
{
|
|
xmlNodePtr node;
|
|
xmlNodePtr tmp;
|
|
guchar *ret;
|
|
|
|
node = gst_cmml_parser_new_node (parser, "head", NULL);
|
|
if (head->title) {
|
|
tmp = gst_cmml_parser_new_node (parser, "title", NULL);
|
|
xmlNodeSetContent (tmp, head->title);
|
|
xmlAddChild (node, tmp);
|
|
}
|
|
|
|
if (head->base) {
|
|
tmp = gst_cmml_parser_new_node (parser, "base", "uri", head->base, NULL);
|
|
xmlAddChild (node, tmp);
|
|
}
|
|
|
|
if (head->meta)
|
|
gst_cmml_parser_meta_to_string (parser, node, head->meta);
|
|
|
|
ret = gst_cmml_parser_node_to_string (parser, node);
|
|
|
|
xmlUnlinkNode (node);
|
|
xmlFreeNode (node);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* convert a GstCmmlTagClip to its string representation
|
|
*/
|
|
guchar *
|
|
gst_cmml_parser_tag_clip_to_string (GstCmmlParser * parser,
|
|
GstCmmlTagClip * clip)
|
|
{
|
|
xmlNodePtr node;
|
|
xmlNodePtr tmp;
|
|
guchar *ret;
|
|
|
|
node = gst_cmml_parser_new_node (parser, "clip",
|
|
"id", clip->id, "track", clip->track, NULL);
|
|
/* add the anchor element */
|
|
if (clip->anchor_href) {
|
|
tmp = gst_cmml_parser_new_node (parser, "a",
|
|
"href", clip->anchor_href, NULL);
|
|
if (clip->anchor_text)
|
|
xmlNodeSetContent (tmp, clip->anchor_text);
|
|
|
|
xmlAddChild (node, tmp);
|
|
}
|
|
/* add the img element */
|
|
if (clip->img_src) {
|
|
tmp = gst_cmml_parser_new_node (parser, "img",
|
|
"src", clip->img_src, "alt", clip->img_alt, NULL);
|
|
|
|
xmlAddChild (node, tmp);
|
|
}
|
|
/* add the desc element */
|
|
if (clip->desc_text) {
|
|
tmp = gst_cmml_parser_new_node (parser, "desc", NULL);
|
|
xmlNodeSetContent (tmp, clip->desc_text);
|
|
|
|
xmlAddChild (node, tmp);
|
|
}
|
|
/* add the meta elements */
|
|
if (clip->meta)
|
|
gst_cmml_parser_meta_to_string (parser, node, clip->meta);
|
|
|
|
if (parser->mode == GST_CMML_PARSER_DECODE) {
|
|
gchar *time_str;
|
|
|
|
time_str = gst_cmml_clock_time_to_npt (clip->start_time);
|
|
if (time_str == NULL)
|
|
goto fail;
|
|
|
|
xmlSetProp (node, (xmlChar *) "start", (xmlChar *) time_str);
|
|
g_free (time_str);
|
|
|
|
if (clip->end_time != GST_CLOCK_TIME_NONE) {
|
|
time_str = gst_cmml_clock_time_to_npt (clip->end_time);
|
|
if (time_str == NULL)
|
|
goto fail;
|
|
|
|
xmlSetProp (node, (xmlChar *) "end", (xmlChar *) time_str);
|
|
g_free (time_str);
|
|
}
|
|
}
|
|
|
|
ret = gst_cmml_parser_node_to_string (parser, node);
|
|
|
|
xmlUnlinkNode (node);
|
|
xmlFreeNode (node);
|
|
|
|
return ret;
|
|
fail:
|
|
xmlUnlinkNode (node);
|
|
xmlFreeNode (node);
|
|
return NULL;
|
|
}
|
|
|
|
guchar *
|
|
gst_cmml_parser_tag_object_to_string (GstCmmlParser * parser, GObject * tag)
|
|
{
|
|
guchar *tag_string = NULL;
|
|
GType tag_type = G_OBJECT_TYPE (tag);
|
|
|
|
if (tag_type == GST_TYPE_CMML_TAG_STREAM)
|
|
tag_string = gst_cmml_parser_tag_stream_to_string (parser,
|
|
GST_CMML_TAG_STREAM (tag));
|
|
else if (tag_type == GST_TYPE_CMML_TAG_HEAD)
|
|
tag_string = gst_cmml_parser_tag_head_to_string (parser,
|
|
GST_CMML_TAG_HEAD (tag));
|
|
else if (tag_type == GST_TYPE_CMML_TAG_CLIP)
|
|
tag_string = gst_cmml_parser_tag_clip_to_string (parser,
|
|
GST_CMML_TAG_CLIP (tag));
|
|
else
|
|
g_warning ("could not convert object to cmml");
|
|
|
|
return tag_string;
|
|
}
|
|
|
|
/*** private section ***/
|
|
|
|
/* create a new node
|
|
*
|
|
* helper to create a node and set its attributes
|
|
*/
|
|
static xmlNodePtr
|
|
gst_cmml_parser_new_node (GstCmmlParser * parser, const gchar * name, ...)
|
|
{
|
|
va_list args;
|
|
xmlNodePtr node;
|
|
xmlChar *prop_name, *prop_value;
|
|
|
|
node = xmlNewNode (NULL, (xmlChar *) name);
|
|
|
|
va_start (args, name);
|
|
|
|
prop_name = va_arg (args, xmlChar *);
|
|
while (prop_name != NULL) {
|
|
prop_value = va_arg (args, xmlChar *);
|
|
if (prop_value != NULL)
|
|
xmlSetProp (node, prop_name, prop_value);
|
|
|
|
prop_name = va_arg (args, xmlChar *);
|
|
}
|
|
va_end (args);
|
|
|
|
return node;
|
|
}
|
|
|
|
/* get the last node of the stream
|
|
*
|
|
* returns the last node at depth 1 (if any) or the root node
|
|
*/
|
|
static xmlNodePtr
|
|
gst_cmml_parser_get_last_element (GstCmmlParser * parser)
|
|
{
|
|
xmlNodePtr node;
|
|
|
|
node = xmlDocGetRootElement (parser->context->myDoc);
|
|
if (!node) {
|
|
g_warning ("no last cmml element");
|
|
return NULL;
|
|
}
|
|
|
|
if (node->children)
|
|
node = xmlGetLastChild (node);
|
|
|
|
return node;
|
|
}
|
|
|
|
static void
|
|
gst_cmml_parser_parse_preamble (GstCmmlParser * parser,
|
|
const guchar * attributes)
|
|
{
|
|
gchar *preamble;
|
|
gchar *element;
|
|
const gchar *version;
|
|
const gchar *encoding;
|
|
const gchar *standalone;
|
|
xmlDocPtr doc;
|
|
|
|
doc = parser->context->myDoc;
|
|
|
|
version = doc->version ? (gchar *) doc->version : "1.0";
|
|
encoding = doc->encoding ? (gchar *) doc->encoding : "UTF-8";
|
|
standalone = doc->standalone ? "yes" : "no";
|
|
|
|
preamble = g_strdup_printf ("<?xml version=\"%s\""
|
|
" encoding=\"%s\" standalone=\"%s\"?>\n"
|
|
"<!DOCTYPE cmml SYSTEM \"cmml.dtd\">\n", version, encoding, standalone);
|
|
|
|
if (attributes == NULL)
|
|
attributes = (guchar *) "";
|
|
|
|
if (parser->mode == GST_CMML_PARSER_ENCODE)
|
|
element = g_strdup_printf ("<?cmml %s?>", attributes);
|
|
else
|
|
element = g_strdup_printf ("<cmml %s>", attributes);
|
|
|
|
parser->preamble_callback (parser->user_data,
|
|
(guchar *) preamble, (guchar *) element);
|
|
|
|
g_free (preamble);
|
|
g_free (element);
|
|
}
|
|
|
|
/* parse the cmml stream tag */
|
|
static void
|
|
gst_cmml_parser_parse_stream (GstCmmlParser * parser, xmlNodePtr stream)
|
|
{
|
|
GstCmmlTagStream *stream_tag;
|
|
GValue str_val = { 0 };
|
|
xmlNodePtr walk;
|
|
guchar *timebase;
|
|
|
|
g_value_init (&str_val, G_TYPE_STRING);
|
|
|
|
/* read the timebase and utc attributes */
|
|
timebase = xmlGetProp (stream, (xmlChar *) "timebase");
|
|
if (timebase == NULL)
|
|
timebase = (guchar *) g_strdup ("0");
|
|
|
|
stream_tag = g_object_new (GST_TYPE_CMML_TAG_STREAM,
|
|
"timebase", timebase, NULL);
|
|
g_free (timebase);
|
|
|
|
stream_tag->utc = xmlGetProp (stream, (xmlChar *) "utc");
|
|
|
|
/* walk the children nodes */
|
|
for (walk = stream->children; walk; walk = walk->next) {
|
|
/* for every import tag add its src attribute to stream_tag->imports */
|
|
if (!xmlStrcmp (walk->name, (xmlChar *) "import")) {
|
|
g_value_take_string (&str_val,
|
|
(gchar *) xmlGetProp (walk, (xmlChar *) "src"));
|
|
|
|
if (stream_tag->imports == NULL)
|
|
stream_tag->imports = g_value_array_new (0);
|
|
|
|
g_value_array_append (stream_tag->imports, &str_val);
|
|
}
|
|
}
|
|
g_value_unset (&str_val);
|
|
|
|
parser->stream_callback (parser->user_data, stream_tag);
|
|
g_object_unref (stream_tag);
|
|
}
|
|
|
|
/* parse the cmml head tag */
|
|
static void
|
|
gst_cmml_parser_parse_head (GstCmmlParser * parser, xmlNodePtr head)
|
|
{
|
|
GstCmmlTagHead *head_tag;
|
|
xmlNodePtr walk;
|
|
GValue str_val = { 0 };
|
|
|
|
head_tag = g_object_new (GST_TYPE_CMML_TAG_HEAD, NULL);
|
|
|
|
g_value_init (&str_val, G_TYPE_STRING);
|
|
|
|
/* Parse the content of the node and setup the GST_TAG_CMML_HEAD tag.
|
|
* Create a GST_TAG_TITLE when we find the title element.
|
|
*/
|
|
for (walk = head->children; walk; walk = walk->next) {
|
|
if (!xmlStrcmp (walk->name, (xmlChar *) "title")) {
|
|
head_tag->title = xmlNodeGetContent (walk);
|
|
} else if (!xmlStrcmp (walk->name, (xmlChar *) "base")) {
|
|
head_tag->base = xmlGetProp (walk, (xmlChar *) "uri");
|
|
} else if (!xmlStrcmp (walk->name, (xmlChar *) "meta")) {
|
|
if (head_tag->meta == NULL)
|
|
head_tag->meta = g_value_array_new (0);
|
|
/* add a pair name, content to the meta value array */
|
|
g_value_take_string (&str_val,
|
|
(gchar *) xmlGetProp (walk, (xmlChar *) "name"));
|
|
g_value_array_append (head_tag->meta, &str_val);
|
|
g_value_take_string (&str_val,
|
|
(gchar *) xmlGetProp (walk, (xmlChar *) "content"));
|
|
g_value_array_append (head_tag->meta, &str_val);
|
|
}
|
|
}
|
|
g_value_unset (&str_val);
|
|
|
|
parser->head_callback (parser->user_data, head_tag);
|
|
g_object_unref (head_tag);
|
|
}
|
|
|
|
/* parse a cmml clip tag */
|
|
static void
|
|
gst_cmml_parser_parse_clip (GstCmmlParser * parser, xmlNodePtr clip)
|
|
{
|
|
GstCmmlTagClip *clip_tag;
|
|
GValue str_val = { 0 };
|
|
guchar *id, *track, *start, *end;
|
|
xmlNodePtr walk;
|
|
GstClockTime start_time = GST_CLOCK_TIME_NONE;
|
|
GstClockTime end_time = GST_CLOCK_TIME_NONE;
|
|
|
|
start = xmlGetProp (clip, (xmlChar *) "start");
|
|
if (parser->mode == GST_CMML_PARSER_ENCODE && start == NULL)
|
|
/* XXX: validate the document */
|
|
return;
|
|
|
|
id = xmlGetProp (clip, (xmlChar *) "id");
|
|
track = xmlGetProp (clip, (xmlChar *) "track");
|
|
end = xmlGetProp (clip, (xmlChar *) "end");
|
|
|
|
if (track == NULL)
|
|
track = (guchar *) g_strdup ("default");
|
|
|
|
if (start) {
|
|
if (!strncmp ((gchar *) start, "smpte", 5))
|
|
start_time = gst_cmml_clock_time_from_smpte ((gchar *) start);
|
|
else
|
|
start_time = gst_cmml_clock_time_from_npt ((gchar *) start);
|
|
}
|
|
|
|
if (end) {
|
|
if (!strncmp ((gchar *) end, "smpte", 5))
|
|
start_time = gst_cmml_clock_time_from_smpte ((gchar *) end);
|
|
else
|
|
end_time = gst_cmml_clock_time_from_npt ((gchar *) end);
|
|
}
|
|
|
|
clip_tag = g_object_new (GST_TYPE_CMML_TAG_CLIP, "id", id,
|
|
"track", track, "start-time", start_time, "end-time", end_time, NULL);
|
|
|
|
g_free (id);
|
|
g_free (track);
|
|
g_free (start);
|
|
g_free (end);
|
|
|
|
g_value_init (&str_val, G_TYPE_STRING);
|
|
|
|
/* parse the children */
|
|
for (walk = clip->children; walk; walk = walk->next) {
|
|
/* the clip is not empty */
|
|
clip_tag->empty = FALSE;
|
|
|
|
if (!xmlStrcmp (walk->name, (xmlChar *) "a")) {
|
|
clip_tag->anchor_href = xmlGetProp (walk, (xmlChar *) "href");
|
|
clip_tag->anchor_text = xmlNodeGetContent (walk);
|
|
} else if (!xmlStrcmp (walk->name, (xmlChar *) "img")) {
|
|
clip_tag->img_src = xmlGetProp (walk, (xmlChar *) "src");
|
|
clip_tag->img_alt = xmlGetProp (walk, (xmlChar *) "alt");
|
|
} else if (!xmlStrcmp (walk->name, (xmlChar *) "desc")) {
|
|
clip_tag->desc_text = xmlNodeGetContent (walk);
|
|
} else if (!xmlStrcmp (walk->name, (xmlChar *) "meta")) {
|
|
if (clip_tag->meta == NULL)
|
|
clip_tag->meta = g_value_array_new (0);
|
|
/* add a pair name, content to the meta value array */
|
|
g_value_take_string (&str_val,
|
|
(char *) xmlGetProp (walk, (xmlChar *) "name"));
|
|
g_value_array_append (clip_tag->meta, &str_val);
|
|
g_value_take_string (&str_val,
|
|
(char *) xmlGetProp (walk, (xmlChar *) "content"));
|
|
g_value_array_append (clip_tag->meta, &str_val);
|
|
}
|
|
}
|
|
g_value_unset (&str_val);
|
|
|
|
parser->clip_callback (parser->user_data, clip_tag);
|
|
g_object_unref (clip_tag);
|
|
}
|
|
|
|
void
|
|
gst_cmml_parser_meta_to_string (GstCmmlParser * parser,
|
|
xmlNodePtr parent, GValueArray * array)
|
|
{
|
|
gint i;
|
|
xmlNodePtr node;
|
|
GValue *name, *content;
|
|
|
|
for (i = 0; i < array->n_values - 1; i += 2) {
|
|
name = g_value_array_get_nth (array, i);
|
|
content = g_value_array_get_nth (array, i + 1);
|
|
node = gst_cmml_parser_new_node (parser, "meta",
|
|
"name", g_value_get_string (name),
|
|
"content", g_value_get_string (content), NULL);
|
|
xmlAddChild (parent, node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_cmml_parser_generic_error (void *ctx, const char *msg, ...)
|
|
{
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
|
va_list varargs;
|
|
|
|
va_start (varargs, msg);
|
|
gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING,
|
|
"", "", 0, NULL, msg, varargs);
|
|
va_end (varargs);
|
|
#endif /* GST_DISABLE_GST_DEBUG */
|
|
}
|
|
|
|
/* sax handler called when an element start tag is found
|
|
* this is used to parse the cmml start tag
|
|
*/
|
|
static void
|
|
gst_cmml_parser_parse_start_element_ns (xmlParserCtxt * ctxt,
|
|
const xmlChar * name, const xmlChar * prefix, const xmlChar * URI,
|
|
int nb_preferences, const xmlChar ** namespaces,
|
|
int nb_attributes, int nb_defaulted, const xmlChar ** attributes)
|
|
{
|
|
GstCmmlParser *parser = (GstCmmlParser *) ctxt->_private;
|
|
|
|
xmlSAX2StartElementNs (ctxt, name, prefix, URI, nb_preferences, namespaces,
|
|
nb_attributes, nb_defaulted, attributes);
|
|
|
|
if (parser->mode == GST_CMML_PARSER_ENCODE)
|
|
if (!xmlStrcmp (name, (xmlChar *) "cmml"))
|
|
if (parser->preamble_callback)
|
|
/* FIXME: parse attributes */
|
|
gst_cmml_parser_parse_preamble (parser, NULL);
|
|
}
|
|
|
|
/* sax processing instruction handler
|
|
* used to parse the cmml processing instruction
|
|
*/
|
|
static void
|
|
gst_cmml_parser_parse_processing_instruction (xmlParserCtxtPtr ctxt,
|
|
const xmlChar * target, const xmlChar * data)
|
|
{
|
|
GstCmmlParser *parser = (GstCmmlParser *) ctxt->_private;
|
|
|
|
xmlSAX2ProcessingInstruction (ctxt, target, data);
|
|
|
|
if (parser->mode == GST_CMML_PARSER_DECODE)
|
|
if (!xmlStrcmp (target, (xmlChar *) "cmml"))
|
|
if (parser->preamble_callback)
|
|
gst_cmml_parser_parse_preamble (parser, data);
|
|
}
|
|
|
|
/* sax handler called when an xml end tag is found
|
|
* used to parse the stream, head and clip nodes
|
|
*/
|
|
static void
|
|
gst_cmml_parser_parse_end_element_ns (xmlParserCtxt * ctxt,
|
|
const xmlChar * name, const xmlChar * prefix, const xmlChar * URI)
|
|
{
|
|
xmlNodePtr node;
|
|
GstCmmlParser *parser = (GstCmmlParser *) ctxt->_private;
|
|
|
|
xmlSAX2EndElementNs (ctxt, name, prefix, URI);
|
|
|
|
if (!xmlStrcmp (name, (xmlChar *) "clip")) {
|
|
if (parser->clip_callback) {
|
|
node = gst_cmml_parser_get_last_element (parser);
|
|
gst_cmml_parser_parse_clip (parser, node);
|
|
}
|
|
} else if (!xmlStrcmp (name, (xmlChar *) "cmml")) {
|
|
if (parser->cmml_end_callback)
|
|
parser->cmml_end_callback (parser->user_data);
|
|
} else if (!xmlStrcmp (name, (xmlChar *) "stream")) {
|
|
if (parser->stream_callback) {
|
|
node = gst_cmml_parser_get_last_element (parser);
|
|
gst_cmml_parser_parse_stream (parser, node);
|
|
}
|
|
} else if (!xmlStrcmp (name, (xmlChar *) "head")) {
|
|
if (parser->head_callback) {
|
|
node = gst_cmml_parser_get_last_element (parser);
|
|
gst_cmml_parser_parse_head (parser, node);
|
|
}
|
|
}
|
|
}
|