validate: media-check: Also check that segments are correct

This commit is contained in:
Thibault Saunier 2018-05-22 19:43:01 +02:00
parent 7e2200d889
commit 3f668f3e80
6 changed files with 211 additions and 3 deletions

View file

@ -361,6 +361,8 @@ gst_validate_report_load_issues (void)
_("detected tags are different than expected ones"), NULL);
REGISTER_VALIDATE_ISSUE (CRITICAL, FILE_FRAMES_INCORRECT,
_("resulting file frames are not as expected"), NULL);
REGISTER_VALIDATE_ISSUE (CRITICAL, FILE_SEGMENT_INCORRECT,
_("resulting segment is not as expected"), NULL);
REGISTER_VALIDATE_ISSUE (WARNING, FILE_NO_STREAM_INFO,
_("the discoverer could not determine the stream info"), NULL);
REGISTER_VALIDATE_ISSUE (WARNING, FILE_NO_STREAM_ID,

View file

@ -104,6 +104,7 @@ typedef enum {
#define FILE_SEEKABLE_INCORRECT _QUARK("file-checking::seekable-incorrect")
#define FILE_PROFILE_INCORRECT _QUARK("file-checking::profile-incorrect")
#define FILE_FRAMES_INCORRECT _QUARK("file-checking::frames-incorrect")
#define FILE_SEGMENT_INCORRECT _QUARK("file-checking::segment-incorrect")
#define ALLOCATION_FAILURE _QUARK("runtime::allocation-failure")
#define MISSING_PLUGIN _QUARK("runtime::missing-plugin")

View file

@ -95,6 +95,43 @@ deserialize_streamnode (const gchar ** names, const gchar ** values)
return streamnode;
}
static GstValidateSegmentNode *
deserialize_segmentnode (const gchar ** names, const gchar ** values)
{
gint i;
GstValidateSegmentNode *node = g_slice_new0 (GstValidateSegmentNode);
for (i = 0; names[i] != NULL; i++) {
if (!g_strcmp0 (names[i], "next-frame-id"))
node->next_frame_id = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "flags"))
node->segment.flags = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "rate"))
node->segment.rate = g_ascii_strtod (values[i], NULL);
else if (!g_strcmp0 (names[i], "applied-rate"))
node->segment.applied_rate = g_ascii_strtod (values[i], NULL);
else if (!g_strcmp0 (names[i], "format"))
node->segment.format = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "base"))
node->segment.base = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "offset"))
node->segment.offset = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "start"))
node->segment.start = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "stop"))
node->segment.stop = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "time"))
node->segment.time = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "position"))
node->segment.position = g_ascii_strtoull (values[i], NULL, 0);
else if (!g_strcmp0 (names[i], "duration"))
node->segment.duration = g_ascii_strtoull (values[i], NULL, 0);
}
return node;
}
static GstValidateMediaTagsNode *
deserialize_tagsnode (const gchar ** names, const gchar ** values)
{
@ -199,6 +236,13 @@ on_start_element_cb (GMarkupParseContext * context,
* node = deserialize_streamnode (attribute_names, attribute_values);
priv->in_stream = TRUE;
filenode->streams = g_list_prepend (filenode->streams, node);
} else if (g_strcmp0 (element_name, "segment") == 0) {
GstValidateMediaStreamNode *streamnode = filenode->streams->data;
GstValidateSegmentNode *node =
deserialize_segmentnode (attribute_names, attribute_values);
streamnode->segments = g_list_append (streamnode->segments, node);
} else if (g_strcmp0 (element_name, "frame") == 0) {
GstValidateMediaStreamNode *streamnode = filenode->streams->data;

View file

@ -146,8 +146,15 @@ serialize_filenode (GstValidateMediaDescriptorWriter * writer)
GstValidateMediaStreamNode
* snode = ((GstValidateMediaStreamNode *) tmp->data);
STR_APPEND2 (snode->str_open);
/* Segment are always prepended, let's bring them back to the right order */
STR_APPEND3 ("<segments>");
for (tmp2 = snode->segments; tmp2; tmp2 = tmp2->next)
STR_APPEND4 (((GstValidateSegmentNode *) tmp2->data)->str_open);
STR_APPEND3 ("</segments>");
for (tmp2 = snode->frames; tmp2; tmp2 = tmp2->next) {
STR_APPEND3 (((GstValidateMediaFrameNode *) tmp2->data)->str_open);
}
@ -317,8 +324,28 @@ _uridecodebin_probe (GstPad * pad, GstPadProbeInfo * info,
(GstValidateMediaDescriptor *)
writer, pad);
if (streamnode) {
GstValidateSegmentNode *segment_node =
g_slice_new0 (GstValidateSegmentNode);
gst_event_parse_segment (event, &segment);
gst_segment_copy_into (segment, &streamnode->segment);
gst_segment_copy_into (segment, &segment_node->segment);
segment_node->next_frame_id = g_list_length (streamnode->frames);
segment_node->str_open =
g_markup_printf_escaped ("<segment next-frame-id=\"%d\""
" flags=\"%d\" rate=\"%f\" applied-rate=\"%f\""
" format=\"%d\" base=\"%" G_GUINT64_FORMAT "\" offset=\"%"
G_GUINT64_FORMAT "\" start=\"%" G_GUINT64_FORMAT "\""
" stop=\"%" G_GUINT64_FORMAT "\" time=\"%" G_GUINT64_FORMAT
"\" position=\"%" G_GUINT64_FORMAT "\" duration=\"%"
G_GUINT64_FORMAT "\"/>", segment_node->next_frame_id,
segment->flags, segment->rate, segment->applied_rate,
segment->format, segment->base, segment->offset, segment->start,
segment->stop, segment->time, segment->position,
segment->duration);
streamnode->segments =
g_list_prepend (streamnode->segments, segment_node);
}
break;
}
@ -519,8 +546,10 @@ _run_frame_analysis (GstValidateMediaDescriptorWriter * writer,
GstValidateRunner * runner, const gchar * uri)
{
GstBus *bus;
GList *tmp;
GstStateChangeReturn sret;
GstValidateMonitor *monitor;
GstValidateMediaFileNode *filenode;
GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
@ -551,6 +580,15 @@ _run_frame_analysis (GstValidateMediaDescriptorWriter * writer,
}
g_main_loop_run (writer->priv->loop);
filenode = ((GstValidateMediaDescriptor *) writer)->filenode;
/* Segment are always prepended, let's reorder them. */
for (tmp = filenode->streams; tmp; tmp = tmp->next) {
GstValidateMediaStreamNode
* snode = ((GstValidateMediaStreamNode *) tmp->data);
snode->segments = g_list_reverse (snode->segments);
}
gst_element_set_state (writer->priv->pipeline, GST_STATE_NULL);
gst_object_unref (writer->priv->pipeline);
writer->priv->pipeline = NULL;
@ -846,6 +884,7 @@ gst_validate_media_descriptor_writer_add_frame (GstValidateMediaDescriptorWriter
gchar *checksum;
guint id;
GstValidateMediaFrameNode *fnode;
GstSegment * segment;
g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR_WRITER (writer),
FALSE);
@ -877,8 +916,11 @@ gst_validate_media_descriptor_writer_add_frame (GstValidateMediaDescriptorWriter
fnode->duration = GST_BUFFER_DURATION (buf);
fnode->pts = GST_BUFFER_PTS (buf);
fnode->dts = GST_BUFFER_DTS (buf);
g_assert (streamnode->segments);
segment = &((GstValidateSegmentNode *)streamnode->segments->data)->segment;
fnode->running_time =
gst_segment_to_running_time (&streamnode->segment, GST_FORMAT_TIME,
gst_segment_to_running_time (segment, GST_FORMAT_TIME,
GST_BUFFER_PTS (buf));
fnode->is_keyframe =
(GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) == FALSE);

View file

@ -62,6 +62,15 @@ free_framenode (GstValidateMediaFrameNode * framenode)
g_slice_free (GstValidateMediaFrameNode, framenode);
}
static inline void
free_segmentnode (GstValidateSegmentNode * segmentnode)
{
g_free (segmentnode->str_open);
g_free (segmentnode->str_close);
g_slice_free (GstValidateSegmentNode, segmentnode);
}
static inline void
free_streamnode (GstValidateMediaStreamNode * streamnode)
{
@ -69,6 +78,7 @@ free_streamnode (GstValidateMediaStreamNode * streamnode)
gst_caps_unref (streamnode->caps);
g_list_free_full (streamnode->frames, (GDestroyNotify) free_framenode);
g_list_free_full (streamnode->segments, (GDestroyNotify) free_segmentnode);
if (streamnode->pad)
gst_object_unref (streamnode->pad);
@ -337,6 +347,103 @@ stream_id_is_equal (const gchar * uri, const gchar * rid, const gchar * cid)
return FALSE;
}
static gboolean
compare_segments (GstValidateMediaDescriptor * ref,
gint i,
GstValidateMediaStreamNode * rstream,
GstValidateSegmentNode * rsegment, GstValidateSegmentNode * csegment)
{
if (rsegment->next_frame_id != csegment->next_frame_id) {
GST_VALIDATE_REPORT (ref, FILE_SEGMENT_INCORRECT,
"Segment %" GST_SEGMENT_FORMAT
" didn't come before the same frame ID, expected to come before %d, came before %d",
&rsegment->segment, rsegment->next_frame_id, csegment->next_frame_id);
return FALSE;
}
#define CHECK_SEGMENT_FIELD(fieldname, format) \
if (rsegment->segment.fieldname != csegment->segment.fieldname) { \
GST_ERROR ("Expected: %" GST_SEGMENT_FORMAT " got: %" GST_SEGMENT_FORMAT, \
&rsegment->segment, &csegment->segment); \
GST_VALIDATE_REPORT (ref, FILE_SEGMENT_INCORRECT, \
"Stream %s segment %d has " #fieldname \
" mismatch, Expected " format " got: " format , \
rstream->id, i, rsegment->segment.fieldname, \
csegment->segment.fieldname); \
return FALSE; \
}
CHECK_SEGMENT_FIELD (flags, "%d");
CHECK_SEGMENT_FIELD (rate, "%f");
CHECK_SEGMENT_FIELD (applied_rate, "%f");
CHECK_SEGMENT_FIELD (base, "%" G_GUINT64_FORMAT);
CHECK_SEGMENT_FIELD (offset, "%" G_GUINT64_FORMAT);
CHECK_SEGMENT_FIELD (start, "%" G_GUINT64_FORMAT);
CHECK_SEGMENT_FIELD (stop, "%" G_GUINT64_FORMAT);
CHECK_SEGMENT_FIELD (time, "%" G_GUINT64_FORMAT);
CHECK_SEGMENT_FIELD (position, "%" G_GUINT64_FORMAT);
CHECK_SEGMENT_FIELD (duration, "%" G_GUINT64_FORMAT);
return TRUE;
}
static void
append_segment_diff (GString * diff, char diffsign, GList * segments)
{
GList *tmp;
for (tmp = segments; tmp; tmp = tmp->next) {
gchar *ssegment =
gst_info_strdup_printf ("%c %" GST_SEGMENT_FORMAT "\n", diffsign,
&((GstValidateSegmentNode *) tmp->data)->segment);
g_string_append (diff, ssegment);
g_free (ssegment);
}
}
static gboolean
compare_segment_list (GstValidateMediaDescriptor * ref,
GstValidateMediaStreamNode * rstream, GstValidateMediaStreamNode * cstream)
{
gint i;
GList *rsegments, *csegments;
/* Keep compatibility with media stream files that do not have segments */
if (rstream->segments
&& g_list_length (rstream->segments) !=
g_list_length (cstream->segments)) {
GString *diff = g_string_new (NULL);
append_segment_diff (diff, '-', rstream->segments);
append_segment_diff (diff, '+', cstream->segments);
GST_VALIDATE_REPORT (ref, FILE_SEGMENT_INCORRECT,
"Stream reference has %i segments, compared one has %i segments\n%s",
g_list_length (rstream->segments), g_list_length (cstream->segments),
diff->str);
g_string_free (diff, TRUE);
}
for (i = 0, rsegments = rstream->segments, csegments = cstream->segments;
rsegments;
rsegments = rsegments->next, csegments = csegments->next, i++) {
GstValidateSegmentNode *rsegment, *csegment;
if (csegment == NULL) {
/* The list was checked to be of the same size */
g_assert_not_reached ();
return FALSE;
}
rsegment = rsegments->data;
csegment = csegments->data;
if (!compare_segments (ref, i, rstream, rsegment, csegment))
return FALSE;
}
return TRUE;
}
static gboolean
compare_frames (GstValidateMediaDescriptor * ref,
GstValidateMediaStreamNode *
@ -445,6 +552,8 @@ compare_streams (GstValidateMediaDescriptor * ref,
/* We ignore the return value on purpose as this is not critical */
compare_tags (ref, rstream, cstream);
compare_segment_list (ref, rstream, cstream);
if (compare_frames_list (ref, rstream, cstream))
return 1;
return 0;

View file

@ -82,7 +82,7 @@ typedef struct
/* Attributes */
GstCaps *caps;
GstSegment segment;
GList * segments;
gchar *id;
gchar *padname;
@ -112,6 +112,16 @@ typedef struct
gchar *str_close;
} GstValidateMediaFrameNode;
typedef struct
{
gint next_frame_id;
GstSegment segment;
gchar *str_open;
gchar *str_close;
} GstValidateSegmentNode;
GST_VALIDATE_API
void gst_validate_filenode_free (GstValidateMediaFileNode *
filenode);