3 * Copyright (c) 2012, Collabora Ltd.
4 * Author: Thibault Saunier <thibault.saunier@collabora.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
25 #include <gst/validate/validate.h>
26 #include "media-descriptor-writer.h"
29 #include "gst-validate-internal.h"
31 struct _GstValidateMediaDescriptorWriterPrivate
38 GstValidateMediaDescriptorWriterFlags flags;
41 G_DEFINE_TYPE_WITH_PRIVATE (GstValidateMediaDescriptorWriter,
42 gst_validate_media_descriptor_writer, GST_TYPE_VALIDATE_MEDIA_DESCRIPTOR);
44 #define STR_APPEND(arg, nb_white) \
45 g_string_append_printf (res, "%*s%s%s", (nb_white), " ", (arg), "\n"); \
47 #define STR_APPEND0(arg) STR_APPEND((arg), 0)
48 #define STR_APPEND1(arg) STR_APPEND((arg), 2)
49 #define STR_APPEND2(arg) STR_APPEND((arg), 4)
50 #define STR_APPEND3(arg) STR_APPEND((arg), 6)
51 #define STR_APPEND4(arg) STR_APPEND((arg), 8)
53 #define FLAG_IS_SET(writer,flag) ((writer->priv->flags & (flag)) == (flag))
63 finalize (GstValidateMediaDescriptorWriter * writer)
65 if (writer->priv->raw_caps)
66 gst_caps_unref (writer->priv->raw_caps);
68 if (writer->priv->parsers)
69 gst_plugin_feature_list_free (writer->priv->parsers);
71 G_OBJECT_CLASS (gst_validate_media_descriptor_writer_parent_class)->finalize
76 get_property (GObject * gobject, guint prop_id, GValue * value,
81 g_assert_not_reached ();
87 set_property (GObject * gobject, guint prop_id, const GValue * value,
92 g_assert_not_reached ();
97 gst_validate_media_descriptor_writer_init (GstValidateMediaDescriptorWriter *
100 GstValidateMediaDescriptorWriterPrivate *priv;
103 writer->priv = priv =
104 gst_validate_media_descriptor_writer_get_instance_private (writer);
106 writer->priv->parsers =
107 gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER,
112 gst_validate_media_descriptor_writer_class_init
113 (GstValidateMediaDescriptorWriterClass * self_class)
115 GObjectClass *object_class = G_OBJECT_CLASS (self_class);
117 object_class->finalize = (void (*)(GObject * object)) finalize;
118 object_class->get_property = get_property;
119 object_class->set_property = set_property;
122 /* Private methods */
124 serialize_filenode (GstValidateMediaDescriptorWriter * writer)
127 gchar *tmpstr, *caps_str;
129 GstValidateMediaTagsNode *tagsnode;
130 GstValidateMediaFileNode
132 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
135 tmpstr = g_markup_printf_escaped ("<file duration=\"%" G_GUINT64_FORMAT
136 "\" frame-detection=\"%i\" skip-parsers=\"%i\" uri=\"%s\" seekable=\"%s\">\n",
137 filenode->duration, filenode->frame_detection, filenode->skip_parsers,
138 filenode->uri, filenode->seekable ? "true" : "false");
141 caps_str = gst_caps_to_string (filenode->caps);
143 caps_str = g_strdup ("");
145 res = g_string_new (tmpstr);
147 tmpstr = g_markup_printf_escaped (" <streams caps=\"%s\">\n", caps_str);
148 g_string_append (res, tmpstr);
151 for (tmp = filenode->streams; tmp; tmp = tmp->next) {
153 GstValidateMediaStreamNode
154 * snode = ((GstValidateMediaStreamNode *) tmp->data);
157 STR_APPEND2 (snode->str_open);
159 /* Segment are always prepended, let's bring them back to the right order */
160 STR_APPEND3 ("<segments>");
161 for (tmp2 = snode->segments; tmp2; tmp2 = tmp2->next)
162 STR_APPEND4 (((GstValidateSegmentNode *) tmp2->data)->str_open);
163 STR_APPEND3 ("</segments>");
165 for (tmp2 = snode->frames; tmp2; tmp2 = tmp2->next) {
166 STR_APPEND3 (((GstValidateMediaFrameNode *) tmp2->data)->str_open);
169 tagsnode = snode->tags;
171 STR_APPEND3 (tagsnode->str_open);
172 for (tmp3 = tagsnode->tags; tmp3; tmp3 = tmp3->next) {
173 STR_APPEND4 (((GstValidateMediaTagNode *) tmp3->data)->str_open);
175 STR_APPEND3 (tagsnode->str_close);
178 STR_APPEND2 (snode->str_close);
180 STR_APPEND1 ("</streams>");
182 tagsnode = filenode->tags;
184 STR_APPEND1 (tagsnode->str_open);
185 for (tmp2 = tagsnode->tags; tmp2; tmp2 = tmp2->next) {
186 STR_APPEND2 (((GstValidateMediaTagNode *)
187 tmp2->data)->str_open);
189 STR_APPEND1 (tagsnode->str_close);
192 g_string_append (res, filenode->str_close);
194 return g_string_free (res, FALSE);
197 /* Should be called with GST_VALIDATE_MEDIA_DESCRIPTOR_LOCK */
199 GstValidateMediaStreamNode
200 * gst_validate_media_descriptor_find_stream_node_by_pad
201 (GstValidateMediaDescriptor * md, GstPad * pad)
205 for (tmp = gst_validate_media_descriptor_get_file_node (md)->streams; tmp;
207 GstValidateMediaStreamNode *streamnode =
208 (GstValidateMediaStreamNode *) tmp->data;
210 if (streamnode->pad == pad) {
219 GstValidateMediaDescriptorWriter *
220 gst_validate_media_descriptor_writer_new (GstValidateRunner * runner,
221 const gchar * uri, GstClockTime duration, gboolean seekable)
223 GstValidateMediaDescriptorWriter *writer;
224 GstValidateMediaFileNode *fnode;
227 g_object_new (GST_TYPE_VALIDATE_MEDIA_DESCRIPTOR_WRITER,
228 "validate-runner", runner, NULL);
231 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
233 fnode->uri = g_strdup (uri);
234 fnode->duration = duration;
235 fnode->seekable = seekable;
236 fnode->str_open = NULL;
238 fnode->str_close = g_markup_printf_escaped ("</file>");
244 strip_caps_to_avoid_parsers (GstValidateMediaDescriptorWriter * writer,
248 GstStructure *structure, *new_struct;
251 /* If parsers are wanted, use exactly the caps reported by the discoverer (which also
253 if (!FLAG_IS_SET (writer,
254 GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER))
255 return gst_caps_copy (caps);
257 /* Otherwise use the simplest version of those caps (with the names only),
258 * meaning that decodebin will never plug any parser */
259 stripped = gst_caps_new_empty ();
260 for (i = 0; i < gst_caps_get_size (caps); i++) {
261 structure = gst_caps_get_structure (caps, i);
262 new_struct = gst_structure_new_empty (gst_structure_get_name (structure));
264 gst_caps_append_structure (stripped, new_struct);
271 gst_validate_media_descriptor_writer_add_stream
272 (GstValidateMediaDescriptorWriter * writer, GstDiscovererStreamInfo * info)
275 gboolean ret = FALSE;
277 gchar *capsstr = NULL;
278 GstValidateMediaStreamNode *snode = NULL;
280 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
282 g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
283 (GstValidateMediaDescriptor *) writer), FALSE);
285 snode = g_slice_new0 (GstValidateMediaStreamNode);
286 snode->frames = NULL;
287 snode->cframe = NULL;
289 snode->id = g_strdup (gst_discoverer_stream_info_get_stream_id (info));
290 if (snode->id == NULL) {
291 caps = gst_discoverer_stream_info_get_caps (info);
292 capsstr = gst_caps_to_string (caps);
294 g_slice_free (GstValidateMediaStreamNode, snode);
295 GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_ID,
296 "Stream with caps: %s has no stream ID", capsstr);
297 gst_caps_unref (caps);
303 caps = gst_discoverer_stream_info_get_caps (info);
304 snode->caps = caps; /* Pass ownership */
305 capsstr = gst_caps_to_string (caps);
306 if (GST_IS_DISCOVERER_AUDIO_INFO (info)) {
308 } else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) {
309 if (gst_discoverer_video_info_is_image (GST_DISCOVERER_VIDEO_INFO (info)))
313 } else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) {
320 g_markup_printf_escaped
321 ("<stream type=\"%s\" caps=\"%s\" id=\"%s\">", stype, capsstr, snode->id);
323 snode->str_close = g_markup_printf_escaped ("</stream>");
325 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor *)
327 g_list_prepend (gst_validate_media_descriptor_get_file_node (
328 (GstValidateMediaDescriptor *) writer)->streams, snode);
330 if (gst_discoverer_stream_info_get_tags (info)) {
331 gst_validate_media_descriptor_writer_add_tags (writer, snode->id,
332 gst_discoverer_stream_info_get_tags (info));
335 if (writer->priv->raw_caps == NULL)
336 writer->priv->raw_caps = strip_caps_to_avoid_parsers (writer, caps);
338 writer->priv->raw_caps = gst_caps_merge (writer->priv->raw_caps,
339 strip_caps_to_avoid_parsers (writer, caps));
346 static GstPadProbeReturn
347 _uridecodebin_probe (GstPad * pad, GstPadProbeInfo * info,
348 GstValidateMediaDescriptorWriter * writer)
350 if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_BUFFER) {
351 gst_validate_media_descriptor_writer_add_frame (writer, pad, info->data);
352 } else if (GST_PAD_PROBE_INFO_TYPE (info) &
353 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
354 GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
355 switch (GST_EVENT_TYPE (event)) {
356 case GST_EVENT_SEGMENT:{
357 const GstSegment *segment;
358 GstValidateMediaStreamNode *streamnode;
361 gst_validate_media_descriptor_find_stream_node_by_pad (
362 (GstValidateMediaDescriptor *)
365 GstValidateSegmentNode *segment_node =
366 g_slice_new0 (GstValidateSegmentNode);
368 gst_event_parse_segment (event, &segment);
369 gst_segment_copy_into (segment, &segment_node->segment);
370 segment_node->next_frame_id = g_list_length (streamnode->frames);
372 segment_node->str_open =
373 g_markup_printf_escaped ("<segment next-frame-id=\"%d\""
374 " flags=\"%d\" rate=\"%f\" applied-rate=\"%f\""
375 " format=\"%d\" base=\"%" G_GUINT64_FORMAT "\" offset=\"%"
376 G_GUINT64_FORMAT "\" start=\"%" G_GUINT64_FORMAT "\""
377 " stop=\"%" G_GUINT64_FORMAT "\" time=\"%" G_GUINT64_FORMAT
378 "\" position=\"%" G_GUINT64_FORMAT "\" duration=\"%"
379 G_GUINT64_FORMAT "\"/>", segment_node->next_frame_id,
380 segment->flags, segment->rate, segment->applied_rate,
381 segment->format, segment->base, segment->offset, segment->start,
382 segment->stop, segment->time, segment->position,
385 streamnode->segments =
386 g_list_prepend (streamnode->segments, segment_node);
394 g_assert_not_reached ();
397 return GST_PAD_PROBE_OK;
401 _find_stream_id (GstPad * pad, GstEvent ** event,
402 GstValidateMediaDescriptorWriter * writer)
404 if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START) {
406 GstValidateMediaStreamNode *snode = NULL;
407 const gchar *stream_id;
409 gst_event_parse_stream_start (*event, &stream_id);
411 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
412 *) writer)->streams; tmp; tmp = tmp->next) {
413 GstValidateMediaStreamNode *subnode =
414 (GstValidateMediaStreamNode *) tmp->data;
415 if (g_strcmp0 (subnode->id, stream_id) == 0) {
422 if (!snode || snode->pad) {
423 GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_ID,
424 "Got pad %s:%s where Discoverer found no stream ID",
425 GST_DEBUG_PAD_NAME (pad));
430 snode->pad = gst_object_ref (pad);
438 static inline GstElement *
439 _get_parser (GstValidateMediaDescriptorWriter * writer, GstPad * pad)
441 GList *parsers1, *parsers;
442 GstElement *parser = NULL;
443 GstElementFactory *parserfact = NULL;
446 if (FLAG_IS_SET (writer,
447 GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER))
450 format = gst_pad_get_current_caps (pad);
452 GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format);
454 gst_element_factory_list_filter (writer->priv->parsers, format,
457 gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE);
458 gst_plugin_feature_list_free (parsers1);
460 if (G_UNLIKELY (parsers == NULL)) {
461 GST_DEBUG ("Couldn't find any compatible parsers");
465 /* Just pick the first one */
466 parserfact = parsers->data;
468 parser = gst_element_factory_create (parserfact, NULL);
470 gst_plugin_feature_list_free (parsers);
474 gst_caps_unref (format);
480 pad_added_cb (GstElement * decodebin, GstPad * pad,
481 GstValidateMediaDescriptorWriter * writer)
483 GstValidateMediaStreamNode *snode = NULL;
484 GstPad *sinkpad, *srcpad;
486 /* Try to plug a parser so we have as much info as possible
487 * about the encoded stream. */
488 GstElement *parser = _get_parser (writer, pad);
489 GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
492 sinkpad = gst_element_get_static_pad (parser, "sink");
493 gst_bin_add (GST_BIN (writer->priv->pipeline), parser);
494 gst_element_sync_state_with_parent (parser);
495 gst_pad_link (pad, sinkpad);
496 gst_object_unref (sinkpad);
498 srcpad = gst_element_get_static_pad (parser, "src");
500 srcpad = gst_object_ref (pad);
503 sinkpad = gst_element_get_static_pad (fakesink, "sink");
504 gst_bin_add (GST_BIN (writer->priv->pipeline), fakesink);
505 gst_element_sync_state_with_parent (fakesink);
506 gst_pad_link (srcpad, sinkpad);
507 gst_object_unref (sinkpad);
508 gst_pad_sticky_events_foreach (pad,
509 (GstPadStickyEventsForeachFunction) _find_stream_id, writer);
513 gst_validate_media_descriptor_find_stream_node_by_pad (
514 (GstValidateMediaDescriptor *)
517 gst_object_unref (snode->pad);
518 snode->pad = gst_object_ref (srcpad);
522 gst_pad_add_probe (srcpad,
523 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
524 (GstPadProbeCallback) _uridecodebin_probe, writer, NULL);
526 gst_object_unref (srcpad);
530 bus_callback (GstBus * bus, GstMessage * message,
531 GstValidateMediaDescriptorWriter * writer)
533 GMainLoop *loop = writer->priv->loop;
535 switch (GST_MESSAGE_TYPE (message)) {
536 case GST_MESSAGE_ERROR:
538 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline),
539 GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate-media-check.error");
540 g_main_loop_quit (loop);
543 case GST_MESSAGE_EOS:
544 GST_INFO ("Got EOS!");
545 g_main_loop_quit (loop);
547 case GST_MESSAGE_STATE_CHANGED:
548 if (GST_MESSAGE_SRC (message) == GST_OBJECT (writer->priv->pipeline)) {
549 GstState oldstate, newstate, pending;
551 gst_message_parse_state_changed (message, &oldstate, &newstate,
554 GST_DEBUG ("State changed (old: %s, new: %s, pending: %s)",
555 gst_element_state_get_name (oldstate),
556 gst_element_state_get_name (newstate),
557 gst_element_state_get_name (pending));
559 if (newstate == GST_STATE_PLAYING) {
560 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline),
561 GST_DEBUG_GRAPH_SHOW_ALL,
562 "gst-validate-media-descriptor-writer.playing");
567 case GST_MESSAGE_BUFFERING:{
570 gst_message_parse_buffering (message, &percent);
572 /* no state management needed for live pipelines */
573 if (percent == 100) {
574 gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING);
576 gst_element_set_state (writer->priv->pipeline, GST_STATE_PAUSED);
588 _run_frame_analysis (GstValidateMediaDescriptorWriter * writer,
589 GstValidateRunner * runner, const gchar * uri)
593 GstStateChangeReturn sret;
594 GstValidateMonitor *monitor;
595 GstValidateMediaFileNode *filenode;
597 GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
599 writer->priv->pipeline = gst_pipeline_new ("frame-analysis");
602 gst_validate_monitor_factory_create (GST_OBJECT_CAST (writer->
603 priv->pipeline), runner, NULL);
604 gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor));
606 g_object_set (uridecodebin, "uri", uri, "caps", writer->priv->raw_caps, NULL);
607 g_signal_connect (uridecodebin, "pad-added", G_CALLBACK (pad_added_cb),
609 gst_bin_add (GST_BIN (writer->priv->pipeline), uridecodebin);
611 writer->priv->loop = g_main_loop_new (NULL, FALSE);
612 bus = gst_element_get_bus (writer->priv->pipeline);
613 gst_bus_add_signal_watch (bus);
614 g_signal_connect (bus, "message", (GCallback) bus_callback, writer);
615 sret = gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING);
617 case GST_STATE_CHANGE_FAILURE:
618 /* ignore, we should get an error message posted on the bus */
619 gst_validate_printf (NULL, "Pipeline failed to go to PLAYING state\n");
625 g_main_loop_run (writer->priv->loop);
628 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
630 /* Segment are always prepended, let's reorder them. */
631 for (tmp = filenode->streams; tmp; tmp = tmp->next) {
632 GstValidateMediaStreamNode
633 * snode = ((GstValidateMediaStreamNode *) tmp->data);
634 snode->segments = g_list_reverse (snode->segments);
637 gst_element_set_state (writer->priv->pipeline, GST_STATE_NULL);
638 gst_object_unref (writer->priv->pipeline);
639 writer->priv->pipeline = NULL;
640 g_main_loop_unref (writer->priv->loop);
641 writer->priv->loop = NULL;
642 gst_bus_remove_signal_watch (bus);
643 gst_object_unref (bus);
644 gst_validate_reporter_purge_reports (GST_VALIDATE_REPORTER (monitor));
645 g_object_unref (monitor);
650 GstValidateMediaDescriptorWriter *
651 gst_validate_media_descriptor_writer_new_discover (GstValidateRunner * runner,
652 const gchar * uri, GstValidateMediaDescriptorWriterFlags flags,
655 GList *tmp, *streams = NULL;
656 GstDiscovererInfo *info = NULL;
657 GstDiscoverer *discoverer;
658 GstDiscovererStreamInfo *streaminfo = NULL;
659 GstValidateMediaDescriptorWriter *writer = NULL;
660 GstValidateMediaDescriptor *media_descriptor;
661 const GstTagList *tags;
662 GError *error = NULL;
664 discoverer = gst_discoverer_new (GST_SECOND * 60, &error);
666 if (discoverer == NULL) {
667 GST_ERROR ("Could not create discoverer");
668 g_propagate_error (err, error);
672 info = gst_discoverer_discover_uri (discoverer, uri, &error);
675 GST_ERROR ("Could not discover URI: %s (error: %s)", uri, error->message);
676 g_propagate_error (err, error);
679 GstDiscovererResult result = gst_discoverer_info_get_result (info);
681 case GST_DISCOVERER_OK:
683 case GST_DISCOVERER_URI_INVALID:
684 GST_ERROR ("URI is not valid");
686 case GST_DISCOVERER_TIMEOUT:
687 GST_ERROR ("Analyzing URI timed out\n");
689 case GST_DISCOVERER_BUSY:
690 GST_ERROR ("Discoverer was busy\n");
692 case GST_DISCOVERER_MISSING_PLUGINS:
695 const gchar **installer_details =
696 gst_discoverer_info_get_missing_elements_installer_details (info);
697 GST_ERROR ("Missing plugins");
698 while (installer_details[i]) {
699 GST_ERROR ("(%s)", installer_details[i]);
710 streaminfo = gst_discoverer_info_get_stream_info (info);
714 gst_validate_media_descriptor_writer_new (runner,
715 gst_discoverer_info_get_uri (info),
716 gst_discoverer_info_get_duration (info),
717 gst_discoverer_info_get_seekable (info));
719 writer->priv->flags = flags;
720 if (FLAG_IS_SET (writer,
721 GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_HANDLE_GLOGS))
722 gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (writer));
724 tags = gst_discoverer_info_get_tags (info);
726 gst_validate_media_descriptor_writer_add_taglist (writer, tags);
728 if (GST_IS_DISCOVERER_CONTAINER_INFO (streaminfo)) {
729 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
731 gst_discoverer_stream_info_get_caps (GST_DISCOVERER_STREAM_INFO
734 streams = gst_discoverer_info_get_stream_list (info);
735 for (tmp = streams; tmp; tmp = tmp->next) {
736 GstDiscovererStreamInfo *streaminfo =
737 (GstDiscovererStreamInfo *) tmp->data;
738 gst_validate_media_descriptor_writer_add_stream (writer, streaminfo);
741 GstDiscovererStreamInfo *nextinfo;
742 if (!GST_IS_DISCOVERER_AUDIO_INFO (info)
743 && !GST_IS_DISCOVERER_VIDEO_INFO (info)) {
744 nextinfo = gst_discoverer_stream_info_get_next (streaminfo);
746 GstValidateMediaFileNode *fn =
747 gst_validate_media_descriptor_get_file_node (
748 (GstValidateMediaDescriptor *) writer);
749 fn->caps = gst_discoverer_stream_info_get_caps (streaminfo);
750 gst_discoverer_stream_info_unref (streaminfo);
751 streaminfo = nextinfo;
755 gst_validate_media_descriptor_writer_add_stream (writer, streaminfo);
756 nextinfo = gst_discoverer_stream_info_get_next (streaminfo);
757 gst_discoverer_stream_info_unref (streaminfo);
758 streaminfo = nextinfo;
759 } while (streaminfo);
762 GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_INFO,
763 "Discoverer info, does not contain the stream info");
767 media_descriptor = (GstValidateMediaDescriptor *) writer;
769 && gst_validate_media_descriptor_get_file_node (media_descriptor)->caps)
770 writer->priv->raw_caps =
771 gst_caps_copy (gst_validate_media_descriptor_get_file_node
772 (media_descriptor)->caps);
774 gst_discoverer_stream_info_list_free (streams);
777 if (FLAG_IS_SET (writer, GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_FULL))
778 _run_frame_analysis (writer, runner, uri);
782 gst_discoverer_info_unref (info);
784 gst_discoverer_stream_info_unref (streaminfo);
785 g_object_unref (discoverer);
790 gst_validate_media_descriptor_writer_add_tags (GstValidateMediaDescriptorWriter
791 * writer, const gchar * stream_id, const GstTagList * taglist)
793 GstValidateMediaTagsNode *tagsnode;
794 GstValidateMediaTagNode *tagnode;
797 gchar *str_str = NULL;
798 GstValidateMediaStreamNode *snode = NULL;
800 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
802 g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
803 (GstValidateMediaDescriptor *) writer), FALSE);
806 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
807 *) writer)->streams; tmp; tmp = tmp->next) {
808 GstValidateMediaStreamNode *subnode =
809 (GstValidateMediaStreamNode *) tmp->data;
810 if (g_strcmp0 (subnode->id, stream_id) == 0) {
818 GST_WARNING ("Could not find stream with id: %s", stream_id);
823 if (snode->tags == NULL) {
824 tagsnode = g_slice_new0 (GstValidateMediaTagsNode);
825 tagsnode->str_open = g_markup_printf_escaped ("<tags>");
826 tagsnode->str_close = g_markup_printf_escaped ("</tags>");
827 snode->tags = tagsnode;
829 tagsnode = snode->tags;
831 for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) {
832 if (gst_validate_tag_node_compare ((GstValidateMediaTagNode *)
833 tmptag->data, taglist)) {
834 GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT,
841 tagnode = g_slice_new0 (GstValidateMediaTagNode);
842 tagnode->taglist = gst_tag_list_copy (taglist);
843 str_str = gst_tag_list_to_string (tagnode->taglist);
845 g_markup_printf_escaped ("<tag content=\"%s\"/>", str_str);
846 tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode);
854 gst_validate_media_descriptor_writer_add_pad (GstValidateMediaDescriptorWriter *
855 writer, GstPad * pad)
858 gboolean ret = FALSE;
860 gchar *capsstr = NULL, *padname = NULL;
861 GstValidateMediaStreamNode *snode = NULL;
863 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
865 g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
866 (GstValidateMediaDescriptor *) writer), FALSE);
868 caps = gst_pad_get_current_caps (pad);
870 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
871 *) writer)->streams; tmp; tmp = tmp->next) {
872 GstValidateMediaStreamNode *streamnode =
873 (GstValidateMediaStreamNode *) tmp->data;
875 if (streamnode->pad == pad) {
880 snode = g_slice_new0 (GstValidateMediaStreamNode);
881 snode->frames = NULL;
882 snode->cframe = NULL;
884 snode->caps = gst_caps_ref (caps);
885 snode->pad = gst_object_ref (pad);
887 capsstr = gst_caps_to_string (caps);
888 padname = gst_pad_get_name (pad);
890 g_markup_printf_escaped
891 ("<stream padname=\"%s\" caps=\"%s\" id=\"%i\">", padname, capsstr, 0);
893 snode->str_close = g_markup_printf_escaped ("</stream>");
895 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor *)
897 g_list_prepend (gst_validate_media_descriptor_get_file_node (
898 (GstValidateMediaDescriptor *) writer)->streams, snode);
902 gst_caps_unref (caps);
910 gst_validate_media_descriptor_writer_add_taglist
911 (GstValidateMediaDescriptorWriter * writer, const GstTagList * taglist) {
912 gchar *str_str = NULL;
913 GstValidateMediaTagsNode *tagsnode;
914 GstValidateMediaTagNode *tagnode;
917 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
919 g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
920 (GstValidateMediaDescriptor *) writer), FALSE);
922 if (gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
923 *) writer)->tags == NULL) {
924 tagsnode = g_slice_new0 (GstValidateMediaTagsNode);
925 tagsnode->str_open = g_markup_printf_escaped ("<tags>");
926 tagsnode->str_close = g_markup_printf_escaped ("</tags>");
927 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor *)
928 writer)->tags = tagsnode;
931 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
933 for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) {
934 if (gst_validate_tag_node_compare ((GstValidateMediaTagNode *)
935 tmptag->data, taglist)) {
936 GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT,
943 tagnode = g_slice_new0 (GstValidateMediaTagNode);
944 tagnode->taglist = gst_tag_list_copy (taglist);
945 str_str = gst_tag_list_to_string (tagnode->taglist);
947 g_markup_printf_escaped ("<tag content=\"%s\"/>", str_str);
948 tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode);
956 gst_validate_media_descriptor_writer_add_frame (GstValidateMediaDescriptorWriter
957 * writer, GstPad * pad, GstBuffer * buf)
959 GstValidateMediaStreamNode *streamnode;
964 GstValidateMediaFrameNode *fnode;
965 GstValidateMediaFileNode *filenode;
967 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
969 g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
970 (GstValidateMediaDescriptor *) writer), FALSE);
973 gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
975 filenode->frame_detection = TRUE;
976 filenode->skip_parsers =
978 GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER);
979 GST_VALIDATE_MEDIA_DESCRIPTOR_LOCK (writer);
981 gst_validate_media_descriptor_find_stream_node_by_pad (
982 (GstValidateMediaDescriptor *)
984 if (streamnode == NULL) {
985 GST_VALIDATE_MEDIA_DESCRIPTOR_UNLOCK (writer);
989 id = g_list_length (streamnode->frames);
990 fnode = g_slice_new0 (GstValidateMediaFrameNode);
992 g_assert (gst_buffer_map (buf, &map, GST_MAP_READ));
993 checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5,
994 (const guchar *) map.data, map.size);
995 gst_buffer_unmap (buf, &map);
998 fnode->offset = GST_BUFFER_OFFSET (buf);
999 fnode->offset_end = GST_BUFFER_OFFSET_END (buf);
1000 fnode->duration = GST_BUFFER_DURATION (buf);
1001 fnode->pts = GST_BUFFER_PTS (buf);
1002 fnode->dts = GST_BUFFER_DTS (buf);
1004 g_assert (streamnode->segments);
1005 segment = &((GstValidateSegmentNode *) streamnode->segments->data)->segment;
1006 fnode->running_time =
1007 gst_segment_to_running_time (segment, GST_FORMAT_TIME,
1008 GST_BUFFER_PTS (buf));
1009 fnode->is_keyframe =
1010 (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) == FALSE);
1013 g_markup_printf_escaped (" <frame duration=\"%" G_GUINT64_FORMAT
1014 "\" id=\"%i\" is-keyframe=\"%s\" offset=\"%" G_GUINT64_FORMAT
1015 "\" offset-end=\"%" G_GUINT64_FORMAT "\" pts=\"%" G_GUINT64_FORMAT
1016 "\" dts=\"%" G_GUINT64_FORMAT "\" running-time=\"%" G_GUINT64_FORMAT
1017 "\" checksum=\"%s\"/>",
1018 fnode->duration, id, fnode->is_keyframe ? "true" : "false",
1019 fnode->offset, fnode->offset_end, fnode->pts, fnode->dts,
1020 fnode->running_time, checksum);
1022 fnode->str_close = NULL;
1024 streamnode->frames = g_list_append (streamnode->frames, fnode);
1027 GST_VALIDATE_MEDIA_DESCRIPTOR_UNLOCK (writer);
1033 gst_validate_media_descriptor_writer_write (GstValidateMediaDescriptorWriter *
1034 writer, const gchar * filename)
1036 gboolean ret = FALSE;
1039 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
1041 g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
1042 (GstValidateMediaDescriptor *) writer), FALSE);
1044 serialized = serialize_filenode (writer);
1047 if (g_file_set_contents (filename, serialized, -1, NULL) == TRUE)
1051 g_free (serialized);
1057 gst_validate_media_descriptor_writer_serialize (GstValidateMediaDescriptorWriter
1060 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
1062 g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
1063 (GstValidateMediaDescriptor *) writer), FALSE);
1065 return serialize_filenode (writer);