mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-19 00:01:23 +00:00
f27e98caee
If the reported position or duration is NONE, do not check its validity
808 lines
25 KiB
C
808 lines
25 KiB
C
/* GStreamer
|
|
*
|
|
* Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
|
|
*
|
|
* gst-validate-pipeline-monitor.c - Validate PipelineMonitor class
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "gst-validate-internal.h"
|
|
#include "gst-validate-pipeline-monitor.h"
|
|
#include "gst-validate-pad-monitor.h"
|
|
#include "gst-validate-monitor-factory.h"
|
|
|
|
#define PRINT_POSITION_TIMEOUT 250
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
static gboolean output_is_tty = TRUE;
|
|
|
|
/**
|
|
* SECTION:gst-validate-pipeline-monitor
|
|
* @title: GstValidatePipelineMonitor
|
|
* @short_description: Class that wraps a #GstPipeline for Validate checks
|
|
*
|
|
* TODO
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
gint caps_struct_num;
|
|
gint filter_caps_struct_num;
|
|
GString *str;
|
|
GstStructure *filter;
|
|
gboolean found;
|
|
} StructureIncompatibleFieldsInfo;
|
|
|
|
enum
|
|
{
|
|
PROP_LAST
|
|
};
|
|
|
|
#define gst_validate_pipeline_monitor_parent_class parent_class
|
|
G_DEFINE_TYPE (GstValidatePipelineMonitor, gst_validate_pipeline_monitor,
|
|
GST_TYPE_VALIDATE_BIN_MONITOR);
|
|
|
|
static void
|
|
gst_validate_pipeline_monitor_dispose (GObject * object)
|
|
{
|
|
GstValidatePipelineMonitor *self = (GstValidatePipelineMonitor *) object;
|
|
|
|
g_clear_object (&self->stream_collection);
|
|
if (self->streams_selected) {
|
|
g_list_free_full (self->streams_selected, gst_object_unref);
|
|
self->streams_selected = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (gst_validate_pipeline_monitor_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_validate_pipeline_monitor_class_init (GstValidatePipelineMonitorClass *
|
|
klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = gst_validate_pipeline_monitor_dispose;
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
output_is_tty = isatty (1);
|
|
#endif
|
|
|
|
}
|
|
|
|
static void
|
|
gst_validate_pipeline_monitor_init (GstValidatePipelineMonitor *
|
|
pipeline_monitor)
|
|
{
|
|
}
|
|
|
|
static gboolean
|
|
print_position (GstValidateMonitor * monitor)
|
|
{
|
|
GstQuery *query;
|
|
gint64 position, duration;
|
|
JsonBuilder *jbuilder;
|
|
GstElement *pipeline =
|
|
GST_ELEMENT (gst_validate_monitor_get_pipeline (monitor));
|
|
|
|
gdouble rate = 1.0;
|
|
GstFormat format = GST_FORMAT_TIME;
|
|
|
|
if (!(GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
|
|
GST_VALIDATE_VERBOSITY_POSITION))
|
|
goto done;
|
|
|
|
if (!gst_element_query_position (pipeline, format, &position)) {
|
|
GST_DEBUG_OBJECT (monitor, "Could not query position");
|
|
|
|
goto done;
|
|
}
|
|
|
|
format = GST_FORMAT_TIME;
|
|
if (!gst_element_query_duration (pipeline, format, &duration)) {
|
|
GST_DEBUG_OBJECT (monitor, "Could not query duration");
|
|
|
|
goto done;
|
|
}
|
|
|
|
if (GST_CLOCK_TIME_IS_VALID (duration) && GST_CLOCK_TIME_IS_VALID (position)
|
|
&& position > duration) {
|
|
GST_VALIDATE_REPORT (monitor, QUERY_POSITION_SUPERIOR_DURATION,
|
|
"Reported position %" GST_TIME_FORMAT " > reported duration %"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (position), GST_TIME_ARGS (duration));
|
|
}
|
|
|
|
query = gst_query_new_segment (GST_FORMAT_DEFAULT);
|
|
if (gst_element_query (pipeline, query))
|
|
gst_query_parse_segment (query, &rate, NULL, NULL, NULL);
|
|
gst_query_unref (query);
|
|
|
|
jbuilder = json_builder_new ();
|
|
json_builder_begin_object (jbuilder);
|
|
json_builder_set_member_name (jbuilder, "type");
|
|
json_builder_add_string_value (jbuilder, "position");
|
|
json_builder_set_member_name (jbuilder, "position");
|
|
json_builder_add_int_value (jbuilder, position);
|
|
json_builder_set_member_name (jbuilder, "duration");
|
|
json_builder_add_int_value (jbuilder, duration);
|
|
json_builder_set_member_name (jbuilder, "speed");
|
|
json_builder_add_double_value (jbuilder, rate);
|
|
json_builder_end_object (jbuilder);
|
|
|
|
gst_validate_send (json_builder_get_root (jbuilder));
|
|
g_object_unref (jbuilder);
|
|
|
|
gst_validate_printf (NULL,
|
|
"<position: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
|
|
" speed: %f />%c", GST_TIME_ARGS (position), GST_TIME_ARGS (duration),
|
|
rate, output_is_tty ? '\r' : '\n');
|
|
|
|
done:
|
|
gst_object_unref (pipeline);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_check_pad_query_failures (GstPad * pad, GString * str,
|
|
GstValidatePadMonitor ** last_query_caps_fail_monitor,
|
|
GstValidatePadMonitor ** last_refused_caps_monitor)
|
|
{
|
|
GstValidatePadMonitor *monitor;
|
|
|
|
monitor = g_object_get_data (G_OBJECT (pad), "validate-monitor");
|
|
|
|
if (!monitor) {
|
|
GST_DEBUG_OBJECT (pad, "Has no monitor");
|
|
return;
|
|
}
|
|
|
|
if (monitor->last_query_res && gst_caps_is_empty (monitor->last_query_res)) {
|
|
gst_object_replace ((GstObject **) last_query_caps_fail_monitor,
|
|
(GstObject *) monitor);
|
|
}
|
|
|
|
if (monitor->last_refused_caps)
|
|
gst_object_replace ((GstObject **) last_refused_caps_monitor,
|
|
(GstObject *) monitor);
|
|
}
|
|
|
|
static GstPad *
|
|
_get_peer_pad (GstPad * pad)
|
|
{
|
|
GstPad *peer = gst_pad_get_peer (pad);
|
|
|
|
if (!peer)
|
|
return NULL;
|
|
|
|
while (GST_IS_PROXY_PAD (peer)) {
|
|
GstPad *next_pad;
|
|
|
|
if (GST_PAD_IS_SINK (peer)) {
|
|
if (GST_IS_GHOST_PAD (peer))
|
|
next_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (peer));
|
|
else
|
|
next_pad = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (peer)));
|
|
} else {
|
|
next_pad = gst_pad_get_peer (peer);
|
|
}
|
|
|
|
gst_object_unref (peer);
|
|
if (!next_pad)
|
|
return NULL;
|
|
|
|
peer = next_pad;
|
|
}
|
|
|
|
return peer;
|
|
}
|
|
|
|
static void
|
|
_gather_pad_negotiation_details (GstPad * pad, GString * str,
|
|
GstValidatePadMonitor ** last_query_caps_fail_monitor,
|
|
GstValidatePadMonitor ** last_refused_caps_monitor)
|
|
{
|
|
GList *tmp;
|
|
GstElement *next;
|
|
GstPad *peer = _get_peer_pad (pad);
|
|
|
|
_check_pad_query_failures (pad, str, last_query_caps_fail_monitor,
|
|
last_refused_caps_monitor);
|
|
|
|
if (!peer)
|
|
return;
|
|
|
|
_check_pad_query_failures (peer, str, last_query_caps_fail_monitor,
|
|
last_refused_caps_monitor);
|
|
|
|
next = GST_ELEMENT (gst_pad_get_parent (peer));
|
|
GST_OBJECT_LOCK (next);
|
|
for (tmp = next->srcpads; tmp; tmp = tmp->next) {
|
|
GstPad *to_check = (GstPad *) tmp->data;
|
|
_gather_pad_negotiation_details (to_check, str,
|
|
last_query_caps_fail_monitor, last_refused_caps_monitor);
|
|
}
|
|
GST_OBJECT_UNLOCK (next);
|
|
|
|
gst_object_unref (peer);
|
|
gst_object_unref (next);
|
|
}
|
|
|
|
static void
|
|
_incompatible_fields_info_set_found (StructureIncompatibleFieldsInfo * info)
|
|
{
|
|
if (info->found == FALSE) {
|
|
g_string_append_printf (info->str, " for the following possible reasons:");
|
|
info->found = TRUE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
_find_structure_incompatible_fields (GQuark field_id, const GValue * value,
|
|
StructureIncompatibleFieldsInfo * info)
|
|
{
|
|
gchar *value_str, *filter_str;
|
|
GValue intersect = { 0, };
|
|
const GValue *filter_value = gst_structure_id_get_value (info->filter,
|
|
field_id);
|
|
|
|
if (!filter_value)
|
|
return TRUE;
|
|
|
|
value_str = gst_value_serialize (value);
|
|
filter_str = gst_value_serialize (filter_value);
|
|
|
|
if (!gst_value_can_intersect (value, filter_value)) {
|
|
_incompatible_fields_info_set_found (info);
|
|
g_string_append_printf (info->str,
|
|
"\n -> Field '%s' downstream value from structure %d '(%s)%s' can't intersect with"
|
|
" filter value from structure number %d '(%s)%s' because of their types.",
|
|
g_quark_to_string (field_id), info->caps_struct_num,
|
|
G_VALUE_TYPE_NAME (value), value_str, info->filter_caps_struct_num,
|
|
G_VALUE_TYPE_NAME (filter_value), filter_str);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (gst_value_intersect (&intersect, value, filter_value)) {
|
|
g_value_reset (&intersect);
|
|
g_free (value_str);
|
|
g_free (filter_str);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
_incompatible_fields_info_set_found (info);
|
|
g_string_append_printf (info->str,
|
|
"\n -> Field '%s' downstream value from structure %d '(%s)%s' can't intersect with"
|
|
" filter value from structure number %d '(%s)%s'",
|
|
g_quark_to_string (field_id), info->caps_struct_num,
|
|
G_VALUE_TYPE_NAME (value), value_str, info->filter_caps_struct_num,
|
|
G_VALUE_TYPE_NAME (filter_value), filter_str);
|
|
|
|
g_free (value_str);
|
|
g_free (filter_str);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_append_query_caps_failure_details (GstValidatePadMonitor * monitor,
|
|
GString * str)
|
|
{
|
|
gint i, j;
|
|
gboolean found = FALSE, empty_filter;
|
|
GstCaps *filter = gst_caps_copy (monitor->last_query_filter);
|
|
const gchar *filter_name, *possible_name;
|
|
GstStructure *filter_struct, *possible_struct;
|
|
GstPad *pad =
|
|
GST_PAD (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
|
|
(monitor)));
|
|
GstCaps *possible_caps = gst_pad_query_caps (pad, NULL);
|
|
|
|
g_string_append_printf (str,
|
|
"\n Caps negotiation failed starting from pad '%s'"
|
|
" as the QUERY_CAPS returned EMPTY caps",
|
|
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)));
|
|
|
|
empty_filter = gst_caps_is_empty (filter);
|
|
if (empty_filter) {
|
|
GstPad *peer = _get_peer_pad (pad);
|
|
gchar *prev_path = NULL;
|
|
|
|
if (peer) {
|
|
GstObject *prev = gst_pad_get_parent (peer);
|
|
if (prev) {
|
|
prev_path = gst_object_get_path_string (prev);
|
|
gst_object_unref (prev);
|
|
}
|
|
}
|
|
|
|
g_string_append_printf (str,
|
|
"\n - The QUERY filter caps is EMPTY, this is invalid and is a bug in "
|
|
"a previous element (probably in: '%s')\n",
|
|
prev_path ? prev_path : "no suspect");
|
|
g_free (prev_path);
|
|
}
|
|
|
|
for (i = 0; i < gst_caps_get_size (possible_caps); i++) {
|
|
possible_struct = gst_caps_get_structure (possible_caps, i);
|
|
possible_name = gst_structure_get_name (possible_struct);
|
|
|
|
for (j = 0; j < gst_caps_get_size (filter); j++) {
|
|
StructureIncompatibleFieldsInfo info = {
|
|
.caps_struct_num = i,
|
|
.filter_caps_struct_num = j,
|
|
.str = str,
|
|
.found = found
|
|
};
|
|
|
|
info.filter = filter_struct = gst_caps_get_structure (filter, j);
|
|
filter_name = gst_structure_get_name (filter_struct);
|
|
|
|
if (g_strcmp0 (possible_name, filter_name)) {
|
|
_incompatible_fields_info_set_found (&info);
|
|
g_string_append_printf (str,
|
|
"\n -> Downstream caps struct %d name '%s' differs from "
|
|
"filter caps struct %d name '%s'",
|
|
i, possible_name, j, filter_name);
|
|
|
|
continue;
|
|
}
|
|
|
|
gst_structure_foreach (possible_struct,
|
|
(GstStructureForeachFunc) _find_structure_incompatible_fields, &info);
|
|
|
|
if (info.found)
|
|
found = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!found && !empty_filter) {
|
|
gchar *filter_caps_str = gst_caps_to_string (filter);
|
|
gchar *possible_caps_str = gst_caps_to_string (possible_caps);
|
|
|
|
g_string_append_printf (str,
|
|
". The exact reason could not be determined but"
|
|
" here is the gathered information:\n"
|
|
" - %s last query caps filter: %s\n"
|
|
" - %s possible caps (as returned by a query on it without filter): %s\n",
|
|
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
|
|
filter_caps_str,
|
|
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
|
|
possible_caps_str);
|
|
}
|
|
|
|
gst_caps_unref (possible_caps);
|
|
gst_caps_unref (filter);
|
|
gst_object_unref (pad);
|
|
|
|
}
|
|
|
|
static gboolean
|
|
_append_accept_caps_failure_details (GstValidatePadMonitor * monitor,
|
|
GString * str)
|
|
{
|
|
gint i, j;
|
|
GstCaps *refused_caps = gst_caps_copy (monitor->last_refused_caps);
|
|
GstPad *pad =
|
|
GST_PAD (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
|
|
(monitor)));
|
|
GstCaps *possible_caps = gst_pad_query_caps (pad, NULL);
|
|
gchar *caps_str = gst_caps_to_string (monitor->last_refused_caps);
|
|
StructureIncompatibleFieldsInfo info = {
|
|
.str = str,
|
|
.found = FALSE
|
|
};
|
|
|
|
g_string_append_printf (str,
|
|
"\n Caps negotiation failed at pad '%s' as it refused caps: %s",
|
|
gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (monitor)),
|
|
caps_str);
|
|
g_free (caps_str);
|
|
|
|
for (i = 0; i < gst_caps_get_size (refused_caps); i++) {
|
|
GstStructure *refused_struct = gst_caps_get_structure (refused_caps, i);
|
|
const gchar *filter_name;
|
|
const gchar *refused_name = gst_structure_get_name (refused_struct);
|
|
|
|
for (j = 0; j < gst_caps_get_size (possible_caps); j++) {
|
|
info.caps_struct_num = i,
|
|
info.filter_caps_struct_num = j,
|
|
info.filter = gst_caps_get_structure (possible_caps, j);
|
|
|
|
filter_name = gst_structure_get_name (info.filter);
|
|
if (g_strcmp0 (refused_name, filter_name)) {
|
|
g_string_append_printf (str,
|
|
"\n -> Downstream caps struct %d name '%s' differs from "
|
|
"filter caps struct %d name '%s'", i, refused_name, j, filter_name);
|
|
|
|
continue;
|
|
}
|
|
|
|
gst_structure_foreach (refused_struct,
|
|
(GstStructureForeachFunc) _find_structure_incompatible_fields, &info);
|
|
}
|
|
}
|
|
|
|
gst_caps_unref (possible_caps);
|
|
gst_object_unref (pad);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gchar *
|
|
_generate_not_negotiated_error_report (GstMessage * msg)
|
|
{
|
|
GString *str;
|
|
GList *tmp;
|
|
GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (msg));
|
|
GstValidatePadMonitor *last_query_caps_fail_monitor = NULL,
|
|
*last_refused_caps_monitor = NULL;
|
|
|
|
str = g_string_new (NULL);
|
|
g_string_append_printf (str, "Error message posted by: %s",
|
|
GST_OBJECT_NAME (element));
|
|
|
|
GST_OBJECT_LOCK (element);
|
|
for (tmp = element->srcpads; tmp; tmp = tmp->next) {
|
|
GstPad *to_check = (GstPad *) tmp->data;
|
|
_gather_pad_negotiation_details (to_check, str,
|
|
&last_query_caps_fail_monitor, &last_refused_caps_monitor);
|
|
}
|
|
GST_OBJECT_UNLOCK (element);
|
|
|
|
if (last_query_caps_fail_monitor)
|
|
_append_query_caps_failure_details (last_query_caps_fail_monitor, str);
|
|
else if (last_refused_caps_monitor)
|
|
_append_accept_caps_failure_details (last_refused_caps_monitor, str);
|
|
else {
|
|
GST_ERROR ("We should always be able to generate a detailed report"
|
|
" about why negotiation failed. Please report a bug against"
|
|
" gst-devtools:validate with this message and a way to reproduce.");
|
|
}
|
|
|
|
gst_object_replace ((GstObject **) & last_query_caps_fail_monitor, NULL);
|
|
gst_object_replace ((GstObject **) & last_refused_caps_monitor, NULL);
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
static void
|
|
_bus_handler (GstBus * bus, GstMessage * message,
|
|
GstValidatePipelineMonitor * monitor)
|
|
{
|
|
GError *err = NULL;
|
|
gchar *debug = NULL;
|
|
const GstStructure *details = NULL;
|
|
gint error_flow = GST_FLOW_OK;
|
|
|
|
if (GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
|
|
GST_VALIDATE_VERBOSITY_MESSAGES
|
|
&& GST_MESSAGE_TYPE (message) != GST_MESSAGE_PROPERTY_NOTIFY) {
|
|
GstObject *src_obj;
|
|
const GstStructure *s;
|
|
guint32 seqnum;
|
|
GString *str = g_string_new (NULL);
|
|
|
|
seqnum = gst_message_get_seqnum (message);
|
|
s = gst_message_get_structure (message);
|
|
src_obj = GST_MESSAGE_SRC (message);
|
|
|
|
if (GST_IS_ELEMENT (src_obj)) {
|
|
g_string_append_printf (str, "Got message #%u from element \"%s\" (%s): ",
|
|
(guint) seqnum, GST_ELEMENT_NAME (src_obj),
|
|
GST_MESSAGE_TYPE_NAME (message));
|
|
} else if (GST_IS_PAD (src_obj)) {
|
|
g_string_append_printf (str, "Got message #%u from pad \"%s:%s\" (%s): ",
|
|
(guint) seqnum, GST_DEBUG_PAD_NAME (src_obj),
|
|
GST_MESSAGE_TYPE_NAME (message));
|
|
} else if (GST_IS_OBJECT (src_obj)) {
|
|
g_string_append_printf (str, "Got message #%u from object \"%s\" (%s): ",
|
|
(guint) seqnum, GST_OBJECT_NAME (src_obj),
|
|
GST_MESSAGE_TYPE_NAME (message));
|
|
} else {
|
|
g_string_append_printf (str, "Got message #%u (%s): ", (guint) seqnum,
|
|
GST_MESSAGE_TYPE_NAME (message));
|
|
}
|
|
if (s) {
|
|
gchar *sstr;
|
|
|
|
sstr = gst_structure_to_string (s);
|
|
g_string_append_printf (str, "%s\n", sstr);
|
|
g_free (sstr);
|
|
} else {
|
|
g_string_append (str, "no message details\n");
|
|
}
|
|
gst_validate_printf (NULL, "%s", str->str);
|
|
g_string_free (str, TRUE);
|
|
}
|
|
switch (GST_MESSAGE_TYPE (message)) {
|
|
case GST_MESSAGE_EOS:
|
|
print_position (GST_VALIDATE_MONITOR (monitor));
|
|
break;
|
|
case GST_MESSAGE_ERROR:
|
|
gst_message_parse_error (message, &err, &debug);
|
|
gst_message_parse_error_details (message, &details);
|
|
|
|
if (g_error_matches (err, GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN)) {
|
|
GST_VALIDATE_REPORT (monitor, MISSING_PLUGIN,
|
|
"Error: %s -- Debug message: %s", err->message, debug);
|
|
} else if ((g_error_matches (err, GST_STREAM_ERROR,
|
|
GST_STREAM_ERROR_FAILED) && details
|
|
&& gst_structure_get_int (details, "flow-return", &error_flow)
|
|
&& error_flow == GST_FLOW_NOT_NEGOTIATED)
|
|
|| g_error_matches (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FORMAT)) {
|
|
gchar *report = _generate_not_negotiated_error_report (message);
|
|
|
|
GST_VALIDATE_REPORT (monitor, NOT_NEGOTIATED, "%s", report);
|
|
g_free (report);
|
|
} else {
|
|
GST_VALIDATE_REPORT (monitor, ERROR_ON_BUS,
|
|
"Got error: %s -- Debug message: %s (%" GST_PTR_FORMAT ")",
|
|
err->message, debug, details);
|
|
}
|
|
|
|
GST_VALIDATE_MONITOR_LOCK (monitor);
|
|
monitor->got_error = TRUE;
|
|
GST_VALIDATE_MONITOR_UNLOCK (monitor);
|
|
g_error_free (err);
|
|
g_free (debug);
|
|
break;
|
|
case GST_MESSAGE_WARNING:
|
|
gst_message_parse_warning (message, &err, &debug);
|
|
GST_VALIDATE_REPORT (monitor, WARNING_ON_BUS,
|
|
"Got warning: %s -- Debug message: %s", err->message, debug);
|
|
g_error_free (err);
|
|
g_free (debug);
|
|
break;
|
|
case GST_MESSAGE_STATE_CHANGED:
|
|
{
|
|
GstObject *target =
|
|
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
|
|
if (GST_MESSAGE_SRC (message) == target) {
|
|
GstState oldstate, newstate, pending;
|
|
|
|
gst_message_parse_state_changed (message, &oldstate, &newstate,
|
|
&pending);
|
|
|
|
if (oldstate == GST_STATE_READY && newstate == GST_STATE_PAUSED) {
|
|
monitor->print_pos_srcid =
|
|
g_timeout_add (PRINT_POSITION_TIMEOUT,
|
|
(GSourceFunc) print_position, monitor);
|
|
} else if (oldstate >= GST_STATE_PAUSED && newstate <= GST_STATE_READY) {
|
|
if (monitor->print_pos_srcid
|
|
&& g_source_remove (monitor->print_pos_srcid))
|
|
monitor->print_pos_srcid = 0;
|
|
monitor->got_error = FALSE;
|
|
}
|
|
}
|
|
|
|
if (target)
|
|
gst_object_unref (target);
|
|
|
|
break;
|
|
}
|
|
case GST_MESSAGE_BUFFERING:
|
|
{
|
|
JsonBuilder *jbuilder = json_builder_new ();
|
|
GstBufferingMode mode;
|
|
gint percent;
|
|
|
|
gst_message_parse_buffering (message, &percent);
|
|
gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL);
|
|
|
|
json_builder_begin_object (jbuilder);
|
|
json_builder_set_member_name (jbuilder, "type");
|
|
json_builder_add_string_value (jbuilder, "buffering");
|
|
json_builder_set_member_name (jbuilder, "state");
|
|
if (percent == 100) {
|
|
/* a 100% message means buffering is done */
|
|
gst_validate_printf (NULL, "\nDone buffering\n");
|
|
json_builder_add_string_value (jbuilder, "done");
|
|
if (monitor->buffering) {
|
|
monitor->print_pos_srcid =
|
|
g_timeout_add (PRINT_POSITION_TIMEOUT,
|
|
(GSourceFunc) print_position, monitor);
|
|
monitor->buffering = FALSE;
|
|
}
|
|
} else {
|
|
/* buffering... */
|
|
if (!monitor->buffering) {
|
|
monitor->buffering = TRUE;
|
|
gst_validate_printf (NULL, "\nStart buffering\n");
|
|
json_builder_add_string_value (jbuilder, "started");
|
|
if (monitor->print_pos_srcid
|
|
&& g_source_remove (monitor->print_pos_srcid)) {
|
|
monitor->print_pos_srcid = 0;
|
|
}
|
|
} else {
|
|
json_builder_add_string_value (jbuilder, "progress");
|
|
}
|
|
gst_validate_printf (NULL, "%s %d%% \r", "Buffering...", percent);
|
|
}
|
|
json_builder_set_member_name (jbuilder, "position");
|
|
json_builder_add_int_value (jbuilder, percent);
|
|
json_builder_end_object (jbuilder);
|
|
|
|
gst_validate_send (json_builder_get_root (jbuilder));
|
|
g_object_unref (jbuilder);
|
|
break;
|
|
}
|
|
case GST_MESSAGE_STREAM_COLLECTION:
|
|
{
|
|
GstStreamCollection *collection = NULL;
|
|
gst_message_parse_stream_collection (message, &collection);
|
|
gst_object_replace ((GstObject **) & monitor->stream_collection,
|
|
(GstObject *) collection);
|
|
gst_object_unref (collection);
|
|
break;
|
|
}
|
|
case GST_MESSAGE_STREAMS_SELECTED:
|
|
{
|
|
guint i;
|
|
|
|
if (monitor->streams_selected) {
|
|
g_list_free_full (monitor->streams_selected, gst_object_unref);
|
|
monitor->streams_selected = NULL;
|
|
}
|
|
|
|
for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
|
|
GstStream *stream =
|
|
gst_message_streams_selected_get_stream (message, i);
|
|
|
|
monitor->streams_selected =
|
|
g_list_append (monitor->streams_selected, stream);
|
|
}
|
|
break;
|
|
}
|
|
case GST_MESSAGE_PROPERTY_NOTIFY:
|
|
{
|
|
const GValue *val;
|
|
const gchar *name;
|
|
GstObject *obj;
|
|
gchar *val_str = NULL;
|
|
gchar *obj_name;
|
|
|
|
if (!(GST_VALIDATE_MONITOR_CAST (monitor)->verbosity &
|
|
GST_VALIDATE_VERBOSITY_PROPS_CHANGES))
|
|
return;
|
|
|
|
gst_message_parse_property_notify (message, &obj, &name, &val);
|
|
|
|
obj_name = gst_object_get_path_string (GST_OBJECT (obj));
|
|
if (val != NULL) {
|
|
if (G_VALUE_HOLDS_STRING (val))
|
|
val_str = g_value_dup_string (val);
|
|
else if (G_VALUE_TYPE (val) == GST_TYPE_CAPS)
|
|
val_str = gst_caps_to_string (g_value_get_boxed (val));
|
|
else if (G_VALUE_TYPE (val) == GST_TYPE_TAG_LIST)
|
|
val_str = gst_tag_list_to_string (g_value_get_boxed (val));
|
|
else if (G_VALUE_TYPE (val) == GST_TYPE_STRUCTURE)
|
|
val_str = gst_structure_to_string (g_value_get_boxed (val));
|
|
else
|
|
val_str = gst_value_serialize (val);
|
|
} else {
|
|
val_str = g_strdup ("(no value)");
|
|
}
|
|
|
|
gst_validate_printf (NULL, "%s: %s = %s\n", obj_name, name, val_str);
|
|
g_free (obj_name);
|
|
g_free (val_str);
|
|
break;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_validate_pipeline_monitor_create_scenarios (GstValidateBinMonitor * monitor)
|
|
{
|
|
/* scenarios currently only make sense for pipelines */
|
|
const gchar *scenarios_names;
|
|
gchar **scenarios = NULL;
|
|
GstObject *target =
|
|
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
|
|
GstValidateRunner *runner =
|
|
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor));
|
|
|
|
|
|
if ((scenarios_names = g_getenv ("GST_VALIDATE_SCENARIO"))) {
|
|
gint i;
|
|
|
|
scenarios = g_strsplit (scenarios_names, G_SEARCHPATH_SEPARATOR_S, 0);
|
|
for (i = 0; scenarios[i]; i++) {
|
|
gchar **scenario_v = g_strsplit (scenarios[i], "->", 2);
|
|
|
|
if (scenario_v[1] && target) {
|
|
if (!g_pattern_match_simple (scenario_v[1], GST_OBJECT_NAME (target))) {
|
|
GST_INFO_OBJECT (monitor, "Not attaching to pipeline %" GST_PTR_FORMAT
|
|
" as not matching pattern %s", target, scenario_v[1]);
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
if (target)
|
|
monitor->scenario =
|
|
gst_validate_scenario_factory_create (runner,
|
|
GST_ELEMENT_CAST (target), scenario_v[0]);
|
|
else
|
|
GST_INFO_OBJECT (monitor, "Not creating scenario as monitor"
|
|
" already does not have a target.");
|
|
g_strfreev (scenario_v);
|
|
}
|
|
}
|
|
done:
|
|
g_strfreev (scenarios);
|
|
if (target)
|
|
gst_object_unref (target);
|
|
if (runner)
|
|
gst_object_unref (runner);
|
|
}
|
|
|
|
/**
|
|
* gst_validate_pipeline_monitor_new:
|
|
* @pipeline: (transfer none): a #GstPipeline to run Validate on
|
|
*/
|
|
GstValidatePipelineMonitor *
|
|
gst_validate_pipeline_monitor_new (GstPipeline * pipeline,
|
|
GstValidateRunner * runner, GstValidateMonitor * parent)
|
|
{
|
|
GstBus *bus;
|
|
GstValidatePipelineMonitor *monitor =
|
|
g_object_new (GST_TYPE_VALIDATE_PIPELINE_MONITOR, "object",
|
|
pipeline, "validate-runner", runner, "validate-parent", parent,
|
|
"pipeline", pipeline, NULL);
|
|
GstObject *target =
|
|
gst_validate_monitor_get_target (GST_VALIDATE_MONITOR (monitor));
|
|
|
|
if (target == NULL) {
|
|
g_object_unref (monitor);
|
|
return NULL;
|
|
}
|
|
|
|
gst_validate_pipeline_monitor_create_scenarios (GST_VALIDATE_BIN_MONITOR
|
|
(monitor));
|
|
|
|
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
|
gst_bus_enable_sync_message_emission (bus);
|
|
g_signal_connect (bus, "sync-message", (GCallback) _bus_handler, monitor);
|
|
|
|
monitor->deep_notify_id =
|
|
gst_element_add_property_deep_notify_watch ((GstElement *) pipeline, NULL,
|
|
TRUE);
|
|
|
|
gst_object_unref (bus);
|
|
|
|
if (g_strcmp0 (G_OBJECT_TYPE_NAME (pipeline), "GstPlayBin") == 0)
|
|
monitor->is_playbin = TRUE;
|
|
else if (g_strcmp0 (G_OBJECT_TYPE_NAME (pipeline), "GstPlayBin3") == 0)
|
|
monitor->is_playbin3 = TRUE;
|
|
gst_object_unref (target);
|
|
|
|
return monitor;
|
|
}
|