2014-01-08 08:49:38 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
#
|
|
|
|
# Copyright (c) 2013,Thibault Saunier <thibault.saunier@collabora.com>
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU Lesser 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 program 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
|
|
|
|
# Lesser General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
|
|
# License along with this program; if not, write to the
|
|
|
|
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
# Boston, MA 02110-1301, USA.
|
|
|
|
import os
|
|
|
|
import urlparse
|
2014-01-09 08:14:27 +00:00
|
|
|
import subprocess
|
2014-01-08 08:49:38 +00:00
|
|
|
import ConfigParser
|
2014-01-08 17:51:14 +00:00
|
|
|
from loggable import Loggable
|
2014-01-08 08:49:38 +00:00
|
|
|
|
2014-01-09 08:39:05 +00:00
|
|
|
from baseclasses import GstValidateTest, TestsManager
|
|
|
|
from utils import MediaFormatCombination, get_profile,\
|
|
|
|
path2url, get_current_position, get_current_size, \
|
2014-01-09 14:23:38 +00:00
|
|
|
DEFAULT_TIMEOUT, which, GST_SECOND, Result, \
|
|
|
|
compare_rendered_with_original
|
2014-01-08 08:49:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_GST_VALIDATE = "gst-validate-1.0"
|
2014-01-09 08:14:27 +00:00
|
|
|
DEFAULT_GST_VALIDATE_TRANSCODING = "gst-validate-transcoding-1.0"
|
2014-01-08 08:49:38 +00:00
|
|
|
DISCOVERER_COMMAND = ["gst-validate-media-check-1.0"]
|
|
|
|
MEDIA_INFO_EXT = "media_info"
|
|
|
|
STREAM_INFO = "stream_info"
|
|
|
|
|
2014-01-09 08:14:27 +00:00
|
|
|
SEEKING_REQUIERED_SCENARIO = ["seek_forward", "seek_backward", "scrub_forward_seeking"]
|
2014-01-08 08:49:38 +00:00
|
|
|
SPECIAL_PROTOCOLS = [("application/x-hls", "hls")]
|
|
|
|
|
2014-01-09 08:14:27 +00:00
|
|
|
PLAYBACK_TESTS = ["playbin uri=__uri__ audio_sink=autoaudiosink video_sink=autovideosink"]
|
|
|
|
COMBINATIONS = [
|
|
|
|
MediaFormatCombination("ogg", "vorbis", "theora"),
|
|
|
|
MediaFormatCombination("webm", "vorbis", "vp8"),
|
|
|
|
MediaFormatCombination("mp4", "mp3", "h264"),
|
|
|
|
MediaFormatCombination("mkv", "vorbis", "h264")]
|
2014-01-08 08:49:38 +00:00
|
|
|
|
|
|
|
|
2014-01-09 08:14:27 +00:00
|
|
|
class GstValidateLaunchTest(GstValidateTest):
|
|
|
|
def __init__(self, classname, options, reporter, pipeline_desc,
|
|
|
|
timeout=DEFAULT_TIMEOUT, scenario=None, file_infos=None):
|
|
|
|
super(GstValidateLaunchTest, self).__init__(DEFAULT_GST_VALIDATE, classname,
|
2014-01-09 14:20:46 +00:00
|
|
|
options, reporter,
|
2014-01-09 08:14:27 +00:00
|
|
|
scenario=scenario,)
|
2014-01-08 08:49:38 +00:00
|
|
|
self.pipeline_desc = pipeline_desc
|
|
|
|
self.file_infos = file_infos
|
|
|
|
|
|
|
|
def build_arguments(self):
|
2014-01-09 08:14:27 +00:00
|
|
|
GstValidateTest.build_arguments(self)
|
2014-01-08 08:49:38 +00:00
|
|
|
self.add_arguments(self.pipeline_desc)
|
|
|
|
|
2014-01-09 08:14:27 +00:00
|
|
|
def get_current_value(self):
|
|
|
|
return get_current_position(self)
|
|
|
|
|
|
|
|
|
|
|
|
class GstValidateTranscodingTest(GstValidateTest):
|
|
|
|
def __init__(self, classname, options, reporter,
|
|
|
|
combination, uri, file_infos):
|
|
|
|
|
|
|
|
super(GstValidateTranscodingTest, self).__init__(
|
|
|
|
DEFAULT_GST_VALIDATE_TRANSCODING, classname,
|
2014-01-09 14:20:46 +00:00
|
|
|
options, reporter, scenario=None)
|
2014-01-09 14:24:05 +00:00
|
|
|
self.file_infos = file_infos
|
2014-01-09 08:14:27 +00:00
|
|
|
self.uri = uri
|
|
|
|
self.combination = combination
|
|
|
|
self.dest_file = ""
|
|
|
|
|
|
|
|
def set_rendering_info(self):
|
|
|
|
self.dest_file = os.path.join(self.options.dest,
|
|
|
|
os.path.basename(self.uri) +
|
|
|
|
'-' + self.combination.acodec +
|
|
|
|
self.combination.vcodec + '.' +
|
|
|
|
self.combination.container)
|
|
|
|
if urlparse.urlparse(self.dest_file).scheme == "":
|
|
|
|
self.dest_file = path2url(self.dest_file)
|
|
|
|
|
|
|
|
profile = get_profile(self.combination)
|
|
|
|
self.add_arguments("-o", profile)
|
|
|
|
|
|
|
|
def build_arguments(self):
|
|
|
|
self.set_rendering_info()
|
|
|
|
self.add_arguments(self.uri, self.dest_file)
|
|
|
|
|
|
|
|
def get_current_value(self):
|
|
|
|
return get_current_size(self)
|
|
|
|
|
2014-01-09 14:23:38 +00:00
|
|
|
def check_results(self):
|
|
|
|
if self.process.returncode == 0:
|
|
|
|
orig_duration = long(self.file_infos.get("media-info", "file-duration"))
|
|
|
|
res, msg = compare_rendered_with_original(orig_duration, self.dest_file)
|
|
|
|
self.set_result(res, msg)
|
|
|
|
else:
|
|
|
|
GstValidateTest.check_results(self)
|
|
|
|
|
2014-01-08 08:49:38 +00:00
|
|
|
|
2014-01-08 17:51:14 +00:00
|
|
|
class GstValidateManager(TestsManager, Loggable):
|
2014-01-08 08:49:38 +00:00
|
|
|
|
|
|
|
name = "validate"
|
|
|
|
|
|
|
|
def __init__(self):
|
2014-01-08 17:51:14 +00:00
|
|
|
TestsManager.__init__(self)
|
|
|
|
Loggable.__init__(self)
|
2014-01-08 08:49:38 +00:00
|
|
|
self._uris = []
|
|
|
|
|
2014-01-09 14:17:53 +00:00
|
|
|
def init(self):
|
|
|
|
if which(DEFAULT_GST_VALIDATE) and which(DEFAULT_GST_VALIDATE_TRANSCODING):
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
2014-01-08 08:49:38 +00:00
|
|
|
def add_options(self, group):
|
|
|
|
group.add_option("-c", "--check-discovering", dest="check_discovering",
|
|
|
|
default=False, action="store_true",
|
|
|
|
help="Whether to check discovering results using %s"
|
|
|
|
% DISCOVERER_COMMAND[0])
|
|
|
|
|
|
|
|
def list_tests(self):
|
2014-01-10 14:27:46 +00:00
|
|
|
SCENARIOS = ["none", "simple_backward",
|
|
|
|
"fast_forward", "seek_forward",
|
|
|
|
"seek_backward", "scrub_forward_seeking"]
|
|
|
|
|
2014-01-09 08:14:27 +00:00
|
|
|
for test_pipeline in PLAYBACK_TESTS:
|
|
|
|
name = "validate.playback"
|
|
|
|
for scenario in SCENARIOS:
|
|
|
|
self._add_playback_test(name, scenario, test_pipeline)
|
|
|
|
|
|
|
|
for uri, config in self._list_uris():
|
2014-01-10 11:41:30 +00:00
|
|
|
if config.getboolean("media-info", "is-image") is True:
|
|
|
|
continue
|
2014-01-09 08:14:27 +00:00
|
|
|
for comb in COMBINATIONS:
|
2014-01-10 10:35:47 +00:00
|
|
|
classname = "validate.transcode.to_%s" % (str(comb).replace(' ', '_'))
|
2014-01-09 08:14:27 +00:00
|
|
|
self.tests.append(GstValidateTranscodingTest(classname,
|
2014-01-09 14:23:38 +00:00
|
|
|
self.options,
|
|
|
|
self.reporter,
|
|
|
|
comb, uri,
|
|
|
|
config))
|
2014-01-08 08:49:38 +00:00
|
|
|
|
|
|
|
def _check_discovering_info(self, media_info, uri=None):
|
2014-01-08 17:51:14 +00:00
|
|
|
self.debug("Checking %s", media_info)
|
2014-01-08 08:49:38 +00:00
|
|
|
config = ConfigParser.ConfigParser()
|
|
|
|
f = open(media_info)
|
|
|
|
config.readfp(f)
|
|
|
|
try:
|
|
|
|
# Just testing that the vairous mandatory infos are present
|
|
|
|
caps = config.get("media-info", "caps")
|
|
|
|
config.get("media-info", "file-duration")
|
|
|
|
config.get("media-info", "seekable")
|
|
|
|
if uri is None:
|
|
|
|
uri = config.get("file-info", "uri")
|
|
|
|
config.set("file-info", "protocol", urlparse.urlparse(uri).scheme)
|
|
|
|
for caps2, prot in SPECIAL_PROTOCOLS:
|
|
|
|
if caps2 == caps:
|
|
|
|
config.set("file-info", "protocol", prot)
|
|
|
|
break
|
|
|
|
self._uris.append((uri, config))
|
|
|
|
except ConfigParser.NoOptionError as e:
|
2014-01-08 17:51:14 +00:00
|
|
|
self.debug("Exception: %s for %s", e, media_info)
|
2014-01-08 08:49:38 +00:00
|
|
|
pass
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
def _discover_file(self, uri, fpath):
|
|
|
|
try:
|
|
|
|
media_info = "%s.%s" % (fpath, MEDIA_INFO_EXT)
|
|
|
|
args = list(DISCOVERER_COMMAND)
|
|
|
|
args.append(uri)
|
|
|
|
if os.path.isfile(media_info):
|
|
|
|
if self.options.check_discovering is False:
|
|
|
|
self._check_discovering_info(media_info, uri)
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
args.extend(["--expected-results", media_info])
|
|
|
|
else:
|
|
|
|
args.extend(["--output-file", media_info])
|
|
|
|
|
|
|
|
subprocess.check_output(args)
|
|
|
|
self._check_discovering_info(media_info, uri)
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2014-01-08 17:51:14 +00:00
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
self.debug("Exception: %s", e)
|
2014-01-08 08:49:38 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
def _list_uris(self):
|
|
|
|
if self._uris:
|
|
|
|
return self._uris
|
|
|
|
|
|
|
|
if not self.args:
|
|
|
|
if isinstance(self.options.paths, str):
|
|
|
|
self.options.paths = [os.path.join(self.options.paths)]
|
|
|
|
|
|
|
|
for path in self.options.paths:
|
|
|
|
for root, dirs, files in os.walk(path):
|
|
|
|
for f in files:
|
2014-01-08 17:51:14 +00:00
|
|
|
fpath = os.path.join(path, root, f)
|
2014-01-08 08:49:38 +00:00
|
|
|
if os.path.isdir(fpath) or fpath.endswith(MEDIA_INFO_EXT):
|
|
|
|
continue
|
|
|
|
elif fpath.endswith(STREAM_INFO):
|
|
|
|
self._check_discovering_info(fpath)
|
|
|
|
else:
|
|
|
|
self._discover_file(path2url(fpath), fpath)
|
|
|
|
|
2014-01-08 17:51:14 +00:00
|
|
|
self.debug("Uris found: %s", self._uris)
|
2014-01-08 08:49:38 +00:00
|
|
|
|
|
|
|
return self._uris
|
|
|
|
|
|
|
|
def _get_fname(self, name, scenario, protocol=None):
|
|
|
|
if protocol is not None:
|
|
|
|
name = "%s.%s" % (name, protocol)
|
|
|
|
|
|
|
|
if scenario is not None and scenario.lower() != "none":
|
|
|
|
return "%s.%s" % (name, scenario)
|
|
|
|
|
|
|
|
return name
|
|
|
|
|
2014-01-08 17:51:14 +00:00
|
|
|
def _add_playback_test(self, name, scenario, pipe):
|
2014-01-08 08:49:38 +00:00
|
|
|
if self.options.mute:
|
|
|
|
if "autovideosink" in pipe:
|
|
|
|
pipe = pipe.replace("autovideosink", "fakesink")
|
|
|
|
if "autoaudiosink" in pipe:
|
|
|
|
pipe = pipe.replace("autoaudiosink", "fakesink")
|
|
|
|
|
|
|
|
if "__uri__" in pipe:
|
|
|
|
for uri, config in self._list_uris():
|
|
|
|
npipe = pipe
|
|
|
|
if scenario in SEEKING_REQUIERED_SCENARIO:
|
|
|
|
if config.getboolean("media-info", "seekable") is False:
|
2014-01-08 17:51:14 +00:00
|
|
|
self.debug("Do not run %s as %s does not support seeking",
|
2014-01-09 14:23:38 +00:00
|
|
|
scenario, uri)
|
2014-01-08 08:49:38 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
if self.options.mute:
|
|
|
|
# In case of seeking we need to make sure the pipeline
|
|
|
|
# is run sync, otherwize some tests will fail
|
|
|
|
npipe = pipe.replace("fakesink", "'fakesink sync=true'")
|
|
|
|
|
|
|
|
fname = "%s.%s" % (self._get_fname(name, scenario,
|
|
|
|
config.get("file-info", "protocol")),
|
|
|
|
os.path.basename(uri).replace(".", "_"))
|
2014-01-08 17:51:14 +00:00
|
|
|
self.debug("Adding: %s", fname)
|
2014-01-08 08:49:38 +00:00
|
|
|
|
2014-01-09 08:14:27 +00:00
|
|
|
self.tests.append(GstValidateLaunchTest(fname,
|
2014-01-08 08:49:38 +00:00
|
|
|
self.options,
|
|
|
|
self.reporter,
|
|
|
|
npipe.replace("__uri__", uri),
|
2014-01-09 08:14:27 +00:00
|
|
|
scenario=scenario,
|
|
|
|
file_infos=config)
|
2014-01-08 08:49:38 +00:00
|
|
|
)
|
|
|
|
else:
|
2014-01-08 17:51:14 +00:00
|
|
|
self.debug("Adding: %s", name)
|
2014-01-09 08:14:27 +00:00
|
|
|
self.tests.append(GstValidateLaunchTest(self._get_fname(fname, scenario),
|
2014-01-08 08:49:38 +00:00
|
|
|
self.options,
|
|
|
|
self.reporter,
|
|
|
|
pipe,
|
2014-01-09 08:14:27 +00:00
|
|
|
scenario=scenario))
|