validate: media-check: Also check that segments are correct
authorThibault Saunier <tsaunier@igalia.com>
Tue, 22 May 2018 17:43:01 +0000 (19:43 +0200)
committerThibault Saunier <tsaunier@igalia.com>
Fri, 15 Jun 2018 16:05:13 +0000 (12:05 -0400)
validate/gst/validate/gst-validate-report.c
validate/gst/validate/gst-validate-report.h
validate/gst/validate/media-descriptor-parser.c
validate/gst/validate/media-descriptor-writer.c
validate/gst/validate/media-descriptor.c
validate/gst/validate/media-descriptor.h

index e67d5c5..72f7749 100644 (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,
index 09db598..4c43aae 100644 (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")
index 29d7849..c9b208a 100644 (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;
 
index f666f5a..4c93f31 100644 (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);
index b4cf758..831812a 100644 (file)
@@ -63,12 +63,22 @@ free_framenode (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)
 {
   if (streamnode->caps)
     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);
@@ -338,6 +348,103 @@ stream_id_is_equal (const gchar * uri, const gchar * rid, const gchar * cid)
 }
 
 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 *
     rstream, GstValidateMediaFrameNode * rframe,
@@ -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;
index 3079cfa..25c7c9a 100644 (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);