From fbcee5790260f86e8464b621e9c8aee3f99fd55b Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 2 May 2014 14:06:18 +0200 Subject: [PATCH] validate: Implement frame by frame writing in the media descriptor writer + Add an option to fully parse media files in the gst-validate-media-check tool --- .../gst/validate/media-descriptor-writer.c | 268 +++++++++++++++++- .../gst/validate/media-descriptor-writer.h | 1 + validate/gst/validate/media-descriptor.h | 6 + validate/tools/gst-validate-media-check.c | 136 +-------- 4 files changed, 272 insertions(+), 139 deletions(-) diff --git a/validate/gst/validate/media-descriptor-writer.c b/validate/gst/validate/media-descriptor-writer.c index 1fc3282e09..a8ffd6bf2d 100644 --- a/validate/gst/validate/media-descriptor-writer.c +++ b/validate/gst/validate/media-descriptor-writer.c @@ -20,6 +20,7 @@ * Boston, MA 02110-1301, USA. */ +#include #include "media-descriptor-writer.h" #include @@ -47,13 +48,22 @@ enum struct _GstMediaDescriptorWriterPrivate { - GList *serialized_string; - guint stream_id; + GstElement *pipeline; + GstCaps *raw_caps; + GMainLoop *loop; + + GList *parsers; }; static void finalize (GstMediaDescriptorWriter * writer) { + if (writer->priv->raw_caps) + gst_caps_unref (writer->priv->raw_caps); + + if (writer->priv->parsers) + gst_plugin_feature_list_free (writer->priv->parsers); + G_OBJECT_CLASS (gst_media_descriptor_writer_parent_class)-> finalize (G_OBJECT (writer)); } @@ -84,11 +94,13 @@ gst_media_descriptor_writer_init (GstMediaDescriptorWriter * writer) { GstMediaDescriptorWriterPrivate *priv; + writer->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (writer, GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriterPrivate); - priv->serialized_string = NULL; - priv->stream_id = 0; + writer->priv->parsers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER, + GST_RANK_MARGINAL); } static void @@ -249,16 +261,245 @@ gst_media_descriptor_writer_add_stream (GstMediaDescriptorWriter * writer, gst_discoverer_stream_info_get_tags (info)); } - if (caps != NULL) - gst_caps_unref (caps); + if (writer->priv->raw_caps == NULL) + writer->priv->raw_caps = gst_caps_copy (caps); + else { + writer->priv->raw_caps = gst_caps_merge (writer->priv->raw_caps, + gst_caps_copy (caps)); + } + gst_caps_unref (caps); g_free (capsstr); return ret; } +static GstPadProbeReturn +_uridecodebin_probe (GstPad * pad, GstPadProbeInfo * info, GstMediaDescriptorWriter *writer) +{ + gst_media_descriptor_writer_add_frame (writer, pad, info->data); + + return GST_PAD_PROBE_OK; +} + +static gboolean +_find_stream_id (GstPad *pad, GstEvent **event, GstMediaDescriptorWriter *writer) +{ + if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START) { + GList *tmp; + StreamNode *snode = NULL; + const gchar *stream_id; + + gst_event_parse_stream_start (*event, &stream_id); + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + if (g_strcmp0 (((StreamNode *) tmp->data)->id, stream_id) == 0) { + snode = tmp->data; + + break; + } + } + + if (!snode || snode->pad) { + GST_VALIDATE_REPORT (writer, FILE_CHECK_FAILURE, + "Got pad %s:%s where Discoverer found no stream ID", + GST_DEBUG_PAD_NAME (pad)); + + return TRUE; + } + + snode->pad = gst_object_ref (pad); + + return FALSE; + } + + return TRUE; +} + +static inline GstElement * +_get_parser (GstMediaDescriptorWriter *writer, GstPad *pad) +{ + GList *parsers1, *parsers; + GstElement *parser = NULL; + GstElementFactory *parserfact = NULL; + GstCaps *format; + + format = gst_pad_get_current_caps (pad); + + GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format); + parsers1 = + gst_element_factory_list_filter (writer->priv->parsers, format, + GST_PAD_SRC, FALSE); + parsers = + gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE); + gst_plugin_feature_list_free (parsers1); + + if (G_UNLIKELY (parsers == NULL)) { + GST_DEBUG ("Couldn't find any compatible parsers"); + goto beach; + } + + /* Just pick the first one */ + parserfact = parsers->data; + if (parserfact) + parser = gst_element_factory_create (parserfact, NULL); + + gst_plugin_feature_list_free (parsers); + +beach: + if (format) + gst_caps_unref (format); + + return parser; +} + +static void +pad_added_cb (GstElement * decodebin, GstPad * pad, GstMediaDescriptorWriter *writer) +{ + GList *tmp; + StreamNode *snode = NULL; + GstPad *sinkpad, *srcpad; + + /* Try to plug a parser so we have as much info as possible + * about the encoded stream. */ + GstElement *parser = _get_parser (writer, pad); + GstElement *fakesink = gst_element_factory_make ("fakesink", NULL); + + if (parser) { + sinkpad = gst_element_get_static_pad (parser, "sink"); + gst_bin_add (GST_BIN (writer->priv->pipeline), parser); + gst_element_sync_state_with_parent (parser); + gst_pad_link (pad, sinkpad); + + srcpad = gst_element_get_static_pad (parser, "src"); + } else { + srcpad = pad; + } + + sinkpad = gst_element_get_static_pad (fakesink, "sink"); + gst_bin_add (GST_BIN (writer->priv->pipeline), fakesink); + gst_element_sync_state_with_parent (fakesink); + gst_pad_link (srcpad, sinkpad); + gst_pad_sticky_events_foreach (pad, (GstPadStickyEventsForeachFunction) _find_stream_id, + writer); + + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; tmp = tmp->next) { + snode = tmp->data; + if (snode->pad == pad && srcpad != pad) { + gst_object_unref (pad); + snode->pad = gst_object_ref (srcpad); + break; + } + } + + gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) _uridecodebin_probe, writer, NULL); +} + +static gboolean +bus_callback (GstBus * bus, GstMessage * message, GstMediaDescriptorWriter *writer) +{ + GMainLoop *loop = writer->priv->loop; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate-media-check.error"); + g_main_loop_quit (loop); + break; + } + case GST_MESSAGE_EOS: + GST_INFO ("Got EOS!"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_STATE_CHANGED: + if (GST_MESSAGE_SRC (message) == GST_OBJECT (writer->priv->pipeline)) { + GstState oldstate, newstate, pending; + + gst_message_parse_state_changed (message, &oldstate, &newstate, + &pending); + + GST_DEBUG ("State changed (old: %s, new: %s, pending: %s)", + gst_element_state_get_name (oldstate), + gst_element_state_get_name (newstate), + gst_element_state_get_name (pending)); + + if (newstate == GST_STATE_PLAYING) { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, + "gst-validate-media-descriptor-writer.playing"); + } + } + + break; + case GST_MESSAGE_BUFFERING:{ + gint percent; + + gst_message_parse_buffering (message, &percent); + g_print ("%s %d%% \r", "Buffering...", percent); + + /* no state management needed for live pipelines */ + if (percent == 100) { + gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING); + } else { + gst_element_set_state (writer->priv->pipeline, GST_STATE_PAUSED); + } + break; + } + default: + break; + } + + return TRUE; +} + +static gboolean +_run_frame_analisis (GstMediaDescriptorWriter *writer, GstValidateRunner *runner, + const gchar *uri) +{ + GstBus *bus; + GstStateChangeReturn sret; + GstValidateMonitor *monitor; + + GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL); + + writer->priv->pipeline = gst_pipeline_new ("frame-analisis"); + + monitor = gst_validate_monitor_factory_create ( + GST_OBJECT_CAST (writer->priv->pipeline), runner, NULL); + gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor)); + + g_object_set (uridecodebin, "uri", uri, "caps", writer->priv->raw_caps, NULL); + g_signal_connect (uridecodebin, "pad-added", G_CALLBACK (pad_added_cb), writer); + gst_bin_add (GST_BIN (writer->priv->pipeline), uridecodebin); + + writer->priv->loop = g_main_loop_new (NULL, FALSE); + bus = gst_element_get_bus (writer->priv->pipeline); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message", (GCallback) bus_callback, writer); + sret = gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING); + switch (sret) { + case GST_STATE_CHANGE_FAILURE: + /* ignore, we should get an error message posted on the bus */ + g_print ("Pipeline failed to go to PLAYING state\n"); + return FALSE; + default: + break; + } + + g_main_loop_run (writer->priv->loop); + sret = gst_element_set_state (writer->priv->pipeline, GST_STATE_NULL); + gst_object_unref (writer->priv->pipeline); + writer->priv->pipeline = NULL; + g_main_loop_unref (writer->priv->loop); + writer->priv->loop = NULL; + + return TRUE; +} + GstMediaDescriptorWriter * gst_media_descriptor_writer_new_discover (GstValidateRunner * runner, - const gchar * uri, GError ** err) + const gchar * uri, gboolean full, GError ** err) { GList *tmp, *streams; GstDiscovererInfo *info; @@ -299,11 +540,18 @@ gst_media_descriptor_writer_new_discover (GstValidateRunner * runner, (streaminfo)); streams = gst_discoverer_info_get_stream_list (info); - for (tmp = streams; tmp; tmp = tmp->next) + for (tmp = streams; tmp; tmp = tmp->next) { gst_media_descriptor_writer_add_stream (writer, tmp->data); + } + + if (streams == NULL) + writer->priv->raw_caps = gst_caps_copy (((GstMediaDescriptor *) writer)->filenode->caps); gst_discoverer_stream_info_list_free(streams); + if (full == TRUE) + _run_frame_analisis (writer, runner, uri); + return writer; } @@ -466,7 +714,7 @@ gst_media_descriptor_writer_add_frame (GstMediaDescriptorWriter g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); ((GstMediaDescriptor *) writer)->filenode->frame_detection = TRUE; - + GST_MEDIA_DESCRIPTOR_LOCK (writer); for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; tmp = tmp->next) { StreamNode *streamnode = (StreamNode *) tmp->data; @@ -495,9 +743,11 @@ gst_media_descriptor_writer_add_frame (GstMediaDescriptorWriter fnode->str_close = NULL; streamnode->frames = g_list_append (streamnode->frames, fnode); + GST_MEDIA_DESCRIPTOR_UNLOCK (writer); return TRUE; } } + GST_MEDIA_DESCRIPTOR_UNLOCK (writer); return FALSE; } diff --git a/validate/gst/validate/media-descriptor-writer.h b/validate/gst/validate/media-descriptor-writer.h index f27e06d0bd..82653e15b7 100644 --- a/validate/gst/validate/media-descriptor-writer.h +++ b/validate/gst/validate/media-descriptor-writer.h @@ -58,6 +58,7 @@ typedef struct { GstMediaDescriptorWriter * gst_media_descriptor_writer_new_discover (GstValidateRunner *runner, const gchar *uri, + gboolean full, GError **err); GstMediaDescriptorWriter * gst_media_descriptor_writer_new (GstValidateRunner *runner, diff --git a/validate/gst/validate/media-descriptor.h b/validate/gst/validate/media-descriptor.h index a81e9cd290..a79c34c547 100644 --- a/validate/gst/validate/media-descriptor.h +++ b/validate/gst/validate/media-descriptor.h @@ -124,6 +124,10 @@ GType gst_media_descriptor_get_type (void); #define GST_IS_MEDIA_DESCRIPTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEDIA_DESCRIPTOR)) #define GST_MEDIA_DESCRIPTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptorClass)) +#define GST_MEDIA_DESCRIPTOR_GET_LOCK(obj) (&GST_MEDIA_DESCRIPTOR(obj)->lock) +#define GST_MEDIA_DESCRIPTOR_LOCK(obj) g_mutex_lock(GST_MEDIA_DESCRIPTOR_GET_LOCK(obj)) +#define GST_MEDIA_DESCRIPTOR_UNLOCK(obj) g_mutex_unlock(GST_MEDIA_DESCRIPTOR_GET_LOCK(obj)) + typedef struct _GstMediaDescriptorPrivate GstMediaDescriptorPrivate; typedef struct { @@ -131,6 +135,8 @@ typedef struct { FileNode *filenode; + GMutex lock; + GstMediaDescriptorPrivate *priv; } GstMediaDescriptor; diff --git a/validate/tools/gst-validate-media-check.c b/validate/tools/gst-validate-media-check.c index 779934540b..113cd56b0e 100644 --- a/validate/tools/gst-validate-media-check.c +++ b/validate/tools/gst-validate-media-check.c @@ -35,141 +35,14 @@ #include #include -/* move this into some utils file */ -#if 0 -static gboolean -_parse_encoding_profile (const gchar * option_name, const gchar * value, - gpointer udata, GError ** error) -{ - GstCaps *caps; - char *preset_name = NULL; - gchar **restriction_format, **preset_v; - - guint i, presence = 0; - GstCaps *restrictioncaps = NULL; - gchar **strpresence_v, **strcaps_v = g_strsplit (value, ":", 0); - - if (strcaps_v[0] && *strcaps_v[0]) { - caps = gst_caps_from_string (strcaps_v[0]); - if (caps == NULL) { - g_printerr ("Could not parse caps %s", strcaps_v[0]); - return FALSE; - } - encoding_profile = - GST_ENCODING_PROFILE (gst_encoding_container_profile_new - ("User profile", "User profile", caps, NULL)); - gst_caps_unref (caps); - } else { - encoding_profile = NULL; - } - - for (i = 1; strcaps_v[i]; i++) { - GstEncodingProfile *profile = NULL; - gchar *strcaps, *strpresence; - - restriction_format = g_strsplit (strcaps_v[i], "->", 0); - if (restriction_format[1]) { - restrictioncaps = gst_caps_from_string (restriction_format[0]); - strcaps = g_strdup (restriction_format[1]); - } else { - restrictioncaps = NULL; - strcaps = g_strdup (restriction_format[0]); - } - g_strfreev (restriction_format); - - preset_v = g_strsplit (strcaps, "+", 0); - if (preset_v[1]) { - strpresence = preset_v[1]; - g_free (strcaps); - strcaps = g_strdup (preset_v[0]); - } else { - strpresence = preset_v[0]; - } - - strpresence_v = g_strsplit (strpresence, "|", 0); - if (strpresence_v[1]) { /* We have a presence */ - gchar *endptr; - - if (preset_v[1]) { /* We have preset and presence */ - preset_name = g_strdup (strpresence_v[0]); - } else { /* We have a presence but no preset */ - g_free (strcaps); - strcaps = g_strdup (strpresence_v[0]); - } - - presence = strtoll (strpresence_v[1], &endptr, 10); - if (endptr == strpresence_v[1]) { - g_printerr ("Wrong presence %s\n", strpresence_v[1]); - - return FALSE; - } - } else { /* We have no presence */ - if (preset_v[1]) { /* Not presence but preset */ - preset_name = g_strdup (preset_v[1]); - g_free (strcaps); - strcaps = g_strdup (preset_v[0]); - } /* Else we have no presence nor preset */ - } - g_strfreev (strpresence_v); - g_strfreev (preset_v); - - GST_DEBUG ("Creating preset with restrictions: %" GST_PTR_FORMAT - ", caps: %s, preset %s, presence %d", restrictioncaps, strcaps, - preset_name ? preset_name : "none", presence); - - caps = gst_caps_from_string (strcaps); - g_free (strcaps); - if (caps == NULL) { - g_warning ("Could not create caps for %s", strcaps_v[i]); - - return FALSE; - } - - if (g_str_has_prefix (strcaps_v[i], "audio/")) { - profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, - preset_name, restrictioncaps, presence)); - } else if (g_str_has_prefix (strcaps_v[i], "video/") || - g_str_has_prefix (strcaps_v[i], "image/")) { - profile = GST_ENCODING_PROFILE (gst_encoding_video_profile_new (caps, - preset_name, restrictioncaps, presence)); - } - - g_free (preset_name); - gst_caps_unref (caps); - if (restrictioncaps) - gst_caps_unref (restrictioncaps); - - if (profile == NULL) { - g_warning ("No way to create a preset for caps: %s", strcaps_v[i]); - - return FALSE; - } - - if (encoding_profile) { - if (gst_encoding_container_profile_add_profile - (GST_ENCODING_CONTAINER_PROFILE (encoding_profile), - profile) == FALSE) { - g_warning ("Can not create a preset for caps: %s", strcaps_v[i]); - - return FALSE; - } - } else { - encoding_profile = profile; - } - } - g_strfreev (strcaps_v); - - return TRUE; -} -#endif - int main (int argc, gchar ** argv) { GOptionContext *ctx; - GError *err = NULL; guint ret = 0; + GError *err = NULL; + gboolean full = FALSE; gchar *output_file = NULL; gchar *expected_file = NULL; gchar *output = NULL; @@ -181,6 +54,9 @@ main (int argc, gchar ** argv) {"output-file", 'o', 0, G_OPTION_ARG_FILENAME, &output_file, "The output file to store the results", NULL}, + {"full", 'f', 0, G_OPTION_ARG_NONE, + &full, "Fully analize the file frame by frame", + NULL}, {"expected-results", 'e', 0, G_OPTION_ARG_FILENAME, &expected_file, "Path to file containing the expected results " "(or the last results found) for comparison with new results", @@ -217,7 +93,7 @@ main (int argc, gchar ** argv) g_option_context_free (ctx); runner = gst_validate_runner_new (); - writer = gst_media_descriptor_writer_new_discover (runner, argv[1], NULL); + writer = gst_media_descriptor_writer_new_discover (runner, argv[1], full, NULL); if (writer == NULL) { g_print ("Could not discover file: %s", argv[1]); return 1;