Create GetStream

Created GetStream, a function used to get the stream descriptor for a
single stream. This allows for finer control over the stream compared
to the batch GetStreams.

Added "trick_play_factor" to GetStream so that the trick play factor
did not have to be added the stream descriptor value and "injected"
into the stream.

Change-Id: Ifbde64b961a673aafa5f6d21f33a68d02fb46610
This commit is contained in:
KongQun Yang 2018-02-07 13:46:11 -08:00 committed by Aaron Vaage
parent 9547000eb6
commit b81b832522
2 changed files with 177 additions and 87 deletions

View File

@ -42,6 +42,23 @@ class StreamDescriptor(object):
return self.buffer return self.buffer
def GetExtension(stream_descriptor, output_format):
# TODO(rkuroiwa): Support ttml.
if stream_descriptor == 'text':
return 'vtt'
if output_format:
return output_format
# Default to mp4.
return 'mp4'
def GetSegmentedExtension(base_extension):
if base_extension == 'mp4':
return 'm4s'
return base_extension
class PackagerAppTest(unittest.TestCase): class PackagerAppTest(unittest.TestCase):
def setUp(self): def setUp(self):
@ -54,7 +71,7 @@ class PackagerAppTest(unittest.TestCase):
self.output_prefix = os.path.join(self.tmp_dir, 'output') self.output_prefix = os.path.join(self.tmp_dir, 'output')
self.mpd_output = self.output_prefix + '.mpd' self.mpd_output = self.output_prefix + '.mpd'
self.hls_master_playlist_output = self.output_prefix + '.m3u8' self.hls_master_playlist_output = self.output_prefix + '.m3u8'
self.output = None self.output = []
# Test variables. # Test variables.
self.encryption_key_id = '31323334353637383930313233343536' self.encryption_key_id = '31323334353637383930313233343536'
@ -76,79 +93,124 @@ class PackagerAppTest(unittest.TestCase):
if test_env.options.remove_temp_files_after_test: if test_env.options.remove_temp_files_after_test:
shutil.rmtree(self.tmp_dir) shutil.rmtree(self.tmp_dir)
def _GetStreams(self, def _GetStream(self,
stream_descriptors, descriptor,
language_override=None, language=None,
output_format=None, output_format=None,
segmented=False, segmented=False,
hls=False, hls=False,
test_files=None): trick_play_factor=None,
if test_files is None: drm_label=None,
test_files = ['bear-640x360.mp4'] skip_encryption=None,
streams = [] test_file=None,
if not self.output: test_file_index=None):
self.output = [] """Get a stream descriptor as a string.
for test_file_index, test_file_name in enumerate(test_files):
test_file = os.path.join(self.test_data_dir, test_file_name)
for stream_descriptor in stream_descriptors:
if len(test_files) == 1:
output_prefix = '%s_%s' % (self.output_prefix, stream_descriptor)
else:
output_prefix = '%s_%d_%s' % (self.output_prefix, test_file_index,
stream_descriptor)
# Replace ',', '=' with '_' to make it more like a filename, also
# avoid potential illegal charactors for a filename.
for ch in [',', '=']:
output_prefix = output_prefix.replace(ch, '_')
stream = StreamDescriptor(test_file) Create the stream descriptor as a string for the given parameters so that
stream.Append('stream', stream_descriptor) it can be passed as an input parameter to the packager.
base_ext = self._GetExtension(stream_descriptor, output_format)
segment_ext = self._GetSegmentedExtension(base_ext)
requires_init_segment = segmented and base_ext not in ['ts', 'vtt'] Args:
descriptor: The name of the stream in the container that should be used as
input for the output.
language: The language override for the input stream.
output_format: Specify the format for the output.
segmented: Should the output use a segmented formatted. This will affect
the output extensions and manifests.
hls: Should the output be for an HLS manifest.
trick_play_factor: Signals the stream is to be used for a trick play
stream and which key frames to use. A trick play factor of 0 is the
same as not specifying a trick play factor.
drm_label: Sets the drm label for the stream.
skip_encryption: If set to true, the stream will not be encrypted.
test_file: Specify the input file to use. If the input file is not
specify, a default file will be used.
test_file_index: Specify the index of the input out of a group of input
files.
if requires_init_segment:
init_seg = '%s-init.%s' % (output_prefix, base_ext)
stream.Append('init_segment', init_seg)
if segmented: Returns:
seg_template = '%s-$Number$.%s' % (output_prefix, segment_ext) A string that makes up a single stream descriptor for input to the
stream.Append('segment_template', seg_template) packager.
"""
self.output.append(output_prefix) test_file = test_file or 'bear-640x360.mp4'
else: test_file = os.path.join(self.test_data_dir, test_file)
output = '%s.%s' % (output_prefix, base_ext)
stream.Append('output', output)
self.output.append(output)
if output_format: if test_file_index is None:
stream.Append('format', output_format) output_file = '%s_%s' % (self.output_prefix, descriptor)
if language_override: else:
stream.Append('lang', language_override) str_params = (self.output_prefix, test_file_index, descriptor)
if hls: output_file = '%s_%d_%s' % str_params
stream.Append('playlist_name', stream_descriptor + '.m3u8')
streams.append(str(stream)) if trick_play_factor:
output_file += '_trick_play_factor_%d' % trick_play_factor
return streams if skip_encryption:
output_file += '_skip_encryption'
stream = StreamDescriptor(test_file)
stream.Append('stream', descriptor)
base_ext = GetExtension(descriptor, output_format)
requires_init_segment = segmented and base_ext not in ['ts', 'vtt']
if requires_init_segment:
init_seg = '%s-init.%s' % (output_file, base_ext)
stream.Append('init_segment', init_seg)
if segmented:
segment_ext = GetSegmentedExtension(base_ext)
seg_template = '%s-$Number$.%s' % (output_file, segment_ext)
stream.Append('segment_template', seg_template)
else:
output_file = '%s.%s' % (output_file, base_ext)
stream.Append('output', output_file)
self.output.append(output_file)
def _GetExtension(self, stream_descriptor, output_format):
# TODO(vaage): Support ttml.
if stream_descriptor == 'text':
return 'vtt'
if output_format: if output_format:
return output_format stream.Append('format', output_format)
# Default to mp4.
return 'mp4'
def _GetSegmentedExtension(self, base_extension): if language:
if base_extension == 'mp4': stream.Append('lang', language)
return 'm4s'
return base_extension if hls:
stream.Append('playlist_name', descriptor + '.m3u8')
if trick_play_factor:
stream.Append('trick_play_factor', trick_play_factor)
if drm_label:
stream.Append('drm_label', drm_label)
if skip_encryption:
stream.Append('skip_encryption', 1)
return str(stream)
def _GetStreams(self, streams, test_files=None, **kwargs):
# Make sure there is a valid list that we can get the length from.
test_files = test_files or []
test_files_count = len(test_files)
out = []
if test_files_count == 0:
for stream in streams:
out.append(self._GetStream(stream, **kwargs))
elif test_files_count == 1:
for stream in streams:
out.append(self._GetStream(stream, test_file=test_files[0], **kwargs))
else:
for index, filename in enumerate(test_files):
for stream in streams:
out.append(self._GetStream(
stream, test_file_index=index, test_file=filename, **kwargs))
return out
def _GetFlags(self, def _GetFlags(self,
strip_parameter_set_nalus=True, strip_parameter_set_nalus=True,
@ -483,19 +545,27 @@ class PackagerFunctionalTest(PackagerAppTest):
self._DiffGold(self.mpd_output, 'bear-640x360-av-golden.mpd') self._DiffGold(self.mpd_output, 'bear-640x360-av-golden.mpd')
def testPackageAudioVideoWithTrickPlay(self): def testPackageAudioVideoWithTrickPlay(self):
self.assertPackageSuccess( streams = [
self._GetStreams(['audio', 'video', 'video,trick_play_factor=1']), self._GetStream('audio'),
self._GetFlags()) self._GetStream('video'),
self._GetStream('video', trick_play_factor=1),
]
self.assertPackageSuccess(streams, self._GetFlags())
self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-golden.mp4') self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-golden.mp4')
self._DiffGold(self.mpd_output, 'bear-640x360-av-trick-1-golden.mpd') self._DiffGold(self.mpd_output, 'bear-640x360-av-trick-1-golden.mpd')
def testPackageAudioVideoWithTwoTrickPlay(self): def testPackageAudioVideoWithTwoTrickPlay(self):
self.assertPackageSuccess( streams = [
self._GetStreams(['audio', 'video', 'video,trick_play_factor=1', self._GetStream('audio'),
'video,trick_play_factor=2']), self._GetStream('video'),
self._GetFlags()) self._GetStream('video', trick_play_factor=1),
self._GetStream('video', trick_play_factor=2),
]
self.assertPackageSuccess(streams, self._GetFlags())
self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-golden.mp4') self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-golden.mp4')
@ -504,10 +574,14 @@ class PackagerFunctionalTest(PackagerAppTest):
'bear-640x360-av-trick-1-trick-2-golden.mpd') 'bear-640x360-av-trick-1-trick-2-golden.mpd')
def testPackageAudioVideoWithTwoTrickPlayDecreasingRate(self): def testPackageAudioVideoWithTwoTrickPlayDecreasingRate(self):
self.assertPackageSuccess( streams = [
self._GetStreams(['audio', 'video', 'video,trick_play_factor=2', self._GetStream('audio'),
'video,trick_play_factor=1']), self._GetStream('video'),
self._GetFlags()) self._GetStream('video', trick_play_factor=2),
self._GetStream('video', trick_play_factor=1),
]
self.assertPackageSuccess(streams, self._GetFlags())
self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
self._DiffGold(self.output[2], 'bear-640x360-v-trick-2-golden.mp4') self._DiffGold(self.output[2], 'bear-640x360-v-trick-2-golden.mp4')
@ -519,7 +593,7 @@ class PackagerFunctionalTest(PackagerAppTest):
def testPackageAudioVideoWithLanguageOverride(self): def testPackageAudioVideoWithLanguageOverride(self):
self.assertPackageSuccess( self.assertPackageSuccess(
self._GetStreams(['audio', 'video'], language_override='por-BR'), self._GetStreams(['audio', 'video'], language='por-BR'),
self._GetFlags()) self._GetFlags())
self._DiffGold(self.output[0], 'bear-640x360-a-por-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-por-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
@ -527,7 +601,7 @@ class PackagerFunctionalTest(PackagerAppTest):
def testPackageAudioVideoWithLanguageOverrideWithSubtag(self): def testPackageAudioVideoWithLanguageOverrideWithSubtag(self):
self.assertPackageSuccess( self.assertPackageSuccess(
self._GetStreams(['audio', 'video'], language_override='por-BR'), self._GetStreams(['audio', 'video'], language='por-BR'),
self._GetFlags()) self._GetFlags())
self._DiffGold(self.output[0], 'bear-640x360-a-por-BR-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-por-BR-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
@ -798,9 +872,12 @@ class PackagerFunctionalTest(PackagerAppTest):
] ]
# DRM label 'MyVideo' is not defined, will fall back to the key for the # DRM label 'MyVideo' is not defined, will fall back to the key for the
# empty default label. # empty default label.
self.assertPackageSuccess( streams = [
self._GetStreams(['audio,drm_label=MyAudio', self._GetStream('audio', drm_label='MyAudio'),
'video,drm_label=MyVideo']), flags) self._GetStream('video', drm_label='MyVideo')
]
self.assertPackageSuccess(streams, flags)
self.encryption_key_id = audio_key_id self.encryption_key_id = audio_key_id
self.encryption_key = audio_key self.encryption_key = audio_key
@ -810,18 +887,27 @@ class PackagerFunctionalTest(PackagerAppTest):
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionOfOnlyVideoStream(self): def testPackageWithEncryptionOfOnlyVideoStream(self):
self.assertPackageSuccess( streams = [
self._GetStreams(['audio,skip_encryption=1', 'video']), self._GetStream('audio', skip_encryption=True),
self._GetFlags(encryption=True)) self._GetStream('video')
]
flags = self._GetFlags(encryption=True)
self.assertPackageSuccess(streams, flags)
self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')
self._DiffGold(self.mpd_output, 'bear-640x360-a-clear-v-cenc-golden.mpd') self._DiffGold(self.mpd_output, 'bear-640x360-a-clear-v-cenc-golden.mpd')
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionAndTrickPlay(self): def testPackageWithEncryptionAndTrickPlay(self):
self.assertPackageSuccess( streams = [
self._GetStreams(['audio', 'video', 'video,trick_play_factor=1']), self._GetStream('audio'),
self._GetFlags(encryption=True)) self._GetStream('video'),
self._GetStream('video', trick_play_factor=1),
]
self.assertPackageSuccess(streams, self._GetFlags(encryption=True))
self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')
self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-cenc-golden.mp4') self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-cenc-golden.mp4')
@ -833,10 +919,14 @@ class PackagerFunctionalTest(PackagerAppTest):
# TODO(hmchen): Add a test case that SD and HD AdapatationSet share one trick # TODO(hmchen): Add a test case that SD and HD AdapatationSet share one trick
# play stream. # play stream.
def testPackageWithEncryptionAndTwoTrickPlays(self): def testPackageWithEncryptionAndTwoTrickPlays(self):
self.assertPackageSuccess( streams = [
self._GetStreams(['audio', 'video', 'video,trick_play_factor=1', self._GetStream('audio'),
'video,trick_play_factor=2']), self._GetStream('video'),
self._GetFlags(encryption=True)) self._GetStream('video', trick_play_factor=1),
self._GetStream('video', trick_play_factor=2),
]
self.assertPackageSuccess(streams, self._GetFlags(encryption=True))
self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')
self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-cenc-golden.mp4') self._DiffGold(self.output[2], 'bear-640x360-v-trick-1-cenc-golden.mp4')

View File

@ -17,7 +17,7 @@
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true"> <AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="126510" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <Representation id="1" bandwidth="126510" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>output_audio_skip_encryption_1.mp4</BaseURL> <BaseURL>output_audio_skip_encryption.mp4</BaseURL>
<SegmentBase indexRange="757-824" timescale="44100"> <SegmentBase indexRange="757-824" timescale="44100">
<Initialization range="0-756"/> <Initialization range="0-756"/>
</SegmentBase> </SegmentBase>