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)),
|
base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)),
|
||||||
key_source_.get());
|
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)) {
|
if (!parser_->Parse(buffer_.get(), bytes_read)) {
|
||||||
init_parsing_status_ =
|
init_parsing_status_ =
|
||||||
Status(error::PARSER_FAILURE, "Cannot parse media file " + file_name_);
|
Status(error::PARSER_FAILURE, "Cannot parse media file " + file_name_);
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'../../../testing/gtest.gyp:gtest',
|
'../../../testing/gtest.gyp:gtest',
|
||||||
'../../../testing/gmock.gyp:gmock',
|
'../../../testing/gmock.gyp:gmock',
|
||||||
|
'../../file/file.gyp:file',
|
||||||
'../../test/media_test.gyp:media_test_support',
|
'../../test/media_test.gyp:media_test_support',
|
||||||
'mp4',
|
'mp4',
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,10 +13,13 @@
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/media/base/aes_encryptor.h"
|
#include "packager/media/base/aes_encryptor.h"
|
||||||
#include "packager/media/base/audio_stream_info.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/decrypt_config.h"
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/video_stream_info.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_definitions.h"
|
||||||
#include "packager/media/formats/mp4/box_reader.h"
|
#include "packager/media/formats/mp4/box_reader.h"
|
||||||
#include "packager/media/formats/mp4/es_descriptor.h"
|
#include "packager/media/formats/mp4/es_descriptor.h"
|
||||||
|
@ -108,6 +111,82 @@ bool MP4MediaParser::Parse(const uint8_t* buf, int size) {
|
||||||
return true;
|
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) {
|
bool MP4MediaParser::ParseBox(bool* err) {
|
||||||
const uint8_t* buf;
|
const uint8_t* buf;
|
||||||
int size;
|
int size;
|
||||||
|
@ -151,6 +230,9 @@ bool MP4MediaParser::ParseBox(bool* err) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
|
if (moov_)
|
||||||
|
return true; // Already parsed the 'moov' box.
|
||||||
|
|
||||||
moov_.reset(new Movie);
|
moov_.reset(new Movie);
|
||||||
RCHECK(moov_->Parse(reader));
|
RCHECK(moov_->Parse(reader));
|
||||||
runs_.reset();
|
runs_.reset();
|
||||||
|
@ -530,10 +612,6 @@ bool MP4MediaParser::ReadAndDiscardMDATsUntil(const int64_t offset) {
|
||||||
if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err))
|
if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (type != FOURCC_MDAT) {
|
|
||||||
LOG(ERROR) << "Unexpected box type while parsing MDATs: "
|
|
||||||
<< FourCCToString(type);
|
|
||||||
}
|
|
||||||
mdat_tail_ += box_sz;
|
mdat_tail_ += box_sz;
|
||||||
}
|
}
|
||||||
queue_.Trim(std::min(mdat_tail_, offset));
|
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;
|
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:
|
private:
|
||||||
enum State {
|
enum State {
|
||||||
kWaitingForInit,
|
kWaitingForInit,
|
||||||
|
|
|
@ -91,7 +91,8 @@ class MP4MediaParserTest : public testing::Test {
|
||||||
|
|
||||||
bool ParseMP4File(const std::string& filename, int append_bytes) {
|
bool ParseMP4File(const std::string& filename, int append_bytes) {
|
||||||
InitializeParser(NULL);
|
InitializeParser(NULL);
|
||||||
|
if (!parser_->LoadMoov(GetTestDataFilePath(filename).value()))
|
||||||
|
return false;
|
||||||
std::vector<uint8_t> buffer = ReadTestDataFile(filename);
|
std::vector<uint8_t> buffer = ReadTestDataFile(filename);
|
||||||
return AppendDataInPieces(buffer.data(), buffer.size(), append_bytes);
|
return AppendDataInPieces(buffer.data(), buffer.size(), append_bytes);
|
||||||
}
|
}
|
||||||
|
@ -120,6 +121,12 @@ TEST_F(MP4MediaParserTest, MultiFragmentAppend) {
|
||||||
EXPECT_EQ(201u, num_samples_);
|
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) {
|
TEST_F(MP4MediaParserTest, Flush) {
|
||||||
// Flush while reading sample data, then start a new stream.
|
// Flush while reading sample data, then start a new stream.
|
||||||
InitializeParser(NULL);
|
InitializeParser(NULL);
|
||||||
|
@ -127,18 +134,10 @@ TEST_F(MP4MediaParserTest, Flush) {
|
||||||
std::vector<uint8_t> buffer = ReadTestDataFile("bear-1280x720-av_frag.mp4");
|
std::vector<uint8_t> buffer = ReadTestDataFile("bear-1280x720-av_frag.mp4");
|
||||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), 65536, 512));
|
EXPECT_TRUE(AppendDataInPieces(buffer.data(), 65536, 512));
|
||||||
parser_->Flush();
|
parser_->Flush();
|
||||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512));
|
|
||||||
EXPECT_EQ(2u, num_streams_);
|
EXPECT_EQ(2u, num_streams_);
|
||||||
EXPECT_EQ(201u, num_samples_);
|
EXPECT_NE(0u, num_samples_);
|
||||||
}
|
num_samples_ = 0;
|
||||||
|
|
||||||
TEST_F(MP4MediaParserTest, Reinitialization) {
|
|
||||||
InitializeParser(NULL);
|
|
||||||
|
|
||||||
std::vector<uint8_t> buffer = ReadTestDataFile("bear-1280x720-av_frag.mp4");
|
|
||||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512));
|
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_);
|
EXPECT_EQ(201u, num_samples_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue