Added support for ISO-BMFF files with trailing 'moov' boxes.
Change-Id: Icdc9058179a998617f865566d635ecdbd0e422c5
This commit is contained in:
parent
ada218c089
commit
a0dc98c13e
|
@ -92,6 +92,10 @@ Status Demuxer::Initialize() {
|
|||
base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)),
|
||||
key_source_.get());
|
||||
|
||||
// Handle trailing 'moov'.
|
||||
if (container == CONTAINER_MOV)
|
||||
static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
|
||||
|
||||
if (!parser_->Parse(buffer_.get(), bytes_read)) {
|
||||
init_parsing_status_ =
|
||||
Status(error::PARSER_FAILURE, "Cannot parse media file " + file_name_);
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
'dependencies': [
|
||||
'../../../testing/gtest.gyp:gtest',
|
||||
'../../../testing/gmock.gyp:gmock',
|
||||
'../../file/file.gyp:file',
|
||||
'../../test/media_test.gyp:media_test_support',
|
||||
'mp4',
|
||||
]
|
||||
|
|
|
@ -13,10 +13,13 @@
|
|||
#include "packager/base/strings/string_number_conversions.h"
|
||||
#include "packager/media/base/aes_encryptor.h"
|
||||
#include "packager/media/base/audio_stream_info.h"
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/base/decrypt_config.h"
|
||||
#include "packager/media/base/key_source.h"
|
||||
#include "packager/media/base/media_sample.h"
|
||||
#include "packager/media/base/video_stream_info.h"
|
||||
#include "packager/media/file/file.h"
|
||||
#include "packager/media/file/file_closer.h"
|
||||
#include "packager/media/formats/mp4/box_definitions.h"
|
||||
#include "packager/media/formats/mp4/box_reader.h"
|
||||
#include "packager/media/formats/mp4/es_descriptor.h"
|
||||
|
@ -108,6 +111,82 @@ bool MP4MediaParser::Parse(const uint8_t* buf, int size) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MP4MediaParser::LoadMoov(const std::string& file_path) {
|
||||
scoped_ptr<File, FileCloser> file(
|
||||
File::OpenWithNoBuffering(file_path.c_str(), "r"));
|
||||
if (!file) {
|
||||
LOG(ERROR) << "Unable to open media file '" << file_path << "'";
|
||||
return false;
|
||||
}
|
||||
if (file->Seek(0) < 0) {
|
||||
LOG(WARNING) << "Filesystem does not support seeking on file '" << file_path
|
||||
<< "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t file_position(0);
|
||||
bool mdat_seen(false);
|
||||
while (true) {
|
||||
const uint32_t kBoxHeaderReadSize(16);
|
||||
std::vector<uint8_t> buffer(kBoxHeaderReadSize);
|
||||
int64_t bytes_read = file->Read(&buffer[0], kBoxHeaderReadSize);
|
||||
if (bytes_read == 0) {
|
||||
LOG(ERROR) << "Could not find 'moov' box in file '" << file_path << "'";
|
||||
return false;
|
||||
}
|
||||
if (bytes_read < kBoxHeaderReadSize) {
|
||||
LOG(ERROR) << "Error reading media file '" << file_path << "'";
|
||||
return false;
|
||||
}
|
||||
uint64_t box_size;
|
||||
FourCC box_type;
|
||||
bool err;
|
||||
if (!BoxReader::StartTopLevelBox(&buffer[0], kBoxHeaderReadSize, &box_type,
|
||||
&box_size, &err)) {
|
||||
LOG(ERROR) << "Could not start top level box from file '" << file_path
|
||||
<< "'";
|
||||
return false;
|
||||
}
|
||||
if (box_type == FOURCC_MDAT) {
|
||||
mdat_seen = true;
|
||||
} else if (box_type == FOURCC_MOOV) {
|
||||
if (!mdat_seen) {
|
||||
// 'moov' is before 'mdat'. Nothing to do.
|
||||
break;
|
||||
}
|
||||
// 'mdat' before 'moov'. Read and parse 'moov'.
|
||||
if (!Parse(&buffer[0], bytes_read)) {
|
||||
LOG(ERROR) << "Error parsing mp4 file '" << file_path << "'";
|
||||
return false;
|
||||
}
|
||||
uint64_t bytes_to_read = box_size - bytes_read;
|
||||
buffer.resize(bytes_to_read);
|
||||
while (bytes_to_read > 0) {
|
||||
bytes_read = file->Read(&buffer[0], bytes_to_read);
|
||||
if (bytes_read <= 0) {
|
||||
LOG(ERROR) << "Error reading 'moov' contents from file '" << file_path
|
||||
<< "'";
|
||||
return false;
|
||||
}
|
||||
if (!Parse(&buffer[0], bytes_read)) {
|
||||
LOG(ERROR) << "Error parsing mp4 file '" << file_path << "'";
|
||||
return false;
|
||||
}
|
||||
bytes_to_read -= bytes_read;
|
||||
}
|
||||
queue_.Reset(); // So that we don't need to adjust data offsets.
|
||||
mdat_tail_ = 0; // So it will skip boxes until mdat.
|
||||
break; // Done.
|
||||
}
|
||||
file_position += box_size;
|
||||
if (!file->Seek(file_position)) {
|
||||
LOG(ERROR) << "Error skipping box in mp4 file '" << file_path << "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MP4MediaParser::ParseBox(bool* err) {
|
||||
const uint8_t* buf;
|
||||
int size;
|
||||
|
@ -151,6 +230,9 @@ bool MP4MediaParser::ParseBox(bool* err) {
|
|||
}
|
||||
|
||||
bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||
if (moov_)
|
||||
return true; // Already parsed the 'moov' box.
|
||||
|
||||
moov_.reset(new Movie);
|
||||
RCHECK(moov_->Parse(reader));
|
||||
runs_.reset();
|
||||
|
@ -530,10 +612,6 @@ bool MP4MediaParser::ReadAndDiscardMDATsUntil(const int64_t offset) {
|
|||
if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err))
|
||||
break;
|
||||
|
||||
if (type != FOURCC_MDAT) {
|
||||
LOG(ERROR) << "Unexpected box type while parsing MDATs: "
|
||||
<< FourCCToString(type);
|
||||
}
|
||||
mdat_tail_ += box_sz;
|
||||
}
|
||||
queue_.Trim(std::min(mdat_tail_, offset));
|
||||
|
|
|
@ -47,6 +47,14 @@ class MP4MediaParser : public MediaParser {
|
|||
virtual bool Parse(const uint8_t* buf, int size) OVERRIDE;
|
||||
/// @}
|
||||
|
||||
/// Handles ISO-BMFF containers which have the 'moov' box trailing the
|
||||
/// movie data ('mdat'). It does this by doing a sparse parse of the file
|
||||
/// to locate the 'moov' box, and parsing its contents if it is found to be
|
||||
/// located after the 'mdat' box(es).
|
||||
/// @param file_path is the path to the media file to be parsed.
|
||||
/// @return true if successful, false otherwise.
|
||||
bool LoadMoov(const std::string& file_path);
|
||||
|
||||
private:
|
||||
enum State {
|
||||
kWaitingForInit,
|
||||
|
|
|
@ -91,7 +91,8 @@ class MP4MediaParserTest : public testing::Test {
|
|||
|
||||
bool ParseMP4File(const std::string& filename, int append_bytes) {
|
||||
InitializeParser(NULL);
|
||||
|
||||
if (!parser_->LoadMoov(GetTestDataFilePath(filename).value()))
|
||||
return false;
|
||||
std::vector<uint8_t> buffer = ReadTestDataFile(filename);
|
||||
return AppendDataInPieces(buffer.data(), buffer.size(), append_bytes);
|
||||
}
|
||||
|
@ -120,6 +121,12 @@ TEST_F(MP4MediaParserTest, MultiFragmentAppend) {
|
|||
EXPECT_EQ(201u, num_samples_);
|
||||
}
|
||||
|
||||
TEST_F(MP4MediaParserTest, TrailingMoov) {
|
||||
EXPECT_TRUE(ParseMP4File("bear-1280x720-trailing-moov.mp4", 1024));
|
||||
EXPECT_EQ(2u, num_streams_);
|
||||
EXPECT_EQ(201u, num_samples_);
|
||||
}
|
||||
|
||||
TEST_F(MP4MediaParserTest, Flush) {
|
||||
// Flush while reading sample data, then start a new stream.
|
||||
InitializeParser(NULL);
|
||||
|
@ -127,18 +134,10 @@ TEST_F(MP4MediaParserTest, Flush) {
|
|||
std::vector<uint8_t> buffer = ReadTestDataFile("bear-1280x720-av_frag.mp4");
|
||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), 65536, 512));
|
||||
parser_->Flush();
|
||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512));
|
||||
EXPECT_EQ(2u, num_streams_);
|
||||
EXPECT_EQ(201u, num_samples_);
|
||||
}
|
||||
|
||||
TEST_F(MP4MediaParserTest, Reinitialization) {
|
||||
InitializeParser(NULL);
|
||||
|
||||
std::vector<uint8_t> buffer = ReadTestDataFile("bear-1280x720-av_frag.mp4");
|
||||
EXPECT_NE(0u, num_samples_);
|
||||
num_samples_ = 0;
|
||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512));
|
||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512));
|
||||
EXPECT_EQ(2u, num_streams_);
|
||||
EXPECT_EQ(201u, num_samples_);
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue