4 * Copyright (c) 2012, Collabora Ltd.
5 * Author: Thibault Saunier <thibault.saunier@collabora.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 #include "media-descriptor.h"
26 struct _GstValidateMediaDescriptorPrivate
31 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstValidateMediaDescriptor,
32 gst_validate_media_descriptor, GST_TYPE_OBJECT,
33 G_ADD_PRIVATE (GstValidateMediaDescriptor)
34 G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, NULL));
37 free_tagnode (GstValidateMediaTagNode * tagnode)
39 g_free (tagnode->str_open);
40 g_free (tagnode->str_close);
42 gst_tag_list_unref (tagnode->taglist);
44 g_slice_free (GstValidateMediaTagNode, tagnode);
48 free_tagsnode (GstValidateMediaTagsNode * tagsnode)
50 g_free (tagsnode->str_open);
51 g_free (tagsnode->str_close);
52 g_list_free_full (tagsnode->tags, (GDestroyNotify) free_tagnode);
53 g_slice_free (GstValidateMediaTagsNode, tagsnode);
57 free_framenode (GstValidateMediaFrameNode * framenode)
59 g_free (framenode->str_open);
60 g_free (framenode->str_close);
63 gst_buffer_unref (framenode->buf);
65 g_slice_free (GstValidateMediaFrameNode, framenode);
69 free_segmentnode (GstValidateSegmentNode * segmentnode)
71 g_free (segmentnode->str_open);
72 g_free (segmentnode->str_close);
74 g_slice_free (GstValidateSegmentNode, segmentnode);
78 free_streamnode (GstValidateMediaStreamNode * streamnode)
81 gst_caps_unref (streamnode->caps);
83 g_list_free_full (streamnode->frames, (GDestroyNotify) free_framenode);
84 g_list_free_full (streamnode->segments, (GDestroyNotify) free_segmentnode);
87 gst_object_unref (streamnode->pad);
90 free_tagsnode (streamnode->tags);
92 g_free (streamnode->padname);
93 g_free (streamnode->id);
94 g_free (streamnode->str_open);
95 g_free (streamnode->str_close);
96 g_slice_free (GstValidateMediaStreamNode, streamnode);
100 gst_validate_filenode_free (GstValidateMediaFileNode * filenode)
102 g_list_free_full (filenode->streams, (GDestroyNotify) free_streamnode);
104 free_tagsnode (filenode->tags);
106 g_free (filenode->uri);
109 gst_caps_unref (filenode->caps);
111 g_free (filenode->str_open);
112 g_free (filenode->str_close);
114 g_slice_free (GstValidateMediaFileNode, filenode);
118 gst_validate_tag_node_compare
119 (GstValidateMediaTagNode * tnode, const GstTagList * tlist)
121 if (gst_structure_is_equal (GST_STRUCTURE (tlist),
122 GST_STRUCTURE (tnode->taglist)) == FALSE) {
140 gst_validate_media_descriptor_dispose (GstValidateMediaDescriptor * self)
142 G_OBJECT_CLASS (gst_validate_media_descriptor_parent_class)->dispose (G_OBJECT
147 gst_validate_media_descriptor_finalize (GstValidateMediaDescriptor * self)
150 gst_validate_filenode_free (self->filenode);
152 G_OBJECT_CLASS (gst_validate_media_descriptor_parent_class)->finalize
157 gst_validate_media_descriptor_init (GstValidateMediaDescriptor * self)
159 self->filenode = g_slice_new0 (GstValidateMediaFileNode);
163 _set_property (GObject * object, guint prop_id,
164 const GValue * value, GParamSpec * pspec)
168 /* we assume the runner is valid as long as this scenario is,
170 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
171 g_value_get_object (value));
179 _get_property (GObject * object, guint prop_id,
180 GValue * value, GParamSpec * pspec)
184 /* we assume the runner is valid as long as this scenario is,
186 g_value_take_object (value,
187 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
195 gst_validate_media_descriptor_class_init (GstValidateMediaDescriptorClass *
198 GObjectClass *object_class = G_OBJECT_CLASS (self_class);
200 object_class->dispose =
201 (void (*)(GObject * object)) gst_validate_media_descriptor_dispose;
202 object_class->finalize =
203 (void (*)(GObject * object)) gst_validate_media_descriptor_finalize;
205 object_class->get_property = _get_property;
206 object_class->set_property = _set_property;
208 g_object_class_install_property (object_class, PROP_RUNNER,
209 g_param_spec_object ("validate-runner", "VALIDATE Runner",
210 "The Validate runner to report errors to",
211 GST_TYPE_VALIDATE_RUNNER,
212 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
216 compare_tags (GstValidateMediaDescriptor * ref,
217 GstValidateMediaStreamNode * rstream, GstValidateMediaStreamNode * cstream)
220 GstValidateMediaTagNode *rtag, *ctag;
221 GList *rtag_list, *ctag_list;
222 GstValidateMediaTagsNode *rtags, *ctags;
224 rtags = rstream->tags;
225 ctags = cstream->tags;
226 if (!rtags && !ctags)
228 else if (!rtags && ctags) {
230 GString *all_tags = g_string_new (NULL);
232 for (taglist = ctags->tags; taglist; taglist = taglist->next) {
233 gchar *stags = gst_tag_list_to_string (((GstValidateMediaTagNode *)
234 taglist->data)->taglist);
236 g_string_append_printf (all_tags, "%s\n", stags);
240 GST_VALIDATE_REPORT (ref, FILE_TAG_DETECTION_INCORRECT,
241 "Reference descriptor for stream %s has NO tags"
242 " but tags found: %s", rstream->id, all_tags->str);
244 g_string_free (all_tags, TRUE);
247 } else if (rtags && !ctags) {
249 GString *all_tags = g_string_new (NULL);
251 for (taglist = rtags->tags; taglist; taglist = taglist->next) {
252 gchar *stags = gst_tag_list_to_string (((GstValidateMediaTagNode *)
253 taglist->data)->taglist);
255 g_string_append_printf (all_tags, "%s\n", stags);
259 GST_VALIDATE_REPORT (ref, FILE_TAG_DETECTION_INCORRECT,
260 "Reference descriptor for stream %s has tags:\n %s\n"
261 " but NO tags found on the stream", rstream->id, all_tags->str);
263 g_string_free (all_tags, TRUE);
267 for (rtag_list = rtags->tags; rtag_list; rtag_list = rtag_list->next) {
268 rtag = rtag_list->data;
270 for (ctag_list = ctags->tags; ctag_list; ctag_list = ctag_list->next) {
271 ctag = ctag_list->data;
272 if (gst_tag_list_is_equal (rtag->taglist, ctag->taglist)) {
279 if (found == FALSE) {
280 gchar *rtaglist = gst_tag_list_to_string (rtag->taglist);
282 GST_VALIDATE_REPORT (ref, FILE_TAG_DETECTION_INCORRECT,
283 "Reference descriptor for stream %s has tags %s"
284 " but no equivalent taglist was found on the compared stream",
285 rstream->id, rtaglist);
295 /* Workaround false warning caused by differnet file path */
297 stream_id_is_equal (const gchar * uri, const gchar * rid, const gchar * cid)
300 const gchar *stream_id;
302 /* Simple case it's the same */
303 if (g_strcmp0 (rid, cid) == 0)
306 /* If it's not from file or from our local http server, it should have been the same */
307 if (!g_str_has_prefix (uri, "file://")
308 && !g_str_has_prefix (uri, "imagesequence:/")
309 && !g_str_has_prefix (uri, "http://127.0.0.1"))
312 /* taken from basesrc, compute the reference stream-id */
313 cs = g_checksum_new (G_CHECKSUM_SHA256);
314 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
316 stream_id = g_checksum_get_string (cs);
318 /* If the reference stream_id is the URI SHA256, that means we have a single
319 * stream file (no demuxing), just assume it's the same id */
320 if (g_strcmp0 (rid, stream_id) == 0) {
321 g_checksum_free (cs);
325 /* It should always be prefixed with the SHA256, otherwise it likely means
326 * that basesrc is no longer using a SHA256 checksum on the URI, and this
327 * workaround will need to be fixed */
328 if (!g_str_has_prefix (rid, stream_id)) {
329 g_checksum_free (cs);
332 g_checksum_free (cs);
334 /* we strip the IDS to the delimitor, and then compare */
335 rid = strchr (rid, '/');
336 cid = strchr (cid, '/');
338 if (rid == NULL || cid == NULL)
341 if (g_strcmp0 (rid, cid) == 0)
348 compare_segments (GstValidateMediaDescriptor * ref,
350 GstValidateMediaStreamNode * rstream,
351 GstValidateSegmentNode * rsegment, GstValidateSegmentNode * csegment)
353 if (rsegment->next_frame_id != csegment->next_frame_id) {
354 GST_VALIDATE_REPORT (ref, FILE_SEGMENT_INCORRECT,
355 "Segment %" GST_SEGMENT_FORMAT
356 " didn't come before the same frame ID, expected to come before %d, came before %d",
357 &rsegment->segment, rsegment->next_frame_id, csegment->next_frame_id);
360 #define CHECK_SEGMENT_FIELD(fieldname, format) \
361 if (rsegment->segment.fieldname != csegment->segment.fieldname) { \
362 GST_ERROR ("Expected: %" GST_SEGMENT_FORMAT " got: %" GST_SEGMENT_FORMAT, \
363 &rsegment->segment, &csegment->segment); \
364 GST_VALIDATE_REPORT (ref, FILE_SEGMENT_INCORRECT, \
365 "Stream %s segment %d has " #fieldname \
366 " mismatch, Expected " format " got: " format , \
367 rstream->id, i, rsegment->segment.fieldname, \
368 csegment->segment.fieldname); \
372 CHECK_SEGMENT_FIELD (flags, "%d");
373 CHECK_SEGMENT_FIELD (rate, "%f");
374 CHECK_SEGMENT_FIELD (applied_rate, "%f");
375 CHECK_SEGMENT_FIELD (base, "%" G_GUINT64_FORMAT);
376 CHECK_SEGMENT_FIELD (offset, "%" G_GUINT64_FORMAT);
377 CHECK_SEGMENT_FIELD (start, "%" G_GUINT64_FORMAT);
378 CHECK_SEGMENT_FIELD (stop, "%" G_GUINT64_FORMAT);
379 CHECK_SEGMENT_FIELD (time, "%" G_GUINT64_FORMAT);
380 /* We do not compare segment position since it's a field for usage only within the element */
381 /* CHECK_SEGMENT_FIELD (position, "%" G_GUINT64_FORMAT); */
382 CHECK_SEGMENT_FIELD (duration, "%" G_GUINT64_FORMAT);
388 append_segment_diff (GString * diff, char diffsign, GList * segments)
392 for (tmp = segments; tmp; tmp = tmp->next) {
394 gst_info_strdup_printf ("%c %" GST_SEGMENT_FORMAT "\n", diffsign,
395 &((GstValidateSegmentNode *) tmp->data)->segment);
396 g_string_append (diff, ssegment);
403 compare_segment_list (GstValidateMediaDescriptor * ref,
404 GstValidateMediaStreamNode * rstream, GstValidateMediaStreamNode * cstream)
407 GList *rsegments, *csegments;
409 /* Keep compatibility with media stream files that do not have segments */
410 if (rstream->segments
411 && g_list_length (rstream->segments) !=
412 g_list_length (cstream->segments)) {
413 GString *diff = g_string_new (NULL);
415 append_segment_diff (diff, '-', rstream->segments);
416 append_segment_diff (diff, '+', cstream->segments);
417 GST_VALIDATE_REPORT (ref, FILE_SEGMENT_INCORRECT,
418 "Stream reference has %i segments, compared one has %i segments\n%s",
419 g_list_length (rstream->segments), g_list_length (cstream->segments),
421 g_string_free (diff, TRUE);
424 for (i = 0, rsegments = rstream->segments, csegments = cstream->segments;
426 rsegments = rsegments->next, csegments = csegments->next, i++) {
427 GstValidateSegmentNode *rsegment, *csegment;
429 if (csegments == NULL) {
430 /* The list was checked to be of the same size */
431 g_assert_not_reached ();
435 rsegment = rsegments->data;
436 csegment = csegments->data;
438 if (!compare_segments (ref, i, rstream, rsegment, csegment))
446 compare_frames (GstValidateMediaDescriptor * ref,
447 GstValidateMediaStreamNode *
448 rstream, GstValidateMediaFrameNode * rframe,
449 GstValidateMediaFrameNode * cframe)
451 if (rframe->id != cframe->id) {
452 GST_VALIDATE_REPORT (ref, FILE_FRAMES_INCORRECT,
453 "Stream frame %s ids mismatch: %" G_GUINT64_FORMAT " != %"
454 G_GUINT64_FORMAT, rstream->id, rframe->id, cframe->id);
457 #define CHECK_FRAME_FIELD(fieldname, format, unknown_value) \
458 if (rframe->fieldname != unknown_value && rframe->fieldname != cframe->fieldname) { \
459 GST_VALIDATE_REPORT (ref, FILE_FRAMES_INCORRECT, \
460 "Stream %s frames with id %" G_GUINT64_FORMAT " have " #fieldname \
461 " mismatch. Expected " format ", got " format, rstream->id, \
462 rframe->id, rframe->fieldname, cframe->fieldname); \
466 CHECK_FRAME_FIELD (pts, "%" G_GUINT64_FORMAT, GST_VALIDATE_UNKNOWN_UINT64);
467 CHECK_FRAME_FIELD (dts, "%" G_GUINT64_FORMAT, GST_VALIDATE_UNKNOWN_UINT64);
468 CHECK_FRAME_FIELD (duration, "%" G_GUINT64_FORMAT,
469 GST_VALIDATE_UNKNOWN_UINT64);
470 CHECK_FRAME_FIELD (running_time, "%" G_GUINT64_FORMAT,
471 GST_VALIDATE_UNKNOWN_UINT64);
472 CHECK_FRAME_FIELD (is_keyframe, "%d", GST_VALIDATE_UNKNOWN_BOOL);
478 compare_frames_list (GstValidateMediaDescriptor * ref,
479 GstValidateMediaStreamNode * rstream, GstValidateMediaStreamNode * cstream)
481 GList *rframes, *cframes;
483 if (g_list_length (rstream->frames) != g_list_length (cstream->frames)) {
484 GST_VALIDATE_REPORT (ref, FILE_FRAMES_INCORRECT,
485 "Stream reference has %i frames, compared one has %i frames",
486 g_list_length (rstream->frames), g_list_length (cstream->frames));
490 for (rframes = rstream->frames, cframes = cstream->frames; rframes;
491 rframes = g_list_next (rframes), cframes = g_list_next (cframes)) {
492 GstValidateMediaFrameNode *rframe, *cframe;
494 if (cframes == NULL) {
495 /* The list was checked to be of the same size */
496 g_assert_not_reached ();
500 rframe = rframes->data;
501 cframe = cframes->data;
503 if (!compare_frames (ref, rstream, rframe, cframe)) {
512 caps_cleanup_parsing_fields (GstCaps * caps)
515 GstCaps *res = gst_caps_copy (caps);
517 for (i = 0; i < gst_caps_get_size (res); i++) {
518 GstStructure *s = gst_caps_get_structure (res, i);
520 gst_structure_remove_fields (s, "stream-format", "codec_data", "parsed",
521 "frames", "alignment", NULL);
527 /* Return TRUE if found FALSE otherwise */
529 compare_streams (GstValidateMediaDescriptor * ref,
530 GstValidateMediaStreamNode * rstream, GstValidateMediaStreamNode * cstream)
532 GstCaps *rcaps, *ccaps;
534 if (!stream_id_is_equal (ref->filenode->uri, rstream->id, cstream->id))
537 rcaps = caps_cleanup_parsing_fields (rstream->caps);
538 ccaps = caps_cleanup_parsing_fields (cstream->caps);
540 if (!gst_caps_is_equal (rcaps, ccaps)) {
541 gchar *rcaps_str = gst_caps_to_string (rcaps),
542 *ccaps_str = gst_caps_to_string (ccaps);
543 GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT,
544 "Reference descriptor for stream %s has caps: %s"
545 " but compared stream %s has caps: %s",
546 rstream->id, rcaps_str, cstream->id, ccaps_str);
551 gst_caps_unref (rcaps);
552 gst_caps_unref (ccaps);
553 /* We ignore the return value on purpose as this is not critical */
554 compare_tags (ref, rstream, cstream);
556 compare_segment_list (ref, rstream, cstream);
557 compare_frames_list (ref, rstream, cstream);
563 gst_validate_media_descriptors_compare (GstValidateMediaDescriptor * ref,
564 GstValidateMediaDescriptor * compared)
567 GstValidateMediaFileNode
568 * rfilenode = ref->filenode, *cfilenode = compared->filenode;
570 if (rfilenode->duration != cfilenode->duration) {
571 GST_VALIDATE_REPORT (ref, FILE_DURATION_INCORRECT,
572 "Duration %" GST_TIME_FORMAT " is different from the reference %"
573 GST_TIME_FORMAT, GST_TIME_ARGS (cfilenode->duration),
574 GST_TIME_ARGS (rfilenode->duration));
577 if (rfilenode->seekable != cfilenode->seekable) {
578 GST_VALIDATE_REPORT (ref, FILE_SEEKABLE_INCORRECT,
579 "File known as %s but is reported %s now",
580 rfilenode->seekable ? "seekable" : "not seekable",
581 cfilenode->seekable ? "seekable" : "not seekable");
584 if (g_list_length (rfilenode->streams) != g_list_length (cfilenode->streams)) {
585 GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT,
586 "Reference descriptor has %i streams != compared which has %i streams",
587 g_list_length (rfilenode->streams), g_list_length (cfilenode->streams));
593 for (rstream_list = rfilenode->streams; rstream_list;
594 rstream_list = rstream_list->next) {
596 gboolean sfound = FALSE;
598 for (cstream_list = cfilenode->streams; cstream_list;
599 cstream_list = cstream_list->next) {
601 sfound = compare_streams (ref, rstream_list->data, cstream_list->data);
607 GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT,
608 "Could not find stream %s in the compared descriptor",
609 ((GstValidateMediaStreamNode *) rstream_list->data)->id);
617 gst_validate_media_descriptor_detects_frames (GstValidateMediaDescriptor * self)
619 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR (self), FALSE);
620 g_return_val_if_fail (self->filenode, FALSE);
622 return self->filenode->frame_detection;
626 * gst_validate_media_descriptor_get_buffers: (skip):
629 gst_validate_media_descriptor_get_buffers (GstValidateMediaDescriptor * self,
630 GstPad * pad, GCompareFunc compare_func, GList ** bufs)
632 GList *tmpstream, *tmpframe;
633 gboolean check = (pad == NULL), ret = FALSE;
634 GstCaps *pad_caps = gst_pad_get_current_caps (pad);
636 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR (self), FALSE);
637 g_return_val_if_fail (self->filenode, FALSE);
639 for (tmpstream = self->filenode->streams;
640 tmpstream; tmpstream = tmpstream->next) {
641 GstValidateMediaStreamNode
642 * streamnode = (GstValidateMediaStreamNode *) tmpstream->data;
644 if (pad && streamnode->pad == pad)
647 if (!streamnode->pad && gst_caps_is_subset (pad_caps, streamnode->caps)) {
653 for (tmpframe = streamnode->frames; tmpframe; tmpframe = tmpframe->next) {
656 g_list_insert_sorted (*bufs,
658 (GstValidateMediaFrameNode
659 *) tmpframe->data)->buf), compare_func);
662 g_list_prepend (*bufs,
664 (GstValidateMediaFrameNode *) tmpframe->data)->buf));
675 if (compare_func == NULL)
676 *bufs = g_list_reverse (*bufs);
678 gst_caps_unref (pad_caps);
683 gst_validate_media_descriptor_has_frame_info (GstValidateMediaDescriptor * self)
687 for (tmpstream = self->filenode->streams;
688 tmpstream; tmpstream = tmpstream->next) {
689 GstValidateMediaStreamNode
690 * streamnode = (GstValidateMediaStreamNode *) tmpstream->data;
692 if (g_list_length (streamnode->frames))
700 gst_validate_media_descriptor_get_duration (GstValidateMediaDescriptor * self)
702 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR (self), FALSE);
703 g_return_val_if_fail (self->filenode, FALSE);
705 return self->filenode->duration;
709 gst_validate_media_descriptor_get_seekable (GstValidateMediaDescriptor * self)
711 g_return_val_if_fail (GST_IS_VALIDATE_MEDIA_DESCRIPTOR (self), FALSE);
712 g_return_val_if_fail (self->filenode, FALSE);
714 return self->filenode->seekable;
718 * gst_validate_media_descriptor_get_pads: (skip):
721 gst_validate_media_descriptor_get_pads (GstValidateMediaDescriptor * self)
723 GList *ret = NULL, *tmp;
725 for (tmp = self->filenode->streams; tmp; tmp = tmp->next) {
726 GstValidateMediaStreamNode
727 * snode = (GstValidateMediaStreamNode *) tmp->data;
728 ret = g_list_append (ret, gst_pad_new (snode->padname, GST_PAD_UNKNOWN));