validate:pipeline-monitor: Minor cleanup setting unused variable
[platform/upstream/gstreamer.git] / subprojects / gst-devtools / validate / gst / validate / gst-validate-pipeline-monitor.c
1 /* GStreamer
2  *
3  * Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
4  *
5  * gst-validate-pipeline-monitor.c - Validate PipelineMonitor class
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include "gst-validate-internal.h"
28 #include "gst-validate-pipeline-monitor.h"
29 #include "gst-validate-pad-monitor.h"
30 #include "gst-validate-monitor-factory.h"
31 #include "gst-validate-report.h"
32 #include "gst-validate-utils.h"
33 #include "validate.h"
34
35 #define PRINT_POSITION_TIMEOUT 250
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 enum
42 {
43   PROP_0,
44   PROP_VERBOSITY,
45 };
46
47 /**
48  * SECTION:gst-validate-pipeline-monitor
49  * @title: GstValidatePipelineMonitor
50  * @short_description: Class that wraps a #GstPipeline for Validate checks
51  *
52  * TODO
53  */
54
55 typedef struct
56 {
57   gint caps_struct_num;
58   gint filter_caps_struct_num;
59   GString *str;
60   GstStructure *filter;
61   gboolean found;
62 } StructureIncompatibleFieldsInfo;
63
64 enum
65 {
66   PROP_LAST
67 };
68
69 #define gst_validate_pipeline_monitor_parent_class parent_class
70 G_DEFINE_TYPE (GstValidatePipelineMonitor, gst_validate_pipeline_monitor,
71     GST_TYPE_VALIDATE_BIN_MONITOR);
72
73 static void
74 gst_validate_pipeline_monitor_dispose (GObject * object)
75 {
76   GstValidatePipelineMonitor *self = (GstValidatePipelineMonitor *) object;
77
78   g_clear_object (&self->stream_collection);
79   if (self->streams_selected) {
80     g_list_free_full (self->streams_selected, gst_object_unref);
81     self->streams_selected = NULL;
82   }
83
84   G_OBJECT_CLASS (gst_validate_pipeline_monitor_parent_class)->dispose (object);
85 }
86
87 static void
88 gst_validate_pipeline_monitor_set_property (GObject * object, guint prop_id,
89     const GValue * value, GParamSpec * pspec)
90 {
91   GstElement *pipeline = NULL;
92   GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (object);
93   GstValidatePipelineMonitor *self = GST_VALIDATE_PIPELINE_MONITOR (object);
94
95   switch (prop_id) {
96     case PROP_VERBOSITY:
97       pipeline = GST_ELEMENT (gst_validate_monitor_get_pipeline (monitor));
98       monitor->verbosity = g_value_get_flags (value);
99       if (monitor->verbosity & GST_VALIDATE_VERBOSITY_PROPS_CHANGES) {
100         if (pipeline && !self->deep_notify_id) {
101           self->deep_notify_id =
102               gst_element_add_property_deep_notify_watch (pipeline, NULL, TRUE);
103         }
104       } else if (pipeline && self->deep_notify_id) {
105         gst_element_remove_property_notify_watch (pipeline,
106             self->deep_notify_id);
107         self->deep_notify_id = 0;
108       }
109       break;
110     default:
111       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
112       break;
113   }
114
115   gst_clear_object (&pipeline);
116 }
117
118 static void
119 gst_validate_pipeline_monitor_get_property (GObject * object, guint prop_id,
120     GValue * value, GParamSpec * pspec)
121 {
122   GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (object);
123
124   switch (prop_id) {
125     case PROP_VERBOSITY:
126       g_value_set_flags (value, monitor->verbosity);
127       break;
128     default:
129       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130       break;
131   }
132
133 }
134
135 static void
136 gst_validate_pipeline_monitor_class_init (GstValidatePipelineMonitorClass *
137     klass)
138 {
139   GObjectClass *object_class = G_OBJECT_CLASS (klass);
140
141   object_class->dispose = gst_validate_pipeline_monitor_dispose;
142   object_class->set_property = gst_validate_pipeline_monitor_set_property;
143   object_class->get_property = gst_validate_pipeline_monitor_get_property;
144
145   g_object_class_override_property (object_class, PROP_VERBOSITY, "verbosity");
146 }
147
148 static void
149 gst_validate_pipeline_monitor_init (GstValidatePipelineMonitor *
150     pipeline_monitor)
151 {
152 }
153
154 static gboolean
155 print_position (GstValidateMonitor * monitor)
156 {
157   GstQuery *query;
158   gint64 position, duration;
159   GstElement *pipeline =
160       GST_ELEMENT (gst_validate_monitor_get_pipeline (monitor));
161
162   gdouble rate = 1.0;
163   GstFormat format = GST_FORMAT_TIME;
164
165   if (!(GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
166           GST_VALIDATE_VERBOSITY_POSITION))
167     goto done;
168
169   if (!gst_element_query_position (pipeline, format, &position)) {
170     GST_DEBUG_OBJECT (monitor, "Could not query position");
171
172     goto done;
173   }
174
175   format = GST_FORMAT_TIME;
176   if (!gst_element_query_duration (pipeline, format, &duration)) {
177     GST_DEBUG_OBJECT (monitor, "Could not query duration");
178
179     goto done;
180   }
181
182   if (GST_CLOCK_TIME_IS_VALID (duration) && GST_CLOCK_TIME_IS_VALID (position)
183       && position > duration) {
184     GST_VALIDATE_REPORT (monitor, QUERY_POSITION_SUPERIOR_DURATION,
185         "Reported position %" GST_TIME_FORMAT " > reported duration %"
186         GST_TIME_FORMAT, GST_TIME_ARGS (position), GST_TIME_ARGS (duration));
187   }
188
189   query = gst_query_new_segment (GST_FORMAT_DEFAULT);
190   if (gst_element_query (pipeline, query))
191     gst_query_parse_segment (query, &rate, NULL, NULL, NULL);
192   gst_query_unref (query);
193
194   gst_validate_print_position (position, duration, rate, NULL);
195
196 done:
197   gst_object_unref (pipeline);
198
199   return TRUE;
200 }
201
202 static void
203 _check_pad_query_failures (GstPad * pad, GString * str,
204     GstValidatePadMonitor ** last_query_caps_fail_monitor,
205     GstValidatePadMonitor ** last_refused_caps_monitor)
206 {
207   GstValidatePadMonitor *monitor;
208
209   monitor = g_object_get_data (G_OBJECT (pad), "validate-monitor");
210
211   if (!monitor) {
212     GST_DEBUG_OBJECT (pad, "Has no monitor");
213     return;
214   }
215
216   if (monitor->last_query_res && gst_caps_is_empty (monitor->last_query_res)) {
217     gst_object_replace ((GstObject **) last_query_caps_fail_monitor,
218         (GstObject *) monitor);
219   }
220
221   if (monitor->last_refused_caps)
222     gst_object_replace ((GstObject **) last_refused_caps_monitor,
223         (GstObject *) monitor);
224 }
225
226 static GstPad *
227 _get_peer_pad (GstPad * pad)
228 {
229   GstPad *peer = gst_pad_get_peer (pad);
230
231   if (!peer)
232     return NULL;
233
234   while (GST_IS_PROXY_PAD (peer)) {
235     GstPad *next_pad;
236
237     if (GST_PAD_IS_SINK (peer)) {
238       if (GST_IS_GHOST_PAD (peer))
239         next_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (peer));
240       else
241         next_pad = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (peer)));
242     } else {
243       next_pad = gst_pad_get_peer (peer);
244     }
245
246     gst_object_unref (peer);
247     if (!next_pad)
248       return NULL;
249
250     peer = next_pad;
251   }
252
253   return peer;
254 }
255
256 static void
257 _gather_pad_negotiation_details (GstPad * pad, GString * str,
258     GstValidatePadMonitor ** last_query_caps_fail_monitor,
259     GstValidatePadMonitor ** last_refused_caps_monitor)
260 {
261   GList *tmp;
262   GstElement *next;
263   GstPad *peer = _get_peer_pad (pad);
264
265   _check_pad_query_failures (pad, str, last_query_caps_fail_monitor,
266       last_refused_caps_monitor);
267
268   if (!peer)
269     return;
270
271   _check_pad_query_failures (peer, str, last_query_caps_fail_monitor,
272       last_refused_caps_monitor);
273
274   next = GST_ELEMENT (gst_pad_get_parent (peer));
275   GST_OBJECT_LOCK (next);
276   for (tmp = next->srcpads; tmp; tmp = tmp->next) {
277     GstPad *to_check = (GstPad *) tmp->data;
278     _gather_pad_negotiation_details (to_check, str,
279         last_query_caps_fail_monitor, last_refused_caps_monitor);
280   }
281   GST_OBJECT_UNLOCK (next);
282
283   gst_object_unref (peer);
284   gst_object_unref (next);
285 }
286
287 static void
288 _incompatible_fields_info_set_found (StructureIncompatibleFieldsInfo * info)
289 {
290   if (info->found == FALSE) {
291     g_string_append_printf (info->str, " for the following possible reasons:");
292     info->found = TRUE;
293   }
294 }
295
296 static gboolean
297 _find_structure_incompatible_fields (GQuark field_id, const GValue * value,
298     StructureIncompatibleFieldsInfo * info)
299 {
300   gchar *value_str, *filter_str;
301   const GValue *filter_value = gst_structure_id_get_value (info->filter,
302       field_id);
303
304   if (!filter_value)
305     return TRUE;
306
307   value_str = gst_value_serialize (value);
308   filter_str = gst_value_serialize (filter_value);
309
310   if (!gst_value_can_intersect (value, filter_value)) {
311     _incompatible_fields_info_set_found (info);
312     g_string_append_printf (info->str,
313         "\n    -> Field '%s' downstream value from structure %d '(%s)%s' can't intersect with"
314         " filter value from structure number %d '(%s)%s' because of their types.",
315         g_quark_to_string (field_id), info->caps_struct_num,
316         G_VALUE_TYPE_NAME (value), value_str, info->filter_caps_struct_num,
317         G_VALUE_TYPE_NAME (filter_value), filter_str);
318
319     return TRUE;
320   }
321
322   if (gst_value_intersect (NULL, value, filter_value)) {
323     g_free (value_str);
324     g_free (filter_str);
325
326     return TRUE;
327   }
328
329   _incompatible_fields_info_set_found (info);
330   g_string_append_printf (info->str,
331       "\n    -> Field '%s' downstream value from structure %d '(%s)%s' can't intersect with"
332       " filter value from structure number %d '(%s)%s'",
333       g_quark_to_string (field_id), info->caps_struct_num,
334       G_VALUE_TYPE_NAME (value), value_str, info->filter_caps_struct_num,
335       G_VALUE_TYPE_NAME (filter_value), filter_str);
336
337   g_free (value_str);
338   g_free (filter_str);
339
340   return TRUE;
341 }
342
343 static void
344 _append_query_caps_failure_details (GstValidatePadMonitor * monitor,
345     GString * str)
346 {
347   gint i, j;
348   gboolean found = FALSE, empty_filter;
349   GstCaps *filter = gst_caps_copy (monitor->last_query_filter);
350   const gchar *filter_name, *possible_name;
351   GstStructure *filter_struct, *possible_struct;
352   GstPad *pad =
353       GST_PAD (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
354           (monitor)));
355   GstCaps *possible_caps = gst_pad_query_caps (pad, NULL);
356
357   g_string_append_printf (str,
358       "\n Caps negotiation failed starting from pad '%s'"
359       " as the QUERY_CAPS returned EMPTY caps",
360       gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)));
361
362   empty_filter = gst_caps_is_empty (filter);
363   if (empty_filter) {
364     GstPad *peer = _get_peer_pad (pad);
365     gchar *prev_path = NULL;
366
367     if (peer) {
368       GstObject *prev = gst_pad_get_parent (peer);
369       if (prev) {
370         prev_path = gst_object_get_path_string (prev);
371         gst_object_unref (prev);
372       }
373     }
374
375     g_string_append_printf (str,
376         "\n - The QUERY filter caps is EMPTY, this is invalid and is a bug in "
377         "a previous element (probably in: '%s')\n",
378         prev_path ? prev_path : "no suspect");
379     g_free (prev_path);
380   }
381
382   for (i = 0; i < gst_caps_get_size (possible_caps); i++) {
383     possible_struct = gst_caps_get_structure (possible_caps, i);
384     possible_name = gst_structure_get_name (possible_struct);
385
386     for (j = 0; j < gst_caps_get_size (filter); j++) {
387       StructureIncompatibleFieldsInfo info = {
388         .caps_struct_num = i,
389         .filter_caps_struct_num = j,
390         .str = str,
391         .found = found
392       };
393
394       info.filter = filter_struct = gst_caps_get_structure (filter, j);
395       filter_name = gst_structure_get_name (filter_struct);
396
397       if (g_strcmp0 (possible_name, filter_name)) {
398         _incompatible_fields_info_set_found (&info);
399         g_string_append_printf (str,
400             "\n    -> Downstream caps struct %d name '%s' differs from "
401             "filter caps struct %d name '%s'",
402             i, possible_name, j, filter_name);
403
404         continue;
405       }
406
407       gst_structure_foreach (possible_struct,
408           (GstStructureForeachFunc) _find_structure_incompatible_fields, &info);
409
410       if (info.found)
411         found = TRUE;
412     }
413   }
414
415   if (!found && !empty_filter) {
416     gchar *filter_caps_str = gst_caps_to_string (filter);
417     gchar *possible_caps_str = gst_caps_to_string (possible_caps);
418
419     g_string_append_printf (str,
420         ". The exact reason could not be determined but"
421         " here is the gathered information:\n"
422         " - %s last query caps filter: %s\n"
423         " - %s possible caps (as returned by a query on it without filter): %s\n",
424         gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
425         filter_caps_str,
426         gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
427         possible_caps_str);
428   }
429
430   gst_caps_unref (possible_caps);
431   gst_caps_unref (filter);
432   gst_object_unref (pad);
433
434 }
435
436 static gboolean
437 _append_accept_caps_failure_details (GstValidatePadMonitor * monitor,
438     GString * str)
439 {
440   gint i, j;
441   GstCaps *refused_caps = gst_caps_copy (monitor->last_refused_caps);
442   GstPad *pad =
443       GST_PAD (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
444           (monitor)));
445   GstCaps *possible_caps = gst_pad_query_caps (pad, NULL);
446   gchar *caps_str = gst_caps_to_string (monitor->last_refused_caps);
447   StructureIncompatibleFieldsInfo info = {
448     .str = str,
449     .found = FALSE
450   };
451
452   g_string_append_printf (str,
453       "\n Caps negotiation failed at pad '%s' as it refused caps: %s",
454       gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
455       caps_str);
456   g_free (caps_str);
457
458   for (i = 0; i < gst_caps_get_size (refused_caps); i++) {
459     GstStructure *refused_struct = gst_caps_get_structure (refused_caps, i);
460     const gchar *filter_name;
461     const gchar *refused_name = gst_structure_get_name (refused_struct);
462
463     for (j = 0; j < gst_caps_get_size (possible_caps); j++) {
464       info.caps_struct_num = i,
465           info.filter_caps_struct_num = j,
466           info.filter = gst_caps_get_structure (possible_caps, j);
467
468       filter_name = gst_structure_get_name (info.filter);
469       if (g_strcmp0 (refused_name, filter_name)) {
470         g_string_append_printf (str,
471             "\n    -> Downstream caps struct %d name '%s' differs from "
472             "filter caps struct %d name '%s'", i, refused_name, j, filter_name);
473
474         continue;
475       }
476
477       gst_structure_foreach (refused_struct,
478           (GstStructureForeachFunc) _find_structure_incompatible_fields, &info);
479     }
480   }
481
482   gst_caps_unref (possible_caps);
483   gst_object_unref (pad);
484
485   return TRUE;
486 }
487
488 static gchar *
489 _generate_not_negotiated_error_report (GstMessage * msg)
490 {
491   GString *str;
492   GList *tmp;
493   GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (msg));
494   GstValidatePadMonitor *last_query_caps_fail_monitor = NULL,
495       *last_refused_caps_monitor = NULL;
496
497   str = g_string_new (NULL);
498   g_string_append_printf (str, "Error message posted by: %s",
499       GST_OBJECT_NAME (element));
500
501   GST_OBJECT_LOCK (element);
502   for (tmp = element->srcpads; tmp; tmp = tmp->next) {
503     GstPad *to_check = (GstPad *) tmp->data;
504     _gather_pad_negotiation_details (to_check, str,
505         &last_query_caps_fail_monitor, &last_refused_caps_monitor);
506   }
507   GST_OBJECT_UNLOCK (element);
508
509   if (last_query_caps_fail_monitor)
510     _append_query_caps_failure_details (last_query_caps_fail_monitor, str);
511   else if (last_refused_caps_monitor)
512     _append_accept_caps_failure_details (last_refused_caps_monitor, str);
513   else {
514     GST_ERROR ("We should always be able to generate a detailed report"
515         " about why negotiation failed. Please report a bug against"
516         " gst-devtools:validate with this message and a way to reproduce.");
517   }
518
519   gst_object_replace ((GstObject **) & last_query_caps_fail_monitor, NULL);
520   gst_object_replace ((GstObject **) & last_refused_caps_monitor, NULL);
521
522   return g_string_free (str, FALSE);
523 }
524
525 static void
526 _bus_handler (GstBus * bus, GstMessage * message,
527     GstValidatePipelineMonitor * monitor)
528 {
529   GError *err = NULL;
530   gchar *debug = NULL;
531   const GstStructure *details = NULL;
532   gint error_flow = GST_FLOW_OK;
533
534   if (GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
535       GST_VALIDATE_VERBOSITY_MESSAGES
536       && GST_MESSAGE_TYPE (message) != GST_MESSAGE_PROPERTY_NOTIFY) {
537     GstObject *src_obj;
538     const GstStructure *s;
539     guint32 seqnum;
540     GString *str = g_string_new (NULL);
541
542     seqnum = gst_message_get_seqnum (message);
543     s = gst_message_get_structure (message);
544     src_obj = GST_MESSAGE_SRC (message);
545
546     if (GST_IS_ELEMENT (src_obj)) {
547       g_string_append_printf (str, "Got message #%u from element \"%s\" (%s): ",
548           (guint) seqnum, GST_ELEMENT_NAME (src_obj),
549           GST_MESSAGE_TYPE_NAME (message));
550     } else if (GST_IS_PAD (src_obj)) {
551       g_string_append_printf (str, "Got message #%u from pad \"%s:%s\" (%s): ",
552           (guint) seqnum, GST_DEBUG_PAD_NAME (src_obj),
553           GST_MESSAGE_TYPE_NAME (message));
554     } else if (GST_IS_OBJECT (src_obj)) {
555       g_string_append_printf (str, "Got message #%u from object \"%s\" (%s): ",
556           (guint) seqnum, GST_OBJECT_NAME (src_obj),
557           GST_MESSAGE_TYPE_NAME (message));
558     } else {
559       g_string_append_printf (str, "Got message #%u (%s): ", (guint) seqnum,
560           GST_MESSAGE_TYPE_NAME (message));
561     }
562     if (s) {
563       gchar *sstr;
564
565       sstr = gst_structure_to_string (s);
566       g_string_append_printf (str, "%s\n", sstr);
567       g_free (sstr);
568     } else {
569       g_string_append (str, "no message details\n");
570     }
571     gst_validate_printf (NULL, "%s", str->str);
572     g_string_free (str, TRUE);
573   }
574   switch (GST_MESSAGE_TYPE (message)) {
575     case GST_MESSAGE_EOS:
576       print_position (GST_VALIDATE_MONITOR (monitor));
577       break;
578     case GST_MESSAGE_ERROR:
579       gst_message_parse_error (message, &err, &debug);
580       gst_message_parse_error_details (message, &details);
581
582       if (g_error_matches (err, GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN)) {
583         if (!gst_validate_fail_on_missing_plugin ()) {
584           gst_validate_skip_test ("missing plugin: %s -- Debug message: %s\n",
585               err->message, debug);
586         } else {
587           GST_VALIDATE_REPORT (monitor, MISSING_PLUGIN,
588               "Error: %s -- Debug message: %s", err->message, debug);
589         }
590       } else if ((g_error_matches (err, GST_STREAM_ERROR,
591                   GST_STREAM_ERROR_FAILED) && details
592               && gst_structure_get_int (details, "flow-return", &error_flow)
593               && error_flow == GST_FLOW_NOT_NEGOTIATED)
594           || g_error_matches (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FORMAT)) {
595         gchar *report = _generate_not_negotiated_error_report (message);
596
597         GST_VALIDATE_REPORT (monitor, NOT_NEGOTIATED, "%s", report);
598         g_free (report);
599       } else {
600         GST_VALIDATE_REPORT (monitor, ERROR_ON_BUS,
601             "Got error: %s -- Debug message: %s (%" GST_PTR_FORMAT ")",
602             err->message, debug, details);
603       }
604
605       GST_VALIDATE_MONITOR_LOCK (monitor);
606       monitor->got_error = TRUE;
607       GST_VALIDATE_MONITOR_UNLOCK (monitor);
608       g_error_free (err);
609       g_free (debug);
610       break;
611     case GST_MESSAGE_WARNING:
612       gst_message_parse_warning (message, &err, &debug);
613       GST_VALIDATE_REPORT (monitor, WARNING_ON_BUS,
614           "Got warning: %s -- Debug message: %s", err->message, debug);
615       g_error_free (err);
616       g_free (debug);
617       break;
618     case GST_MESSAGE_STATE_CHANGED:
619     {
620       GstObject *target =
621           gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
622       if (GST_MESSAGE_SRC (message) == target) {
623         GstState oldstate, newstate, pending;
624
625         gst_message_parse_state_changed (message, &oldstate, &newstate,
626             &pending);
627
628         if (oldstate == GST_STATE_READY && newstate == GST_STATE_PAUSED) {
629           monitor->print_pos_srcid =
630               g_timeout_add (PRINT_POSITION_TIMEOUT,
631               (GSourceFunc) print_position, monitor);
632         } else if (oldstate >= GST_STATE_PAUSED && newstate <= GST_STATE_READY) {
633           if (monitor->print_pos_srcid
634               && g_source_remove (monitor->print_pos_srcid))
635             monitor->print_pos_srcid = 0;
636           monitor->got_error = FALSE;
637         }
638       }
639
640       if (target)
641         gst_object_unref (target);
642
643       break;
644     }
645     case GST_MESSAGE_BUFFERING:
646     {
647       JsonBuilder *jbuilder = json_builder_new ();
648       GstBufferingMode mode;
649       gint percent;
650
651       gst_message_parse_buffering (message, &percent);
652       gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL);
653
654       json_builder_begin_object (jbuilder);
655       json_builder_set_member_name (jbuilder, "type");
656       json_builder_add_string_value (jbuilder, "buffering");
657       json_builder_set_member_name (jbuilder, "state");
658       if (percent == 100) {
659         /* a 100% message means buffering is done */
660         gst_validate_printf (NULL, "\nDone buffering\n");
661         json_builder_add_string_value (jbuilder, "done");
662         if (monitor->buffering) {
663           monitor->print_pos_srcid =
664               g_timeout_add (PRINT_POSITION_TIMEOUT,
665               (GSourceFunc) print_position, monitor);
666           monitor->buffering = FALSE;
667         }
668       } else {
669         /* buffering... */
670         if (!monitor->buffering) {
671           monitor->buffering = TRUE;
672           gst_validate_printf (NULL, "\nStart buffering\n");
673           json_builder_add_string_value (jbuilder, "started");
674           if (monitor->print_pos_srcid
675               && g_source_remove (monitor->print_pos_srcid)) {
676             monitor->print_pos_srcid = 0;
677           }
678         } else {
679           json_builder_add_string_value (jbuilder, "progress");
680         }
681         if (is_tty ())
682           gst_validate_printf (NULL, "%s %d%%  \r", "Buffering...", percent);
683       }
684       json_builder_set_member_name (jbuilder, "position");
685       json_builder_add_int_value (jbuilder, percent);
686       json_builder_end_object (jbuilder);
687
688       gst_validate_send (json_builder_get_root (jbuilder));
689       g_object_unref (jbuilder);
690       break;
691     }
692     case GST_MESSAGE_STREAM_COLLECTION:
693     {
694       GstStreamCollection *collection = NULL;
695       gst_message_parse_stream_collection (message, &collection);
696       gst_object_replace ((GstObject **) & monitor->stream_collection,
697           (GstObject *) collection);
698       gst_object_unref (collection);
699       break;
700     }
701     case GST_MESSAGE_STREAMS_SELECTED:
702     {
703       guint i;
704
705       if (monitor->streams_selected) {
706         g_list_free_full (monitor->streams_selected, gst_object_unref);
707         monitor->streams_selected = NULL;
708       }
709
710       for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
711         GstStream *stream =
712             gst_message_streams_selected_get_stream (message, i);
713
714         monitor->streams_selected =
715             g_list_append (monitor->streams_selected, stream);
716       }
717       break;
718     }
719     case GST_MESSAGE_PROPERTY_NOTIFY:
720     {
721       const GValue *val;
722       const gchar *name;
723       GstObject *obj;
724       gchar *val_str = NULL;
725       gchar *obj_name;
726
727       if (!(GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
728               GST_VALIDATE_VERBOSITY_PROPS_CHANGES))
729         return;
730
731       gst_message_parse_property_notify (message, &obj, &name, &val);
732
733       obj_name = gst_object_get_path_string (GST_OBJECT (obj));
734       if (val != NULL) {
735         if (G_VALUE_HOLDS_STRING (val))
736           val_str = g_value_dup_string (val);
737         else if (G_VALUE_TYPE (val) == GST_TYPE_CAPS)
738           val_str = gst_caps_to_string (g_value_get_boxed (val));
739         else if (G_VALUE_TYPE (val) == GST_TYPE_TAG_LIST)
740           val_str = gst_tag_list_to_string (g_value_get_boxed (val));
741         else if (G_VALUE_TYPE (val) == GST_TYPE_STRUCTURE)
742           val_str = gst_structure_to_string (g_value_get_boxed (val));
743         else
744           val_str = gst_value_serialize (val);
745       } else {
746         val_str = g_strdup ("(no value)");
747       }
748
749       gst_validate_printf (NULL, "%s: %s = %s\n", obj_name, name, val_str);
750       g_free (obj_name);
751       g_free (val_str);
752       break;
753
754       break;
755     }
756     default:
757       break;
758   }
759 }
760
761 static void
762 gst_validate_pipeline_monitor_create_scenarios (GstValidateBinMonitor * monitor)
763 {
764   /* scenarios currently only make sense for pipelines */
765   const gchar *scenarios_names, *scenario_name = NULL;
766   gchar **scenarios = NULL, *testfile = NULL;
767   GstObject *target =
768       gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
769   GstValidateRunner *runner =
770       gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor));
771   GList *scenario_structs = NULL;
772
773   if (gst_validate_get_test_file_scenario (&scenario_structs, &scenario_name,
774           &testfile)) {
775     if (scenario_name) {
776       monitor->scenario =
777           gst_validate_scenario_factory_create (runner,
778           GST_ELEMENT_CAST (target), scenario_name);
779       goto done;
780     }
781
782     monitor->scenario =
783         gst_validate_scenario_from_structs (runner,
784         GST_ELEMENT_CAST (target), scenario_structs, testfile);
785
786     goto done;
787   }
788
789   if ((scenarios_names = g_getenv ("GST_VALIDATE_SCENARIO"))) {
790     gint i;
791
792     scenarios = g_strsplit (scenarios_names, G_SEARCHPATH_SEPARATOR_S, 0);
793     for (i = 0; scenarios[i]; i++) {
794       gchar **scenario_v = g_strsplit (scenarios[i], "->", 2);
795
796       if (scenario_v[1] && target) {
797         if (!g_pattern_match_simple (scenario_v[1], GST_OBJECT_NAME (target))) {
798           GST_INFO_OBJECT (monitor, "Not attaching to pipeline %" GST_PTR_FORMAT
799               " as not matching pattern %s", target, scenario_v[1]);
800
801           g_strfreev (scenario_v);
802           goto done;
803         }
804       }
805       if (target)
806         monitor->scenario =
807             gst_validate_scenario_factory_create (runner,
808             GST_ELEMENT_CAST (target), scenario_v[0]);
809       else
810         GST_INFO_OBJECT (monitor, "Not creating scenario as monitor"
811             " already does not have a target.");
812       g_strfreev (scenario_v);
813     }
814   }
815 done:
816   g_strfreev (scenarios);
817   if (target)
818     gst_object_unref (target);
819   if (runner)
820     gst_object_unref (runner);
821 }
822
823 /**
824  * gst_validate_pipeline_monitor_new:
825  * @pipeline: (transfer none): a #GstPipeline to run Validate on
826  * @runner: (transfer none): a #GstValidateRunner
827  * @parent: (nullable): The parent of the new monitor
828  *
829  * Returns: (transfer full): A #GstValidatePipelineMonitor or NULL
830  */
831 GstValidatePipelineMonitor *
832 gst_validate_pipeline_monitor_new (GstPipeline * pipeline,
833     GstValidateRunner * runner, GstValidateMonitor * parent)
834 {
835   GstBus *bus;
836   GstValidatePipelineMonitor *monitor =
837       g_object_new (GST_TYPE_VALIDATE_PIPELINE_MONITOR, "object",
838       pipeline, "validate-runner", runner, "validate-parent", parent,
839       "pipeline", pipeline, NULL);
840   GstObject *target =
841       gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
842
843   if (target == NULL) {
844     g_object_unref (monitor);
845     return NULL;
846   }
847
848   gst_validate_pipeline_monitor_create_scenarios (GST_VALIDATE_BIN_MONITOR
849       (monitor));
850
851   bus = gst_element_get_bus (GST_ELEMENT (pipeline));
852   gst_bus_enable_sync_message_emission (bus);
853   g_signal_connect (bus, "sync-message", (GCallback) _bus_handler, monitor);
854
855   if (GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
856       GST_VALIDATE_VERBOSITY_PROPS_CHANGES) {
857     monitor->deep_notify_id =
858         gst_element_add_property_deep_notify_watch ((GstElement *) pipeline,
859         NULL, TRUE);
860   }
861
862   gst_object_unref (bus);
863
864   if (g_strcmp0 (G_OBJECT_TYPE_NAME (pipeline), "GstPlayBin") == 0)
865     monitor->is_playbin = TRUE;
866   else if (g_strcmp0 (G_OBJECT_TYPE_NAME (pipeline), "GstPlayBin3") == 0)
867     monitor->is_playbin3 = TRUE;
868   gst_object_unref (target);
869
870   return monitor;
871 }