diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 1c8b9bc010..25fee9265b 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -63,6 +63,7 @@ class PackagerAppTest(unittest.TestCase): language_override=None, output_format=None, live=False, + hls=False, test_files=None): if test_files is None: test_files = ['bear-640x360.mp4'] @@ -85,10 +86,8 @@ class PackagerAppTest(unittest.TestCase): 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)) + stream = ('input=%s,stream=%s,segment_template=%s-$Number$.ts' % + (test_file, stream_descriptor, output_prefix)) else: stream = ( 'input=%s,stream=%s,init_segment=%s-init.mp4,' @@ -106,6 +105,8 @@ class PackagerAppTest(unittest.TestCase): stream += ',format=%s' % output_format if language_override: stream += ',lang=%s' % language_override + if hls: + stream += ',playlist_name=%s.m3u8' % stream_descriptor streams.append(stream) return streams @@ -417,10 +418,12 @@ class PackagerFunctionalTest(PackagerAppTest): def testPackageAvcTs(self): # Currently we only support live packaging for ts. self.assertPackageSuccess( - self._GetStreams(['audio', 'video'], - output_format='ts', - live=True, - test_files=['bear-640x360.ts']), + self._GetStreams( + ['audio', 'video'], + output_format='ts', + live=True, + hls=True, + test_files=['bear-640x360.ts']), self._GetFlags(output_hls=True)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-golden', @@ -441,6 +444,7 @@ class PackagerFunctionalTest(PackagerAppTest): ['audio', 'video'], output_format='ts', live=True, + hls=True, test_files=['bear-640x360.ts']), self._GetFlags( output_hls=True, @@ -467,6 +471,7 @@ class PackagerFunctionalTest(PackagerAppTest): ['audio', 'video'], output_format='ts', live=True, + hls=True, test_files=['bear-640x360.ts']), self._GetFlags( encryption=True, @@ -495,6 +500,7 @@ class PackagerFunctionalTest(PackagerAppTest): ['audio', 'video'], output_format='ts', live=True, + hls=True, test_files=['bear-640x360.ts']), self._GetFlags( output_hls=True, @@ -680,10 +686,12 @@ class PackagerFunctionalTest(PackagerAppTest): def testPackageAvcTsWithEncryption(self): # Currently we only support live packaging for ts. self.assertPackageSuccess( - self._GetStreams(['audio', 'video'], - output_format='ts', - live=True, - test_files=['bear-640x360.ts']), + self._GetStreams( + ['audio', 'video'], + output_format='ts', + live=True, + hls=True, + test_files=['bear-640x360.ts']), self._GetFlags(encryption=True, output_hls=True)) self._DiffLiveGold(self.output[0], 'bear-640x360-a-enc-golden', @@ -708,6 +716,7 @@ class PackagerFunctionalTest(PackagerAppTest): ['video'], output_format='ts', live=True, + hls=True, test_files=['sintel-1024x436.mp4']), self._GetFlags( encryption=True, @@ -844,6 +853,49 @@ class PackagerFunctionalTest(PackagerAppTest): self._DiffMediaInfoGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4') self._DiffMediaInfoGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4') + def testPackageWithHlsSingleSegmentMp4Encrypted(self): + self.assertPackageSuccess( + self._GetStreams(['audio', 'video'], hls=True), + self._GetFlags(encryption=True, output_hls=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.hls_master_playlist_output, + 'bear-640x360-av-mp4-master-cenc-golden.m3u8') + self._DiffGold( + os.path.join(self.tmp_dir, 'audio.m3u8'), + 'bear-640x360-a-mp4-cenc-golden.m3u8') + self._DiffGold( + os.path.join(self.tmp_dir, 'video.m3u8'), + 'bear-640x360-v-mp4-cenc-golden.m3u8') + + # Test HLS with multi-segment mp4 and content in subdirectories. + def testPackageWithHlsMultiSegmentMp4WithCustomPath(self): + test_file = os.path.join(self.test_data_dir, 'bear-640x360.mp4') + # {tmp}/audio/audio-init.mp4, {tmp}/audio/audio-1.m4s etc. + audio_output_prefix = os.path.join(self.tmp_dir, 'audio', 'audio') + # {tmp}/video/video-init.mp4, {tmp}/video/video-1.m4s etc. + video_output_prefix = os.path.join(self.tmp_dir, 'video', 'video') + self.assertPackageSuccess( + [ + 'input=%s,stream=audio,init_segment=%s-init.mp4,' + 'segment_template=%s-$Number$.m4s,playlist_name=audio/audio.m3u8' % + (test_file, audio_output_prefix, audio_output_prefix), + 'input=%s,stream=video,init_segment=%s-init.mp4,' + 'segment_template=%s-$Number$.m4s,playlist_name=video/video.m3u8' % + (test_file, video_output_prefix, video_output_prefix), + ], + self._GetFlags(output_hls=True)) + self._DiffLiveGold(audio_output_prefix, 'bear-640x360-a-live-golden') + self._DiffLiveGold(video_output_prefix, 'bear-640x360-v-live-golden') + self._DiffGold(self.hls_master_playlist_output, + 'bear-640x360-av-mp4-master-golden.m3u8') + self._DiffGold( + os.path.join(self.tmp_dir, 'audio', 'audio.m3u8'), + 'bear-640x360-a-mp4-golden.m3u8') + self._DiffGold( + os.path.join(self.tmp_dir, 'video', 'video.m3u8'), + 'bear-640x360-v-mp4-golden.m3u8') + def testPackageWithLiveProfile(self): self.assertPackageSuccess( self._GetStreams(['audio', 'video'], live=True), self._GetFlags()) diff --git a/packager/app/test/testdata/bear-640x360-a-mp4-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-mp4-cenc-golden.m3u8 new file mode 100644 index 0000000000..2d839ae79d --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-a-mp4-cenc-golden.m3u8 @@ -0,0 +1,17 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="output_audio.mp4",BYTERANGE="967@0" +#EXT-X-KEY:METHOD=SAMPLE-AES-CENC,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity" +#EXTINF:1.022, +#EXT-X-BYTERANGE:16655@1035 +output_audio.mp4 +#EXTINF:0.998, +#EXT-X-BYTERANGE:16650 +output_audio.mp4 +#EXTINF:0.743, +#EXT-X-BYTERANGE:10272 +output_audio.mp4 +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/bear-640x360-a-mp4-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-mp4-golden.m3u8 new file mode 100644 index 0000000000..c9719d6f6f --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-a-mp4-golden.m3u8 @@ -0,0 +1,13 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="audio-init.mp4" +#EXTINF:1.022, +audio-1.m4s +#EXTINF:0.998, +audio-2.m4s +#EXTINF:0.743, +audio-3.m4s +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-golden.m3u8 new file mode 100644 index 0000000000..b895318cfe --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-golden.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="stream_0",URI="audio.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=1111147,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio" +video.m3u8 diff --git a/packager/app/test/testdata/bear-640x360-av-mp4-master-golden.m3u8 b/packager/app/test/testdata/bear-640x360-av-mp4-master-golden.m3u8 new file mode 100644 index 0000000000..543dc9a79e --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-av-mp4-master-golden.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="stream_0",URI="audio/audio.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=1105129,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio" +video/video.m3u8 diff --git a/packager/app/test/testdata/bear-640x360-v-mp4-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-v-mp4-cenc-golden.m3u8 new file mode 100644 index 0000000000..ac1d7d58d8 --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-v-mp4-cenc-golden.m3u8 @@ -0,0 +1,17 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="output_video.mp4",BYTERANGE="1091@0" +#EXT-X-KEY:METHOD=SAMPLE-AES-CENC,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity" +#EXTINF:1.001, +#EXT-X-BYTERANGE:99313@1159 +output_video.mp4 +#EXTINF:1.001, +#EXT-X-BYTERANGE:122340 +output_video.mp4 +#EXTINF:0.734, +#EXT-X-BYTERANGE:80067 +output_video.mp4 +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/bear-640x360-v-mp4-golden.m3u8 b/packager/app/test/testdata/bear-640x360-v-mp4-golden.m3u8 new file mode 100644 index 0000000000..0f3fbe7f5a --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-v-mp4-golden.m3u8 @@ -0,0 +1,13 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="video-init.mp4" +#EXTINF:1.001, +video-1.m4s +#EXTINF:1.001, +video-2.m4s +#EXTINF:0.734, +video-3.m4s +#EXT-X-ENDLIST diff --git a/packager/file/local_file.cc b/packager/file/local_file.cc index 46c1dfc024..cfc446b65e 100644 --- a/packager/file/local_file.cc +++ b/packager/file/local_file.cc @@ -105,8 +105,21 @@ bool LocalFile::Tell(uint64_t* position) { LocalFile::~LocalFile() {} bool LocalFile::Open() { - internal_file_ = base::OpenFile(base::FilePath::FromUTF8Unsafe(file_name()), - file_mode_.c_str()); + base::FilePath file_path(base::FilePath::FromUTF8Unsafe(file_name())); + + // Create upper level directories for write mode. + if (file_mode_.find("w") != std::string::npos) { + base::File::Error error; + // The function returns success if the directories already exist. + if (!base::CreateDirectoryAndGetError(file_path.DirName(), &error)) { + LOG(ERROR) << "Failed to create directories for file '" + << file_path.AsUTF8Unsafe() + << "'. Error: " << base::File::ErrorToString(error); + return false; + } + } + + internal_file_ = base::OpenFile(file_path, file_mode_.c_str()); return (internal_file_ != NULL); }