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
This commit is contained in:
Thibault Saunier 2014-05-02 14:06:18 +02:00
parent ba38d09961
commit fbcee57902
4 changed files with 272 additions and 139 deletions

View file

@ -20,6 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
#include <gst/validate/validate.h>
#include "media-descriptor-writer.h"
#include <string.h>
@ -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)
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;
}

View file

@ -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,

View file

@ -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;

View file

@ -35,141 +35,14 @@
#include <gst/validate/media-descriptor.h>
#include <gst/pbutils/encoding-profile.h>
/* 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;