AC_MSG_NOTICE([Cairo is needed for the gst-validate-images-tool])
fi
+PKG_CHECK_MODULES(JSON_GLIB, json-glib-1.0)
+AC_SUBST(JSON_GLIB_LIBS)
+AC_SUBST(JSON_GLIB_CFLAGS)
+
dnl checks for gstreamer
AG_GST_CHECK_GST_CHECK($GST_API_VERSION, [$GST_REQ], no)
libgstvalidate_@GST_API_VERSION@_la_SOURCES = $(source_c)
libgstvalidate_@GST_API_VERSION@include_HEADERS = $(source_h)
libgstvalidate_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS)\
- $(GIO_CFLAGS) $(GST_PBUTILS_CFLAGS) \
+ $(JSON_GLIB_CFLAGS) $(GIO_CFLAGS) $(GST_PBUTILS_CFLAGS) \
-DGST_USE_UNSTABLE_API
libgstvalidate_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \
$(GST_LT_LDFLAGS) $(GIO_LDFLAGS) $(GST_PBUTILS_LDFAGS)
libgstvalidate_@GST_API_VERSION@_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
$(GST_ALL_LIBS) $(GIO_LIBS) $(GST_PBUTILS_LIBS) \
- $(GLIB_LIBS) $(LIBM)
+ $(JSON_GLIB_LIBS) $(GLIB_LIBS) $(LIBM)
libgstvalidate_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/validate
libgstvalidateplugin_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \
$(GST_LT_LDFLAGS) $(GIO_LDFLAGS) $(GST_PBUTILS_LDFAGS)
libgstvalidateplugin_@GST_API_VERSION@_la_LIBADD = \
- $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
+ $(JSON_GLIB_CFLAGS) $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
$(GST_ALL_LIBS) $(GIO_LIBS) $(GST_PBUTILS_LIBS) \
- $(GLIB_LIBS) $(LIBM)
+ $(JSON_GLIB_LIBS) $(GLIB_LIBS) $(LIBM)
CLEANFILES =
#include <gst/gst.h>
#include "gst-validate-scenario.h"
#include "gst-validate-monitor.h"
+#include <json-glib/json-glib.h>
GST_DEBUG_CATEGORY_EXTERN (gstvalidate_debug);
#define GST_CAT_DEFAULT gstvalidate_debug
G_GNUC_INTERNAL GstValidateMonitor * gst_validate_get_monitor (GObject *object);
G_GNUC_INTERNAL void gst_validate_init_runner (void);
G_GNUC_INTERNAL void gst_validate_deinit_runner (void);
-
+G_GNUC_INTERNAL void gst_validate_report_deinit (void);
+gboolean gst_validate_send (JsonNode * root);
#endif
{
GstQuery *query;
gint64 position, duration;
+ JsonBuilder *jbuilder;
GstElement *pipeline =
GST_ELEMENT (GST_VALIDATE_MONITOR_GET_OBJECT (monitor));
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 />\r", GST_TIME_ARGS (position), GST_TIME_ARGS (duration),
}
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,
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:
static GHashTable *_gst_validate_issues = NULL;
static FILE **log_files = NULL;
-GType _gst_validate_report_type;
-GST_DEFINE_MINI_OBJECT_TYPE (GstValidateReport, gst_validate_report);
+/* Tcp server for communications with gst-validate-launcher */
+GSocketClient *socket_client = NULL;
+GSocketConnection *server_connection = NULL;
+GOutputStream *server_ostream = NULL;
+
+GType _gst_validate_report_type = 0;
+
+static JsonNode *
+gst_validate_report_serialize (GstValidateReport * report)
+{
+ JsonNode *node = json_node_alloc ();
+ JsonObject *jreport = json_object_new ();
+
+ json_object_set_string_member (jreport, "type", "report");
+ json_object_set_string_member (jreport, "summary", report->issue->summary);
+ json_object_set_string_member (jreport, "level",
+ gst_validate_report_level_get_name (report->level));
+ json_object_set_string_member (jreport, "detected-on", report->reporter_name);
+ json_object_set_string_member (jreport, "details", report->message);
+
+ node = json_node_init_object (node, jreport);
+
+ return node;
+}
+
+GType
+gst_validate_report_get_type (void)
+{
+ if (_gst_validate_report_type == 0) {
+ _gst_validate_report_type =
+ g_boxed_type_register_static (g_intern_static_string
+ ("GstValidateReport"), (GBoxedCopyFunc) gst_mini_object_ref,
+ (GBoxedFreeFunc) gst_mini_object_unref);
+
+ json_boxed_register_serialize_func (_gst_validate_report_type,
+ JSON_NODE_OBJECT,
+ (JsonBoxedSerializeFunc) gst_validate_report_serialize);
+ }
+
+ return _gst_validate_report_type;
+}
+
GRegex *newline_regex = NULL;
_("a gstreamer plugin is missing and prevented Validate from running"),
NULL);
REGISTER_VALIDATE_ISSUE (CRITICAL, NOT_NEGOTIATED,
- _("a NOT NEGOTIATED message has been emitted on the bus."),
- NULL);
+ _("a NOT NEGOTIATED message has been posted on the bus."), NULL);
REGISTER_VALIDATE_ISSUE (WARNING, WARNING_ON_BUS,
_("We got a WARNING message on the bus"), NULL);
REGISTER_VALIDATE_ISSUE (CRITICAL, ERROR_ON_BUS,
REGISTER_VALIDATE_ISSUE (ISSUE, G_LOG_ISSUE, _("We got a g_log issue"), NULL);
}
+gboolean
+gst_validate_send (JsonNode * root)
+{
+ gboolean res = FALSE;
+ JsonGenerator *jgen;
+ gsize message_length;
+ gchar *object, *message;
+ GError *error = NULL;
+
+ if (!server_ostream)
+ goto done;
+
+ jgen = json_generator_new ();
+ json_generator_set_root (jgen, root);
+
+ object = json_generator_to_data (jgen, &message_length);
+ message = g_malloc0 (message_length + 5);
+ GST_WRITE_UINT32_BE (message, message_length);
+ strcpy (&message[4], object);
+ g_free (object);
+
+ res = g_output_stream_write_all (server_ostream, message, message_length + 4,
+ NULL, NULL, &error);
+
+ if (!res) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING)) {
+ GST_ERROR ("Stream was busy, trying again later.");
+
+ g_free (message);
+ g_object_unref (jgen);
+ g_idle_add ((GSourceFunc) gst_validate_send, root);
+ return G_SOURCE_REMOVE;
+ }
+
+ GST_ERROR ("ERROR: Can't write to remote: %s", error->message);
+ } else if (!g_output_stream_flush (server_ostream, NULL, &error)) {
+ GST_ERROR ("ERROR: Can't flush stream: %s", error->message);
+ }
+
+ g_free (message);
+ g_object_unref (jgen);
+
+done:
+ json_node_free (root);
+
+ return G_SOURCE_REMOVE;
+}
+
void
gst_validate_report_init (void)
{
- const gchar *var, *file_env;
+ const gchar *var, *file_env, *server_env;
const GDebugKey keys[] = {
{"fatal_criticals", GST_VALIDATE_FATAL_CRITICALS},
{"fatal_warnings", GST_VALIDATE_FATAL_WARNINGS},
gst_validate_report_load_issues ();
}
+ server_env = g_getenv ("GST_VALIDATE_SERVER");
+ if (server_env) {
+ GstUri *server_uri = gst_uri_from_string (server_env);
+
+ if (server_uri && !g_strcmp0 (gst_uri_get_scheme (server_uri), "tcp")) {
+ JsonBuilder *jbuilder;
+ GError *err = NULL;
+ socket_client = g_socket_client_new ();
+
+ server_connection = g_socket_client_connect_to_host (socket_client,
+ gst_uri_get_host (server_uri), gst_uri_get_port (server_uri),
+ NULL, &err);
+
+ if (!server_connection) {
+ g_clear_error (&err);
+ g_clear_object (&socket_client);
+
+ } else {
+ server_ostream =
+ g_io_stream_get_output_stream (G_IO_STREAM (server_connection));
+ jbuilder = json_builder_new ();
+ json_builder_begin_object (jbuilder);
+ json_builder_set_member_name (jbuilder, "started");
+ json_builder_add_boolean_value (jbuilder, TRUE);
+ json_builder_end_object (jbuilder);
+
+ gst_validate_send (json_builder_get_root (jbuilder));
+ g_object_unref (jbuilder);
+ }
+
+ gst_uri_unref (server_uri);
+ } else {
+ GST_ERROR ("Server URI not valid: %s", server_env);
+ }
+ }
+
file_env = g_getenv ("GST_VALIDATE_FILE");
if (file_env != NULL && *file_env != '\0') {
gint i;
g_malloc0 (sizeof (FILE *) * (g_strv_length (wanted_files) + 1));
for (i = 0; i < g_strv_length (wanted_files); i++) {
FILE *log_file;
-
if (g_strcmp0 (wanted_files[i], "stderr") == 0) {
log_file = stderr;
- } else if (g_strcmp0 (wanted_files[i], "stdout") == 0)
+ } else if (g_strcmp0 (wanted_files[i], "stdout") == 0) {
log_file = stdout;
- else {
+ } else {
log_file = g_fopen (wanted_files[i], "w");
}
#endif
}
+void
+gst_validate_report_deinit (void)
+{
+ if (server_ostream)
+ g_output_stream_close (server_ostream, NULL, NULL);
+ g_clear_object (&socket_client);
+ g_clear_object (&server_connection);
+ g_clear_object (&server_ostream);
+}
+
GstValidateIssue *
gst_validate_issue_from_id (GstValidateIssueId issue_id)
{
GstValidateReportingDetails reporter_level =
gst_validate_reporter_get_reporting_level (report->reporter);
+ gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE (report),
+ report));
/* Let's use our own reporting strategy */
if (reporter_level == GST_VALIDATE_SHOW_UNKNOWN) {
gst_validate_report_set_reporting_level (report,
continue;
report = (GstValidateReport *) (reports->data);
+
gst_validate_report_print_level (report);
gst_validate_report_print_detected_on (report);
- if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
+ if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
criticals = g_list_append (criticals, report);
+ gst_validate_report_print_details (report);
+ }
for (tmp = g_list_next (reports); tmp; tmp = tmp->next) {
report = (GstValidateReport *) (tmp->data);
gst_validate_report_print_detected_on (report);
- if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
+ if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
criticals = g_list_append (criticals, report);
+ gst_validate_report_print_details (report);
+ }
}
report = (GstValidateReport *) (reports->data);
gst_validate_report_print_description (report);
_reporter_iface_init));
/* GstValidateAction implementation */
-GType _gst_validate_action_type;
+GType _gst_validate_action_type = 0;
struct _GstValidateActionPrivate
{
GWeakRef scenario;
};
-GST_DEFINE_MINI_OBJECT_TYPE (GstValidateAction, gst_validate_action);
+static JsonNode *
+gst_validate_action_serialize (GstValidateAction * action)
+{
+ JsonNode *node = json_node_alloc ();
+ JsonObject *jreport = json_object_new ();
+ gchar *action_args = gst_structure_to_string (action->structure);
+
+ json_object_set_string_member (jreport, "type", "action");
+ json_object_set_string_member (jreport, "action-type", action->type);
+ json_object_set_int_member (jreport, "playback-time",
+ (gint64) action->playback_time);
+ json_object_set_string_member (jreport, "args", action_args);
+ g_free (action_args);
+
+ node = json_node_init_object (node, jreport);
+
+ return node;
+}
+
+GType
+gst_validate_action_get_type (void)
+{
+ if (_gst_validate_action_type == 0) {
+ _gst_validate_action_type =
+ g_boxed_type_register_static (g_intern_static_string
+ ("GstValidateAction"), (GBoxedCopyFunc) gst_mini_object_ref,
+ (GBoxedFreeFunc) gst_mini_object_unref);
+
+ json_boxed_register_serialize_func (_gst_validate_action_type,
+ JSON_NODE_OBJECT,
+ (JsonBoxedSerializeFunc) gst_validate_action_serialize);
+ }
+
+ return _gst_validate_action_type;
+}
+
static GstValidateAction *gst_validate_action_new (GstValidateScenario *
scenario, GstValidateActionType * type);
static gboolean execute_next_action (GstValidateScenario * scenario);
_action_check_and_set_printed (GstValidateAction * action)
{
if (action->priv->printed == FALSE) {
+ gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
+ (action), action));
+
action->priv->printed = TRUE;
return FALSE;
install: true,
c_args : [gst_c_args],
dependencies : [gst_dep, glib_dep, gio_dep, gmodule_dep,
- gst_pbutils_dep, mathlib])
+ gst_pbutils_dep, mathlib, json_dep])
validate_gen_sources = []
if build_gir
_priv_validate_override_registry_deinit ();
core_config = NULL;
validate_initialized = FALSE;
+ gst_validate_report_deinit ();
g_mutex_unlock (&_gst_validate_registry_mutex);
g_mutex_clear (&_gst_validate_registry_mutex);
""" Class representing tests and test managers. """
+import json
import os
import sys
import re
+import SocketServer
+import struct
import time
import utils
import signal
return self.result
+class GstValidateListener(SocketServer.BaseRequestHandler):
+ def handle(self):
+ """Implements BaseRequestHandler handle method"""
+ while True:
+ raw_len = self.request.recv(4)
+ if raw_len == '':
+ return
+ msglen = struct.unpack('>I', raw_len)[0]
+ msg = self.request.recv(msglen)
+ if msg == '':
+ return
+
+ obj = json.loads(msg)
+ test = getattr(self.server, "test")
+
+ obj_type = obj.get("type", '')
+ if obj_type == 'position':
+ test.set_position(obj['position'], obj['duration'],
+ obj['speed'])
+ elif obj_type == 'buffering':
+ test.set_position(obj['position'], 100)
+ elif obj_type == 'action':
+ test.add_action_execution(obj)
+ elif obj_type == 'report':
+ test.add_report(obj)
+
+
class GstValidateTest(Test):
""" A class representing a particular test. """
if p:
application_name = p
+ self.reports = []
+ self.position = -1
+ self.duration = -1
+ self.speed = 1.0
+ self.actions_infos = []
self.media_descriptor = media_descriptor
override_path = self.get_override_file(media_descriptor)
self.scenario = None
else:
self.scenario = scenario
+ self.server = None
+
+ def stop_server(self):
+ if self.server:
+ self.server.server_close()
+ self.server.shutdown()
+ self.server_thread.join()
+ self.server = None
+
+ def kill_subprocess(self):
+ Test.kill_subprocess(self)
+ self.stop_server()
+
+ def add_report(self, report):
+ self.reports.append(report)
+
+ def set_position(self, position, duration, speed=None):
+ self.position = position
+ self.duration = duration
+ if speed:
+ self.speed = speed
+
+ def add_action_execution(self, action_infos):
+ if action_infos['action-type'] == 'eos':
+ self._sent_eos_pos = time.time()
+ self.actions_infos.append(action_infos)
+
+ def server_wrapper(self, ready):
+ self.server = SocketServer.TCPServer(('localhost', 0), GstValidateListener)
+ self.server.socket.settimeout(0.0)
+ self.server.test = self
+ self.serverport = self.server.socket.getsockname()[1]
+ self.info("%s server port: %s" % (self, self.serverport))
+ ready.set()
+
+ # Activate the server; this will keep running until you
+ # interrupt the program with Ctrl-C
+ self.server.serve_forever()
+
+ def test_start(self, queue):
+ ready = threading.Event()
+ self.server_thread = threading.Thread(target=self.server_wrapper,
+ kwargs={'ready': ready})
+ self.server_thread.start()
+ ready.wait()
+
+ Test.test_start(self, queue)
+
+ def test_end(self):
+ Test.test_end(self)
+ self.stop_server()
def get_override_file(self, media_descriptor):
if media_descriptor:
return None
+ def get_current_position(self):
+ return self.position
+
def get_current_value(self):
if self.scenario:
- sent_eos = self.sent_eos_position()
- if sent_eos is not None:
+ if self._sent_eos_pos is not None:
t = time.time()
if ((t - sent_eos)) > 30:
if self.media_descriptor.get_protocol() == Protocols.HLS:
return Result.FAILED
- return self.get_current_position()
+ return self.position
def get_subproc_env(self):
self.validatelogs = self.logfile + '.validate.logs'
utils.touch(self.validatelogs)
subproc_env["GST_VALIDATE_FILE"] = logfiles
+ subproc_env["GST_VALIDATE_SERVER"] = "tcp://localhost:%s" % self.serverport
self.extra_logfiles.append(self.validatelogs)
if 'GST_DEBUG' in os.environ and not self.options.redirect_logs:
return value
def get_validate_criticals_errors(self):
- ret = "["
- errors = []
- for l in open(self.validatelogs, 'r').readlines():
- if "critical : " in l:
- error = l.split("critical : ")[1].replace("\n", '')
- if error not in errors:
- if ret != "[":
- ret += ", "
- ret += error
- errors.append(error)
-
- if ret == "[":
+ ret = []
+ for report in self.reports:
+ if report['level'] == 'critical':
+ ret.append(report['summary'])
+
+ if not ret:
return None
- else:
- return ret + "]"
+
+ return str(ret)
def check_results(self):
if self.result is Result.FAILED or self.result is Result.PASSED or self.result is Result.TIMEOUT:
else:
self.set_result(Result.PASSED)
- def _parse_position(self, p):
- self.log("Parsing %s" % p)
- times = self.findpos_regex.findall(p)
-
- if len(times) != 1:
- self.warning("Got a unparsable value: %s" % p)
- return 0, 0
-
- return (utils.gsttime_from_tuple(times[0][:4]),
- utils.gsttime_from_tuple(times[0][4:]))
-
- def _parse_buffering(self, b):
- return b.lower().split("buffering... ")[1].split("%")[0], 100
-
- def _get_position(self):
- position = duration = -1
-
- self.debug("Getting position")
- m = None
- for l in reversed(open(self.validatelogs, 'r').readlines()):
- l = l.lower()
- if "<position:" in l or "buffering" in l:
- m = l
- break
-
- if m is None:
- self.debug("Could not fine any positionning info")
- return position, duration
-
- for j in m.split("\r"):
- j = j.lstrip().rstrip()
- if j.startswith("<position:") and j.endswith("/>"):
- position, duration = self._parse_position(j)
- elif j.startswith("buffering") and j.endswith("%"):
- position, duration = self._parse_buffering(j)
- else:
- self.log("No info in %s" % j)
-
- return position, duration
-
- def _get_last_seek_values(self):
- m = None
- rate = start = stop = None
-
- for l in reversed(open(self.validatelogs, 'r').readlines()):
- l = l.lower()
- if "seeking to: " in l:
- m = l
- break
-
- if m is None:
- self.debug("Could not fine any seeking info")
- return start, stop, rate
-
- values = self.findlastseek_regex.findall(m)
- if len(values) != 1:
- self.warning("Got an unparsable seek value %s", m)
- return start, stop, rate
-
- v = values[0]
- return (utils.gsttime_from_tuple(v[:4]),
- utils.gsttime_from_tuple(v[4:8]),
- float(str(v[8]) + "." + str(v[9])))
-
- def sent_eos_position(self):
- if self._sent_eos_pos is not None:
- return self._sent_eos_pos
-
- for l in reversed(open(self.validatelogs, 'r').readlines()):
- l = l.lower()
- if "sending eos" in l:
- self._sent_eos_pos = time.time()
- return self._sent_eos_pos
-
- return None
-
- def get_current_position(self):
- position, duration = self._get_position()
- if position == -1:
- return position
-
- return position
-
def get_valgrind_suppression_file(self, subdir, name):
p = get_data_file(subdir, name)
if p:
inc_dirs = include_directories('.')
+json_dep = dependency('json-glib-1.0')
cdata = configuration_data()
cdata.set('GST_API_VERSION', '"@0@"'.format(apiversion))
cdata.set('VALIDATEPLUGINDIR', '"@0@/@1@/gstreamer-1.0/validate"'.format(get_option('prefix'),get_option('libdir')))