#!/usr/bin/python # # Copyright 2014 Google Inc. All Rights Reserved. # # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file or at # https://developers.google.com/open-source/licenses/bsd """Tests utilizing the sample packager binary.""" import filecmp import os import platform import re import shutil import subprocess import tempfile import unittest import packager_app import test_env _TEST_FAILURE_COMMAND_LINE_MESSAGE = """ !!! To reproduce the failure, change the output files to an !!! !!! existing directory, e.g. output artifacts to current !!! !!! directory by removing /tmp/something/ in the following !!! !!! command line. !!! The test executed the following command line: """ class PackagerAppTest(unittest.TestCase): def setUp(self): self.packager = packager_app.PackagerApp() self.tmp_dir = tempfile.mkdtemp() self.test_data_dir = os.path.join(test_env.SRC_DIR, 'packager', 'media', 'test', 'data') self.golden_file_dir = os.path.join(test_env.SRC_DIR, 'packager', 'app', 'test', 'testdata') self.output_prefix = os.path.join(self.tmp_dir, 'output') self.mpd_output = self.output_prefix + '.mpd' self.hls_master_playlist_output = self.output_prefix + '.m3u8' self.output = None def tearDown(self): if test_env.options.remove_temp_files_after_test: shutil.rmtree(self.tmp_dir) def testVersion(self): self.assertRegexpMatches( self.packager.Version(), '^packager(.exe)? version ' r'((?P[\w\.]+)-)?(?P[a-f\d]+)-(debug|release)[\r\n]+.*$') def testDumpStreamInfo(self): test_file = os.path.join(self.test_data_dir, 'bear-640x360.mp4') stream_info = self.packager.DumpStreamInfo(test_file) expected_stream_info = ('Found 2 stream(s).\n' 'Stream [0] type: Video\n' ' codec_string: avc1.64001e\n' ' time_scale: 30000\n' ' duration: 82082 (2.7 seconds)\n' ' is_encrypted: false\n' ' codec: H264\n' ' width: 640\n' ' height: 360\n' ' pixel_aspect_ratio: 1:1\n' ' trick_play_factor: 0\n' ' nalu_length_size: 4\n\n' 'Stream [1] type: Audio\n' ' codec_string: mp4a.40.2\n' ' time_scale: 44100\n' ' duration: 121856 (2.8 seconds)\n' ' is_encrypted: false\n' ' codec: AAC\n' ' sample_bits: 16\n' ' num_channels: 2\n' ' sampling_frequency: 44100\n' ' language: und\n') stream_info = stream_info.replace('\r\n', '\n') self.assertIn(expected_stream_info, stream_info, '\nExpecting: \n %s\n\nBut seeing: \n%s' % (expected_stream_info, stream_info)) def testPackageFirstStream(self): self.packager.Package(self._GetStreams(['0']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-v-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-v-golden.mpd') def testPackageText(self): self.packager.Package( self._GetStreams(['text'], test_files=['subtitle-english.vtt']), self._GetFlags()) self._DiffGold(self.output[0], 'subtitle-english-golden.vtt') self._DiffGold(self.mpd_output, 'subtitle-english-vtt-golden.mpd') # Probably one of the most common scenarios is to package audio and video. def testPackageAudioVideo(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-golden.mpd') def testPackageAudioVideoWithTrickPlay(self): self.packager.Package( self._GetStreams(['audio', 'video', 'video,trick_play_factor=1']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-a-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.mpd_output, 'bear-640x360-av-trick-1-golden.mpd') def testPackageAudioVideoWithTwoTrickPlay(self): self.packager.Package( self._GetStreams(['audio', 'video', 'video,trick_play_factor=1', 'video,trick_play_factor=2']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-a-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[3], 'bear-640x360-v-trick-2-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-trick-1-trick-2-golden.mpd') def testPackageAudioVideoWithTwoTrickPlayDecreasingRate(self): self.packager.Package( self._GetStreams(['audio', 'video', 'video,trick_play_factor=2', 'video,trick_play_factor=1']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-a-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[3], 'bear-640x360-v-trick-1-golden.mp4') # Since the stream descriptors are sorted in packager app, a different # order of trick play factors gets the same mpd. self._DiffGold(self.mpd_output, 'bear-640x360-av-trick-1-trick-2-golden.mpd') def testPackageAudioVideoWithLanguageOverride(self): self.packager.Package( self._GetStreams(['audio', 'video'], language_override='por-BR'), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-a-por-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-por-golden.mpd') def testPackageAudioVideoWithLanguageOverrideWithSubtag(self): self.packager.Package( self._GetStreams(['audio', 'video'], language_override='por-BR'), self._GetFlags()) 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.mpd_output, 'bear-640x360-av-por-BR-golden.mpd') def testPackageAacHe(self): self.packager.Package( self._GetStreams( ['audio'], test_files=['bear-640x360-aac_he-silent_right.mp4']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-aac_he-silent_right-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-aac_he-silent_right-golden.mpd') # Package all video, audio, and text. def testPackageVideoAudioText(self): audio_video_streams = self._GetStreams(['audio', 'video']) text_stream = self._GetStreams(['text'], test_files=['subtitle-english.vtt']) self.packager.Package(audio_video_streams + text_stream, self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-a-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') self._DiffGold(self.output[2], 'subtitle-english-golden.vtt') self._DiffGold(self.mpd_output, 'bear-640x360-avt-golden.mpd') def testPackageAvcTs(self): # Currently we only support live packaging for ts. self.packager.Package( self._GetStreams(['audio', 'video'], output_format='ts', live=True, test_files=['bear-640x360.ts']), self._GetFlags(output_hls=True)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-golden', output_format='ts') self._DiffLiveGold(self.output[1], 'bear-640x360-v-golden', output_format='ts') self._DiffGold(self.hls_master_playlist_output, 'bear-640x360-av-master-golden.m3u8') self._DiffGold( os.path.join(self.tmp_dir, 'audio.m3u8'), 'bear-640x360-a-golden.m3u8') self._DiffGold( os.path.join(self.tmp_dir, 'video.m3u8'), 'bear-640x360-v-golden.m3u8') def testPackageVp8Webm(self): self.packager.Package( self._GetStreams(['video'], output_format='webm', test_files=['bear-640x360.webm']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-640x360-vp8-golden.webm') self._DiffGold(self.mpd_output, 'bear-640x360-vp8-webm-golden.mpd') def testPackageVp9Webm(self): self.packager.Package( self._GetStreams(['audio', 'video'], output_format='webm', test_files=['bear-320x240-vp9-opus.webm']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-320x240-opus-golden.webm') self._DiffGold(self.output[1], 'bear-320x240-vp9-golden.webm') self._DiffGold(self.mpd_output, 'bear-320x240-vp9-opus-webm-golden.mpd') def testPackageVp9WebmWithBlockgroup(self): self.packager.Package( self._GetStreams(['video'], output_format='webm', test_files=['bear-vp9-blockgroup.webm']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-vp9-blockgroup-golden.webm') def testPackageVorbisWebm(self): self.packager.Package( self._GetStreams(['audio'], output_format='webm', test_files=['bear-320x240-audio-only.webm']), self._GetFlags()) self._DiffGold(self.output[0], 'bear-320x240-vorbis-golden.webm') self._DiffGold(self.mpd_output, 'bear-320x240-vorbis-webm-golden.mpd') def testPackageWithEncryption(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True)) 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.mpd_output, 'bear-640x360-av-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionOfOnlyVideoStream(self): self.packager.Package( self._GetStreams(['audio,skip_encryption=1', 'video']), self._GetFlags(encryption=True)) self._DiffGold(self.output[0], 'bear-640x360-a-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._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionAndTrickPlay(self): self.packager.Package( self._GetStreams(['audio', 'video', 'video,trick_play_factor=1']), self._GetFlags(encryption=True)) 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[2], 'bear-640x360-v-trick-1-cenc-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-trick-1-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') self._VerifyDecryption(self.output[2], 'bear-640x360-v-trick-1-golden.mp4') # TODO(hmchen): Add a test case that SD and HD AdapatationSet share one trick # play stream. def testPackageWithEncryptionAndTwoTrickPlays(self): self.packager.Package( self._GetStreams(['audio', 'video', 'video,trick_play_factor=1', 'video,trick_play_factor=2']), self._GetFlags(encryption=True)) 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[2], 'bear-640x360-v-trick-1-cenc-golden.mp4') self._DiffGold(self.output[3], 'bear-640x360-v-trick-2-cenc-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-trick-1-trick-2-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') self._VerifyDecryption(self.output[2], 'bear-640x360-v-trick-1-golden.mp4') self._VerifyDecryption(self.output[3], 'bear-640x360-v-trick-2-golden.mp4') def testPackageWithEncryptionAndNoClearLead(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, clear_lead=0)) self._DiffGold(self.output[0], 'bear-640x360-a-cenc-no-clear-lead-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cenc-no-clear-lead-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-cenc-no-clear-lead-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionAndNoPsshInStream(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, include_pssh_in_stream=False)) self._DiffGold(self.output[0], 'bear-640x360-a-cenc-no-pssh-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cenc-no-pssh-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-cenc-no-pssh-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionCbc1(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, protection_scheme='cbc1')) self._DiffGold(self.output[0], 'bear-640x360-a-cbc1-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cbc1-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-cbc1-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionCens(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, protection_scheme='cens')) self._DiffGold(self.output[0], 'bear-640x360-a-cens-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cens-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-cens-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionCbcs(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, protection_scheme='cbcs')) self._DiffGold(self.output[0], 'bear-640x360-a-cbcs-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cbcs-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-av-cbcs-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithWebmSubsampleEncryption(self): self.packager.Package( self._GetStreams(['video'], output_format='webm', test_files=['bear-320x180-vp9-altref.webm']), self._GetFlags(encryption=True)) self._DiffGold(self.output[0], 'bear-320x180-vp9-altref-enc-golden.webm') self._VerifyDecryption(self.output[0], 'bear-320x180-vp9-altref-dec-golden.webm') def testPackageWithWebmVp9FullSampleEncryption(self): self.packager.Package( self._GetStreams(['video'], output_format='webm', test_files=['bear-320x180-vp9-altref.webm']), self._GetFlags(encryption=True, vp9_subsample_encryption=False)) self._DiffGold(self.output[0], 'bear-320x180-vp9-fullsample-enc-golden.webm') self._VerifyDecryption(self.output[0], 'bear-320x180-vp9-altref-dec-golden.webm') def testPackageAvcTsWithEncryption(self): # Currently we only support live packaging for ts. self.packager.Package( self._GetStreams(['audio', 'video'], output_format='ts', live=True, test_files=['bear-640x360.ts']), self._GetFlags(encryption=True, output_hls=True)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-enc-golden', output_format='ts') self._DiffLiveGold(self.output[1], 'bear-640x360-v-enc-golden', output_format='ts') self._DiffGold(self.hls_master_playlist_output, 'bear-640x360-av-enc-master-golden.m3u8') self._DiffGold( os.path.join(self.tmp_dir, 'audio.m3u8'), 'bear-640x360-a-enc-golden.m3u8') self._DiffGold( os.path.join(self.tmp_dir, 'video.m3u8'), 'bear-640x360-v-enc-golden.m3u8') def testPackageAvcTsWithEncryptionExerciseEmulationPrevention(self): # Currently we only support live packaging for ts. self.packager.Package( self._GetStreams( ['video'], output_format='ts', live=True, test_files=['sintel-1024x436.mp4']), self._GetFlags( encryption=True, encryption_key='ad7e9786def9159db6724be06dfcde7a', output_hls=True)) self._DiffLiveGold(self.output[0], 'sintel-1024x436-v-enc-golden', output_format='ts') self._DiffGold(self.hls_master_playlist_output, 'sintel-1024x436-v-enc-master-golden.m3u8') self._DiffGold( os.path.join(self.tmp_dir, 'video.m3u8'), 'sintel-1024x436-v-enc-golden.m3u8') def testPackageWebmWithEncryption(self): self.packager.Package( self._GetStreams(['video'], output_format='webm', test_files=['bear-640x360.webm']), self._GetFlags(encryption=True)) self._DiffGold(self.output[0], 'bear-640x360-vp8-cenc-golden.webm') self._DiffGold(self.mpd_output, 'bear-640x360-vp8-cenc-webm-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-vp8-golden.webm') def testPackageHevcWithEncryption(self): self.packager.Package( self._GetStreams(['video'], test_files=['bear-640x360-hevc.mp4']), self._GetFlags(encryption=True)) self._DiffGold(self.output[0], 'bear-640x360-hevc-cenc-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-hevc-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-hevc-golden.mp4') def testPackageVp8Mp4WithEncryption(self): self.packager.Package( self._GetStreams(['video'], output_format='mp4', test_files=['bear-640x360.webm']), self._GetFlags(encryption=True)) self._DiffGold(self.output[0], 'bear-640x360-vp8-cenc-golden.mp4') self._DiffGold(self.mpd_output, 'bear-640x360-vp8-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-vp8-golden.mp4') def testPackageOpusVp9Mp4WithEncryption(self): self.packager.Package( self._GetStreams(['audio', 'video'], output_format='mp4', test_files=['bear-320x240-vp9-opus.webm']), self._GetFlags(encryption=True)) self._DiffGold(self.output[0], 'bear-320x240-opus-cenc-golden.mp4') self._DiffGold(self.output[1], 'bear-320x240-vp9-cenc-golden.mp4') self._DiffGold(self.mpd_output, 'bear-320x240-opus-vp9-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-320x240-opus-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-320x240-vp9-golden.mp4') def testPackageWvmInput(self): self.packager.Package( self._GetStreams( ['0', '1', '2', '3'], test_files=['bear-multi-configs.wvm']), self._GetFlags( decryption=True, encryption_key='9248d245390e0a49d483ba9b43fc69c3')) # Output timescale is 90000. self._DiffGold(self.output[0], 'bear-320x180-v-wvm-golden.mp4') self._DiffGold(self.output[1], 'bear-320x180-a-wvm-golden.mp4') self._DiffGold(self.output[2], 'bear-640x360-v-wvm-golden.mp4') self._DiffGold(self.output[3], 'bear-640x360-a-wvm-golden.mp4') self._DiffGold(self.mpd_output, 'bear-wvm-golden.mpd') def testPackageWvmInputWithoutStrippingParameterSetNalus(self): self.packager.Package( self._GetStreams( ['0', '1', '2', '3'], test_files=['bear-multi-configs.wvm']), self._GetFlags( strip_parameter_set_nalus=False, decryption=True, encryption_key='9248d245390e0a49d483ba9b43fc69c3')) # Output timescale is 90000. self._DiffGold(self.output[0], 'bear-320x180-avc3-wvm-golden.mp4') self._DiffGold(self.output[1], 'bear-320x180-a-wvm-golden.mp4') self._DiffGold(self.output[2], 'bear-640x360-avc3-wvm-golden.mp4') self._DiffGold(self.output[3], 'bear-640x360-a-wvm-golden.mp4') self._DiffGold(self.mpd_output, 'bear-avc3-wvm-golden.mpd') def testPackageWithEncryptionAndRandomIv(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, random_iv=True)) self._AssertStreamInfo(self.output[0], 'is_encrypted: true') self._AssertStreamInfo(self.output[1], 'is_encrypted: true') # The outputs are encrypted with random iv, so they are not the same as # golden files. self.assertFalse(self._CompareWithGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4')) self.assertFalse(self._CompareWithGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')) self._DiffGold(self.mpd_output, 'bear-640x360-av-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionAndRealClock(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, use_fake_clock=False)) self._AssertStreamInfo(self.output[0], 'is_encrypted: true') self._AssertStreamInfo(self.output[1], 'is_encrypted: true') # The outputs are generated with real clock, so they are not the same as # golden files. self.assertFalse(self._CompareWithGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4')) self.assertFalse(self._CompareWithGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')) self._DiffGold(self.mpd_output, 'bear-640x360-av-cenc-golden.mpd') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') def testPackageWithEncryptionAndNonDashIfIop(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, dash_if_iop=False)) 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.mpd_output, 'bear-640x360-av-cenc-non-iop-golden.mpd') def testPackageWithEncryptionAndOutputMediaInfo(self): self.packager.Package( self._GetStreams(['audio', 'video']), self._GetFlags(encryption=True, output_media_info=True)) self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4') self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4') self._DiffMediaInfoGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4') self._DiffMediaInfoGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4') def testPackageWithLiveProfile(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True), self._GetFlags()) self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-golden') self._DiffLiveMpdGold(self.mpd_output, 'bear-640x360-av-live-golden.mpd') def testPackageWithLiveStaticProfile(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True), self._GetFlags(generate_static_mpd=True)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-golden') self._DiffGold(self.mpd_output, 'bear-640x360-av-live-static-golden.mpd') def testPackageWithLiveProfileAndEncryption(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True), self._GetFlags(encryption=True)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-golden') self._DiffLiveMpdGold(self.mpd_output, 'bear-640x360-av-live-cenc-golden.mpd') def testPackageWithLiveProfileAndEncryptionAndNonDashIfIop(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True), self._GetFlags(encryption=True, dash_if_iop=False)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-golden') self._DiffLiveMpdGold(self.mpd_output, 'bear-640x360-av-live-cenc-non-iop-golden.mpd') def testPackageWithLiveProfileAndEncryptionAndMultFiles(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True, test_files=['bear-1280x720.mp4', 'bear-640x360.mp4', 'bear-320x180.mp4']), self._GetFlags(encryption=True)) self._DiffLiveGold(self.output[2], 'bear-640x360-a-live-cenc-golden') self._DiffLiveGold(self.output[3], 'bear-640x360-v-live-cenc-golden') # Mpd cannot be validated right now since we don't generate determinstic # mpd with multiple inputs due to thread racing. # TODO(kqyang): Generate determinstic mpd or at least validate mpd schema. def testPackageWithLiveProfileAndKeyRotation(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True), self._GetFlags(encryption=True, key_rotation=True)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-rotation-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-rotation-golden') self._DiffLiveMpdGold(self.mpd_output, 'bear-640x360-av-live-cenc-rotation-golden.mpd') def testPackageWithLiveProfileAndKeyRotationAndNoPsshInStream(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True), self._GetFlags( encryption=True, key_rotation=True, include_pssh_in_stream=False)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-rotation-no-pssh-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-rotation-no-pssh-golden') self._DiffLiveMpdGold( self.mpd_output, 'bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd') def testPackageWithLiveProfileAndKeyRotationAndNonDashIfIop(self): self.packager.Package( self._GetStreams(['audio', 'video'], live=True), self._GetFlags(encryption=True, key_rotation=True, dash_if_iop=False)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-rotation-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-rotation-golden') self._DiffLiveMpdGold( self.mpd_output, 'bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd') @unittest.skipUnless(test_env.has_aes_flags, 'Requires AES credentials.') def testWidevineEncryptionWithAes(self): flags = self._GetFlags(widevine_encryption=True) flags += ['--aes_signing_key=' + test_env.options.aes_signing_key, '--aes_signing_iv=' + test_env.options.aes_signing_iv] self.packager.Package(self._GetStreams(['audio', 'video']), flags) self._AssertStreamInfo(self.output[0], 'is_encrypted: true') self._AssertStreamInfo(self.output[1], 'is_encrypted: true') @unittest.skipUnless(test_env.has_aes_flags, 'Requires AES credentials.') def testWidevineEncryptionWithAesAndMultFiles(self): flags = self._GetFlags(widevine_encryption=True) flags += ['--aes_signing_key=' + test_env.options.aes_signing_key, '--aes_signing_iv=' + test_env.options.aes_signing_iv] self.packager.Package( self._GetStreams(['audio', 'video'], test_files=['bear-1280x720.mp4', 'bear-640x360.mp4', 'bear-320x180.mp4']), flags) with open(self.mpd_output, 'rb') as f: print f.read() # TODO(kqyang): Add some validations. @unittest.skipUnless(test_env.has_aes_flags, 'Requires AES credentials.') def testKeyRotationWithAes(self): flags = self._GetFlags(widevine_encryption=True, key_rotation=True) flags += ['--aes_signing_key=' + test_env.options.aes_signing_key, '--aes_signing_iv=' + test_env.options.aes_signing_iv] self.packager.Package(self._GetStreams(['audio', 'video']), flags) self._AssertStreamInfo(self.output[0], 'is_encrypted: true') self._AssertStreamInfo(self.output[1], 'is_encrypted: true') @unittest.skipUnless(test_env.has_rsa_flags, 'Requires RSA credentials.') def testWidevineEncryptionWithRsa(self): flags = self._GetFlags(widevine_encryption=True) flags += ['--rsa_signing_key_path=' + test_env.options.rsa_signing_key_path] self.packager.Package(self._GetStreams(['audio', 'video']), flags) self._AssertStreamInfo(self.output[0], 'is_encrypted: true') self._AssertStreamInfo(self.output[1], 'is_encrypted: true') def _GetStreams(self, stream_descriptors, language_override=None, output_format=None, live=False, test_files=None): if test_files is None: test_files = ['bear-640x360.mp4'] streams = [] if not self.output: self.output = [] 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, '_') if live: if output_format == 'ts': stream = ('input=%s,stream=%s,segment_template=%s-$Number$.ts,' 'playlist_name=%s.m3u8' % (test_file, stream_descriptor, output_prefix, stream_descriptor)) else: stream = ( 'input=%s,stream=%s,init_segment=%s-init.mp4,' 'segment_template=%s-$Number$.m4s' % (test_file, stream_descriptor, output_prefix, output_prefix)) self.output.append(output_prefix) else: output = '%s.%s' % ( output_prefix, self._GetExtension(stream_descriptor, output_format)) stream = ('input=%s,stream=%s,output=%s' % (test_file, stream_descriptor, output)) self.output.append(output) if output_format: stream += ',format=%s' % output_format if language_override: stream += ',lang=%s' % language_override streams.append(stream) return streams def _GetExtension(self, 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 _GetFlags(self, strip_parameter_set_nalus=True, encryption=False, clear_lead=1, protection_scheme=None, vp9_subsample_encryption=True, decryption=False, encryption_key='32333435363738393021323334353637', encryption_iv='3334353637383930', random_iv=False, widevine_encryption=False, key_rotation=False, include_pssh_in_stream=True, dash_if_iop=True, output_media_info=False, output_hls=False, generate_static_mpd=False, use_fake_clock=True): flags = [] if not strip_parameter_set_nalus: flags += ['--strip_parameter_set_nalus=false'] if widevine_encryption: widevine_server_url = ('https://license.uat.widevine.com/cenc' '/getcontentkey/widevine_test') flags += ['--enable_widevine_encryption', '--key_server_url=' + widevine_server_url, '--content_id=3031323334353637', '--signer=widevine_test'] elif encryption: flags += ['--enable_fixed_key_encryption', '--key_id=31323334353637383930313233343536', '--clear_lead={0}'.format(clear_lead)] if test_env.options.encryption_key: encryption_key = test_env.options.encryption_key flags.append('--key=' + encryption_key) if not random_iv: if test_env.options.encryption_iv: encryption_iv = test_env.options.encryption_iv flags.append('--iv=' + encryption_iv) if protection_scheme: flags += ['--protection_scheme', protection_scheme] if not vp9_subsample_encryption: flags += ['--vp9_subsample_encryption=false'] if decryption: flags += ['--enable_fixed_key_decryption', '--key_id=31323334353637383930313233343536', '--key=' + encryption_key] if key_rotation: flags.append('--crypto_period_duration=1') if not include_pssh_in_stream: flags.append('--mp4_include_pssh_in_stream=false') if not dash_if_iop: flags.append('--generate_dash_if_iop_compliant_mpd=false') if output_media_info: flags.append('--output_media_info') elif output_hls: flags += ['--hls_master_playlist_output', self.hls_master_playlist_output] else: flags += ['--mpd_output', self.mpd_output] if generate_static_mpd: flags += ['--generate_static_mpd'] flags.append('--segment_duration=1') # Use fake clock, so output can be compared. if use_fake_clock: flags.append('--use_fake_clock_for_muxer') # Override packager version string for testing. flags += ['--override_version', '--test_version', '--'] return flags def _CompareWithGold(self, test_output, golden_file_name): golden_file = os.path.join(self.golden_file_dir, golden_file_name) return filecmp.cmp(test_output, golden_file) def _DiffGold(self, test_output, golden_file_name): golden_file = os.path.join(self.golden_file_dir, golden_file_name) if test_env.options.test_update_golden_files: if not os.path.exists(golden_file) or not filecmp.cmp(test_output, golden_file): print 'Updating golden file: ', golden_file_name shutil.copyfile(test_output, golden_file) else: match = filecmp.cmp(test_output, golden_file) if not match: p = subprocess.Popen(['git', '--no-pager', 'diff', '--color=auto', '--no-ext-diff', '--no-index', golden_file, test_output], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, error = p.communicate() command_line = self.packager.GetCommandLine() failure_message = (output + error + '\n' + _TEST_FAILURE_COMMAND_LINE_MESSAGE + command_line) self.fail(failure_message) # '*.media_info' outputs contain media file names, which is changing for # every test run. These needs to be replaced for comparison. def _DiffMediaInfoGold(self, test_output, golden_file_name): if platform.system() == 'Windows': test_output = test_output.replace('\\', '\\\\') media_info_output = test_output + '.media_info' # Replaces filename, which is changing for every test run. with open(media_info_output, 'rb') as f: content = f.read() with open(media_info_output, 'wb') as f: f.write(content.replace(test_output, 'place_holder')) self._DiffGold(media_info_output, golden_file_name + '.media_info') def _DiffLiveGold(self, test_output_prefix, golden_file_name_prefix, output_format='mp4'): # Compare init and the first three segments. if output_format == 'ts': for i in range(1, 4): self._DiffGold('%s-%d.ts' % (test_output_prefix, i), '%s-%d.ts' % (golden_file_name_prefix, i)) else: self._DiffGold(test_output_prefix + '-init.mp4', golden_file_name_prefix + '-init.mp4') for i in range(1, 4): self._DiffGold('%s-%d.m4s' % (test_output_prefix, i), '%s-%d.m4s' % (golden_file_name_prefix, i)) # Live mpd contains current availabilityStartTime and publishTime, which # needs to be replaced for comparison. def _DiffLiveMpdGold(self, test_output, golden_file_name): with open(test_output, 'rb') as f: content = f.read() # Extract availabilityStartTime. m = re.search('availabilityStartTime="[^"]+"', content) self.assertIsNotNone(m) availability_start_time = m.group(0) print availability_start_time # Extract publishTime. m = re.search('publishTime="[^"]+"', content) self.assertIsNotNone(m) publish_time = m.group(0) print publish_time with open(test_output, 'wb') as f: f.write(content.replace( availability_start_time, 'availabilityStartTime="some_availability_start_time"').replace( publish_time, 'publishTime="some_publish_time"')) self._DiffGold(test_output, golden_file_name) def _AssertStreamInfo(self, stream, info): stream_info = self.packager.DumpStreamInfo(stream) self.assertIn('Found 1 stream(s).', stream_info) self.assertIn(info, stream_info) def _VerifyDecryption(self, test_encrypted_file, golden_clear_file): output_extension = os.path.splitext(golden_clear_file)[1][1:] self.packager.Package( self._GetStreams(['0'], output_format=output_extension, test_files=[test_encrypted_file]), self._GetFlags(decryption=True)) self._DiffGold(self.output[-1], golden_clear_file) if __name__ == '__main__': unittest.main()