Merging gst-build
[platform/upstream/gstreamer.git] / subprojects / gst-devtools / validate / gst / validate / media-descriptor-writer.c
1 /* GstValidate
2  *
3  * Copyright (c) 2012, Collabora Ltd.
4  * Author: Thibault Saunier <thibault.saunier@collabora.com>
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 #include <gst/validate/validate.h>
23 #include "media-descriptor-writer.h"
24 #include <string.h>
25
26 struct _GstValidateMediaDescriptorWriterPrivate
27 {
28   GstElement *pipeline;
29   GstCaps *raw_caps;
30   GMainLoop *loop;
31
32   GList *parsers;
33   GstValidateMediaDescriptorWriterFlags flags;
34 };
35
36 G_DEFINE_TYPE_WITH_PRIVATE (GstValidateMediaDescriptorWriter,
37     gst_validate_media_descriptor_writer, GST_TYPE_VALIDATE_MEDIA_DESCRIPTOR);
38
39 #define STR_APPEND(arg, nb_white)  \
40   g_string_append_printf (res, "%*s%s%s", (nb_white), " ", (arg), "\n"); \
41
42 #define STR_APPEND0(arg) STR_APPEND((arg), 0)
43 #define STR_APPEND1(arg) STR_APPEND((arg), 2)
44 #define STR_APPEND2(arg) STR_APPEND((arg), 4)
45 #define STR_APPEND3(arg) STR_APPEND((arg), 6)
46 #define STR_APPEND4(arg) STR_APPEND((arg), 8)
47
48 #define FLAG_IS_SET(writer,flag)       ((writer->priv->flags & (flag)) == (flag))
49
50 enum
51 {
52   PROP_0,
53   PROP_PATH,
54   N_PROPERTIES
55 };
56
57 static void
58 finalize (GstValidateMediaDescriptorWriter * writer)
59 {
60   if (writer->priv->raw_caps)
61     gst_caps_unref (writer->priv->raw_caps);
62
63   if (writer->priv->parsers)
64     gst_plugin_feature_list_free (writer->priv->parsers);
65
66   G_OBJECT_CLASS (gst_validate_media_descriptor_writer_parent_class)->finalize
67       (G_OBJECT (writer));
68 }
69
70 static void
71 get_property (GObject * gobject, guint prop_id, GValue * value,
72     GParamSpec * pspec)
73 {
74   switch (prop_id) {
75     default:
76       g_assert_not_reached ();
77   }
78
79 }
80
81 static void
82 set_property (GObject * gobject, guint prop_id, const GValue * value,
83     GParamSpec * pspec)
84 {
85   switch (prop_id) {
86     default:
87       g_assert_not_reached ();
88   }
89 }
90
91 static void
92 gst_validate_media_descriptor_writer_init (GstValidateMediaDescriptorWriter *
93     writer)
94 {
95   GstValidateMediaDescriptorWriterPrivate *priv;
96
97
98   writer->priv = priv =
99       gst_validate_media_descriptor_writer_get_instance_private (writer);
100
101   writer->priv->parsers =
102       gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER,
103       GST_RANK_MARGINAL);
104 }
105
106 static void
107     gst_validate_media_descriptor_writer_class_init
108     (GstValidateMediaDescriptorWriterClass * self_class)
109 {
110   GObjectClass *object_class = G_OBJECT_CLASS (self_class);
111
112   object_class->finalize = (void (*)(GObject * object)) finalize;
113   object_class->get_property = get_property;
114   object_class->set_property = set_property;
115 }
116
117 /* Private methods */
118 static gchar *
119 serialize_filenode (GstValidateMediaDescriptorWriter * writer)
120 {
121   GString *res;
122   gchar *tmpstr, *caps_str;
123   GList *tmp, *tmp2;
124   GstValidateMediaTagsNode *tagsnode;
125   GstValidateMediaFileNode
126       * filenode = ((GstValidateMediaDescriptor *) writer)->filenode;
127
128   tmpstr = g_markup_printf_escaped ("<file duration=\"%" G_GUINT64_FORMAT
129       "\" frame-detection=\"%i\" skip-parsers=\"%i\" uri=\"%s\" seekable=\"%s\">\n",
130       filenode->duration, filenode->frame_detection, filenode->skip_parsers,
131       filenode->uri, filenode->seekable ? "true" : "false");
132
133   if (filenode->caps)
134     caps_str = gst_caps_to_string (filenode->caps);
135   else
136     caps_str = g_strdup ("");
137
138   res = g_string_new (tmpstr);
139   g_free (tmpstr);
140   tmpstr = g_markup_printf_escaped ("  <streams caps=\"%s\">\n", caps_str);
141   g_string_append (res, tmpstr);
142   g_free (tmpstr);
143   g_free (caps_str);
144   for (tmp = filenode->streams; tmp; tmp = tmp->next) {
145     GList *tmp3;
146     GstValidateMediaStreamNode
147         * snode = ((GstValidateMediaStreamNode *) tmp->data);
148
149
150     STR_APPEND2 (snode->str_open);
151
152     /* Segment are always prepended, let's bring them back to the right order */
153     STR_APPEND3 ("<segments>");
154     for (tmp2 = snode->segments; tmp2; tmp2 = tmp2->next)
155       STR_APPEND4 (((GstValidateSegmentNode *) tmp2->data)->str_open);
156     STR_APPEND3 ("</segments>");
157
158     for (tmp2 = snode->frames; tmp2; tmp2 = tmp2->next) {
159       STR_APPEND3 (((GstValidateMediaFrameNode *) tmp2->data)->str_open);
160     }
161
162     tagsnode = snode->tags;
163     if (tagsnode) {
164       STR_APPEND3 (tagsnode->str_open);
165       for (tmp3 = tagsnode->tags; tmp3; tmp3 = tmp3->next) {
166         STR_APPEND4 (((GstValidateMediaTagNode *) tmp3->data)->str_open);
167       }
168       STR_APPEND3 (tagsnode->str_close);
169     }
170
171     STR_APPEND2 (snode->str_close);
172   }
173   STR_APPEND1 ("</streams>");
174
175   tagsnode = filenode->tags;
176   if (tagsnode) {
177     STR_APPEND1 (tagsnode->str_open);
178     for (tmp2 = tagsnode->tags; tmp2; tmp2 = tmp2->next) {
179       STR_APPEND2 (((GstValidateMediaTagNode *)
180               tmp2->data)->str_open);
181     }
182     STR_APPEND1 (tagsnode->str_close);
183   }
184
185   g_string_append (res, filenode->str_close);
186
187   return g_string_free (res, FALSE);
188 }
189
190 /* Should be called with GST_VALIDATE_MEDIA_DESCRIPTOR_LOCK */
191 static
192     GstValidateMediaStreamNode
193     * gst_validate_media_descriptor_find_stream_node_by_pad
194     (GstValidateMediaDescriptor * md, GstPad * pad)
195 {
196   GList *tmp;
197
198   for (tmp = md->filenode->streams; tmp; tmp = tmp->next) {
199     GstValidateMediaStreamNode
200         * streamnode = (GstValidateMediaStreamNode *) tmp->data;
201
202     if (streamnode->pad == pad) {
203       return streamnode;
204     }
205   }
206
207   return NULL;
208 }
209
210 /* Public methods */
211 GstValidateMediaDescriptorWriter *
212 gst_validate_media_descriptor_writer_new (GstValidateRunner * runner,
213     const gchar * uri, GstClockTime duration, gboolean seekable)
214 {
215   GstValidateMediaDescriptorWriter *writer;
216   GstValidateMediaFileNode *fnode;
217
218   writer =
219       g_object_new (GST_TYPE_VALIDATE_MEDIA_DESCRIPTOR_WRITER,
220       "validate-runner", runner, NULL);
221
222   fnode = ((GstValidateMediaDescriptor *) writer)->filenode;
223   fnode->uri = g_strdup (uri);
224   fnode->duration = duration;
225   fnode->seekable = seekable;
226   fnode->str_open = NULL;
227
228   fnode->str_close = g_markup_printf_escaped ("</file>");
229
230   return writer;
231 }
232
233 static GstCaps *
234 strip_caps_to_avoid_parsers (GstValidateMediaDescriptorWriter * writer,
235     GstCaps * caps)
236 {
237   gint i;
238   GstStructure *structure, *new_struct;
239   GstCaps *stripped;
240
241   /* If parsers are wanted, use exactly the caps reported by the discoverer (which also
242    * plugs parsers). */
243   if (!FLAG_IS_SET (writer,
244           GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER))
245     return gst_caps_copy (caps);
246
247   /* Otherwise use the simplest version of those caps (with the names only),
248    * meaning that decodebin will never plug any parser */
249   stripped = gst_caps_new_empty ();
250   for (i = 0; i < gst_caps_get_size (caps); i++) {
251     structure = gst_caps_get_structure (caps, i);
252     new_struct = gst_structure_new_empty (gst_structure_get_name (structure));
253
254     gst_caps_append_structure (stripped, new_struct);
255   }
256
257   return stripped;
258 }
259
260 static gboolean
261     gst_validate_media_descriptor_writer_add_stream
262     (GstValidateMediaDescriptorWriter * writer, GstDiscovererStreamInfo * info)
263 {
264   const gchar *stype;
265   gboolean ret = FALSE;
266   GstCaps *caps;
267   gchar *capsstr = NULL;
268   GstValidateMediaStreamNode *snode = NULL;
269
270   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
271       FALSE);
272   g_return_val_if_fail (((GstValidateMediaDescriptor *) writer)->filenode,
273       FALSE);
274
275   snode = g_slice_new0 (GstValidateMediaStreamNode);
276   snode->frames = NULL;
277   snode->cframe = NULL;
278
279   snode->id = g_strdup (gst_discoverer_stream_info_get_stream_id (info));
280   if (snode->id == NULL) {
281     caps = gst_discoverer_stream_info_get_caps (info);
282     capsstr = gst_caps_to_string (caps);
283
284     g_slice_free (GstValidateMediaStreamNode, snode);
285     GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_ID,
286         "Stream with caps: %s has no stream ID", capsstr);
287     gst_caps_unref (caps);
288     g_free (capsstr);
289
290     return FALSE;
291   }
292
293   caps = gst_discoverer_stream_info_get_caps (info);
294   snode->caps = caps;           /* Pass ownership */
295   capsstr = gst_caps_to_string (caps);
296   if (GST_IS_DISCOVERER_AUDIO_INFO (info)) {
297     stype = "audio";
298   } else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) {
299     if (gst_discoverer_video_info_is_image (GST_DISCOVERER_VIDEO_INFO (info)))
300       stype = "image";
301     else
302       stype = "video";
303   } else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) {
304     stype = "subtitle";
305   } else {
306     stype = "Unknown";
307   }
308
309   snode->str_open =
310       g_markup_printf_escaped
311       ("<stream type=\"%s\" caps=\"%s\" id=\"%s\">", stype, capsstr, snode->id);
312
313   snode->str_close = g_markup_printf_escaped ("</stream>");
314
315   ((GstValidateMediaDescriptor *) writer)->filenode->streams =
316       g_list_prepend (((GstValidateMediaDescriptor *) writer)->
317       filenode->streams, snode);
318
319   if (gst_discoverer_stream_info_get_tags (info)) {
320     gst_validate_media_descriptor_writer_add_tags (writer, snode->id,
321         gst_discoverer_stream_info_get_tags (info));
322   }
323
324   if (writer->priv->raw_caps == NULL)
325     writer->priv->raw_caps = strip_caps_to_avoid_parsers (writer, caps);
326   else {
327     writer->priv->raw_caps = gst_caps_merge (writer->priv->raw_caps,
328         strip_caps_to_avoid_parsers (writer, caps));
329   }
330   g_free (capsstr);
331
332   return ret;
333 }
334
335 static GstPadProbeReturn
336 _uridecodebin_probe (GstPad * pad, GstPadProbeInfo * info,
337     GstValidateMediaDescriptorWriter * writer)
338 {
339   if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_BUFFER) {
340     gst_validate_media_descriptor_writer_add_frame (writer, pad, info->data);
341   } else if (GST_PAD_PROBE_INFO_TYPE (info) &
342       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
343     GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
344     switch (GST_EVENT_TYPE (event)) {
345       case GST_EVENT_SEGMENT:{
346         const GstSegment *segment;
347         GstValidateMediaStreamNode *streamnode;
348
349         streamnode =
350             gst_validate_media_descriptor_find_stream_node_by_pad (
351             (GstValidateMediaDescriptor *)
352             writer, pad);
353         if (streamnode) {
354           GstValidateSegmentNode *segment_node =
355               g_slice_new0 (GstValidateSegmentNode);
356
357           gst_event_parse_segment (event, &segment);
358           gst_segment_copy_into (segment, &segment_node->segment);
359           segment_node->next_frame_id = g_list_length (streamnode->frames);
360
361           segment_node->str_open =
362               g_markup_printf_escaped ("<segment next-frame-id=\"%d\""
363               " flags=\"%d\" rate=\"%f\" applied-rate=\"%f\""
364               " format=\"%d\" base=\"%" G_GUINT64_FORMAT "\" offset=\"%"
365               G_GUINT64_FORMAT "\" start=\"%" G_GUINT64_FORMAT "\""
366               " stop=\"%" G_GUINT64_FORMAT "\" time=\"%" G_GUINT64_FORMAT
367               "\" position=\"%" G_GUINT64_FORMAT "\" duration=\"%"
368               G_GUINT64_FORMAT "\"/>", segment_node->next_frame_id,
369               segment->flags, segment->rate, segment->applied_rate,
370               segment->format, segment->base, segment->offset, segment->start,
371               segment->stop, segment->time, segment->position,
372               segment->duration);
373
374           streamnode->segments =
375               g_list_prepend (streamnode->segments, segment_node);
376         }
377         break;
378       }
379       default:
380         break;
381     }
382   } else {
383     g_assert_not_reached ();
384   }
385
386   return GST_PAD_PROBE_OK;
387 }
388
389 static gboolean
390 _find_stream_id (GstPad * pad, GstEvent ** event,
391     GstValidateMediaDescriptorWriter * writer)
392 {
393   if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START) {
394     GList *tmp;
395     GstValidateMediaStreamNode *snode = NULL;
396     const gchar *stream_id;
397
398     gst_event_parse_stream_start (*event, &stream_id);
399     for (tmp = ((GstValidateMediaDescriptor *) writer)->filenode->streams; tmp;
400         tmp = tmp->next) {
401       GstValidateMediaStreamNode *subnode =
402           (GstValidateMediaStreamNode *) tmp->data;
403       if (g_strcmp0 (subnode->id, stream_id) == 0) {
404         snode = subnode;
405
406         break;
407       }
408     }
409
410     if (!snode || snode->pad) {
411       GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_ID,
412           "Got pad %s:%s where Discoverer found no stream ID",
413           GST_DEBUG_PAD_NAME (pad));
414
415       return TRUE;
416     }
417
418     snode->pad = gst_object_ref (pad);
419
420     return FALSE;
421   }
422
423   return TRUE;
424 }
425
426 static inline GstElement *
427 _get_parser (GstValidateMediaDescriptorWriter * writer, GstPad * pad)
428 {
429   GList *parsers1, *parsers;
430   GstElement *parser = NULL;
431   GstElementFactory *parserfact = NULL;
432   GstCaps *format;
433
434   if (FLAG_IS_SET (writer,
435           GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER))
436     return NULL;
437
438   format = gst_pad_get_current_caps (pad);
439
440   GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format);
441   parsers1 =
442       gst_element_factory_list_filter (writer->priv->parsers, format,
443       GST_PAD_SRC, FALSE);
444   parsers =
445       gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE);
446   gst_plugin_feature_list_free (parsers1);
447
448   if (G_UNLIKELY (parsers == NULL)) {
449     GST_DEBUG ("Couldn't find any compatible parsers");
450     goto beach;
451   }
452
453   /* Just pick the first one */
454   parserfact = parsers->data;
455   if (parserfact)
456     parser = gst_element_factory_create (parserfact, NULL);
457
458   gst_plugin_feature_list_free (parsers);
459
460 beach:
461   if (format)
462     gst_caps_unref (format);
463
464   return parser;
465 }
466
467 static void
468 pad_added_cb (GstElement * decodebin, GstPad * pad,
469     GstValidateMediaDescriptorWriter * writer)
470 {
471   GstValidateMediaStreamNode *snode = NULL;
472   GstPad *sinkpad, *srcpad;
473
474   /*  Try to plug a parser so we have as much info as possible
475    *  about the encoded stream. */
476   GstElement *parser = _get_parser (writer, pad);
477   GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
478
479   if (parser) {
480     sinkpad = gst_element_get_static_pad (parser, "sink");
481     gst_bin_add (GST_BIN (writer->priv->pipeline), parser);
482     gst_element_sync_state_with_parent (parser);
483     gst_pad_link (pad, sinkpad);
484     gst_object_unref (sinkpad);
485
486     srcpad = gst_element_get_static_pad (parser, "src");
487   } else {
488     srcpad = gst_object_ref (pad);
489   }
490
491   sinkpad = gst_element_get_static_pad (fakesink, "sink");
492   gst_bin_add (GST_BIN (writer->priv->pipeline), fakesink);
493   gst_element_sync_state_with_parent (fakesink);
494   gst_pad_link (srcpad, sinkpad);
495   gst_object_unref (sinkpad);
496   gst_pad_sticky_events_foreach (pad,
497       (GstPadStickyEventsForeachFunction) _find_stream_id, writer);
498
499   if (srcpad != pad) {
500     snode =
501         gst_validate_media_descriptor_find_stream_node_by_pad (
502         (GstValidateMediaDescriptor *)
503         writer, pad);
504     if (snode) {
505       gst_object_unref (snode->pad);
506       snode->pad = gst_object_ref (srcpad);
507     }
508   }
509
510   gst_pad_add_probe (srcpad,
511       GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
512       (GstPadProbeCallback) _uridecodebin_probe, writer, NULL);
513
514   gst_object_unref (srcpad);
515 }
516
517 static gboolean
518 bus_callback (GstBus * bus, GstMessage * message,
519     GstValidateMediaDescriptorWriter * writer)
520 {
521   GMainLoop *loop = writer->priv->loop;
522
523   switch (GST_MESSAGE_TYPE (message)) {
524     case GST_MESSAGE_ERROR:
525     {
526       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline),
527           GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate-media-check.error");
528       g_main_loop_quit (loop);
529       break;
530     }
531     case GST_MESSAGE_EOS:
532       GST_INFO ("Got EOS!");
533       g_main_loop_quit (loop);
534       break;
535     case GST_MESSAGE_STATE_CHANGED:
536       if (GST_MESSAGE_SRC (message) == GST_OBJECT (writer->priv->pipeline)) {
537         GstState oldstate, newstate, pending;
538
539         gst_message_parse_state_changed (message, &oldstate, &newstate,
540             &pending);
541
542         GST_DEBUG ("State changed (old: %s, new: %s, pending: %s)",
543             gst_element_state_get_name (oldstate),
544             gst_element_state_get_name (newstate),
545             gst_element_state_get_name (pending));
546
547         if (newstate == GST_STATE_PLAYING) {
548           GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline),
549               GST_DEBUG_GRAPH_SHOW_ALL,
550               "gst-validate-media-descriptor-writer.playing");
551         }
552       }
553
554       break;
555     case GST_MESSAGE_BUFFERING:{
556       gint percent;
557
558       gst_message_parse_buffering (message, &percent);
559
560       /* no state management needed for live pipelines */
561       if (percent == 100) {
562         gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING);
563       } else {
564         gst_element_set_state (writer->priv->pipeline, GST_STATE_PAUSED);
565       }
566       break;
567     }
568     default:
569       break;
570   }
571
572   return TRUE;
573 }
574
575 static gboolean
576 _run_frame_analysis (GstValidateMediaDescriptorWriter * writer,
577     GstValidateRunner * runner, const gchar * uri)
578 {
579   GstBus *bus;
580   GList *tmp;
581   GstStateChangeReturn sret;
582   GstValidateMonitor *monitor;
583   GstValidateMediaFileNode *filenode;
584
585   GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
586
587   writer->priv->pipeline = gst_pipeline_new ("frame-analysis");
588
589   monitor =
590       gst_validate_monitor_factory_create (GST_OBJECT_CAST (writer->
591           priv->pipeline), runner, NULL);
592   gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor));
593
594   g_object_set (uridecodebin, "uri", uri, "caps", writer->priv->raw_caps, NULL);
595   g_signal_connect (uridecodebin, "pad-added", G_CALLBACK (pad_added_cb),
596       writer);
597   gst_bin_add (GST_BIN (writer->priv->pipeline), uridecodebin);
598
599   writer->priv->loop = g_main_loop_new (NULL, FALSE);
600   bus = gst_element_get_bus (writer->priv->pipeline);
601   gst_bus_add_signal_watch (bus);
602   g_signal_connect (bus, "message", (GCallback) bus_callback, writer);
603   sret = gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING);
604   switch (sret) {
605     case GST_STATE_CHANGE_FAILURE:
606       /* ignore, we should get an error message posted on the bus */
607       gst_validate_printf (NULL, "Pipeline failed to go to PLAYING state\n");
608       return FALSE;
609     default:
610       break;
611   }
612
613   g_main_loop_run (writer->priv->loop);
614
615   filenode = ((GstValidateMediaDescriptor *) writer)->filenode;
616   /* Segment are always prepended, let's reorder them. */
617   for (tmp = filenode->streams; tmp; tmp = tmp->next) {
618     GstValidateMediaStreamNode
619         * snode = ((GstValidateMediaStreamNode *) tmp->data);
620     snode->segments = g_list_reverse (snode->segments);
621   }
622
623   gst_element_set_state (writer->priv->pipeline, GST_STATE_NULL);
624   gst_object_unref (writer->priv->pipeline);
625   writer->priv->pipeline = NULL;
626   g_main_loop_unref (writer->priv->loop);
627   writer->priv->loop = NULL;
628   gst_bus_remove_signal_watch (bus);
629   gst_object_unref (bus);
630   gst_validate_reporter_purge_reports (GST_VALIDATE_REPORTER (monitor));
631   g_object_unref (monitor);
632
633   return TRUE;
634 }
635
636 GstValidateMediaDescriptorWriter *
637 gst_validate_media_descriptor_writer_new_discover (GstValidateRunner * runner,
638     const gchar * uri, GstValidateMediaDescriptorWriterFlags flags,
639     GError ** err)
640 {
641   GList *tmp, *streams = NULL;
642   GstDiscovererInfo *info = NULL;
643   GstDiscoverer *discoverer;
644   GstDiscovererStreamInfo *streaminfo = NULL;
645   GstValidateMediaDescriptorWriter *writer = NULL;
646   GstValidateMediaDescriptor *media_descriptor;
647   const GstTagList *tags;
648   GError *error = NULL;
649
650   discoverer = gst_discoverer_new (GST_SECOND * 60, &error);
651
652   if (discoverer == NULL) {
653     GST_ERROR ("Could not create discoverer");
654     g_propagate_error (err, error);
655     return NULL;
656   }
657
658   info = gst_discoverer_discover_uri (discoverer, uri, &error);
659
660   if (error) {
661     GST_ERROR ("Could not discover URI: %s (error: %s)", uri, error->message);
662     g_propagate_error (err, error);
663     goto out;
664   } else {
665     GstDiscovererResult result = gst_discoverer_info_get_result (info);
666     switch (result) {
667       case GST_DISCOVERER_OK:
668         break;
669       case GST_DISCOVERER_URI_INVALID:
670         GST_ERROR ("URI is not valid");
671         goto out;
672       case GST_DISCOVERER_TIMEOUT:
673         GST_ERROR ("Analyzing URI timed out\n");
674         goto out;
675       case GST_DISCOVERER_BUSY:
676         GST_ERROR ("Discoverer was busy\n");
677         goto out;
678       case GST_DISCOVERER_MISSING_PLUGINS:
679       {
680         gint i = 0;
681         const gchar **installer_details =
682             gst_discoverer_info_get_missing_elements_installer_details (info);
683         GST_ERROR ("Missing plugins");
684         while (installer_details[i]) {
685           GST_ERROR ("(%s)", installer_details[i]);
686           i++;
687         }
688
689         goto out;
690       }
691       default:
692         break;
693     }
694   }
695
696   streaminfo = gst_discoverer_info_get_stream_info (info);
697
698   if (streaminfo) {
699     writer =
700         gst_validate_media_descriptor_writer_new (runner,
701         gst_discoverer_info_get_uri (info),
702         gst_discoverer_info_get_duration (info),
703         gst_discoverer_info_get_seekable (info));
704
705     writer->priv->flags = flags;
706     if (FLAG_IS_SET (writer,
707             GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_HANDLE_GLOGS))
708       gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (writer));
709
710     tags = gst_discoverer_info_get_tags (info);
711     if (tags)
712       gst_validate_media_descriptor_writer_add_taglist (writer, tags);
713
714     if (GST_IS_DISCOVERER_CONTAINER_INFO (streaminfo)) {
715       ((GstValidateMediaDescriptor *) writer)->filenode->caps =
716           gst_discoverer_stream_info_get_caps (GST_DISCOVERER_STREAM_INFO
717           (streaminfo));
718
719       streams = gst_discoverer_info_get_stream_list (info);
720       for (tmp = streams; tmp; tmp = tmp->next) {
721         GstDiscovererStreamInfo *streaminfo =
722             (GstDiscovererStreamInfo *) tmp->data;
723         gst_validate_media_descriptor_writer_add_stream (writer, streaminfo);
724       }
725     } else {
726       if (!GST_IS_DISCOVERER_AUDIO_INFO (info)
727           && !GST_IS_DISCOVERER_AUDIO_INFO (info)
728           && gst_discoverer_stream_info_get_next (streaminfo)) {
729         ((GstValidateMediaDescriptor *) writer)->filenode->caps =
730             gst_discoverer_stream_info_get_caps (streaminfo);
731         streaminfo = gst_discoverer_stream_info_get_next (streaminfo);
732       }
733       do {
734         gst_validate_media_descriptor_writer_add_stream (writer, streaminfo);
735       } while ((streaminfo = gst_discoverer_stream_info_get_next (streaminfo)));
736     }
737   } else {
738     GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_INFO,
739         "Discoverer info, does not contain the stream info");
740     goto out;
741   }
742
743   media_descriptor = (GstValidateMediaDescriptor *) writer;
744   if (streams == NULL && media_descriptor->filenode->caps)
745     writer->priv->raw_caps = gst_caps_copy (media_descriptor->filenode->caps);
746
747   gst_discoverer_stream_info_list_free (streams);
748
749
750   if (FLAG_IS_SET (writer, GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_FULL))
751     _run_frame_analysis (writer, runner, uri);
752
753 out:
754   if (info)
755     gst_discoverer_info_unref (info);
756   if (streaminfo)
757     gst_discoverer_stream_info_unref (streaminfo);
758   g_object_unref (discoverer);
759   return writer;
760 }
761
762 gboolean
763 gst_validate_media_descriptor_writer_add_tags (GstValidateMediaDescriptorWriter
764     * writer, const gchar * stream_id, const GstTagList * taglist)
765 {
766   GstValidateMediaTagsNode *tagsnode;
767   GstValidateMediaTagNode *tagnode;
768   GList *tmp, *tmptag;
769
770   gchar *str_str = NULL;
771   GstValidateMediaStreamNode *snode = NULL;
772
773   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
774       FALSE);
775   g_return_val_if_fail (((GstValidateMediaDescriptor *) writer)->filenode,
776       FALSE);
777
778   for (tmp = ((GstValidateMediaDescriptor *) writer)->filenode->streams; tmp;
779       tmp = tmp->next) {
780     GstValidateMediaStreamNode *subnode =
781         (GstValidateMediaStreamNode *) tmp->data;
782     if (g_strcmp0 (subnode->id, stream_id) == 0) {
783       snode = subnode;
784
785       break;
786     }
787   }
788
789   if (snode == NULL) {
790     GST_WARNING ("Could not find stream with id: %s", stream_id);
791
792     return FALSE;
793   }
794
795   if (snode->tags == NULL) {
796     tagsnode = g_slice_new0 (GstValidateMediaTagsNode);
797     tagsnode->str_open = g_markup_printf_escaped ("<tags>");
798     tagsnode->str_close = g_markup_printf_escaped ("</tags>");
799     snode->tags = tagsnode;
800   } else {
801     tagsnode = snode->tags;
802
803     for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) {
804       if (gst_validate_tag_node_compare ((GstValidateMediaTagNode *)
805               tmptag->data, taglist)) {
806         GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT,
807             taglist);
808         return TRUE;
809       }
810     }
811   }
812
813   tagnode = g_slice_new0 (GstValidateMediaTagNode);
814   tagnode->taglist = gst_tag_list_copy (taglist);
815   str_str = gst_tag_list_to_string (tagnode->taglist);
816   tagnode->str_open =
817       g_markup_printf_escaped ("<tag content=\"%s\"/>", str_str);
818   tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode);
819
820   g_free (str_str);
821
822   return FALSE;
823 }
824
825 gboolean
826 gst_validate_media_descriptor_writer_add_pad (GstValidateMediaDescriptorWriter *
827     writer, GstPad * pad)
828 {
829   GList *tmp;
830   gboolean ret = FALSE;
831   GstCaps *caps;
832   gchar *capsstr = NULL, *padname = NULL;
833   GstValidateMediaStreamNode *snode = NULL;
834
835   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
836       FALSE);
837   g_return_val_if_fail (((GstValidateMediaDescriptor *) writer)->filenode,
838       FALSE);
839
840   caps = gst_pad_get_current_caps (pad);
841   for (tmp = ((GstValidateMediaDescriptor *) writer)->filenode->streams; tmp;
842       tmp = tmp->next) {
843     GstValidateMediaStreamNode
844         * streamnode = (GstValidateMediaStreamNode *) tmp->data;
845
846     if (streamnode->pad == pad) {
847       goto done;
848     }
849   }
850
851   snode = g_slice_new0 (GstValidateMediaStreamNode);
852   snode->frames = NULL;
853   snode->cframe = NULL;
854
855   snode->caps = gst_caps_ref (caps);
856   snode->pad = gst_object_ref (pad);
857
858   capsstr = gst_caps_to_string (caps);
859   padname = gst_pad_get_name (pad);
860   snode->str_open =
861       g_markup_printf_escaped
862       ("<stream padname=\"%s\" caps=\"%s\" id=\"%i\">", padname, capsstr, 0);
863
864   snode->str_close = g_markup_printf_escaped ("</stream>");
865
866   ((GstValidateMediaDescriptor *) writer)->filenode->streams =
867       g_list_prepend (((GstValidateMediaDescriptor *) writer)->
868       filenode->streams, snode);
869
870 done:
871   if (caps != NULL)
872     gst_caps_unref (caps);
873   g_free (capsstr);
874   g_free (padname);
875
876   return ret;
877 }
878
879 gboolean
880     gst_validate_media_descriptor_writer_add_taglist
881     (GstValidateMediaDescriptorWriter * writer, const GstTagList * taglist) {
882   gchar *str_str = NULL;
883   GstValidateMediaTagsNode *tagsnode;
884   GstValidateMediaTagNode *tagnode;
885   GList *tmptag;
886
887   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
888       FALSE);
889   g_return_val_if_fail (((GstValidateMediaDescriptor *) writer)->filenode,
890       FALSE);
891
892   if (((GstValidateMediaDescriptor *) writer)->filenode->tags == NULL) {
893     tagsnode = g_slice_new0 (GstValidateMediaTagsNode);
894     tagsnode->str_open = g_markup_printf_escaped ("<tags>");
895     tagsnode->str_close = g_markup_printf_escaped ("</tags>");
896     ((GstValidateMediaDescriptor *) writer)->filenode->tags = tagsnode;
897   } else {
898     tagsnode = ((GstValidateMediaDescriptor *) writer)->filenode->tags;
899     for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) {
900       if (gst_validate_tag_node_compare ((GstValidateMediaTagNode *)
901               tmptag->data, taglist)) {
902         GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT,
903             taglist);
904         return TRUE;
905       }
906     }
907   }
908
909   tagnode = g_slice_new0 (GstValidateMediaTagNode);
910   tagnode->taglist = gst_tag_list_copy (taglist);
911   str_str = gst_tag_list_to_string (tagnode->taglist);
912   tagnode->str_open =
913       g_markup_printf_escaped ("<tag content=\"%s\"/>", str_str);
914   tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode);
915
916   g_free (str_str);
917
918   return FALSE;
919 }
920
921 gboolean
922 gst_validate_media_descriptor_writer_add_frame (GstValidateMediaDescriptorWriter
923     * writer, GstPad * pad, GstBuffer * buf)
924 {
925   GstValidateMediaStreamNode *streamnode;
926   GstMapInfo map;
927   gchar *checksum;
928   guint id;
929   GstSegment *segment;
930   GstValidateMediaFrameNode *fnode;
931   GstValidateMediaFileNode *filenode;
932
933   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
934       FALSE);
935   g_return_val_if_fail (((GstValidateMediaDescriptor *) writer)->filenode,
936       FALSE);
937
938   filenode = ((GstValidateMediaDescriptor *) writer)->filenode;
939   filenode->frame_detection = TRUE;
940   filenode->skip_parsers =
941       FLAG_IS_SET (writer,
942       GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER);
943   GST_VALIDATE_MEDIA_DESCRIPTOR_LOCK (writer);
944   streamnode =
945       gst_validate_media_descriptor_find_stream_node_by_pad (
946       (GstValidateMediaDescriptor *)
947       writer, pad);
948   if (streamnode == NULL) {
949     GST_VALIDATE_MEDIA_DESCRIPTOR_UNLOCK (writer);
950     return FALSE;
951   }
952
953   id = g_list_length (streamnode->frames);
954   fnode = g_slice_new0 (GstValidateMediaFrameNode);
955
956   g_assert (gst_buffer_map (buf, &map, GST_MAP_READ));
957   checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5,
958       (const guchar *) map.data, map.size);
959   gst_buffer_unmap (buf, &map);
960
961   fnode->id = id;
962   fnode->offset = GST_BUFFER_OFFSET (buf);
963   fnode->offset_end = GST_BUFFER_OFFSET_END (buf);
964   fnode->duration = GST_BUFFER_DURATION (buf);
965   fnode->pts = GST_BUFFER_PTS (buf);
966   fnode->dts = GST_BUFFER_DTS (buf);
967
968   g_assert (streamnode->segments);
969   segment = &((GstValidateSegmentNode *) streamnode->segments->data)->segment;
970   fnode->running_time =
971       gst_segment_to_running_time (segment, GST_FORMAT_TIME,
972       GST_BUFFER_PTS (buf));
973   fnode->is_keyframe =
974       (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) == FALSE);
975
976   fnode->str_open =
977       g_markup_printf_escaped (" <frame duration=\"%" G_GUINT64_FORMAT
978       "\" id=\"%i\" is-keyframe=\"%s\" offset=\"%" G_GUINT64_FORMAT
979       "\" offset-end=\"%" G_GUINT64_FORMAT "\" pts=\"%" G_GUINT64_FORMAT
980       "\" dts=\"%" G_GUINT64_FORMAT "\" running-time=\"%" G_GUINT64_FORMAT
981       "\" checksum=\"%s\"/>",
982       fnode->duration, id, fnode->is_keyframe ? "true" : "false",
983       fnode->offset, fnode->offset_end, fnode->pts, fnode->dts,
984       fnode->running_time, checksum);
985
986   fnode->str_close = NULL;
987
988   streamnode->frames = g_list_append (streamnode->frames, fnode);
989
990   g_free (checksum);
991   GST_VALIDATE_MEDIA_DESCRIPTOR_UNLOCK (writer);
992
993   return TRUE;
994 }
995
996 gboolean
997 gst_validate_media_descriptor_writer_write (GstValidateMediaDescriptorWriter *
998     writer, const gchar * filename)
999 {
1000   gboolean ret = FALSE;
1001   gchar *serialized;
1002
1003   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
1004       FALSE);
1005   g_return_val_if_fail (((GstValidateMediaDescriptor *) writer)->filenode,
1006       FALSE);
1007
1008   serialized = serialize_filenode (writer);
1009
1010
1011   if (g_file_set_contents (filename, serialized, -1, NULL) == TRUE)
1012     ret = TRUE;
1013
1014
1015   g_free (serialized);
1016
1017   return ret;
1018 }
1019
1020 gchar *
1021 gst_validate_media_descriptor_writer_serialize (GstValidateMediaDescriptorWriter
1022     * writer)
1023 {
1024   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
1025       FALSE);
1026   g_return_val_if_fail (((GstValidateMediaDescriptor *) writer)->filenode,
1027       FALSE);
1028
1029   return serialize_filenode (writer);
1030 }