Fix possible packager hangs when reading mp4 from FIFO
For MP4 files, mp4 parser tries to open and read the same file again to handle trailing MOOV box. Open FIFO again may result in packager hang. Update the code to only attempt to load MOOV for local regular files, as FIFO and remote files are unseekable anyway. Fixes #664. Change-Id: Ib286d2876d202cd5a248ffe70b13589b3cc74bc9
This commit is contained in:
parent
7973c5396f
commit
0342adb132
|
@ -727,6 +727,13 @@ class PackagerFunctionalTest(PackagerAppTest):
|
||||||
self._GetFlags(output_dash=True, output_hls=True))
|
self._GetFlags(output_dash=True, output_hls=True))
|
||||||
self._CheckTestResults('audio-video-with-language-override-with-subtag')
|
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):
|
def testAacHe(self):
|
||||||
self.assertPackageSuccess(
|
self.assertPackageSuccess(
|
||||||
self._GetStreams(
|
self._GetStreams(
|
||||||
|
|
BIN
packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-audio.mp4
vendored
Normal file
BIN
packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-audio.mp4
vendored
Normal file
Binary file not shown.
BIN
packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-video.mp4
vendored
Normal file
BIN
packager/app/test/testdata/mp4-trailing-moov/bear-640x360-trailing-moov-video.mp4
vendored
Normal file
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
#EXTM3U
|
||||||
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
|
|
||||||
|
#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
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
|
||||||
|
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.7360665798187256S">
|
||||||
|
<Period id="0">
|
||||||
|
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
|
||||||
|
<Representation id="0" bandwidth="973483" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||||
|
<BaseURL>bear-640x360-trailing-moov-video.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="859-926" timescale="30000">
|
||||||
|
<Initialization range="0-858"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
|
||||||
|
<Representation id="1" bandwidth="133334" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||||
|
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||||
|
<BaseURL>bear-640x360-trailing-moov-audio.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="793-860" timescale="44100">
|
||||||
|
<Initialization range="0-792"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
|
@ -0,0 +1,16 @@
|
||||||
|
#EXTM3U
|
||||||
|
#EXT-X-VERSION:6
|
||||||
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
|
#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
|
|
@ -0,0 +1,16 @@
|
||||||
|
#EXTM3U
|
||||||
|
#EXT-X-VERSION:6
|
||||||
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
|
#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
|
|
@ -351,6 +351,31 @@ int64_t File::CopyFile(File* source, File* destination, int64_t max_copy) {
|
||||||
return bytes_copied;
|
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(
|
std::string File::MakeCallbackFileName(
|
||||||
const BufferCallbackParams& callback_params,
|
const BufferCallbackParams& callback_params,
|
||||||
const std::string& name) {
|
const std::string& name) {
|
||||||
|
|
|
@ -142,6 +142,10 @@ class File {
|
||||||
/// @return Number of bytes written, or a value < 0 on error.
|
/// @return Number of bytes written, or a value < 0 on error.
|
||||||
static int64_t CopyFile(File* source, File* destination, int64_t max_copy);
|
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.
|
/// Generate callback file name.
|
||||||
/// NOTE: THE GENERATED NAME IS ONLY VAID WHILE @a callback_params IS VALID.
|
/// NOTE: THE GENERATED NAME IS ONLY VAID WHILE @a callback_params IS VALID.
|
||||||
/// @param callback_params references BufferCallbackParams, which will be
|
/// @param callback_params references BufferCallbackParams, which will be
|
||||||
|
|
|
@ -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,
|
class ParamLocalFileTest : public LocalFileTest,
|
||||||
public ::testing::WithParamInterface<uint8_t> {};
|
public ::testing::WithParamInterface<uint8_t> {};
|
||||||
|
|
||||||
|
|
|
@ -218,8 +218,12 @@ Status Demuxer::InitializeParser() {
|
||||||
key_source_.get());
|
key_source_.get());
|
||||||
|
|
||||||
// Handle trailing 'moov'.
|
// 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<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
|
static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
|
||||||
|
}
|
||||||
if (!parser_->Parse(buffer_.get(), bytes_read)) {
|
if (!parser_->Parse(buffer_.get(), bytes_read)) {
|
||||||
return Status(error::PARSER_FAILURE,
|
return Status(error::PARSER_FAILURE,
|
||||||
"Cannot parse media file " + file_name_);
|
"Cannot parse media file " + file_name_);
|
||||||
|
|
Loading…
Reference in New Issue