diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 36aff4a633..fdb29f60aa 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -727,6 +727,13 @@ class PackagerFunctionalTest(PackagerAppTest): self._GetFlags(output_dash=True, output_hls=True)) self._CheckTestResults('audio-video-with-language-override-with-subtag') + def testMp4TrailingMoov(self): + self.assertPackageSuccess( + self._GetStreams(['audio', 'video'], + test_files=['bear-640x360-trailing-moov.mp4']), + self._GetFlags(output_dash=True, output_hls=True)) + self._CheckTestResults('mp4-trailing-moov') + def testAacHe(self): self.assertPackageSuccess( self._GetStreams( diff --git a/packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-audio.mp4 b/packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-audio.mp4 new file mode 100644 index 0000000000..87f89a93c0 Binary files /dev/null and b/packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-audio.mp4 differ diff --git a/packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-video.mp4 b/packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-video.mp4 new file mode 100644 index 0000000000..9bc668f8f6 Binary files /dev/null and b/packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-video.mp4 differ diff --git a/packager/app/test/testdata/mp4-trailing-moov/output.m3u8 b/packager/app/test/testdata/mp4-trailing-moov/output.m3u8 new file mode 100644 index 0000000000..31596b92f5 --- /dev/null +++ b/packager/app/test/testdata/mp4-trailing-moov/output.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +## Generated with https://github.com/google/shaka-packager version -- + +#EXT-X-MEDIA:TYPE=AUDIO,URI="stream_0.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2" + +#EXT-X-STREAM-INF:BANDWIDTH=1106817,AVERAGE-BANDWIDTH=1004632,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="default-audio-group" +stream_1.m3u8 diff --git a/packager/app/test/testdata/mp4-trailing-moov/output.mpd b/packager/app/test/testdata/mp4-trailing-moov/output.mpd new file mode 100644 index 0000000000..b1a395497f --- /dev/null +++ b/packager/app/test/testdata/mp4-trailing-moov/output.mpd @@ -0,0 +1,23 @@ + + + + + + + bear-640x360-trailing-moov-video.mp4 + + + + + + + + + bear-640x360-trailing-moov-audio.mp4 + + + + + + + diff --git a/packager/app/test/testdata/mp4-trailing-moov/stream_0.m3u8 b/packager/app/test/testdata/mp4-trailing-moov/stream_0.m3u8 new file mode 100644 index 0000000000..66c3b375a1 --- /dev/null +++ b/packager/app/test/testdata/mp4-trailing-moov/stream_0.m3u8 @@ -0,0 +1,16 @@ +#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="bear-640x360-trailing-moov-audio.mp4",BYTERANGE="793@0" +#EXTINF:1.022, +#EXT-X-BYTERANGE:17028@861 +bear-640x360-trailing-moov-audio.mp4 +#EXTINF:0.998, +#EXT-X-BYTERANGE:16285 +bear-640x360-trailing-moov-audio.mp4 +#EXTINF:0.720, +#EXT-X-BYTERANGE:9558 +bear-640x360-trailing-moov-audio.mp4 +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/mp4-trailing-moov/stream_1.m3u8 b/packager/app/test/testdata/mp4-trailing-moov/stream_1.m3u8 new file mode 100644 index 0000000000..08568d3384 --- /dev/null +++ b/packager/app/test/testdata/mp4-trailing-moov/stream_1.m3u8 @@ -0,0 +1,16 @@ +#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="bear-640x360-trailing-moov-video.mp4",BYTERANGE="859@0" +#EXTINF:1.001, +#EXT-X-BYTERANGE:99313@927 +bear-640x360-trailing-moov-video.mp4 +#EXTINF:1.001, +#EXT-X-BYTERANGE:121807 +bear-640x360-trailing-moov-video.mp4 +#EXTINF:0.734, +#EXT-X-BYTERANGE:79662 +bear-640x360-trailing-moov-video.mp4 +#EXT-X-ENDLIST diff --git a/packager/file/file.cc b/packager/file/file.cc index c0b4586e2d..c1a62053c2 100644 --- a/packager/file/file.cc +++ b/packager/file/file.cc @@ -351,6 +351,31 @@ int64_t File::CopyFile(File* source, File* destination, int64_t max_copy) { return bytes_copied; } +bool File::IsLocalRegularFile(const char* file_name) { + base::StringPiece real_file_name; + const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name); + DCHECK(file_type); + if (file_type->type != kLocalFilePrefix) + return false; +#if defined(OS_WIN) + const base::FilePath file_path( + base::FilePath::FromUTF8Unsafe(real_file_name)); + const DWORD fileattr = GetFileAttributes(file_path.value().c_str()); + if (fileattr == INVALID_FILE_ATTRIBUTES) { + LOG(ERROR) << "Failed to GetFileAttributes of " << file_path.value(); + return false; + } + return (fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0; +#else + struct stat info; + if (stat(real_file_name.data(), &info) != 0) { + LOG(ERROR) << "Failed to run stat on " << real_file_name; + return false; + } + return S_ISREG(info.st_mode); +#endif +} + std::string File::MakeCallbackFileName( const BufferCallbackParams& callback_params, const std::string& name) { diff --git a/packager/file/file.h b/packager/file/file.h index f47b217e87..295fddb75c 100644 --- a/packager/file/file.h +++ b/packager/file/file.h @@ -142,6 +142,10 @@ class File { /// @return Number of bytes written, or a value < 0 on error. static int64_t CopyFile(File* source, File* destination, int64_t max_copy); + /// @param file_name is the name of the file to be checked. + /// @return true if `file_name` is a local and regular file. + static bool IsLocalRegularFile(const char* file_name); + /// Generate callback file name. /// NOTE: THE GENERATED NAME IS ONLY VAID WHILE @a callback_params IS VALID. /// @param callback_params references BufferCallbackParams, which will be diff --git a/packager/file/file_unittest.cc b/packager/file/file_unittest.cc index 26c8c9e005..f40c635dc9 100644 --- a/packager/file/file_unittest.cc +++ b/packager/file/file_unittest.cc @@ -195,6 +195,12 @@ TEST_F(LocalFileTest, WriteFlushCheckSize) { } } +TEST_F(LocalFileTest, IsLocalReguar) { + ASSERT_EQ(kDataSize, + base::WriteFile(test_file_path_, data_.data(), kDataSize)); + ASSERT_TRUE(File::IsLocalRegularFile(local_file_name_.c_str())); +} + class ParamLocalFileTest : public LocalFileTest, public ::testing::WithParamInterface {}; diff --git a/packager/media/demuxer/demuxer.cc b/packager/media/demuxer/demuxer.cc index 094319fae7..e089595cee 100644 --- a/packager/media/demuxer/demuxer.cc +++ b/packager/media/demuxer/demuxer.cc @@ -218,8 +218,12 @@ Status Demuxer::InitializeParser() { key_source_.get()); // Handle trailing 'moov'. - if (container_name_ == CONTAINER_MOV) + if (container_name_ == CONTAINER_MOV && + File::IsLocalRegularFile(file_name_.c_str())) { + // TODO(kqyang): Investigate whether we can reuse the existing file + // descriptor |media_file_| instead of opening the same file again. static_cast(parser_.get())->LoadMoov(file_name_); + } if (!parser_->Parse(buffer_.get(), bytes_read)) { return Status(error::PARSER_FAILURE, "Cannot parse media file " + file_name_);