validate: cleanup the use of GST_VALIDATE_API on Windows
[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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <gst/validate/validate.h>
26 #include "media-descriptor-writer.h"
27 #include <string.h>
28
29 #include "gst-validate-internal.h"
30
31 struct _GstValidateMediaDescriptorWriterPrivate
32 {
33   GstElement *pipeline;
34   GstCaps *raw_caps;
35   GMainLoop *loop;
36
37   GList *parsers;
38   GstValidateMediaDescriptorWriterFlags flags;
39 };
40
41 G_DEFINE_TYPE_WITH_PRIVATE (GstValidateMediaDescriptorWriter,
42     gst_validate_media_descriptor_writer, GST_TYPE_VALIDATE_MEDIA_DESCRIPTOR);
43
44 #define STR_APPEND(arg, nb_white)  \
45   g_string_append_printf (res, "%*s%s%s", (nb_white), " ", (arg), "\n"); \
46
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)
52
53 #define FLAG_IS_SET(writer,flag)       ((writer->priv->flags & (flag)) == (flag))
54
55 enum
56 {
57   PROP_0,
58   PROP_PATH,
59   N_PROPERTIES
60 };
61
62 static void
63 finalize (GstValidateMediaDescriptorWriter * writer)
64 {
65   if (writer->priv->raw_caps)
66     gst_caps_unref (writer->priv->raw_caps);
67
68   if (writer->priv->parsers)
69     gst_plugin_feature_list_free (writer->priv->parsers);
70
71   G_OBJECT_CLASS (gst_validate_media_descriptor_writer_parent_class)->finalize
72       (G_OBJECT (writer));
73 }
74
75 static void
76 get_property (GObject * gobject, guint prop_id, GValue * value,
77     GParamSpec * pspec)
78 {
79   switch (prop_id) {
80     default:
81       g_assert_not_reached ();
82   }
83
84 }
85
86 static void
87 set_property (GObject * gobject, guint prop_id, const GValue * value,
88     GParamSpec * pspec)
89 {
90   switch (prop_id) {
91     default:
92       g_assert_not_reached ();
93   }
94 }
95
96 static void
97 gst_validate_media_descriptor_writer_init (GstValidateMediaDescriptorWriter *
98     writer)
99 {
100   GstValidateMediaDescriptorWriterPrivate *priv;
101
102
103   writer->priv = priv =
104       gst_validate_media_descriptor_writer_get_instance_private (writer);
105
106   writer->priv->parsers =
107       gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER,
108       GST_RANK_MARGINAL);
109 }
110
111 static void
112     gst_validate_media_descriptor_writer_class_init
113     (GstValidateMediaDescriptorWriterClass * self_class)
114 {
115   GObjectClass *object_class = G_OBJECT_CLASS (self_class);
116
117   object_class->finalize = (void (*)(GObject * object)) finalize;
118   object_class->get_property = get_property;
119   object_class->set_property = set_property;
120 }
121
122 /* Private methods */
123 static gchar *
124 serialize_filenode (GstValidateMediaDescriptorWriter * writer)
125 {
126   GString *res;
127   gchar *tmpstr, *caps_str;
128   GList *tmp, *tmp2;
129   GstValidateMediaTagsNode *tagsnode;
130   GstValidateMediaFileNode
131       * filenode =
132       gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
133           *) writer);
134
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");
139
140   if (filenode->caps)
141     caps_str = gst_caps_to_string (filenode->caps);
142   else
143     caps_str = g_strdup ("");
144
145   res = g_string_new (tmpstr);
146   g_free (tmpstr);
147   tmpstr = g_markup_printf_escaped ("  <streams caps=\"%s\">\n", caps_str);
148   g_string_append (res, tmpstr);
149   g_free (tmpstr);
150   g_free (caps_str);
151   for (tmp = filenode->streams; tmp; tmp = tmp->next) {
152     GList *tmp3;
153     GstValidateMediaStreamNode
154         * snode = ((GstValidateMediaStreamNode *) tmp->data);
155
156
157     STR_APPEND2 (snode->str_open);
158
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>");
164
165     for (tmp2 = snode->frames; tmp2; tmp2 = tmp2->next) {
166       STR_APPEND3 (((GstValidateMediaFrameNode *) tmp2->data)->str_open);
167     }
168
169     tagsnode = snode->tags;
170     if (tagsnode) {
171       STR_APPEND3 (tagsnode->str_open);
172       for (tmp3 = tagsnode->tags; tmp3; tmp3 = tmp3->next) {
173         STR_APPEND4 (((GstValidateMediaTagNode *) tmp3->data)->str_open);
174       }
175       STR_APPEND3 (tagsnode->str_close);
176     }
177
178     STR_APPEND2 (snode->str_close);
179   }
180   STR_APPEND1 ("</streams>");
181
182   tagsnode = filenode->tags;
183   if (tagsnode) {
184     STR_APPEND1 (tagsnode->str_open);
185     for (tmp2 = tagsnode->tags; tmp2; tmp2 = tmp2->next) {
186       STR_APPEND2 (((GstValidateMediaTagNode *)
187               tmp2->data)->str_open);
188     }
189     STR_APPEND1 (tagsnode->str_close);
190   }
191
192   g_string_append (res, filenode->str_close);
193
194   return g_string_free (res, FALSE);
195 }
196
197 /* Should be called with GST_VALIDATE_MEDIA_DESCRIPTOR_LOCK */
198 static
199     GstValidateMediaStreamNode
200     * gst_validate_media_descriptor_find_stream_node_by_pad
201     (GstValidateMediaDescriptor * md, GstPad * pad)
202 {
203   GList *tmp;
204
205   for (tmp = gst_validate_media_descriptor_get_file_node (md)->streams; tmp;
206       tmp = tmp->next) {
207     GstValidateMediaStreamNode *streamnode =
208         (GstValidateMediaStreamNode *) tmp->data;
209
210     if (streamnode->pad == pad) {
211       return streamnode;
212     }
213   }
214
215   return NULL;
216 }
217
218 /* Public methods */
219 GstValidateMediaDescriptorWriter *
220 gst_validate_media_descriptor_writer_new (GstValidateRunner * runner,
221     const gchar * uri, GstClockTime duration, gboolean seekable)
222 {
223   GstValidateMediaDescriptorWriter *writer;
224   GstValidateMediaFileNode *fnode;
225
226   writer =
227       g_object_new (GST_TYPE_VALIDATE_MEDIA_DESCRIPTOR_WRITER,
228       "validate-runner", runner, NULL);
229
230   fnode =
231       gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
232           *) writer);
233   fnode->uri = g_strdup (uri);
234   fnode->duration = duration;
235   fnode->seekable = seekable;
236   fnode->str_open = NULL;
237
238   fnode->str_close = g_markup_printf_escaped ("</file>");
239
240   return writer;
241 }
242
243 static GstCaps *
244 strip_caps_to_avoid_parsers (GstValidateMediaDescriptorWriter * writer,
245     GstCaps * caps)
246 {
247   gint i;
248   GstStructure *structure, *new_struct;
249   GstCaps *stripped;
250
251   /* If parsers are wanted, use exactly the caps reported by the discoverer (which also
252    * plugs parsers). */
253   if (!FLAG_IS_SET (writer,
254           GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER))
255     return gst_caps_copy (caps);
256
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));
263
264     gst_caps_append_structure (stripped, new_struct);
265   }
266
267   return stripped;
268 }
269
270 static gboolean
271     gst_validate_media_descriptor_writer_add_stream
272     (GstValidateMediaDescriptorWriter * writer, GstDiscovererStreamInfo * info)
273 {
274   const gchar *stype;
275   gboolean ret = FALSE;
276   GstCaps *caps;
277   gchar *capsstr = NULL;
278   GstValidateMediaStreamNode *snode = NULL;
279
280   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
281       FALSE);
282   g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
283           (GstValidateMediaDescriptor *) writer), FALSE);
284
285   snode = g_slice_new0 (GstValidateMediaStreamNode);
286   snode->frames = NULL;
287   snode->cframe = NULL;
288
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);
293
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);
298     g_free (capsstr);
299
300     return FALSE;
301   }
302
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)) {
307     stype = "audio";
308   } else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) {
309     if (gst_discoverer_video_info_is_image (GST_DISCOVERER_VIDEO_INFO (info)))
310       stype = "image";
311     else
312       stype = "video";
313   } else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) {
314     stype = "subtitle";
315   } else {
316     stype = "Unknown";
317   }
318
319   snode->str_open =
320       g_markup_printf_escaped
321       ("<stream type=\"%s\" caps=\"%s\" id=\"%s\">", stype, capsstr, snode->id);
322
323   snode->str_close = g_markup_printf_escaped ("</stream>");
324
325   gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor *)
326       writer)->streams =
327       g_list_prepend (gst_validate_media_descriptor_get_file_node (
328           (GstValidateMediaDescriptor *) writer)->streams, snode);
329
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));
333   }
334
335   if (writer->priv->raw_caps == NULL)
336     writer->priv->raw_caps = strip_caps_to_avoid_parsers (writer, caps);
337   else {
338     writer->priv->raw_caps = gst_caps_merge (writer->priv->raw_caps,
339         strip_caps_to_avoid_parsers (writer, caps));
340   }
341   g_free (capsstr);
342
343   return ret;
344 }
345
346 static GstPadProbeReturn
347 _uridecodebin_probe (GstPad * pad, GstPadProbeInfo * info,
348     GstValidateMediaDescriptorWriter * writer)
349 {
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;
359
360         streamnode =
361             gst_validate_media_descriptor_find_stream_node_by_pad (
362             (GstValidateMediaDescriptor *)
363             writer, pad);
364         if (streamnode) {
365           GstValidateSegmentNode *segment_node =
366               g_slice_new0 (GstValidateSegmentNode);
367
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);
371
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,
383               segment->duration);
384
385           streamnode->segments =
386               g_list_prepend (streamnode->segments, segment_node);
387         }
388         break;
389       }
390       default:
391         break;
392     }
393   } else {
394     g_assert_not_reached ();
395   }
396
397   return GST_PAD_PROBE_OK;
398 }
399
400 static gboolean
401 _find_stream_id (GstPad * pad, GstEvent ** event,
402     GstValidateMediaDescriptorWriter * writer)
403 {
404   if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START) {
405     GList *tmp;
406     GstValidateMediaStreamNode *snode = NULL;
407     const gchar *stream_id;
408
409     gst_event_parse_stream_start (*event, &stream_id);
410     for (tmp =
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) {
416         snode = subnode;
417
418         break;
419       }
420     }
421
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));
426
427       return TRUE;
428     }
429
430     snode->pad = gst_object_ref (pad);
431
432     return FALSE;
433   }
434
435   return TRUE;
436 }
437
438 static inline GstElement *
439 _get_parser (GstValidateMediaDescriptorWriter * writer, GstPad * pad)
440 {
441   GList *parsers1, *parsers;
442   GstElement *parser = NULL;
443   GstElementFactory *parserfact = NULL;
444   GstCaps *format;
445
446   if (FLAG_IS_SET (writer,
447           GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER))
448     return NULL;
449
450   format = gst_pad_get_current_caps (pad);
451
452   GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format);
453   parsers1 =
454       gst_element_factory_list_filter (writer->priv->parsers, format,
455       GST_PAD_SRC, FALSE);
456   parsers =
457       gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE);
458   gst_plugin_feature_list_free (parsers1);
459
460   if (G_UNLIKELY (parsers == NULL)) {
461     GST_DEBUG ("Couldn't find any compatible parsers");
462     goto beach;
463   }
464
465   /* Just pick the first one */
466   parserfact = parsers->data;
467   if (parserfact)
468     parser = gst_element_factory_create (parserfact, NULL);
469
470   gst_plugin_feature_list_free (parsers);
471
472 beach:
473   if (format)
474     gst_caps_unref (format);
475
476   return parser;
477 }
478
479 static void
480 pad_added_cb (GstElement * decodebin, GstPad * pad,
481     GstValidateMediaDescriptorWriter * writer)
482 {
483   GstValidateMediaStreamNode *snode = NULL;
484   GstPad *sinkpad, *srcpad;
485
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);
490
491   if (parser) {
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);
497
498     srcpad = gst_element_get_static_pad (parser, "src");
499   } else {
500     srcpad = gst_object_ref (pad);
501   }
502
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);
510
511   if (srcpad != pad) {
512     snode =
513         gst_validate_media_descriptor_find_stream_node_by_pad (
514         (GstValidateMediaDescriptor *)
515         writer, pad);
516     if (snode) {
517       gst_object_unref (snode->pad);
518       snode->pad = gst_object_ref (srcpad);
519     }
520   }
521
522   gst_pad_add_probe (srcpad,
523       GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
524       (GstPadProbeCallback) _uridecodebin_probe, writer, NULL);
525
526   gst_object_unref (srcpad);
527 }
528
529 static gboolean
530 bus_callback (GstBus * bus, GstMessage * message,
531     GstValidateMediaDescriptorWriter * writer)
532 {
533   GMainLoop *loop = writer->priv->loop;
534
535   switch (GST_MESSAGE_TYPE (message)) {
536     case GST_MESSAGE_ERROR:
537     {
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);
541       break;
542     }
543     case GST_MESSAGE_EOS:
544       GST_INFO ("Got EOS!");
545       g_main_loop_quit (loop);
546       break;
547     case GST_MESSAGE_STATE_CHANGED:
548       if (GST_MESSAGE_SRC (message) == GST_OBJECT (writer->priv->pipeline)) {
549         GstState oldstate, newstate, pending;
550
551         gst_message_parse_state_changed (message, &oldstate, &newstate,
552             &pending);
553
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));
558
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");
563         }
564       }
565
566       break;
567     case GST_MESSAGE_BUFFERING:{
568       gint percent;
569
570       gst_message_parse_buffering (message, &percent);
571
572       /* no state management needed for live pipelines */
573       if (percent == 100) {
574         gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING);
575       } else {
576         gst_element_set_state (writer->priv->pipeline, GST_STATE_PAUSED);
577       }
578       break;
579     }
580     default:
581       break;
582   }
583
584   return TRUE;
585 }
586
587 static gboolean
588 _run_frame_analysis (GstValidateMediaDescriptorWriter * writer,
589     GstValidateRunner * runner, const gchar * uri)
590 {
591   GstBus *bus;
592   GList *tmp;
593   GstStateChangeReturn sret;
594   GstValidateMonitor *monitor;
595   GstValidateMediaFileNode *filenode;
596
597   GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
598
599   writer->priv->pipeline = gst_pipeline_new ("frame-analysis");
600
601   monitor =
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));
605
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),
608       writer);
609   gst_bin_add (GST_BIN (writer->priv->pipeline), uridecodebin);
610
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);
616   switch (sret) {
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");
620       return FALSE;
621     default:
622       break;
623   }
624
625   g_main_loop_run (writer->priv->loop);
626
627   filenode =
628       gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
629           *) writer);
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);
635   }
636
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);
646
647   return TRUE;
648 }
649
650 GstValidateMediaDescriptorWriter *
651 gst_validate_media_descriptor_writer_new_discover (GstValidateRunner * runner,
652     const gchar * uri, GstValidateMediaDescriptorWriterFlags flags,
653     GError ** err)
654 {
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;
663
664   discoverer = gst_discoverer_new (GST_SECOND * 60, &error);
665
666   if (discoverer == NULL) {
667     GST_ERROR ("Could not create discoverer");
668     g_propagate_error (err, error);
669     return NULL;
670   }
671
672   info = gst_discoverer_discover_uri (discoverer, uri, &error);
673
674   if (error) {
675     GST_ERROR ("Could not discover URI: %s (error: %s)", uri, error->message);
676     g_propagate_error (err, error);
677     goto out;
678   } else {
679     GstDiscovererResult result = gst_discoverer_info_get_result (info);
680     switch (result) {
681       case GST_DISCOVERER_OK:
682         break;
683       case GST_DISCOVERER_URI_INVALID:
684         GST_ERROR ("URI is not valid");
685         goto out;
686       case GST_DISCOVERER_TIMEOUT:
687         GST_ERROR ("Analyzing URI timed out\n");
688         goto out;
689       case GST_DISCOVERER_BUSY:
690         GST_ERROR ("Discoverer was busy\n");
691         goto out;
692       case GST_DISCOVERER_MISSING_PLUGINS:
693       {
694         gint i = 0;
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]);
700           i++;
701         }
702
703         goto out;
704       }
705       default:
706         break;
707     }
708   }
709
710   streaminfo = gst_discoverer_info_get_stream_info (info);
711
712   if (streaminfo) {
713     writer =
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));
718
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));
723
724     tags = gst_discoverer_info_get_tags (info);
725     if (tags)
726       gst_validate_media_descriptor_writer_add_taglist (writer, tags);
727
728     if (GST_IS_DISCOVERER_CONTAINER_INFO (streaminfo)) {
729       gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
730               *) writer)->caps =
731           gst_discoverer_stream_info_get_caps (GST_DISCOVERER_STREAM_INFO
732           (streaminfo));
733
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);
739       }
740     } else {
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);
745         if (nextinfo) {
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;
752         }
753       }
754       do {
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);
760     }
761   } else {
762     GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_INFO,
763         "Discoverer info, does not contain the stream info");
764     goto out;
765   }
766
767   media_descriptor = (GstValidateMediaDescriptor *) writer;
768   if (streams == NULL
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);
773
774   gst_discoverer_stream_info_list_free (streams);
775
776
777   if (FLAG_IS_SET (writer, GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_FULL))
778     _run_frame_analysis (writer, runner, uri);
779
780 out:
781   if (info)
782     gst_discoverer_info_unref (info);
783   if (streaminfo)
784     gst_discoverer_stream_info_unref (streaminfo);
785   g_object_unref (discoverer);
786   return writer;
787 }
788
789 gboolean
790 gst_validate_media_descriptor_writer_add_tags (GstValidateMediaDescriptorWriter
791     * writer, const gchar * stream_id, const GstTagList * taglist)
792 {
793   GstValidateMediaTagsNode *tagsnode;
794   GstValidateMediaTagNode *tagnode;
795   GList *tmp, *tmptag;
796
797   gchar *str_str = NULL;
798   GstValidateMediaStreamNode *snode = NULL;
799
800   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
801       FALSE);
802   g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
803           (GstValidateMediaDescriptor *) writer), FALSE);
804
805   for (tmp =
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) {
811       snode = subnode;
812
813       break;
814     }
815   }
816
817   if (snode == NULL) {
818     GST_WARNING ("Could not find stream with id: %s", stream_id);
819
820     return FALSE;
821   }
822
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;
828   } else {
829     tagsnode = snode->tags;
830
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,
835             taglist);
836         return TRUE;
837       }
838     }
839   }
840
841   tagnode = g_slice_new0 (GstValidateMediaTagNode);
842   tagnode->taglist = gst_tag_list_copy (taglist);
843   str_str = gst_tag_list_to_string (tagnode->taglist);
844   tagnode->str_open =
845       g_markup_printf_escaped ("<tag content=\"%s\"/>", str_str);
846   tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode);
847
848   g_free (str_str);
849
850   return FALSE;
851 }
852
853 gboolean
854 gst_validate_media_descriptor_writer_add_pad (GstValidateMediaDescriptorWriter *
855     writer, GstPad * pad)
856 {
857   GList *tmp;
858   gboolean ret = FALSE;
859   GstCaps *caps;
860   gchar *capsstr = NULL, *padname = NULL;
861   GstValidateMediaStreamNode *snode = NULL;
862
863   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
864       FALSE);
865   g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
866           (GstValidateMediaDescriptor *) writer), FALSE);
867
868   caps = gst_pad_get_current_caps (pad);
869   for (tmp =
870       gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
871               *) writer)->streams; tmp; tmp = tmp->next) {
872     GstValidateMediaStreamNode *streamnode =
873         (GstValidateMediaStreamNode *) tmp->data;
874
875     if (streamnode->pad == pad) {
876       goto done;
877     }
878   }
879
880   snode = g_slice_new0 (GstValidateMediaStreamNode);
881   snode->frames = NULL;
882   snode->cframe = NULL;
883
884   snode->caps = gst_caps_ref (caps);
885   snode->pad = gst_object_ref (pad);
886
887   capsstr = gst_caps_to_string (caps);
888   padname = gst_pad_get_name (pad);
889   snode->str_open =
890       g_markup_printf_escaped
891       ("<stream padname=\"%s\" caps=\"%s\" id=\"%i\">", padname, capsstr, 0);
892
893   snode->str_close = g_markup_printf_escaped ("</stream>");
894
895   gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor *)
896       writer)->streams =
897       g_list_prepend (gst_validate_media_descriptor_get_file_node (
898           (GstValidateMediaDescriptor *) writer)->streams, snode);
899
900 done:
901   if (caps != NULL)
902     gst_caps_unref (caps);
903   g_free (capsstr);
904   g_free (padname);
905
906   return ret;
907 }
908
909 gboolean
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;
915   GList *tmptag;
916
917   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
918       FALSE);
919   g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
920           (GstValidateMediaDescriptor *) writer), FALSE);
921
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;
929   } else {
930     tagsnode =
931         gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
932             *) writer)->tags;
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,
937             taglist);
938         return TRUE;
939       }
940     }
941   }
942
943   tagnode = g_slice_new0 (GstValidateMediaTagNode);
944   tagnode->taglist = gst_tag_list_copy (taglist);
945   str_str = gst_tag_list_to_string (tagnode->taglist);
946   tagnode->str_open =
947       g_markup_printf_escaped ("<tag content=\"%s\"/>", str_str);
948   tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode);
949
950   g_free (str_str);
951
952   return FALSE;
953 }
954
955 gboolean
956 gst_validate_media_descriptor_writer_add_frame (GstValidateMediaDescriptorWriter
957     * writer, GstPad * pad, GstBuffer * buf)
958 {
959   GstValidateMediaStreamNode *streamnode;
960   GstMapInfo map;
961   gchar *checksum;
962   guint id;
963   GstSegment *segment;
964   GstValidateMediaFrameNode *fnode;
965   GstValidateMediaFileNode *filenode;
966
967   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
968       FALSE);
969   g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
970           (GstValidateMediaDescriptor *) writer), FALSE);
971
972   filenode =
973       gst_validate_media_descriptor_get_file_node ((GstValidateMediaDescriptor
974           *) writer);
975   filenode->frame_detection = TRUE;
976   filenode->skip_parsers =
977       FLAG_IS_SET (writer,
978       GST_VALIDATE_MEDIA_DESCRIPTOR_WRITER_FLAGS_NO_PARSER);
979   GST_VALIDATE_MEDIA_DESCRIPTOR_LOCK (writer);
980   streamnode =
981       gst_validate_media_descriptor_find_stream_node_by_pad (
982       (GstValidateMediaDescriptor *)
983       writer, pad);
984   if (streamnode == NULL) {
985     GST_VALIDATE_MEDIA_DESCRIPTOR_UNLOCK (writer);
986     return FALSE;
987   }
988
989   id = g_list_length (streamnode->frames);
990   fnode = g_slice_new0 (GstValidateMediaFrameNode);
991
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);
996
997   fnode->id = id;
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);
1003
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);
1011
1012   fnode->str_open =
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);
1021
1022   fnode->str_close = NULL;
1023
1024   streamnode->frames = g_list_append (streamnode->frames, fnode);
1025
1026   g_free (checksum);
1027   GST_VALIDATE_MEDIA_DESCRIPTOR_UNLOCK (writer);
1028
1029   return TRUE;
1030 }
1031
1032 gboolean
1033 gst_validate_media_descriptor_writer_write (GstValidateMediaDescriptorWriter *
1034     writer, const gchar * filename)
1035 {
1036   gboolean ret = FALSE;
1037   gchar *serialized;
1038
1039   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
1040       FALSE);
1041   g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
1042           (GstValidateMediaDescriptor *) writer), FALSE);
1043
1044   serialized = serialize_filenode (writer);
1045
1046
1047   if (g_file_set_contents (filename, serialized, -1, NULL) == TRUE)
1048     ret = TRUE;
1049
1050
1051   g_free (serialized);
1052
1053   return ret;
1054 }
1055
1056 gchar *
1057 gst_validate_media_descriptor_writer_serialize (GstValidateMediaDescriptorWriter
1058     * writer)
1059 {
1060   g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
1061       FALSE);
1062   g_return_val_if_fail (gst_validate_media_descriptor_get_file_node (
1063           (GstValidateMediaDescriptor *) writer), FALSE);
1064
1065   return serialize_filenode (writer);
1066 }