2013-08-05 17:16:06 +00:00
|
|
|
/* GStreamer
|
|
|
|
* Copyright (C) 2013 Thiago Santos <thiago.sousa.santos@collabora.com>
|
|
|
|
*
|
|
|
|
* gst-qa-file-checker.c - QA File conformance check utility functions / structs
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "gst-qa-file-checker.h"
|
|
|
|
#include "gst-qa-reporter.h"
|
|
|
|
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <gst/pbutils/pbutils.h>
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
PROP_RUNNER,
|
|
|
|
PROP_URI,
|
|
|
|
PROP_PROFILE,
|
|
|
|
PROP_DURATION,
|
|
|
|
PROP_DURATION_TOLERANCE,
|
|
|
|
PROP_FILE_SIZE,
|
|
|
|
PROP_FILE_SIZE_TOLERANCE,
|
|
|
|
PROP_SEEKABLE,
|
2013-08-07 14:31:04 +00:00
|
|
|
PROP_TEST_PLAYBACK,
|
2013-08-05 17:16:06 +00:00
|
|
|
PROP_LAST
|
|
|
|
};
|
|
|
|
|
|
|
|
#define DEFAULT_DURATION GST_CLOCK_TIME_NONE
|
|
|
|
#define DEFAULT_DURATION_TOLERANCE 0
|
|
|
|
#define DEFAULT_FILE_SIZE 0
|
|
|
|
#define DEFAULT_FILE_SIZE_TOLERANCE 0
|
|
|
|
#define DEFAULT_SEEKABLE FALSE
|
2013-08-07 14:31:04 +00:00
|
|
|
#define DEFAULT_PLAYBACK FALSE
|
2013-08-05 17:16:06 +00:00
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_qa_file_checker_debug);
|
|
|
|
#define GST_CAT_DEFAULT gst_qa_file_checker_debug
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_get_property (GObject * object, guint prop_id,
|
|
|
|
GValue * value, GParamSpec * pspec);
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_set_property (GObject * object, guint prop_id,
|
|
|
|
const GValue * value, GParamSpec * pspec);
|
|
|
|
|
|
|
|
#define _do_init \
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_qa_file_checker_debug, "qa_file_checker", 0, "QA FileChecker");\
|
|
|
|
G_IMPLEMENT_INTERFACE (GST_TYPE_QA_REPORTER, _reporter_iface_init)
|
|
|
|
|
|
|
|
static void
|
|
|
|
_reporter_iface_init (GstQaReporterInterface * iface)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#define gst_qa_file_checker_parent_class parent_class
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstQaFileChecker, gst_qa_file_checker,
|
|
|
|
G_TYPE_OBJECT, _do_init);
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_dispose (GObject * object)
|
|
|
|
{
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_finalize (GObject * object)
|
|
|
|
{
|
|
|
|
GstQaFileChecker *fc = GST_QA_FILE_CHECKER_CAST (object);
|
|
|
|
|
|
|
|
gst_qa_reporter_set_name (GST_QA_REPORTER (object), NULL);
|
|
|
|
|
|
|
|
g_free (fc->uri);
|
|
|
|
if (fc->profile)
|
|
|
|
gst_encoding_profile_unref (fc->profile);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_class_init (GstQaFileCheckerClass * klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class;
|
|
|
|
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
gobject_class->get_property = gst_qa_file_checker_get_property;
|
|
|
|
gobject_class->set_property = gst_qa_file_checker_set_property;
|
|
|
|
gobject_class->dispose = gst_qa_file_checker_dispose;
|
|
|
|
gobject_class->finalize = gst_qa_file_checker_finalize;
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_RUNNER,
|
|
|
|
g_param_spec_object ("qa-runner", "QA Runner", "The QA runner to "
|
|
|
|
"report errors to", GST_TYPE_QA_RUNNER,
|
|
|
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_URI,
|
|
|
|
g_param_spec_string ("uri", "URI", "The URI of the file to be checked",
|
|
|
|
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_PROFILE,
|
|
|
|
gst_param_spec_mini_object ("profile", "Profile",
|
|
|
|
"The GstEncodingProfile " "that should match what the file contains",
|
|
|
|
GST_TYPE_ENCODING_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DURATION,
|
|
|
|
g_param_spec_uint64 ("duration", "duration", "Stream duration "
|
|
|
|
"in nanosecs, use GST_CLOCK_TIME_NONE to disable this check",
|
|
|
|
0, G_MAXUINT64, DEFAULT_DURATION,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DURATION_TOLERANCE,
|
|
|
|
g_param_spec_uint64 ("duration-tolerance", "duration tolerance",
|
|
|
|
"Acceptable margin of error of the duration check (in nanoseconds)",
|
|
|
|
0, G_MAXUINT64, DEFAULT_DURATION_TOLERANCE,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_FILE_SIZE,
|
|
|
|
g_param_spec_uint64 ("file-size", "file size", "File size in bytes",
|
|
|
|
0, G_MAXUINT64, DEFAULT_FILE_SIZE,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_FILE_SIZE_TOLERANCE,
|
|
|
|
g_param_spec_uint64 ("file-size-tolerance", "file size tolerance",
|
|
|
|
"Acceptable margin of error of the file size check (in bytes)",
|
|
|
|
0, G_MAXUINT64, DEFAULT_FILE_SIZE_TOLERANCE,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_SEEKABLE,
|
|
|
|
g_param_spec_boolean ("is-seekable", "is seekable",
|
|
|
|
"If the resulting file should be seekable", DEFAULT_SEEKABLE,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
2013-08-07 14:31:04 +00:00
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_TEST_PLAYBACK,
|
|
|
|
g_param_spec_boolean ("test-playback", "test playback",
|
|
|
|
"If the file should be tested for playback", DEFAULT_PLAYBACK,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
|
2013-08-05 17:16:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_init (GstQaFileChecker * fc)
|
|
|
|
{
|
|
|
|
fc->uri = NULL;
|
|
|
|
fc->profile = NULL;
|
|
|
|
fc->duration = DEFAULT_DURATION;
|
|
|
|
fc->duration_tolerance = DEFAULT_DURATION_TOLERANCE;
|
|
|
|
fc->file_size = DEFAULT_FILE_SIZE;
|
|
|
|
fc->file_size_tolerance = DEFAULT_FILE_SIZE_TOLERANCE;
|
|
|
|
fc->seekable = DEFAULT_SEEKABLE;
|
2013-08-07 14:31:04 +00:00
|
|
|
fc->test_playback = DEFAULT_PLAYBACK;
|
2013-08-05 17:16:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_set_property (GObject * object, guint prop_id,
|
|
|
|
const GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstQaFileChecker *fc;
|
|
|
|
|
|
|
|
fc = GST_QA_FILE_CHECKER_CAST (object);
|
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_RUNNER:
|
|
|
|
gst_qa_reporter_set_runner (GST_QA_REPORTER (fc),
|
|
|
|
g_value_get_object (value));
|
|
|
|
break;
|
|
|
|
case PROP_URI:
|
|
|
|
g_free (fc->uri);
|
|
|
|
fc->uri = g_value_dup_string (value);
|
|
|
|
break;
|
|
|
|
case PROP_PROFILE:
|
|
|
|
if (fc->profile)
|
|
|
|
gst_encoding_profile_unref (fc->profile);
|
|
|
|
fc->profile = (GstEncodingProfile *) gst_value_dup_mini_object (value);
|
|
|
|
break;
|
|
|
|
case PROP_DURATION:
|
|
|
|
fc->duration = g_value_get_uint64 (value);
|
|
|
|
break;
|
|
|
|
case PROP_DURATION_TOLERANCE:
|
|
|
|
fc->duration_tolerance = g_value_get_uint64 (value);
|
|
|
|
break;
|
|
|
|
case PROP_FILE_SIZE:
|
|
|
|
fc->file_size = g_value_get_uint64 (value);
|
|
|
|
break;
|
|
|
|
case PROP_FILE_SIZE_TOLERANCE:
|
|
|
|
fc->file_size_tolerance = g_value_get_uint64 (value);
|
|
|
|
break;
|
|
|
|
case PROP_SEEKABLE:
|
|
|
|
fc->seekable = g_value_get_boolean (value);
|
|
|
|
break;
|
2013-08-07 14:31:04 +00:00
|
|
|
case PROP_TEST_PLAYBACK:
|
|
|
|
fc->test_playback = g_value_get_boolean (value);
|
|
|
|
break;
|
2013-08-05 17:16:06 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_qa_file_checker_get_property (GObject * object, guint prop_id,
|
|
|
|
GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstQaFileChecker *fc;
|
|
|
|
|
|
|
|
fc = GST_QA_FILE_CHECKER_CAST (object);
|
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_RUNNER:
|
|
|
|
g_value_set_object (value,
|
|
|
|
gst_qa_reporter_get_runner (GST_QA_REPORTER (fc)));
|
|
|
|
break;
|
|
|
|
case PROP_URI:
|
|
|
|
g_value_set_string (value, fc->uri);
|
|
|
|
break;
|
|
|
|
case PROP_PROFILE:
|
|
|
|
gst_value_set_mini_object (value, GST_MINI_OBJECT_CAST (fc->profile));
|
|
|
|
break;
|
|
|
|
case PROP_DURATION:
|
|
|
|
g_value_set_uint64 (value, fc->duration);
|
|
|
|
break;
|
|
|
|
case PROP_DURATION_TOLERANCE:
|
|
|
|
g_value_set_uint64 (value, fc->duration_tolerance);
|
|
|
|
break;
|
|
|
|
case PROP_FILE_SIZE:
|
|
|
|
g_value_set_uint64 (value, fc->file_size);
|
|
|
|
break;
|
|
|
|
case PROP_FILE_SIZE_TOLERANCE:
|
|
|
|
g_value_set_uint64 (value, fc->file_size_tolerance);
|
|
|
|
break;
|
|
|
|
case PROP_SEEKABLE:
|
|
|
|
g_value_set_boolean (value, fc->seekable);
|
|
|
|
break;
|
2013-08-07 14:31:04 +00:00
|
|
|
case PROP_TEST_PLAYBACK:
|
|
|
|
g_value_set_boolean (value, fc->test_playback);
|
|
|
|
break;
|
2013-08-05 17:16:06 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
check_file_size (GstQaFileChecker * fc)
|
|
|
|
{
|
|
|
|
GStatBuf statbuf;
|
|
|
|
gchar *filepath;
|
|
|
|
guint64 size = 0;
|
|
|
|
gboolean ret = TRUE;
|
2013-08-06 13:36:02 +00:00
|
|
|
GError *err;
|
2013-08-05 17:16:06 +00:00
|
|
|
|
2013-08-06 13:36:02 +00:00
|
|
|
filepath = g_filename_from_uri (fc->uri, NULL, &err);
|
2013-08-05 17:16:06 +00:00
|
|
|
if (!filepath) {
|
2013-08-06 13:36:02 +00:00
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_NOT_FOUND,
|
|
|
|
"Failed to get filepath from uri %s. %s", fc->uri, err->message);
|
|
|
|
g_error_free (err);
|
2013-08-05 17:16:06 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_stat (filepath, &statbuf) == 0) {
|
|
|
|
size = statbuf.st_size;
|
2013-08-06 13:36:02 +00:00
|
|
|
} else {
|
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_NOT_FOUND,
|
|
|
|
"Failed to get file stats from uri %s", fc->uri);
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
2013-08-05 17:16:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (size == 0) {
|
2013-08-06 13:36:02 +00:00
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_SIZE_IS_ZERO, "File %s has size 0",
|
|
|
|
fc->uri);
|
2013-08-05 17:16:06 +00:00
|
|
|
ret = FALSE;
|
2013-08-06 21:17:39 +00:00
|
|
|
} else if (fc->file_size != 0
|
|
|
|
&& (size < fc->file_size - fc->file_size_tolerance
|
|
|
|
|| size > fc->file_size + fc->file_size_tolerance)) {
|
2013-08-06 13:36:02 +00:00
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_SIZE_INCORRECT,
|
|
|
|
"File %s has size %" G_GUINT64_FORMAT ", it was expected to have %"
|
2013-08-06 21:17:39 +00:00
|
|
|
G_GUINT64_FORMAT " (+-%" G_GUINT64_FORMAT ")", fc->uri, size,
|
|
|
|
fc->file_size, fc->file_size_tolerance);
|
2013-08-05 17:16:06 +00:00
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
g_free (filepath);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
check_file_duration (GstQaFileChecker * fc, GstDiscovererInfo * info)
|
|
|
|
{
|
|
|
|
GstClockTime real_duration;
|
|
|
|
|
|
|
|
if (!GST_CLOCK_TIME_IS_VALID (fc->duration))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
real_duration = gst_discoverer_info_get_duration (info);
|
|
|
|
if (real_duration < fc->duration - fc->duration_tolerance ||
|
|
|
|
real_duration > fc->duration + fc->duration_tolerance) {
|
2013-08-06 13:36:02 +00:00
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_SIZE_INCORRECT,
|
|
|
|
"File %s has duration %" GST_TIME_FORMAT ", it was expected to have %"
|
|
|
|
GST_TIME_FORMAT " (+-%" GST_TIME_FORMAT ")",
|
|
|
|
fc->uri, GST_TIME_ARGS (real_duration), GST_TIME_ARGS (fc->duration),
|
|
|
|
GST_TIME_ARGS (fc->duration_tolerance));
|
2013-08-05 17:16:06 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
check_seekable (GstQaFileChecker * fc, GstDiscovererInfo * info)
|
|
|
|
{
|
|
|
|
gboolean real_seekable;
|
|
|
|
|
|
|
|
real_seekable = gst_discoverer_info_get_seekable (info);
|
|
|
|
if (real_seekable != fc->seekable) {
|
2013-08-06 13:36:02 +00:00
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_SEEKABLE_INCORRECT,
|
|
|
|
"File was expected to %s be seekable, but it %s",
|
|
|
|
fc->seekable ? "" : "not", real_seekable ? "is" : "isn't");
|
2013-08-05 17:16:06 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-08-06 21:17:39 +00:00
|
|
|
static inline gboolean
|
|
|
|
_gst_caps_can_intersect_safe (const GstCaps * a, const GstCaps * b)
|
|
|
|
{
|
|
|
|
if (a == b)
|
|
|
|
return TRUE;
|
|
|
|
if ((a == NULL) || (b == NULL))
|
|
|
|
return FALSE;
|
|
|
|
return gst_caps_can_intersect (a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
GstEncodingProfile *profile;
|
|
|
|
gint count;
|
|
|
|
} ExpectedStream;
|
|
|
|
|
|
|
|
#define SET_MESSAGE(placeholder, msg) \
|
|
|
|
G_STMT_START { \
|
|
|
|
if (placeholder) { \
|
|
|
|
*placeholder = msg; \
|
|
|
|
} \
|
|
|
|
} G_STMT_END
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
compare_encoding_profile_with_discoverer_stream (GstQaFileChecker * fc,
|
|
|
|
GstEncodingProfile * prof, GstDiscovererStreamInfo * stream, gchar ** msg);
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
compare_container_profile_with_container_discoverer_stream (GstQaFileChecker *
|
|
|
|
fc, GstEncodingContainerProfile * prof, GstDiscovererContainerInfo * stream,
|
|
|
|
gchar ** msg)
|
|
|
|
{
|
|
|
|
ExpectedStream *expected_streams = NULL;
|
|
|
|
GList *container_streams;
|
|
|
|
const GList *profile_iter;
|
|
|
|
const GList *streams_iter;
|
|
|
|
gint i;
|
|
|
|
gint expected_count = g_list_length ((GList *)
|
|
|
|
gst_encoding_container_profile_get_profiles (prof));
|
|
|
|
gboolean ret = TRUE;
|
|
|
|
|
|
|
|
container_streams = gst_discoverer_container_info_get_streams (stream);
|
|
|
|
|
|
|
|
if (expected_count == 0) {
|
|
|
|
if (g_list_length (container_streams) != 0) {
|
|
|
|
SET_MESSAGE (msg,
|
|
|
|
g_strdup_printf
|
|
|
|
("No streams expected on this container, but found %u",
|
|
|
|
g_list_length (container_streams)));
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize expected streams data */
|
|
|
|
expected_streams = g_malloc0 (sizeof (ExpectedStream) * expected_count);
|
|
|
|
for (i = 0, profile_iter = gst_encoding_container_profile_get_profiles (prof);
|
|
|
|
profile_iter; profile_iter = g_list_next (profile_iter), i++) {
|
|
|
|
GstEncodingProfile *prof = profile_iter->data;
|
|
|
|
ExpectedStream *expected = &(expected_streams[i]);
|
|
|
|
|
|
|
|
expected->profile = prof;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* look for the streams on discoverer info */
|
|
|
|
for (streams_iter = container_streams; streams_iter;
|
|
|
|
streams_iter = g_list_next (streams_iter)) {
|
|
|
|
GstDiscovererStreamInfo *info = streams_iter->data;
|
|
|
|
gboolean found = FALSE;
|
|
|
|
for (i = 0; i < expected_count; i++) {
|
|
|
|
ExpectedStream *expected = &(expected_streams[i]);
|
|
|
|
|
|
|
|
if (compare_encoding_profile_with_discoverer_stream (fc,
|
|
|
|
expected->profile, info, NULL)) {
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
GstCaps *caps = gst_discoverer_stream_info_get_caps (info);
|
|
|
|
gchar *caps_str = gst_caps_to_string (caps);
|
|
|
|
SET_MESSAGE (msg,
|
|
|
|
g_strdup_printf ("Stream with caps '%s' wasn't found on file",
|
|
|
|
caps_str));
|
|
|
|
g_free (caps_str);
|
|
|
|
gst_caps_unref (caps);
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if all expected streams are present */
|
|
|
|
for (i = 0; i < expected_count; i++) {
|
|
|
|
ExpectedStream *expected = &(expected_streams[i]);
|
|
|
|
guint presence = gst_encoding_profile_get_presence (expected->profile);
|
|
|
|
|
|
|
|
if (presence == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (presence != expected->count) {
|
|
|
|
gchar *caps_str =
|
|
|
|
gst_caps_to_string (gst_encoding_profile_get_format
|
|
|
|
(expected->profile));
|
|
|
|
SET_MESSAGE (msg,
|
|
|
|
g_strdup_printf ("Stream from profile %s (with caps '%s"
|
|
|
|
"' has presence %u but the number of streams found was %d",
|
|
|
|
gst_encoding_profile_get_name (expected->profile), caps_str,
|
|
|
|
presence, expected->count));
|
|
|
|
g_free (caps_str);
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
g_free (expected_streams);
|
|
|
|
gst_discoverer_stream_info_list_free (container_streams);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
compare_encoding_profile_with_discoverer_stream (GstQaFileChecker * fc,
|
|
|
|
GstEncodingProfile * prof, GstDiscovererStreamInfo * stream, gchar ** msg)
|
|
|
|
{
|
|
|
|
gboolean ret = TRUE;
|
|
|
|
GstCaps *caps = NULL;
|
|
|
|
const GstCaps *profile_caps;
|
2013-08-06 22:39:58 +00:00
|
|
|
const GstCaps *restriction_caps;
|
|
|
|
|
|
|
|
caps = gst_discoverer_stream_info_get_caps (stream);
|
|
|
|
profile_caps = gst_encoding_profile_get_format (prof);
|
|
|
|
restriction_caps = gst_encoding_profile_get_restriction (prof);
|
|
|
|
|
|
|
|
/* TODO need to consider profile caps restrictions */
|
|
|
|
if (!_gst_caps_can_intersect_safe (caps, profile_caps)) {
|
|
|
|
gchar *caps_str = gst_caps_to_string (caps);
|
|
|
|
gchar *profile_caps_str = gst_caps_to_string (profile_caps);
|
|
|
|
SET_MESSAGE (msg, g_strdup_printf ("Caps '%s' didn't match profile '%s'",
|
|
|
|
profile_caps_str, caps_str));
|
|
|
|
g_free (caps_str);
|
|
|
|
g_free (profile_caps_str);
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (restriction_caps) {
|
|
|
|
GstStructure *structure;
|
|
|
|
gint i;
|
|
|
|
gboolean found = FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < gst_caps_get_size (restriction_caps); i++) {
|
|
|
|
structure = gst_caps_get_structure (restriction_caps, i);
|
|
|
|
structure = gst_structure_copy (structure);
|
|
|
|
gst_structure_set_name (structure,
|
|
|
|
gst_structure_get_name (gst_caps_get_structure (caps, 0)));
|
|
|
|
if (gst_structure_can_intersect (structure, gst_caps_get_structure (caps,
|
|
|
|
0))) {
|
|
|
|
gst_structure_free (structure);
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gst_structure_free (structure);
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
gchar *caps_str = gst_caps_to_string (caps);
|
|
|
|
gchar *restriction_caps_str = gst_caps_to_string (restriction_caps);
|
|
|
|
SET_MESSAGE (msg,
|
|
|
|
g_strdup_printf ("Caps restriction '%s' wasn't respected on file "
|
|
|
|
"with caps '%s'", restriction_caps_str, caps_str));
|
|
|
|
g_free (caps_str);
|
|
|
|
g_free (restriction_caps_str);
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
2013-08-06 21:17:39 +00:00
|
|
|
|
|
|
|
if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
|
|
|
|
if (GST_IS_DISCOVERER_CONTAINER_INFO (stream)) {
|
|
|
|
ret =
|
|
|
|
ret & compare_container_profile_with_container_discoverer_stream (fc,
|
|
|
|
(GstEncodingContainerProfile *) prof,
|
|
|
|
(GstDiscovererContainerInfo *) stream, msg);
|
|
|
|
} else {
|
|
|
|
SET_MESSAGE (msg,
|
|
|
|
g_strdup_printf ("Expected container profile but found stream of %s",
|
|
|
|
gst_discoverer_stream_info_get_stream_type_nick (stream)));
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (GST_IS_ENCODING_VIDEO_PROFILE (prof)) {
|
|
|
|
if (!GST_IS_DISCOVERER_VIDEO_INFO (stream)) {
|
|
|
|
SET_MESSAGE (msg,
|
|
|
|
g_strdup_printf ("Expected video profile but found stream of %s",
|
|
|
|
gst_discoverer_stream_info_get_stream_type_nick (stream)));
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (GST_IS_ENCODING_AUDIO_PROFILE (prof)) {
|
|
|
|
if (!GST_IS_DISCOVERER_AUDIO_INFO (stream)) {
|
|
|
|
SET_MESSAGE (msg,
|
|
|
|
g_strdup_printf ("Expected audio profile but found stream of %s",
|
|
|
|
gst_discoverer_stream_info_get_stream_type_nick (stream)));
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
g_assert_not_reached ();
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (caps)
|
|
|
|
gst_caps_unref (caps);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-05 17:16:06 +00:00
|
|
|
static gboolean
|
|
|
|
check_encoding_profile (GstQaFileChecker * fc, GstDiscovererInfo * info)
|
|
|
|
{
|
|
|
|
GstEncodingProfile *profile = fc->profile;
|
2013-08-06 21:17:39 +00:00
|
|
|
GstDiscovererStreamInfo *stream;
|
2013-08-05 17:16:06 +00:00
|
|
|
gboolean ret = TRUE;
|
2013-08-06 21:17:39 +00:00
|
|
|
gchar *msg = NULL;
|
2013-08-05 17:16:06 +00:00
|
|
|
|
|
|
|
if (profile == NULL)
|
|
|
|
return TRUE;
|
|
|
|
|
2013-08-06 21:17:39 +00:00
|
|
|
stream = gst_discoverer_info_get_stream_info (info);
|
2013-08-05 17:16:06 +00:00
|
|
|
|
2013-08-06 21:17:39 +00:00
|
|
|
if (!compare_encoding_profile_with_discoverer_stream (fc, fc->profile, stream,
|
|
|
|
&msg)) {
|
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_PROFILE_INCORRECT, msg);
|
|
|
|
g_free (msg);
|
2013-08-05 17:16:06 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 21:17:39 +00:00
|
|
|
gst_discoverer_stream_info_unref (stream);
|
2013-08-05 17:16:06 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-07 14:31:04 +00:00
|
|
|
static gboolean
|
|
|
|
check_playback (GstQaFileChecker * fc)
|
|
|
|
{
|
|
|
|
GstElement *playbin;
|
|
|
|
GstElement *videosink, *audiosink;
|
|
|
|
GstBus *bus;
|
|
|
|
GstMessage *msg;
|
|
|
|
gboolean ret = TRUE;
|
|
|
|
|
|
|
|
if (!fc->test_playback)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
playbin = gst_element_factory_make ("playbin2", "fc-playbin");
|
|
|
|
videosink = gst_element_factory_make ("fakesink", "fc-videosink");
|
|
|
|
audiosink = gst_element_factory_make ("fakesink", "fc-audiosink");
|
|
|
|
|
|
|
|
if (!playbin || !videosink || !audiosink) {
|
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_MISSING_PLUGIN, "file check requires "
|
|
|
|
"playbin2 and fakesink to be available");
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_set (playbin, "video-sink", videosink, "audio-sink", audiosink,
|
|
|
|
"uri", fc->uri, NULL);
|
|
|
|
|
|
|
|
if (gst_element_set_state (playbin,
|
|
|
|
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
|
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_PLAYBACK_START_FAILURE,
|
|
|
|
"Failed to " "change pipeline state to playing");
|
|
|
|
ret = FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus = gst_pipeline_get_bus (GST_PIPELINE (playbin));
|
|
|
|
msg =
|
|
|
|
gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
|
|
|
|
GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
|
|
|
|
if (msg) {
|
|
|
|
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
|
|
|
|
/* all good */
|
|
|
|
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
|
|
|
|
GError *error = NULL;
|
|
|
|
gchar *debug = NULL;
|
|
|
|
|
|
|
|
gst_message_parse_error (msg, &error, &debug);
|
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_PLAYBACK_ERROR, "File %s failed "
|
|
|
|
"during playback. Error: %s : %s", fc->uri, error->message, debug);
|
|
|
|
g_error_free (error);
|
|
|
|
g_free (debug);
|
|
|
|
|
|
|
|
ret = FALSE;
|
|
|
|
} else {
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
gst_message_unref (msg);
|
|
|
|
} else {
|
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_PLAYBACK_ERROR, "File playback "
|
|
|
|
"finished unexpectedly");
|
|
|
|
}
|
|
|
|
gst_object_unref (bus);
|
|
|
|
|
|
|
|
end:
|
|
|
|
gst_element_set_state (playbin, GST_STATE_NULL);
|
|
|
|
gst_object_unref (playbin);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2013-08-05 17:16:06 +00:00
|
|
|
|
|
|
|
gboolean
|
|
|
|
gst_qa_file_checker_run (GstQaFileChecker * fc)
|
|
|
|
{
|
|
|
|
GError *err = NULL;
|
|
|
|
GstDiscovererInfo *info;
|
|
|
|
GstDiscoverer *discoverer = gst_discoverer_new (GST_SECOND * 60, &err);
|
|
|
|
gboolean ret = TRUE;
|
|
|
|
|
2013-08-06 13:36:02 +00:00
|
|
|
g_return_val_if_fail (fc->uri != NULL, FALSE);
|
|
|
|
|
2013-08-05 17:16:06 +00:00
|
|
|
if (!discoverer) {
|
2013-08-06 13:36:02 +00:00
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_ALLOCATION_FAILURE,
|
|
|
|
"Failed to create GstDiscoverer");
|
2013-08-05 17:16:06 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
info = gst_discoverer_discover_uri (discoverer, fc->uri, &err);
|
|
|
|
|
|
|
|
if (gst_discoverer_info_get_result (info) != GST_DISCOVERER_OK) {
|
2013-08-06 13:36:02 +00:00
|
|
|
GST_QA_REPORT (fc, GST_QA_ISSUE_ID_FILE_CHECK_FAILURE,
|
|
|
|
"Discoverer failed to discover the file, result: %d",
|
|
|
|
gst_discoverer_info_get_result (info));
|
2013-08-05 17:16:06 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = check_file_size (fc) & ret;
|
|
|
|
ret = check_file_duration (fc, info) & ret;
|
|
|
|
ret = check_seekable (fc, info) & ret;
|
|
|
|
ret = check_encoding_profile (fc, info) & ret;
|
2013-08-07 14:31:04 +00:00
|
|
|
ret = check_playback (fc) & ret;
|
2013-08-05 17:16:06 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|