From d280d0dbc9a26a3ad8a078d9f84c49dcec1dac74 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 24 Apr 2014 15:41:50 +0200 Subject: [PATCH] validate: Add a media-descriptor parser and writer --- validate/gst/validate/Makefile.am | 8 +- validate/gst/validate/gst-validate-report.c | 2 + validate/gst/validate/gst-validate-report.h | 1 + validate/gst/validate/gst-validate-reporter.c | 5 +- .../gst/validate/media-descriptor-parser.c | 591 ++++++++++++++++++ .../gst/validate/media-descriptor-parser.h | 82 +++ .../gst/validate/media-descriptor-writer.c | 534 ++++++++++++++++ .../gst/validate/media-descriptor-writer.h | 91 +++ validate/gst/validate/media-descriptor.c | 326 ++++++++++ validate/gst/validate/media-descriptor.h | 147 +++++ validate/tools/gst-validate-media-check.c | 95 ++- 11 files changed, 1830 insertions(+), 52 deletions(-) create mode 100644 validate/gst/validate/media-descriptor-parser.c create mode 100644 validate/gst/validate/media-descriptor-parser.h create mode 100644 validate/gst/validate/media-descriptor-writer.c create mode 100644 validate/gst/validate/media-descriptor-writer.h create mode 100644 validate/gst/validate/media-descriptor.c create mode 100644 validate/gst/validate/media-descriptor.h diff --git a/validate/gst/validate/Makefile.am b/validate/gst/validate/Makefile.am index d6959f4e78..41af0baa57 100644 --- a/validate/gst/validate/Makefile.am +++ b/validate/gst/validate/Makefile.am @@ -11,8 +11,11 @@ libgstvalidate_@GST_API_VERSION@_la_SOURCES = \ gst-validate-override.c \ gst-validate-utils.c \ gst-validate-override-registry.c \ + media-descriptor.c \ + media-descriptor-writer.c \ + media-descriptor-parser.c \ gst-validate-media-info.c \ - validate.c + validate.c libgstvalidate_@GST_API_VERSION@include_HEADERS = \ validate.h \ @@ -31,6 +34,9 @@ libgstvalidate_@GST_API_VERSION@include_HEADERS = \ gst-validate-runner.h \ gst-validate-scenario.h \ gst-validate-utils.h \ + media-descriptor.h \ + media-descriptor-writer.h \ + media-descriptor-parser.h \ gst-validate-media-info.h diff --git a/validate/gst/validate/gst-validate-report.c b/validate/gst/validate/gst-validate-report.c index 065c10e56d..95f90b1b00 100644 --- a/validate/gst/validate/gst-validate-report.c +++ b/validate/gst/validate/gst-validate-report.c @@ -202,6 +202,8 @@ gst_validate_report_load_issues (void) _("an error occured while starting playback of the test file"), NULL); REGISTER_VALIDATE_ISSUE (CRITICAL, FILE_PLAYBACK_ERROR, _("an error during playback of the file"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, FILE_NO_STREAM_ID, + _("the discoverer found a stream that had no stream ID"), NULL); REGISTER_VALIDATE_ISSUE (CRITICAL, ALLOCATION_FAILURE, _("a memory allocation failed during Validate run"), NULL); diff --git a/validate/gst/validate/gst-validate-report.h b/validate/gst/validate/gst-validate-report.h index 313de84d37..2418886a60 100644 --- a/validate/gst/validate/gst-validate-report.h +++ b/validate/gst/validate/gst-validate-report.h @@ -103,6 +103,7 @@ typedef enum { #define GST_VALIDATE_ISSUE_ID_FILE_CHECK_FAILURE (((GstValidateIssueId) GST_VALIDATE_AREA_FILE_CHECK) << GST_VALIDATE_ISSUE_ID_SHIFT | 7) #define GST_VALIDATE_ISSUE_ID_FILE_PLAYBACK_START_FAILURE (((GstValidateIssueId) GST_VALIDATE_AREA_FILE_CHECK) << GST_VALIDATE_ISSUE_ID_SHIFT | 8) #define GST_VALIDATE_ISSUE_ID_FILE_PLAYBACK_ERROR (((GstValidateIssueId) GST_VALIDATE_AREA_FILE_CHECK) << GST_VALIDATE_ISSUE_ID_SHIFT | 9) +#define GST_VALIDATE_ISSUE_ID_FILE_NO_STREAM_ID (((GstValidateIssueId) GST_VALIDATE_AREA_FILE_CHECK) << GST_VALIDATE_ISSUE_ID_SHIFT | 10) #define GST_VALIDATE_ISSUE_ID_ALLOCATION_FAILURE (((GstValidateIssueId) GST_VALIDATE_AREA_RUN_ERROR) << GST_VALIDATE_ISSUE_ID_SHIFT | 1) #define GST_VALIDATE_ISSUE_ID_MISSING_PLUGIN (((GstValidateIssueId) GST_VALIDATE_AREA_RUN_ERROR) << GST_VALIDATE_ISSUE_ID_SHIFT | 2) diff --git a/validate/gst/validate/gst-validate-reporter.c b/validate/gst/validate/gst-validate-reporter.c index fe69de75c7..af3bb176e8 100644 --- a/validate/gst/validate/gst-validate-reporter.c +++ b/validate/gst/validate/gst-validate-reporter.c @@ -69,8 +69,9 @@ _free_priv (GstValidateReporterPrivate * priv) static GstValidateReporterPrivate * gst_validate_reporter_get_priv (GstValidateReporter * reporter) { - GstValidateReporterPrivate *priv = - g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE); + GstValidateReporterPrivate *priv; + + priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE); if (priv == NULL) { priv = g_slice_new0 (GstValidateReporterPrivate); diff --git a/validate/gst/validate/media-descriptor-parser.c b/validate/gst/validate/media-descriptor-parser.c new file mode 100644 index 0000000000..f62bdd7c32 --- /dev/null +++ b/validate/gst/validate/media-descriptor-parser.c @@ -0,0 +1,591 @@ +/** + * Gstreamer + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "media-descriptor-parser.h" + +G_DEFINE_TYPE (GstMediaDescriptorParser, gst_media_descriptor_parser, + GST_TYPE_MEDIA_DESCRIPTOR); + +enum +{ + PROP_0, + PROP_PATH, + N_PROPERTIES +}; + +struct _GstMediaDescriptorParserPrivate +{ + gchar *xmlpath; + + gboolean in_stream; + gchar *xmlcontent; + GMarkupParseContext *parsecontext; +}; + +/* Private methods and callbacks */ +static gint +compare_frames (FrameNode * frm, FrameNode * frm1) +{ + if (frm->id < frm1->id) + return -1; + + else if (frm->id == frm1->id) + return 0; + + return 1; +} + +static void +deserialize_filenode (FileNode *filenode, + const gchar ** names, const gchar ** values) +{ + gint i; + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "uri") == 0) + filenode->uri = g_strdup (values[i]); + else if (g_strcmp0 (names[i], "id") == 0) + filenode->id = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "frame-detection") == 0) + filenode->frame_detection = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "duration") == 0) + filenode->duration = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "seekable") == 0) + filenode->seekable = (gboolean) g_strcmp0 (values[i], "false"); + } +} + +static StreamNode * +deserialize_streamnode (const gchar ** names, const gchar ** values) +{ + gint i; + StreamNode *streamnode = g_slice_new0 (StreamNode); + + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "id") == 0) + streamnode->id = g_strdup (values[i]); + else if (g_strcmp0 (names[i], "caps") == 0) + streamnode->caps = gst_caps_from_string (values[i]); + else if (g_strcmp0 (names[i], "padname") == 0) + streamnode->padname = g_strdup (values[i]); + } + + + return streamnode; +} + +static TagsNode * +deserialize_tagsnode (const gchar ** names, const gchar ** values) +{ + TagsNode *tagsnode = g_slice_new0 (TagsNode); + + return tagsnode; +} + +static TagNode * +deserialize_tagnode (const gchar ** names, const gchar ** values) +{ + gint i; + TagNode *tagnode = g_slice_new0 (TagNode); + + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "content") == 0) + tagnode->taglist = gst_tag_list_new_from_string (values[i]); + } + + return tagnode; +} + +static FrameNode * +deserialize_framenode (const gchar ** names, const gchar ** values) +{ + gint i; + + FrameNode *framenode = g_slice_new0 (FrameNode); + + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "id") == 0) + framenode->id = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "offset") == 0) + framenode->offset = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "offset-end") == 0) + framenode->offset_end = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "duration") == 0) + framenode->duration = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "pts") == 0) + framenode->pts = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "dts") == 0) + framenode->dts = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "is-keyframe") == 0) + framenode->is_keyframe = g_ascii_strtoull (values[i], NULL, 0); + } + + framenode->buf = gst_buffer_new (); + + GST_BUFFER_OFFSET (framenode->buf) = framenode->offset; + GST_BUFFER_OFFSET_END (framenode->buf) = framenode->offset_end; + GST_BUFFER_DURATION (framenode->buf) = framenode->duration; + GST_BUFFER_PTS (framenode->buf) = framenode->pts; + GST_BUFFER_DTS (framenode->buf) = framenode->dts; + + if (framenode->is_keyframe == FALSE) + GST_BUFFER_FLAG_SET (framenode->buf, GST_BUFFER_FLAG_DELTA_UNIT); + + return framenode; +} + + +static gboolean +frame_node_compare (FrameNode * fnode, GstBuffer * buf, GstBuffer * expected) +{ + if (expected != NULL) { + GST_BUFFER_OFFSET (expected) = fnode->offset; + GST_BUFFER_OFFSET_END (expected) = fnode->offset_end; + GST_BUFFER_DURATION (expected) = fnode->duration; + GST_BUFFER_PTS (expected) = fnode->pts; + GST_BUFFER_DTS (expected) = fnode->dts; + if (fnode->is_keyframe) + GST_BUFFER_FLAG_SET (expected, GST_BUFFER_FLAG_DELTA_UNIT); + } + + if ((fnode->offset == GST_BUFFER_OFFSET (buf) && + fnode->offset_end == GST_BUFFER_OFFSET_END (buf) && + fnode->duration == GST_BUFFER_DURATION (buf) && + fnode->pts == GST_BUFFER_PTS (buf) && + fnode->dts == GST_BUFFER_DTS (buf) && + fnode->is_keyframe == GST_BUFFER_FLAG_IS_SET (buf, + GST_BUFFER_FLAG_DELTA_UNIT)) == FALSE) { + return TRUE; + } + + return FALSE; +} + +static void +on_end_element_cb (GMarkupParseContext * context, + const gchar * element_name, gpointer user_data, GError ** error) +{ + GstMediaDescriptorParserPrivate *priv = + GST_MEDIA_DESCRIPTOR_PARSER (user_data)->priv; + + if (g_strcmp0 (element_name, "stream") == 0) { + priv->in_stream = FALSE; + } +} + +static void +on_start_element_cb (GMarkupParseContext * context, + const gchar * element_name, const gchar ** attribute_names, + const gchar ** attribute_values, gpointer user_data, GError ** error) +{ + FileNode *filenode = GST_MEDIA_DESCRIPTOR (user_data)->filenode; + + GstMediaDescriptorParserPrivate *priv = + GST_MEDIA_DESCRIPTOR_PARSER (user_data)->priv; + + if (g_strcmp0 (element_name, "file") == 0) { + deserialize_filenode (filenode, attribute_names, attribute_values); + } else if (g_strcmp0 (element_name, "stream") == 0) { + StreamNode *node = + deserialize_streamnode (attribute_names, attribute_values); + priv->in_stream = TRUE; + filenode->streams = g_list_prepend (filenode->streams, node); + } else if (g_strcmp0 (element_name, "frame") == 0) { + StreamNode *streamnode = filenode->streams->data; + + streamnode->cframe = streamnode->frames = + g_list_insert_sorted (streamnode->frames, + deserialize_framenode (attribute_names, attribute_values), + (GCompareFunc) compare_frames); + } else if (g_strcmp0 (element_name, "tags") == 0) { + if (priv->in_stream) { + StreamNode *snode = (StreamNode *) filenode->streams->data; + + snode->tags = deserialize_tagsnode (attribute_names, attribute_values); + } else { + filenode->tags = deserialize_tagsnode (attribute_names, attribute_values); + } + } else if (g_strcmp0 (element_name, "tag") == 0) { + TagsNode *tagsnode; + + if (priv->in_stream) { + StreamNode *snode = (StreamNode *) filenode->streams->data; + tagsnode = snode->tags; + } else { + tagsnode = filenode->tags; + } + + tagsnode->tags = g_list_prepend (tagsnode->tags, + deserialize_tagnode (attribute_names, attribute_values)); + } +} + +static void +on_error_cb (GMarkupParseContext * context, GError * error, gpointer user_data) +{ + GST_ERROR ("Error parsing file: %s", error->message); +} + +static const GMarkupParser content_parser = { + on_start_element_cb, + on_end_element_cb, + NULL, + NULL, + &on_error_cb +}; + +static gboolean +set_xml_path (GstMediaDescriptorParser * parser, const gchar * path, + GError ** error) +{ + gsize xmlsize; + GError *err = NULL; + GstMediaDescriptorParserPrivate *priv = parser->priv; + + if (!g_file_get_contents (path, &priv->xmlcontent, &xmlsize, &err)) + goto failed; + + priv->xmlpath = g_strdup (path); + priv->parsecontext = g_markup_parse_context_new (&content_parser, + G_MARKUP_TREAT_CDATA_AS_TEXT, parser, NULL); + + if (g_markup_parse_context_parse (priv->parsecontext, priv->xmlcontent, + xmlsize, &err) == FALSE) + goto failed; + + return TRUE; + +failed: + g_propagate_error (error, err); + return FALSE; +} + +/* GObject standard vmethods */ +static void +dispose (GstMediaDescriptorParser * parser) +{ + G_OBJECT_CLASS (gst_media_descriptor_parser_parent_class)->dispose (G_OBJECT + (parser)); +} + +static void +finalize (GstMediaDescriptorParser * parser) +{ + GstMediaDescriptorParserPrivate *priv; + + priv = parser->priv; + + g_free (priv->xmlpath); + g_free (priv->xmlcontent); + + if (priv->parsecontext != NULL) + g_markup_parse_context_free (priv->parsecontext); + + G_OBJECT_CLASS (gst_media_descriptor_parser_parent_class)->finalize (G_OBJECT + (parser)); +} + + +static void +get_property (GObject * gobject, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } + +} + +static void +set_property (GObject * gobject, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } +} + +static void +gst_media_descriptor_parser_init (GstMediaDescriptorParser * parser) +{ + GstMediaDescriptorParserPrivate *priv; + + parser->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (parser, + GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParserPrivate); + + priv->xmlpath = NULL; +} + +static void +gst_media_descriptor_parser_class_init (GstMediaDescriptorParserClass * + self_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + + g_type_class_add_private (self_class, + sizeof (GstMediaDescriptorParserPrivate)); + object_class->dispose = (void (*)(GObject * object)) dispose; + object_class->finalize = (void (*)(GObject * object)) finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; +} + +/* Public methods */ +GstMediaDescriptorParser * +gst_media_descriptor_parser_new (GstValidateRunner * runner, + const gchar * xmlpath, GError ** error) +{ + GstMediaDescriptorParser *parser; + + parser = g_object_new (GST_TYPE_MEDIA_DESCRIPTOR_PARSER, "validate-runner", + runner, NULL); + + if (set_xml_path (parser, xmlpath, error) == FALSE) { + g_object_unref (parser); + + return NULL; + } + + + return parser; +} + +gchar * +gst_media_descriptor_parser_get_xml_path (GstMediaDescriptorParser * parser) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), NULL); + + return g_strdup (parser->priv->xmlpath); +} + +gboolean +gst_media_descriptor_parser_add_stream (GstMediaDescriptorParser * parser, + GstPad * pad) +{ + GList *tmp; + gboolean ret = FALSE; + GstCaps *caps; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + caps = gst_pad_query_caps (pad, NULL); + for (tmp = ((GstMediaDescriptor *) parser)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == NULL && gst_caps_is_equal (streamnode->caps, caps)) { + ret = TRUE; + streamnode->pad = gst_object_ref (pad); + + goto done; + } + } + +done: + if (caps != NULL) + gst_caps_unref (caps); + + return ret; +} + +gboolean +gst_media_descriptor_parser_all_stream_found (GstMediaDescriptorParser * parser) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + for (tmp = ((GstMediaDescriptor *) parser)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == NULL) + return FALSE; + + } + + return TRUE; +} + +gboolean +gst_media_descriptor_parser_add_frame (GstMediaDescriptorParser * parser, + GstPad * pad, GstBuffer * buf, GstBuffer * expected) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + for (tmp = ((GstMediaDescriptor *) parser)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == pad && streamnode->cframe) { + FrameNode *fnode = streamnode->cframe->data; + + streamnode->cframe = streamnode->cframe->next; + return frame_node_compare (fnode, buf, expected); + } + } + + return FALSE; +} + +gboolean +gst_media_descriptor_parser_add_taglist (GstMediaDescriptorParser * parser, + GstTagList * taglist) +{ + GList *tmptag; + TagsNode *tagsnode; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + g_return_val_if_fail (GST_IS_STRUCTURE (taglist), FALSE); + + tagsnode = ((GstMediaDescriptor *) parser)->filenode->tags; + + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + if (tag_node_compare ((TagNode *) tmptag->data, taglist)) { + GST_DEBUG ("Adding tag %" GST_PTR_FORMAT, taglist); + return TRUE; + } + } + + return FALSE; +} + +gboolean +gst_media_descriptor_parser_all_tags_found (GstMediaDescriptorParser * parser) +{ + GList *tmptag; + TagsNode *tagsnode; + gboolean ret = TRUE; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + tagsnode = ((GstMediaDescriptor *) parser)->filenode->tags; + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + gchar *tag = NULL; + + tag = gst_tag_list_to_string (((TagNode *) tmptag->data)->taglist); + if (((TagNode *) tmptag->data)->found == FALSE) { + + if (((TagNode *) tmptag->data)->taglist != NULL) { + GST_DEBUG ("Tag not found %s", tag); + } else { + GST_DEBUG ("Tag not not properly deserialized"); + } + + ret = FALSE; + } + + GST_DEBUG ("Tag properly found found %s", tag); + g_free (tag); + } + + return ret; +} + +gboolean +gst_media_descriptor_parser_detects_frames (GstMediaDescriptorParser * parser) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + return ((GstMediaDescriptor *) parser)->filenode->frame_detection; +} + +GstClockTime +gst_media_descriptor_parser_get_duration (GstMediaDescriptorParser * parser) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + return ((GstMediaDescriptor *) parser)->filenode->duration; +} + +gboolean +gst_media_descriptor_parser_get_seekable (GstMediaDescriptorParser * parser) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + return ((GstMediaDescriptor *) parser)->filenode->seekable; +} + +GList * +gst_media_descriptor_parser_get_buffers (GstMediaDescriptorParser * parser, + GstPad * pad, GCompareFunc compare_func) +{ + GList *ret = NULL, *tmpstream, *tmpframe; + gboolean check = (pad == NULL); + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + for (tmpstream = ((GstMediaDescriptor *) parser)->filenode->streams; + tmpstream; tmpstream = tmpstream->next) { + StreamNode *streamnode = (StreamNode *) tmpstream->data; + + if (pad && streamnode->pad == pad) + check = TRUE; + + if (check) { + for (tmpframe = streamnode->frames; tmpframe; tmpframe = tmpframe->next) { + if (compare_func) + ret = + g_list_insert_sorted (ret, + gst_buffer_ref (((FrameNode *) tmpframe->data)->buf), + compare_func); + else + ret = + g_list_prepend (ret, + gst_buffer_ref (((FrameNode *) tmpframe->data)->buf)); + } + + if (pad != NULL) + goto done; + } + } + + +done: + return ret; +} + +GList * +gst_media_descriptor_parser_get_pads (GstMediaDescriptorParser * parser) +{ + GList *ret = NULL, *tmp; + + for (tmp = ((GstMediaDescriptor *) parser)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *snode = (StreamNode *) tmp->data; + ret = g_list_append (ret, gst_pad_new (snode->padname, GST_PAD_UNKNOWN)); + } + + return ret; +} diff --git a/validate/gst/validate/media-descriptor-parser.h b/validate/gst/validate/media-descriptor-parser.h new file mode 100644 index 0000000000..9a783448a3 --- /dev/null +++ b/validate/gst/validate/media-descriptor-parser.h @@ -0,0 +1,82 @@ +/** + * Insanity QA system + * + * Copyright (c) 2012, Collabora Ltd + * Author: Thibault Saunier + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GST_MEDIA_DESCRIPTOR_PARSER_h +#define GST_MEDIA_DESCRIPTOR_PARSER_h + +#include +#include +#include +#include "media-descriptor.h" + +G_BEGIN_DECLS + +GType gst_media_descriptor_parser_get_type (void); + +#define GST_TYPE_MEDIA_DESCRIPTOR_PARSER (gst_media_descriptor_parser_get_type ()) +#define GST_MEDIA_DESCRIPTOR_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParser)) +#define GST_MEDIA_DESCRIPTOR_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParserClass)) +#define GST_IS_MEDIA_DESCRIPTOR_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MEDIA_DESCRIPTOR_PARSER)) +#define GST_IS_MEDIA_DESCRIPTOR_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEDIA_DESCRIPTOR_PARSER)) +#define GST_MEDIA_DESCRIPTOR_PARSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParserClass)) + +typedef struct _GstMediaDescriptorParserPrivate GstMediaDescriptorParserPrivate; + + +typedef struct { + GstMediaDescriptor parent; + + GstMediaDescriptorParserPrivate *priv; + +} GstMediaDescriptorParser; + +typedef struct { + + GstMediaDescriptorClass parent; + +} GstMediaDescriptorParserClass; + +GstMediaDescriptorParser * gst_media_descriptor_parser_new (GstValidateRunner *runner, + const gchar * xmlpath, + GError **error); +gchar * gst_media_descriptor_parser_get_xml_path (GstMediaDescriptorParser *parser); +gboolean gst_media_descriptor_parser_detects_frames (GstMediaDescriptorParser *parser); +GstClockTime gst_media_descriptor_parser_get_duration (GstMediaDescriptorParser *parser); +gboolean gst_media_descriptor_parser_get_seekable (GstMediaDescriptorParser * parser); +gboolean gst_media_descriptor_parser_add_stream (GstMediaDescriptorParser *parser, + GstPad *pad); +gboolean gst_media_descriptor_parser_add_taglist (GstMediaDescriptorParser *parser, + GstTagList *taglist); +gboolean gst_media_descriptor_parser_all_stream_found (GstMediaDescriptorParser *parser); +gboolean gst_media_descriptor_parser_all_tags_found (GstMediaDescriptorParser *parser); +gboolean gst_media_descriptor_parser_add_frame (GstMediaDescriptorParser *parser, + GstPad *pad, + GstBuffer *buf, + GstBuffer *expected); +GList * gst_media_descriptor_parser_get_buffers (GstMediaDescriptorParser * parser, + GstPad *pad, + GCompareFunc compare_func); +GList * gst_media_descriptor_parser_get_pads (GstMediaDescriptorParser * parser); + +G_END_DECLS + +#endif /* GST_MEDIA_DESCRIPTOR_PARSER_h */ diff --git a/validate/gst/validate/media-descriptor-writer.c b/validate/gst/validate/media-descriptor-writer.c new file mode 100644 index 0000000000..1fc3282e09 --- /dev/null +++ b/validate/gst/validate/media-descriptor-writer.c @@ -0,0 +1,534 @@ +/** + * Gstreamer + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "media-descriptor-writer.h" +#include + +G_DEFINE_TYPE (GstMediaDescriptorWriter, + gst_media_descriptor_writer, GST_TYPE_MEDIA_DESCRIPTOR); + +#define STR_APPEND(arg, nb_white) \ + tmpstr = res; \ + res = g_strdup_printf ("%s%*s%s%s", res, (nb_white), " ", (arg), "\n"); \ + g_free (tmpstr); + +#define STR_APPEND0(arg) STR_APPEND((arg), 0) +#define STR_APPEND1(arg) STR_APPEND((arg), 2) +#define STR_APPEND2(arg) STR_APPEND((arg), 4) +#define STR_APPEND3(arg) STR_APPEND((arg), 6) +#define STR_APPEND4(arg) STR_APPEND((arg), 8) + + +enum +{ + PROP_0, + PROP_PATH, + N_PROPERTIES +}; + +struct _GstMediaDescriptorWriterPrivate +{ + GList *serialized_string; + guint stream_id; +}; + +static void +finalize (GstMediaDescriptorWriter * writer) +{ + G_OBJECT_CLASS (gst_media_descriptor_writer_parent_class)-> + finalize (G_OBJECT (writer)); +} + +static void +get_property (GObject * gobject, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } + +} + +static void +set_property (GObject * gobject, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } +} + +static void +gst_media_descriptor_writer_init (GstMediaDescriptorWriter * writer) +{ + GstMediaDescriptorWriterPrivate *priv; + + writer->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (writer, + GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriterPrivate); + + priv->serialized_string = NULL; + priv->stream_id = 0; +} + +static void + gst_media_descriptor_writer_class_init + (GstMediaDescriptorWriterClass * self_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + + g_type_class_add_private (self_class, + sizeof (GstMediaDescriptorWriterPrivate)); + object_class->finalize = (void (*)(GObject * object)) finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; +} + +/* Private methods */ +static gchar * +serialize_filenode (GstMediaDescriptorWriter * writer) +{ + gchar *res, *tmpstr, *caps_str, *tmpnode; + GList *tmp, *tmp2; + TagsNode *tagsnode; + FileNode *filenode = ((GstMediaDescriptor *) writer)->filenode; + + res = g_markup_printf_escaped ("\n", + filenode->duration, filenode->frame_detection, filenode->uri, + filenode->seekable ? "true" : "false"); + + if (filenode->caps) + caps_str = gst_caps_to_string (filenode->caps); + else + caps_str = g_strdup (""); + + tmpnode = g_strdup_printf ("", caps_str); + STR_APPEND1 (tmpnode); + g_free (caps_str); + g_free (tmpnode); + + for (tmp = filenode->streams; tmp; tmp = tmp->next) { + GList *tmp3; + StreamNode *snode = ((StreamNode *) tmp->data); + + STR_APPEND2 (snode->str_open); + + for (tmp2 = snode->frames; tmp2; tmp2 = tmp2->next) { + STR_APPEND3 (((FrameNode *) tmp2->data)->str_open); + } + + tagsnode = snode->tags; + STR_APPEND3 (tagsnode->str_open); + for (tmp3 = tagsnode->tags; tmp3; tmp3 = tmp3->next) { + STR_APPEND4 (((TagNode *) tmp3->data)->str_open); + } + STR_APPEND3 (tagsnode->str_close); + + STR_APPEND2 (snode->str_close); + } + STR_APPEND1 (""); + + tagsnode = filenode->tags; + STR_APPEND1 (tagsnode->str_open); + for (tmp2 = tagsnode->tags; tmp2; tmp2 = tmp2->next) { + STR_APPEND2 (((TagNode *) tmp2->data)->str_open); + } + STR_APPEND1 (tagsnode->str_close); + + tmpstr = res; + res = g_strdup_printf ("%s%s", res, filenode->str_close); + g_free (tmpstr); + + return res; +} + +/* Public methods */ +GstMediaDescriptorWriter * +gst_media_descriptor_writer_new (GstValidateRunner * runner, + const gchar * uri, GstClockTime duration, gboolean seekable) +{ + GstMediaDescriptorWriter *writer; + FileNode *fnode; + + writer = + g_object_new (GST_TYPE_MEDIA_DESCRIPTOR_WRITER, "validate-runner", runner, + NULL); + + fnode = ((GstMediaDescriptor *) writer)->filenode; + fnode->uri = g_strdup (uri); + fnode->duration = duration; + fnode->seekable = seekable; + fnode->str_open = NULL; + + fnode->str_close = g_markup_printf_escaped (""); + + return writer; +} + +static gboolean +gst_media_descriptor_writer_add_stream (GstMediaDescriptorWriter * writer, + GstDiscovererStreamInfo * info) +{ + const gchar *stype; + gboolean ret = FALSE; + GstCaps *caps; + gchar *capsstr = NULL; + StreamNode *snode = NULL; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + snode = g_slice_new0 (StreamNode); + snode->frames = NULL; + snode->cframe = NULL; + + snode->id = g_strdup (gst_discoverer_stream_info_get_stream_id (info)); + if (snode->id == NULL) { + caps = gst_discoverer_stream_info_get_caps (info); + capsstr = gst_caps_to_string (caps); + + g_slice_free (StreamNode, snode); + GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_ID, + "Stream with caps: %s has no stream ID", + capsstr); + gst_caps_unref (caps); + g_free (capsstr); + + return FALSE; + } + + caps = gst_discoverer_stream_info_get_caps (info); + snode->caps = caps; + capsstr = gst_caps_to_string (caps); + if (GST_IS_DISCOVERER_AUDIO_INFO (info)) { + stype = "audio"; + } else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) { + if (gst_discoverer_video_info_is_image (GST_DISCOVERER_VIDEO_INFO (info))) + stype = "image"; + else + stype = "video"; + } else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) { + stype = "subtitle"; + } else { + stype = "Unknown"; + } + + snode->str_open = + g_markup_printf_escaped + ("", stype, capsstr, snode->id); + + snode->str_close = g_markup_printf_escaped (""); + + ((GstMediaDescriptor *) writer)->filenode->streams = + g_list_prepend (((GstMediaDescriptor *) writer)->filenode->streams, + snode); + + if (gst_discoverer_stream_info_get_tags (info)) { + gst_media_descriptor_writer_add_tags (writer, snode->id, + gst_discoverer_stream_info_get_tags (info)); + } + + if (caps != NULL) + gst_caps_unref (caps); + g_free (capsstr); + + return ret; +} + +GstMediaDescriptorWriter * +gst_media_descriptor_writer_new_discover (GstValidateRunner * runner, + const gchar * uri, GError ** err) +{ + GList *tmp, *streams; + GstDiscovererInfo *info; + GstDiscoverer *discoverer; + GstDiscovererStreamInfo *streaminfo; + GstMediaDescriptorWriter *writer; + + discoverer = gst_discoverer_new (GST_SECOND * 60, err); + + if (discoverer == NULL) { + GST_ERROR ("Could not create discoverer"); + + return NULL; + } + + info = gst_discoverer_discover_uri (discoverer, uri, err); + if (info == NULL + || gst_discoverer_info_get_result (info) != GST_DISCOVERER_OK) { + + GST_ERROR ("Could not discover URI: %s", uri); + + return NULL; + } + + writer = + gst_media_descriptor_writer_new (runner, + gst_discoverer_info_get_uri (info), + gst_discoverer_info_get_duration (info), + gst_discoverer_info_get_seekable (info)); + + if (gst_discoverer_info_get_tags (info)) + gst_media_descriptor_writer_add_taglist (writer, + gst_discoverer_info_get_tags (info)); + + streaminfo = gst_discoverer_info_get_stream_info (info); + ((GstMediaDescriptor *) writer)->filenode->caps = + gst_discoverer_stream_info_get_caps (GST_DISCOVERER_STREAM_INFO + (streaminfo)); + + streams = gst_discoverer_info_get_stream_list (info); + for (tmp = streams; tmp; tmp = tmp->next) + gst_media_descriptor_writer_add_stream (writer, tmp->data); + gst_discoverer_stream_info_list_free(streams); + + + return writer; +} + +gboolean +gst_media_descriptor_writer_add_tags (GstMediaDescriptorWriter + * writer, const gchar * stream_id, const GstTagList * taglist) +{ + TagsNode *tagsnode; + TagNode *tagnode; + GList *tmp, *tmptag; + + gchar *str_str = NULL; + StreamNode *snode = NULL; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + if (g_strcmp0 (((StreamNode *) tmp->data)->id, stream_id) == 0) { + snode = tmp->data; + + break; + } + } + + if (snode == NULL) { + GST_WARNING ("Could not find stream with id: %s", stream_id); + + return FALSE; + } + + if (snode->tags == NULL) { + tagsnode = g_slice_new0 (TagsNode); + tagsnode->str_open = g_markup_printf_escaped (""); + tagsnode->str_close = g_markup_printf_escaped (""); + snode->tags = tagsnode; + } else { + tagsnode = snode->tags; + + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + if (tag_node_compare ((TagNode *) tmptag->data, taglist)) { + GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT, + taglist); + return TRUE; + } + } + } + + tagnode = g_slice_new0 (TagNode); + tagnode->taglist = gst_tag_list_copy (taglist); + str_str = gst_tag_list_to_string (tagnode->taglist); + tagnode->str_open = + g_markup_printf_escaped ("", str_str); + tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode); + + g_free (str_str); + + return FALSE; +} + +gboolean +gst_media_descriptor_writer_add_pad (GstMediaDescriptorWriter * + writer, GstPad * pad) +{ + GList *tmp; + gboolean ret = FALSE; + GstCaps *caps; + gchar *capsstr = NULL, *padname = NULL; + StreamNode *snode = NULL; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + caps = gst_pad_get_current_caps (pad); + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == pad) { + goto done; + } + } + + snode = g_slice_new0 (StreamNode); + snode->frames = NULL; + snode->cframe = NULL; + + snode->caps = gst_caps_ref (caps); + snode->pad = gst_object_ref (pad); + + capsstr = gst_caps_to_string (caps); + padname = gst_pad_get_name (pad); + snode->str_open = + g_markup_printf_escaped + ("", padname, capsstr, 0); + + snode->str_close = g_markup_printf_escaped (""); + + ((GstMediaDescriptor *) writer)->filenode->streams = + g_list_prepend (((GstMediaDescriptor *) writer)->filenode->streams, + snode); + +done: + if (caps != NULL) + gst_caps_unref (caps); + g_free (capsstr); + g_free (padname); + + return ret; +} + +gboolean +gst_media_descriptor_writer_add_taglist (GstMediaDescriptorWriter * writer, + const GstTagList * taglist) +{ + gchar *str_str = NULL; + TagsNode *tagsnode; + TagNode *tagnode; + GList *tmptag; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + if (((GstMediaDescriptor *) writer)->filenode->tags == NULL) { + tagsnode = g_slice_new0 (TagsNode); + tagsnode->str_open = g_markup_printf_escaped (""); + tagsnode->str_close = g_markup_printf_escaped (""); + ((GstMediaDescriptor *) writer)->filenode->tags = tagsnode; + } else { + tagsnode = ((GstMediaDescriptor *) writer)->filenode->tags; + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + if (tag_node_compare ((TagNode *) tmptag->data, taglist)) { + GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT, + taglist); + return TRUE; + } + } + } + + tagnode = g_slice_new0 (TagNode); + tagnode->taglist = gst_tag_list_copy (taglist); + str_str = gst_tag_list_to_string (tagnode->taglist); + tagnode->str_open = + g_markup_printf_escaped ("", str_str); + tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode); + + g_free (str_str); + + return FALSE; +} + +gboolean +gst_media_descriptor_writer_add_frame (GstMediaDescriptorWriter + * writer, GstPad * pad, GstBuffer * buf) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + ((GstMediaDescriptor *) writer)->filenode->frame_detection = TRUE; + + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == pad) { + guint id = g_list_length (streamnode->frames); + FrameNode *fnode = g_slice_new0 (FrameNode); + + fnode->id = id; + fnode->offset = GST_BUFFER_OFFSET (buf); + fnode->offset_end = GST_BUFFER_OFFSET_END (buf); + fnode->duration = GST_BUFFER_DURATION (buf); + fnode->pts = GST_BUFFER_PTS (buf); + fnode->dts = GST_BUFFER_DTS (buf); + fnode->is_keyframe = (GST_BUFFER_FLAG_IS_SET (buf, + GST_BUFFER_FLAG_DELTA_UNIT) == FALSE); + + fnode->str_open = + g_markup_printf_escaped (" ", + fnode->duration, id, fnode->is_keyframe, + fnode->offset, fnode->offset_end, fnode->pts, fnode->dts); + + fnode->str_close = NULL; + + streamnode->frames = g_list_append (streamnode->frames, fnode); + return TRUE; + } + } + + return FALSE; +} + +gboolean +gst_media_descriptor_writer_write (GstMediaDescriptorWriter * + writer, const gchar * filename) +{ + gboolean ret = FALSE; + gchar *serialized; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + serialized = serialize_filenode (writer); + + + if (g_file_set_contents (filename, serialized, -1, NULL) == TRUE) + ret = TRUE; + + + g_free (serialized); + + return ret; +} + +gchar * +gst_media_descriptor_writer_serialize (GstMediaDescriptorWriter * writer) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + return serialize_filenode (writer); +} diff --git a/validate/gst/validate/media-descriptor-writer.h b/validate/gst/validate/media-descriptor-writer.h new file mode 100644 index 0000000000..f27e06d0bd --- /dev/null +++ b/validate/gst/validate/media-descriptor-writer.h @@ -0,0 +1,91 @@ +/** + * Insanity QA system + * + * Copyright (c) 2012, Collabora Ltd + * Author: Thibault Saunier + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GST_MEDIA_DESCRIPTOR_WRITER_h +#define GST_MEDIA_DESCRIPTOR_WRITER_h + +#include +#include +#include +#include +#include "media-descriptor.h" + +G_BEGIN_DECLS + +GType gst_media_descriptor_writer_get_type (void); + +#define GST_TYPE_MEDIA_DESCRIPTOR_WRITER (gst_media_descriptor_writer_get_type ()) +#define GST_MEDIA_DESCRIPTOR_WRITER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriter)) +#define GST_MEDIA_DESCRIPTOR_WRITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriterClass)) +#define GST_IS_MEDIA_DESCRIPTOR_WRITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MEDIA_DESCRIPTOR_WRITER)) +#define GST_IS_MEDIA_DESCRIPTOR_WRITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEDIA_DESCRIPTOR_WRITER)) +#define GST_MEDIA_DESCRIPTOR_WRITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriterClass)) + +typedef struct _GstMediaDescriptorWriterPrivate GstMediaDescriptorWriterPrivate; + + +typedef struct { + GstMediaDescriptor parent; + + GstMediaDescriptorWriterPrivate *priv; + +} GstMediaDescriptorWriter; + +typedef struct { + + GstMediaDescriptorClass parent; + +} GstMediaDescriptorWriterClass; + +GstMediaDescriptorWriter * gst_media_descriptor_writer_new_discover (GstValidateRunner *runner, + const gchar *uri, + GError **err); + +GstMediaDescriptorWriter * gst_media_descriptor_writer_new (GstValidateRunner *runner, + const gchar *location, + GstClockTime duration, + gboolean seekable); + +gchar * gst_media_descriptor_writer_get_xml_path (GstMediaDescriptorWriter *writer); + +gboolean gst_media_descriptor_writer_detects_frames (GstMediaDescriptorWriter *writer); +GstClockTime gst_media_descriptor_writer_get_duration (GstMediaDescriptorWriter *writer); +gboolean gst_media_descriptor_writer_get_seekable (GstMediaDescriptorWriter * writer); + +gboolean gst_media_descriptor_writer_add_pad (GstMediaDescriptorWriter *writer, + GstPad *pad); +gboolean gst_media_descriptor_writer_add_taglist (GstMediaDescriptorWriter *writer, + const GstTagList *taglist); +gboolean gst_media_descriptor_writer_add_frame (GstMediaDescriptorWriter *writer, + GstPad *pad, + GstBuffer *buf); +gboolean gst_media_descriptor_writer_add_tags (GstMediaDescriptorWriter *writer, + const gchar *stream_id, + const GstTagList *taglist); +gboolean gst_media_descriptor_writer_write (GstMediaDescriptorWriter * writer, + const gchar * filename); +gchar * gst_media_descriptor_writer_serialize (GstMediaDescriptorWriter *writer); + + +G_END_DECLS + +#endif /* GST_MEDIA_DESCRIPTOR_WRITER_h */ diff --git a/validate/gst/validate/media-descriptor.c b/validate/gst/validate/media-descriptor.c new file mode 100644 index 0000000000..9ced8ebeef --- /dev/null +++ b/validate/gst/validate/media-descriptor.c @@ -0,0 +1,326 @@ +/** + * Gstreamer + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "media-descriptor.h" + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstMediaDescriptor, gst_media_descriptor, + G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, NULL)); + +#define GST_MEDIA_DESCRIPTOR_GET_PRIVATE(o)\ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptorPrivate)) + +static inline void +free_tagnode (TagNode * tagnode) +{ + g_free (tagnode->str_open); + g_free (tagnode->str_close); + if (tagnode->taglist) + gst_tag_list_unref (tagnode->taglist); + + g_slice_free (TagNode, tagnode); +} + +static inline void +free_tagsnode (TagsNode * tagsnode) +{ + g_free (tagsnode->str_open); + g_free (tagsnode->str_close); + g_list_free_full (tagsnode->tags, (GDestroyNotify) free_tagnode); + g_slice_free (TagsNode, tagsnode); +} + +static inline void +free_framenode (FrameNode * framenode) +{ + g_free (framenode->str_open); + g_free (framenode->str_close); + + if (framenode->buf) + gst_buffer_unref (framenode->buf); + + g_slice_free (FrameNode, framenode); +} + +static inline void +free_streamnode (StreamNode * streamnode) +{ + if (streamnode->caps) + gst_caps_unref (streamnode->caps); + + g_list_free_full (streamnode->frames, (GDestroyNotify) free_framenode); + + if (streamnode->pad) + gst_object_unref (streamnode->pad); + + if (streamnode->tags) + free_tagsnode (streamnode->tags); + + if (streamnode->padname) + g_free (streamnode->padname); + + if (streamnode->id) + g_free (streamnode->id); + + g_free (streamnode->str_open); + g_free (streamnode->str_close); + g_slice_free (StreamNode, streamnode); +} + +void +free_filenode (FileNode * filenode) +{ + g_list_free_full (filenode->streams, (GDestroyNotify) free_streamnode); + if (filenode->tags) + free_tagsnode (filenode->tags); + + if (filenode->uri) + g_free (filenode->uri); + + g_free (filenode->str_open); + g_free (filenode->str_close); + + g_slice_free (FileNode, filenode); +} + +gboolean +tag_node_compare (TagNode * tnode, const GstTagList * tlist) +{ + if (gst_structure_is_equal (GST_STRUCTURE (tlist), + GST_STRUCTURE (tnode->taglist)) == FALSE) { + return FALSE; + } + + tnode->found = TRUE; + + return TRUE; +} + +struct _GstMediaDescriptorPrivate +{ + gpointer dummy; +}; + +enum +{ + PROP_0, + PROP_RUNNER, + PROP_LAST +}; + + +static void +gst_media_descriptor_dispose (GstMediaDescriptor * self) +{ + G_OBJECT_CLASS (gst_media_descriptor_parent_class)->dispose (G_OBJECT (self)); +} + +static void +gst_media_descriptor_finalize (GstMediaDescriptor * self) +{ + if (self->filenode) + free_filenode (self->filenode); + + G_OBJECT_CLASS (gst_media_descriptor_parent_class)-> + finalize (G_OBJECT (self)); +} + +static void +gst_media_descriptor_init (GstMediaDescriptor * self) +{ + self->filenode = g_slice_new0 (FileNode); +} + +static void +_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object), + g_value_get_object (value)); + break; + default: + break; + } +} + +static void +_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + g_value_set_object (value, + gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object))); + break; + default: + break; + } +} + +static void +gst_media_descriptor_class_init (GstMediaDescriptorClass * self_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + + g_type_class_add_private (self_class, sizeof (GstMediaDescriptorPrivate)); + object_class->dispose = + (void (*)(GObject * object)) gst_media_descriptor_dispose; + object_class->finalize = + (void (*)(GObject * object)) gst_media_descriptor_finalize; + + object_class->get_property = _get_property; + object_class->set_property = _set_property; + + g_object_class_install_property (object_class, PROP_RUNNER, + g_param_spec_object ("validate-runner", "VALIDATE Runner", + "The Validate runner to " "report errors to", + GST_TYPE_VALIDATE_RUNNER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); +} + +static gint +compare_tags (GstMediaDescriptor *ref, StreamNode *rstream, StreamNode *cstream) +{ + gboolean found; + TagNode *rtag, *ctag; + GList *rtag_list, *ctag_list; + TagsNode *rtags, *ctags; + + rtags = rstream->tags; + if (rtags == NULL) + return 1; + + ctags = cstream->tags; + for (rtag_list = rtags->tags; rtag_list; rtag_list = rtag_list->next) { + rtag = rtag_list->data; + found = FALSE; + for (ctag_list = ctags->tags; ctag_list; ctag_list = ctag_list->next) { + ctag = ctag_list->data; + if (gst_tag_list_is_equal (rtag->taglist, ctag->taglist)) { + found = TRUE; + + break; + } + } + + if (found == FALSE) { + gchar *rtaglist = gst_tag_list_to_string (rtag->taglist); + + GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT, + "Reference descriptor for stream %s has tags %s" + " but no equivalent taglist was found on the compared stream", + rstream->id, rtaglist); + g_free (rtaglist); + + return 0; + } + } + + return 1; +} + +/* Return -1 if not found 1 if OK 0 if an error occured */ +static gint +comparse_stream (GstMediaDescriptor *ref, StreamNode *rstream, StreamNode *cstream) +{ + if (g_strcmp0 (rstream->id, cstream->id) == 0) { + if (!gst_caps_is_equal (rstream->caps, cstream->caps)) { + gchar *rcaps = gst_caps_to_string (rstream->caps), + *ccaps = gst_caps_to_string (cstream->caps); + GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT, + "Reference descriptor for stream %s has caps: %s" + " but compared stream %s has caps: %s", + rstream->id, rcaps, + cstream->id, ccaps); + g_free (rcaps); + g_free (ccaps); + return 0; + } + + return compare_tags (ref, rstream, cstream); + } + + return -1; +} + +gboolean +gst_media_descriptors_compare (GstMediaDescriptor * ref, + GstMediaDescriptor * compared) +{ + GList *rstream_list; + FileNode *rfilenode = ref->filenode, *cfilenode = compared->filenode; + + if (rfilenode->duration != cfilenode->duration) { + GST_VALIDATE_REPORT (ref, FILE_DURATION_INCORRECT, + "Duration %" GST_TIME_FORMAT " is different from the reference %" GST_TIME_FORMAT, + GST_TIME_ARGS (cfilenode->duration), + GST_TIME_ARGS (rfilenode->duration)); + } + + if (rfilenode->seekable != cfilenode->seekable) { + GST_VALIDATE_REPORT (ref, FILE_SEEKABLE_INCORRECT, + "File known as %s but is reported %s now", + rfilenode->seekable ? "seekable" : "not seekable", + cfilenode->seekable ? "seekable" : "not seekable"); + } + + if (g_list_length (rfilenode->streams) != g_list_length (cfilenode->streams)) { + GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT, + "Reference descriptor has %i streams != compared which has %i streams", + g_list_length (rfilenode->streams), g_list_length (cfilenode->streams)); + + return FALSE; + } + + + for (rstream_list = rfilenode->streams; rstream_list; + rstream_list = rstream_list->next) { + GList *cstream_list; + gint sfound = -1; + + for (cstream_list = cfilenode->streams; cstream_list; + cstream_list = cstream_list->next) { + + sfound = comparse_stream (ref, rstream_list->data, cstream_list->data); + if (sfound == 0) { + return FALSE; + } else if (sfound == 1) { + break; + } + } + + if (sfound == FALSE) { + GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT, + "Could not find stream %s in the compared descriptor", + ((StreamNode*) rstream_list->data)->id); + + return FALSE; + } + } + + return TRUE; +} diff --git a/validate/gst/validate/media-descriptor.h b/validate/gst/validate/media-descriptor.h new file mode 100644 index 0000000000..a81e9cd290 --- /dev/null +++ b/validate/gst/validate/media-descriptor.h @@ -0,0 +1,147 @@ +/** + * Gstreamer + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_MEDIA_DESCRIPTOR_H__ +#define __GST_MEDIA_DESCRIPTOR_H__ + +#include +#include +#include +#include "gst-validate-report.h" + +G_BEGIN_DECLS + +typedef struct +{ + /* Children */ + /* TagNode */ + GList *tags; + + gchar *str_open; + gchar *str_close; +} TagsNode; + +/* Parsing structures */ +typedef struct +{ + /* Children */ + /* StreamNode */ + GList *streams; + /* TagsNode */ + TagsNode *tags; + + /* attributes */ + guint64 id; + gchar *uri; + GstClockTime duration; + gboolean frame_detection; + gboolean seekable; + + GstCaps *caps; + + gchar *str_open; + gchar *str_close; +} FileNode; + +typedef struct +{ + /* Children */ + GstTagList *taglist; + + /* Testing infos */ + gboolean found; + + gchar *str_open; + gchar *str_close; +} TagNode; + +typedef struct +{ + /* Children */ + /* FrameNode */ + GList *frames; + + /* TagsNode */ + TagsNode *tags; + + /* Attributes */ + GstCaps *caps; + gchar *id; + gchar *padname; + + /* Testing infos */ + GstPad *pad; + GList *cframe; + + gchar *str_open; + gchar *str_close; +} StreamNode; + +typedef struct +{ + /* Attributes */ + guint64 id; + guint64 offset; + guint64 offset_end; + GstClockTime duration; + GstClockTime pts, dts; + gboolean is_keyframe; + + GstBuffer *buf; + + gchar *str_open; + gchar *str_close; +} FrameNode; + +void free_filenode (FileNode * filenode); +gboolean tag_node_compare (TagNode * tnode, const GstTagList * tlist); + +GType gst_media_descriptor_get_type (void); + +#define GST_TYPE_MEDIA_DESCRIPTOR (gst_media_descriptor_get_type ()) +#define GST_MEDIA_DESCRIPTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptor)) +#define GST_MEDIA_DESCRIPTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptorClass)) +#define GST_IS_MEDIA_DESCRIPTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MEDIA_DESCRIPTOR)) +#define GST_IS_MEDIA_DESCRIPTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEDIA_DESCRIPTOR)) +#define GST_MEDIA_DESCRIPTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptorClass)) + +typedef struct _GstMediaDescriptorPrivate GstMediaDescriptorPrivate; + +typedef struct { + GObject parent; + + FileNode *filenode; + + GstMediaDescriptorPrivate *priv; +} GstMediaDescriptor; + +typedef struct { + GObjectClass parent; + +} GstMediaDescriptorClass; + +gboolean gst_media_descriptors_compare (GstMediaDescriptor *ref, + GstMediaDescriptor *compared); + +G_END_DECLS + +#endif diff --git a/validate/tools/gst-validate-media-check.c b/validate/tools/gst-validate-media-check.c index 870f9a798a..779934540b 100644 --- a/validate/tools/gst-validate-media-check.c +++ b/validate/tools/gst-validate-media-check.c @@ -30,6 +30,9 @@ #include #include +#include +#include +#include #include /* move this into some utils file */ @@ -164,14 +167,15 @@ int main (int argc, gchar ** argv) { GOptionContext *ctx; - GstValidateMediaInfo mi; GError *err = NULL; + guint ret = 0; gchar *output_file = NULL; gchar *expected_file = NULL; gchar *output = NULL; - gsize outputlength; - gboolean ret, discover_only; + GstMediaDescriptorWriter *writer; + GstValidateRunner *runner; + GstMediaDescriptorParser * reference = NULL; GOptionEntry options[] = { {"output-file", 'o', 0, G_OPTION_ARG_FILENAME, @@ -181,9 +185,6 @@ main (int argc, gchar ** argv) &expected_file, "Path to file containing the expected results " "(or the last results found) for comparison with new results", NULL}, - {"discover-only", 'e', 0, G_OPTION_ARG_NONE, - &discover_only, "Only discover files, no other playback tests", - NULL}, {NULL} }; @@ -215,50 +216,46 @@ main (int argc, gchar ** argv) } g_option_context_free (ctx); - gst_validate_media_info_init (&mi); - ret = gst_validate_media_info_inspect_uri (&mi, argv[1], discover_only, NULL); - output = gst_validate_media_info_to_string (&mi, &outputlength); - - if (output_file) - gst_validate_media_info_save (&mi, output_file, NULL); - - if (expected_file) { - GstValidateMediaInfo *expected_mi; - GError *err = NULL; - - ret = TRUE; - if (!g_path_is_absolute (expected_file)) { - gchar *cdir = g_get_current_dir (); - gchar *absolute = g_build_filename (cdir, expected_file, NULL); - - g_free (expected_file); - g_free (cdir); - - expected_file = absolute; - } - - expected_mi = gst_validate_media_info_load (expected_file, &err); - if (err) { - g_print ("Error loading %s: %s", expected_file, err->message); - ret = FALSE; - } else if (expected_mi) { - if (!gst_validate_media_info_compare (expected_mi, &mi)) { - g_print ("Expected results didn't match\n"); - ret = FALSE; - } - gst_validate_media_info_free (expected_mi); - } else { - g_print ("Failed to load expected results file: %s\n", err->message); - g_error_free (err); - ret = FALSE; - } + runner = gst_validate_runner_new (); + writer = gst_media_descriptor_writer_new_discover (runner, argv[1], NULL); + if (writer == NULL) { + g_print ("Could not discover file: %s", argv[1]); + return 1; } - gst_validate_media_info_clear (&mi); + if (output_file) + gst_media_descriptor_writer_write (writer, output_file); - g_print ("Media info:\n%s\n", output); - g_free (output); - if (!ret) - return 1; - return 0; + if (expected_file) { + reference = gst_media_descriptor_parser_new (runner, + expected_file, NULL); + + if (reference == NULL) { + g_print ("Could not parse file: %s", expected_file); + gst_object_unref (writer); + + return 1; + } + + gst_media_descriptors_compare (GST_MEDIA_DESCRIPTOR (reference), + GST_MEDIA_DESCRIPTOR (writer)); + } else { + output = gst_media_descriptor_writer_serialize (writer); + g_print ("Media info:\n%s\n", output); + g_free (output); + } + + ret = gst_validate_runner_printf (runner); + if (ret && expected_file) { + output = gst_media_descriptor_writer_serialize (writer); + g_print ("Media info:\n%s\n", output); + g_free (output); + } + + if (reference) + gst_object_unref (reference); + gst_object_unref (writer); + gst_object_unref (runner); + + return ret; }