Removed EditList, added NAL and several other cleanup.
Change-Id: I2658eae0789f1c4e8d0534a6ff70267058bee2fc
This commit is contained in:
parent
21aad421ce
commit
dc88702315
|
@ -60,10 +60,9 @@ Status Demuxer::Initialize() {
|
|||
return Status(error::UNIMPLEMENTED, "Container not supported.");
|
||||
}
|
||||
|
||||
parser_->Init(
|
||||
base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)),
|
||||
base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)),
|
||||
base::Bind(&Demuxer::KeyNeededEvent, base::Unretained(this)));
|
||||
parser_->Init(base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)),
|
||||
base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)),
|
||||
base::Bind(&Demuxer::KeyNeededEvent, base::Unretained(this)));
|
||||
|
||||
if (!parser_->Parse(buffer_.get(), bytes_read))
|
||||
return Status(error::PARSER_FAILURE,
|
||||
|
@ -72,7 +71,8 @@ Status Demuxer::Initialize() {
|
|||
// TODO(kqyang): Does not look clean. Consider refactoring later.
|
||||
Status status;
|
||||
while (!init_event_received_) {
|
||||
if (!(status = Parse()).ok()) break;
|
||||
if (!(status = Parse()).ok())
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -87,8 +87,8 @@ void Demuxer::ParserInitEvent(
|
|||
}
|
||||
}
|
||||
|
||||
bool Demuxer::NewSampleEvent(
|
||||
uint32 track_id, const scoped_refptr<MediaSample>& sample) {
|
||||
bool Demuxer::NewSampleEvent(uint32 track_id,
|
||||
const scoped_refptr<MediaSample>& sample) {
|
||||
std::vector<MediaStream*>::iterator it = streams_.begin();
|
||||
for (; it != streams_.end(); ++it) {
|
||||
if (track_id == (*it)->info()->track_id()) {
|
||||
|
@ -98,9 +98,9 @@ bool Demuxer::NewSampleEvent(
|
|||
return false;
|
||||
}
|
||||
|
||||
void Demuxer::KeyNeededEvent(
|
||||
MediaContainerName container, scoped_ptr<uint8[]> init_data,
|
||||
int init_data_size) {
|
||||
void Demuxer::KeyNeededEvent(MediaContainerName container,
|
||||
scoped_ptr<uint8[]> init_data,
|
||||
int init_data_size) {
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
@ -116,8 +116,9 @@ Status Demuxer::Run() {
|
|||
return status;
|
||||
}
|
||||
|
||||
while ((status = Parse()).ok()) continue;
|
||||
return status.Matches(Status(error::EOF, "")) ? Status::OK : status;
|
||||
while ((status = Parse()).ok())
|
||||
continue;
|
||||
return status.Matches(Status(error::END_OF_STREAM, "")) ? Status::OK : status;
|
||||
}
|
||||
|
||||
Status Demuxer::Parse() {
|
||||
|
@ -127,9 +128,9 @@ Status Demuxer::Parse() {
|
|||
|
||||
int64 bytes_read = media_file_->Read(buffer_.get(), kBufSize);
|
||||
if (bytes_read <= 0) {
|
||||
return media_file_->Eof() ?
|
||||
Status(error::EOF, "End of file") :
|
||||
Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
|
||||
return media_file_->Eof()
|
||||
? Status(error::END_OF_STREAM, "End of stream.")
|
||||
: Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
|
||||
}
|
||||
|
||||
return parser_->Parse(buffer_.get(), bytes_read)
|
||||
|
|
|
@ -43,16 +43,10 @@ class Demuxer {
|
|||
// Reads from the source and send it to the parser.
|
||||
Status Parse();
|
||||
|
||||
uint32 num_streams() const {
|
||||
return streams_.size();
|
||||
}
|
||||
|
||||
// Demuxer retains the ownership of streams.
|
||||
MediaStream* streams(uint32 index) {
|
||||
if (index >= streams_.size())
|
||||
return NULL;
|
||||
return streams_[index];
|
||||
}
|
||||
// Returns the vector of streams in this Demuxer. The caller cannot add or
|
||||
// remove streams from the returned vector, but the caller could change
|
||||
// the internal state of the streams in the vector through MediaStream APIs.
|
||||
const std::vector<MediaStream*>& streams() { return streams_; }
|
||||
|
||||
private:
|
||||
// Parser event handlers.
|
||||
|
@ -73,7 +67,6 @@ class Demuxer {
|
|||
|
||||
DISALLOW_COPY_AND_ASSIGN(Demuxer);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MEDIA_BASE_DEMUXER_H_
|
||||
|
|
|
@ -20,8 +20,7 @@ class DecryptConfig;
|
|||
|
||||
// Holds media sample. Also includes decoder specific functionality for
|
||||
// decryption.
|
||||
class MediaSample
|
||||
: public base::RefCountedThreadSafe<MediaSample> {
|
||||
class MediaSample : public base::RefCountedThreadSafe<MediaSample> {
|
||||
public:
|
||||
// Create a MediaSample whose |data_| is copied from |data|.
|
||||
// |data| must not be NULL and |size| >= 0.
|
||||
|
@ -82,12 +81,12 @@ class MediaSample
|
|||
|
||||
const uint8* data() const {
|
||||
DCHECK(!end_of_stream());
|
||||
return data_.data();
|
||||
return &data_[0];
|
||||
}
|
||||
|
||||
uint8* writable_data() {
|
||||
DCHECK(!end_of_stream());
|
||||
return data_.data();
|
||||
return &data_[0];
|
||||
}
|
||||
|
||||
int data_size() const {
|
||||
|
@ -97,7 +96,7 @@ class MediaSample
|
|||
|
||||
const uint8* side_data() const {
|
||||
DCHECK(!end_of_stream());
|
||||
return side_data_.data();
|
||||
return &side_data_[0];
|
||||
}
|
||||
|
||||
int side_data_size() const {
|
||||
|
@ -116,9 +115,7 @@ class MediaSample
|
|||
}
|
||||
|
||||
// If there's no data in this buffer, it represents end of stream.
|
||||
bool end_of_stream() const {
|
||||
return data_.size() == 0;
|
||||
}
|
||||
bool end_of_stream() const { return data_.size() == 0; }
|
||||
|
||||
// Returns a human-readable string describing |*this|.
|
||||
std::string ToString() const;
|
||||
|
|
|
@ -59,7 +59,6 @@ Status MediaStream::Start(MediaStreamOperation operation) {
|
|||
DCHECK(demuxer_ != NULL);
|
||||
DCHECK(operation == kPush || operation == kPull);
|
||||
|
||||
|
||||
switch (state_) {
|
||||
case kIdle:
|
||||
// Disconnect the stream if it is not connected to a muxer.
|
||||
|
@ -79,8 +78,8 @@ Status MediaStream::Start(MediaStreamOperation operation) {
|
|||
} else {
|
||||
// We need to disconnect all its peer streams which are not connected
|
||||
// to a muxer.
|
||||
for (int i = 0; i < demuxer_->num_streams(); ++i) {
|
||||
Status status = demuxer_->streams(i)->Start(operation);
|
||||
for (int i = 0; i < demuxer_->streams().size(); ++i) {
|
||||
Status status = demuxer_->streams()[i]->Start(operation);
|
||||
if (!status.ok())
|
||||
return status;
|
||||
}
|
||||
|
@ -95,14 +94,11 @@ Status MediaStream::Start(MediaStreamOperation operation) {
|
|||
}
|
||||
}
|
||||
|
||||
scoped_refptr<StreamInfo> MediaStream::info() {
|
||||
return info_;
|
||||
}
|
||||
const scoped_refptr<StreamInfo> MediaStream::info() const { return info_; }
|
||||
|
||||
std::string MediaStream::ToString() const {
|
||||
std::ostringstream s;
|
||||
s << "state: " << state_
|
||||
<< " samples in the queue: " << samples_.size()
|
||||
s << "state: " << state_ << " samples in the queue: " << samples_.size()
|
||||
<< " " << info_->ToString();
|
||||
return s.str();
|
||||
}
|
||||
|
|
|
@ -43,13 +43,9 @@ class MediaStream {
|
|||
// Pull sample from Demuxer (triggered by Muxer).
|
||||
Status PullSample(scoped_refptr<MediaSample>* sample);
|
||||
|
||||
Demuxer* demuxer() {
|
||||
return demuxer_;
|
||||
}
|
||||
Muxer* muxer() {
|
||||
return muxer_;
|
||||
}
|
||||
scoped_refptr<StreamInfo> info();
|
||||
Demuxer* demuxer() { return demuxer_; }
|
||||
Muxer* muxer() { return muxer_; }
|
||||
const scoped_refptr<StreamInfo> info() const;
|
||||
|
||||
// Returns a human-readable string describing |*this|.
|
||||
std::string ToString() const;
|
||||
|
|
|
@ -37,12 +37,18 @@ enum Code {
|
|||
// Cannot open file.
|
||||
FILE_FAILURE,
|
||||
|
||||
// End of file.
|
||||
EOF,
|
||||
// End of stream.
|
||||
END_OF_STREAM,
|
||||
|
||||
// Unable to parse the media file.
|
||||
PARSER_FAILURE,
|
||||
|
||||
// Fail to mux the media file.
|
||||
MUXER_FAILURE,
|
||||
|
||||
// This track fragment is finalized.
|
||||
FRAGMENT_FINALIZED,
|
||||
|
||||
// TODO(kqyang): define packager specific error codes.
|
||||
};
|
||||
|
||||
|
@ -56,18 +62,16 @@ class Status {
|
|||
// Create a status with the specified code, and error message. If "code == 0",
|
||||
// error_message is ignored and a Status object identical to Status::OK is
|
||||
// constructed.
|
||||
Status(error::Code error_code, const std::string& error_message) :
|
||||
error_code_(error_code) {
|
||||
Status(error::Code error_code, const std::string& error_message)
|
||||
: error_code_(error_code) {
|
||||
if (!ok())
|
||||
error_message_ = error_message;
|
||||
}
|
||||
|
||||
//Status(const Status&);
|
||||
//Status& operator=(const Status& x);
|
||||
~Status() {}
|
||||
|
||||
// Some pre-defined Status objects.
|
||||
static const Status& OK; // Identical to 0-arg constructor.
|
||||
static const Status& OK; // Identical to 0-arg constructor.
|
||||
static const Status& UNKNOWN;
|
||||
|
||||
// Store the specified error in this Status object.
|
||||
|
@ -102,28 +106,18 @@ class Status {
|
|||
}
|
||||
|
||||
// Accessor.
|
||||
bool ok() const {
|
||||
return error_code_ == error::OK;
|
||||
}
|
||||
error::Code error_code() const {
|
||||
return error_code_;
|
||||
}
|
||||
const std::string& error_message() const {
|
||||
return error_message_;
|
||||
}
|
||||
bool ok() const { return error_code_ == error::OK; }
|
||||
error::Code error_code() const { return error_code_; }
|
||||
const std::string& error_message() const { return error_message_; }
|
||||
|
||||
bool operator==(const Status& x) const {
|
||||
return error_code_ == x.error_code() && error_message_ == x.error_message();
|
||||
}
|
||||
bool operator!=(const Status& x) const {
|
||||
return !(*this == x);
|
||||
}
|
||||
bool operator!=(const Status& x) const { return !(*this == x); }
|
||||
|
||||
// Returns true iff this has the same error_code as "x". I.e., the two
|
||||
// Status objects are identical except possibly for the error message.
|
||||
bool Matches(const Status& x) const {
|
||||
return error_code_ == x.error_code();
|
||||
}
|
||||
bool Matches(const Status& x) const { return error_code_ == x.error_code(); }
|
||||
|
||||
// Return a combination of the error code name and message.
|
||||
std::string ToString() const;
|
||||
|
|
|
@ -17,7 +17,7 @@ enum StreamType {
|
|||
kStreamVideo,
|
||||
};
|
||||
|
||||
class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
|
||||
class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
|
||||
public:
|
||||
StreamInfo(StreamType stream_type,
|
||||
int track_id,
|
||||
|
@ -46,12 +46,7 @@ class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
|
|||
|
||||
bool is_encrypted() const { return is_encrypted_; }
|
||||
|
||||
const uint8* extra_data() const {
|
||||
return extra_data_.empty() ? NULL : &extra_data_[0];
|
||||
}
|
||||
size_t extra_data_size() const {
|
||||
return extra_data_.size();
|
||||
}
|
||||
const std::vector<uint8>& extra_data() const { return extra_data_; }
|
||||
|
||||
void set_duration(int duration) { duration_ = duration; }
|
||||
|
||||
|
|
|
@ -15,8 +15,9 @@ base::FilePath GetTestDataFilePath(const std::string& name) {
|
|||
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
|
||||
|
||||
file_path = file_path.Append(FILE_PATH_LITERAL("media"))
|
||||
.Append(FILE_PATH_LITERAL("test")).Append(FILE_PATH_LITERAL("data"))
|
||||
.AppendASCII(name);
|
||||
.Append(FILE_PATH_LITERAL("test"))
|
||||
.Append(FILE_PATH_LITERAL("data"))
|
||||
.AppendASCII(name);
|
||||
return file_path;
|
||||
}
|
||||
|
||||
|
@ -25,8 +26,9 @@ std::vector<uint8> ReadTestDataFile(const std::string& name) {
|
|||
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
|
||||
|
||||
file_path = file_path.Append(FILE_PATH_LITERAL("media"))
|
||||
.Append(FILE_PATH_LITERAL("test")).Append(FILE_PATH_LITERAL("data"))
|
||||
.AppendASCII(name);
|
||||
.Append(FILE_PATH_LITERAL("test"))
|
||||
.Append(FILE_PATH_LITERAL("data"))
|
||||
.AppendASCII(name);
|
||||
|
||||
int64 tmp = 0;
|
||||
CHECK(file_util::GetFileSize(file_path, &tmp))
|
||||
|
@ -37,8 +39,8 @@ std::vector<uint8> ReadTestDataFile(const std::string& name) {
|
|||
|
||||
CHECK_EQ(file_size,
|
||||
file_util::ReadFile(
|
||||
file_path, reinterpret_cast<char*>(buffer.data()),
|
||||
file_size)) << "Failed to read '" << name << "'";
|
||||
file_path, reinterpret_cast<char*>(&buffer[0]), file_size))
|
||||
<< "Failed to read '" << name << "'";
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
|
|||
const std::string& language,
|
||||
uint16 width,
|
||||
uint16 height,
|
||||
uint8 nalu_length_size,
|
||||
const uint8* extra_data,
|
||||
size_t extra_data_size,
|
||||
bool is_encrypted)
|
||||
|
@ -34,14 +35,16 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
|
|||
is_encrypted),
|
||||
codec_(codec),
|
||||
width_(width),
|
||||
height_(height) {}
|
||||
height_(height),
|
||||
nalu_length_size_(nalu_length_size) {}
|
||||
|
||||
VideoStreamInfo::~VideoStreamInfo() {}
|
||||
|
||||
bool VideoStreamInfo::IsValidConfig() const {
|
||||
return codec_ != kUnknownVideoCodec &&
|
||||
width_ > 0 && width_ <= limits::kMaxDimension &&
|
||||
height_ > 0 && height_ <= limits::kMaxDimension;
|
||||
height_ > 0 && height_ <= limits::kMaxDimension &&
|
||||
(nalu_length_size_ <= 2 || nalu_length_size_ == 4);
|
||||
}
|
||||
|
||||
std::string VideoStreamInfo::ToString() const {
|
||||
|
@ -49,6 +52,7 @@ std::string VideoStreamInfo::ToString() const {
|
|||
s << "codec: " << codec_
|
||||
<< " width: " << width_
|
||||
<< " height: " << height_
|
||||
<< " nalu_length_size_: " << static_cast<int>(nalu_length_size_)
|
||||
<< " " << StreamInfo::ToString();
|
||||
return s.str();
|
||||
}
|
||||
|
@ -64,8 +68,8 @@ std::string VideoStreamInfo::GetCodecString(VideoCodec codec,
|
|||
return "vp9";
|
||||
case kCodecH264: {
|
||||
const uint8 bytes[] = {profile, compatible_profiles, level};
|
||||
return "avc1."
|
||||
+ StringToLowerASCII(base::HexEncode(bytes, arraysize(bytes)));
|
||||
return "avc1." +
|
||||
StringToLowerASCII(base::HexEncode(bytes, arraysize(bytes)));
|
||||
}
|
||||
default:
|
||||
NOTIMPLEMENTED() << "Codec: " << codec;
|
||||
|
|
|
@ -18,7 +18,6 @@ enum VideoCodec {
|
|||
kCodecTheora,
|
||||
kCodecVP8,
|
||||
kCodecVP9,
|
||||
|
||||
kNumVideoCodec
|
||||
};
|
||||
|
||||
|
@ -34,6 +33,7 @@ class VideoStreamInfo : public StreamInfo {
|
|||
const std::string& language,
|
||||
uint16 width,
|
||||
uint16 height,
|
||||
uint8 nalu_length_size,
|
||||
const uint8* extra_data,
|
||||
size_t extra_data_size,
|
||||
bool is_encrypted);
|
||||
|
@ -50,17 +50,25 @@ class VideoStreamInfo : public StreamInfo {
|
|||
VideoCodec codec() const { return codec_; }
|
||||
uint16 width() const { return width_; }
|
||||
uint16 height() const { return height_; }
|
||||
uint8 nalu_length_size() const { return nalu_length_size_; }
|
||||
|
||||
// Returns the codec string. The parameters beyond codec are only used by
|
||||
// H.264 codec.
|
||||
static std::string GetCodecString(VideoCodec codec, uint8 profile,
|
||||
uint8 compatible_profiles, uint8 level);
|
||||
static std::string GetCodecString(VideoCodec codec,
|
||||
uint8 profile,
|
||||
uint8 compatible_profiles,
|
||||
uint8 level);
|
||||
|
||||
private:
|
||||
VideoCodec codec_;
|
||||
uint16 width_;
|
||||
uint16 height_;
|
||||
|
||||
// Specifies the normalized size of the NAL unit length field. Can be 1, 2 or
|
||||
// 4 bytes, or 0 if the size if unknown or the stream is not a AVC stream
|
||||
// (H.264).
|
||||
uint8 nalu_length_size_;
|
||||
|
||||
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
|
||||
// generated copy constructor and assignment operator. Since the extra data is
|
||||
// typically small, the performance impact is minimal.
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#include "media/file/file.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/file_util.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
|
|
|
@ -37,8 +37,7 @@ MP4MediaParser::MP4MediaParser()
|
|||
audio_track_id_(0),
|
||||
video_track_id_(0),
|
||||
is_audio_track_encrypted_(false),
|
||||
is_video_track_encrypted_(false) {
|
||||
}
|
||||
is_video_track_encrypted_(false) {}
|
||||
|
||||
MP4MediaParser::~MP4MediaParser() {}
|
||||
|
||||
|
@ -102,10 +101,12 @@ bool MP4MediaParser::ParseBox(bool* err) {
|
|||
const uint8* buf;
|
||||
int size;
|
||||
queue_.Peek(&buf, &size);
|
||||
if (!size) return false;
|
||||
if (!size)
|
||||
return false;
|
||||
|
||||
scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err));
|
||||
if (reader.get() == NULL) return false;
|
||||
if (reader.get() == NULL)
|
||||
return false;
|
||||
|
||||
// Set up mdat offset for ReadMDATsUntil().
|
||||
mdat_tail_ = queue_.head() + reader->size();
|
||||
|
@ -130,7 +131,6 @@ bool MP4MediaParser::ParseBox(bool* err) {
|
|||
return !(*err);
|
||||
}
|
||||
|
||||
|
||||
bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||
moov_.reset(new Movie);
|
||||
RCHECK(moov_->Parse(reader));
|
||||
|
@ -152,12 +152,13 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
} else if (moov_->extends.header.fragment_duration > 0) {
|
||||
DCHECK(moov_->header.timescale != 0);
|
||||
duration = Rescale(moov_->extends.header.fragment_duration,
|
||||
moov_->header.timescale, timescale);
|
||||
moov_->header.timescale,
|
||||
timescale);
|
||||
} else if (moov_->header.duration > 0 &&
|
||||
moov_->header.duration != kuint64max) {
|
||||
DCHECK(moov_->header.timescale != 0);
|
||||
duration = Rescale(moov_->header.duration, moov_->header.timescale,
|
||||
timescale);
|
||||
duration =
|
||||
Rescale(moov_->header.duration, moov_->header.timescale, timescale);
|
||||
}
|
||||
|
||||
// TODO(strobe): Only the first audio and video track present in a file are
|
||||
|
@ -202,7 +203,6 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
if (desc_idx >= samp_descr.audio_entries.size())
|
||||
desc_idx = 0;
|
||||
const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
|
||||
const AAC& aac = entry.esds.aac;
|
||||
|
||||
if (!(entry.format == FOURCC_MP4A || entry.format == FOURCC_EAC3 ||
|
||||
(entry.format == FOURCC_ENCA &&
|
||||
|
@ -212,7 +212,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
return false;
|
||||
}
|
||||
|
||||
ObjectType audio_type = static_cast<ObjectType>(entry.esds.object_type);
|
||||
ObjectType audio_type = entry.esds.es_descriptor.object_type();
|
||||
DVLOG(1) << "audio_type " << std::hex << audio_type;
|
||||
if (audio_type == kForbidden && entry.format == FOURCC_EAC3) {
|
||||
audio_type = kEAC3;
|
||||
|
@ -225,12 +225,13 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
std::vector<uint8> extra_data;
|
||||
// Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or
|
||||
// supported MPEG2 AAC variants.
|
||||
if (ESDescriptor::IsAAC(audio_type)) {
|
||||
if (entry.esds.es_descriptor.IsAAC()) {
|
||||
codec = kCodecAAC;
|
||||
const AAC& aac = entry.esds.aac;
|
||||
num_channels = aac.num_channels();
|
||||
sampling_frequency = aac.frequency();
|
||||
audio_object_type = aac.audio_object_type();
|
||||
extra_data = aac.codec_specific_data();
|
||||
extra_data = entry.esds.es_descriptor.decoder_specific_info();
|
||||
} else if (audio_type == kEAC3) {
|
||||
codec = kCodecEAC3;
|
||||
num_channels = entry.channelcount;
|
||||
|
@ -243,20 +244,19 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
|
||||
is_audio_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted;
|
||||
DVLOG(1) << "is_audio_track_encrypted_: " << is_audio_track_encrypted_;
|
||||
streams.push_back(
|
||||
new AudioStreamInfo(
|
||||
track->header.track_id,
|
||||
timescale,
|
||||
duration,
|
||||
codec,
|
||||
AudioStreamInfo::GetCodecString(codec, audio_object_type),
|
||||
track->media.header.language,
|
||||
entry.samplesize,
|
||||
num_channels,
|
||||
sampling_frequency,
|
||||
extra_data.size() ? &extra_data[0] : NULL,
|
||||
extra_data.size(),
|
||||
is_audio_track_encrypted_));
|
||||
streams.push_back(new AudioStreamInfo(
|
||||
track->header.track_id,
|
||||
timescale,
|
||||
duration,
|
||||
codec,
|
||||
AudioStreamInfo::GetCodecString(codec, audio_object_type),
|
||||
track->media.header.language,
|
||||
entry.samplesize,
|
||||
num_channels,
|
||||
sampling_frequency,
|
||||
extra_data.size() ? &extra_data[0] : NULL,
|
||||
extra_data.size(),
|
||||
is_audio_track_encrypted_));
|
||||
has_audio_ = true;
|
||||
audio_track_id_ = track->header.track_id;
|
||||
}
|
||||
|
@ -280,25 +280,25 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
}
|
||||
|
||||
const std::string codec_string =
|
||||
VideoStreamInfo::GetCodecString(
|
||||
kCodecH264, entry.avcc.profile_indication,
|
||||
entry.avcc.profile_compatibility, entry.avcc.avc_level);
|
||||
VideoStreamInfo::GetCodecString(kCodecH264,
|
||||
entry.avcc.profile_indication,
|
||||
entry.avcc.profile_compatibility,
|
||||
entry.avcc.avc_level);
|
||||
|
||||
is_video_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted;
|
||||
DVLOG(1) << "is_video_track_encrypted_: " << is_video_track_encrypted_;
|
||||
streams.push_back(
|
||||
new VideoStreamInfo(
|
||||
track->header.track_id,
|
||||
timescale,
|
||||
duration,
|
||||
kCodecH264,
|
||||
codec_string,
|
||||
track->media.header.language,
|
||||
entry.width,
|
||||
entry.height,
|
||||
&entry.avcc.data[0],
|
||||
entry.avcc.data.size(),
|
||||
is_video_track_encrypted_));
|
||||
streams.push_back(new VideoStreamInfo(track->header.track_id,
|
||||
timescale,
|
||||
duration,
|
||||
kCodecH264,
|
||||
codec_string,
|
||||
track->media.header.language,
|
||||
entry.width,
|
||||
entry.height,
|
||||
entry.avcc.length_size,
|
||||
&entry.avcc.data[0],
|
||||
entry.avcc.data.size(),
|
||||
is_video_track_encrypted_));
|
||||
has_video_ = true;
|
||||
video_track_id_ = track->header.track_id;
|
||||
}
|
||||
|
@ -338,7 +338,8 @@ void MP4MediaParser::EmitNeedKeyIfNecessary(
|
|||
scoped_ptr<uint8[]> init_data(new uint8[total_size]);
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i < headers.size(); i++) {
|
||||
memcpy(&init_data.get()[pos], &headers[i].raw_box[0],
|
||||
memcpy(&init_data.get()[pos],
|
||||
&headers[i].raw_box[0],
|
||||
headers[i].raw_box.size());
|
||||
pos += headers[i].raw_box.size();
|
||||
}
|
||||
|
@ -366,7 +367,8 @@ bool MP4MediaParser::EnqueueSample(bool* err) {
|
|||
const uint8* buf;
|
||||
int buf_size;
|
||||
queue_.Peek(&buf, &buf_size);
|
||||
if (!buf_size) return false;
|
||||
if (!buf_size)
|
||||
return false;
|
||||
|
||||
bool audio = has_audio_ && audio_track_id_ == runs_->track_id();
|
||||
bool video = has_video_ && video_track_id_ == runs_->track_id();
|
||||
|
@ -384,13 +386,15 @@ bool MP4MediaParser::EnqueueSample(bool* err) {
|
|||
// portion of the total system memory.
|
||||
if (runs_->AuxInfoNeedsToBeCached()) {
|
||||
queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size);
|
||||
if (buf_size < runs_->aux_info_size()) return false;
|
||||
if (buf_size < runs_->aux_info_size())
|
||||
return false;
|
||||
*err = !runs_->CacheAuxInfo(buf, buf_size);
|
||||
return !*err;
|
||||
}
|
||||
|
||||
queue_.PeekAt(runs_->sample_offset() + moof_head_, &buf, &buf_size);
|
||||
if (buf_size < runs_->sample_size()) return false;
|
||||
if (buf_size < runs_->sample_size())
|
||||
return false;
|
||||
|
||||
scoped_ptr<DecryptConfig> decrypt_config;
|
||||
std::vector<SubsampleEntry> subsamples;
|
||||
|
@ -405,12 +409,11 @@ bool MP4MediaParser::EnqueueSample(bool* err) {
|
|||
|
||||
if (decrypt_config) {
|
||||
if (!subsamples.empty()) {
|
||||
// Create a new config with the updated subsamples.
|
||||
decrypt_config.reset(new DecryptConfig(
|
||||
decrypt_config->key_id(),
|
||||
decrypt_config->iv(),
|
||||
decrypt_config->data_offset(),
|
||||
subsamples));
|
||||
// Create a new config with the updated subsamples.
|
||||
decrypt_config.reset(new DecryptConfig(decrypt_config->key_id(),
|
||||
decrypt_config->iv(),
|
||||
decrypt_config->data_offset(),
|
||||
subsamples));
|
||||
}
|
||||
// else, use the existing config.
|
||||
}
|
||||
|
|
|
@ -12,10 +12,6 @@
|
|||
#include "media/mp4/rcheck.h"
|
||||
#include "media/mp4/sync_sample_iterator.h"
|
||||
|
||||
namespace {
|
||||
static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
|
||||
}
|
||||
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
|
@ -54,12 +50,11 @@ TrackRunInfo::TrackRunInfo()
|
|||
is_audio(false),
|
||||
aux_info_start_offset(-1),
|
||||
aux_info_default_size(0),
|
||||
aux_info_total_size(0) {
|
||||
}
|
||||
aux_info_total_size(0) {}
|
||||
TrackRunInfo::~TrackRunInfo() {}
|
||||
|
||||
TrackRunIterator::TrackRunIterator(const Movie* moov)
|
||||
: moov_(moov), sample_offset_(0) {
|
||||
: moov_(moov), sample_dts_(0), sample_offset_(0) {
|
||||
CHECK(moov);
|
||||
}
|
||||
|
||||
|
@ -68,7 +63,6 @@ TrackRunIterator::~TrackRunIterator() {}
|
|||
static void PopulateSampleInfo(const TrackExtends& trex,
|
||||
const TrackFragmentHeader& tfhd,
|
||||
const TrackFragmentRun& trun,
|
||||
const int64 edit_list_offset,
|
||||
const uint32 i,
|
||||
SampleInfo* sample_info) {
|
||||
if (i < trun.sample_sizes.size()) {
|
||||
|
@ -92,17 +86,16 @@ static void PopulateSampleInfo(const TrackExtends& trex,
|
|||
} else {
|
||||
sample_info->cts_offset = 0;
|
||||
}
|
||||
sample_info->cts_offset += edit_list_offset;
|
||||
|
||||
uint32 flags;
|
||||
if (i < trun.sample_flags.size()) {
|
||||
flags = trun.sample_flags[i];
|
||||
} else if (tfhd.has_default_sample_flags) {
|
||||
} else if (tfhd.flags & kDefaultSampleFlagsPresentMask) {
|
||||
flags = tfhd.default_sample_flags;
|
||||
} else {
|
||||
flags = trex.default_sample_flags;
|
||||
}
|
||||
sample_info->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask);
|
||||
sample_info->is_keyframe = !(flags & kNonKeySampleMask);
|
||||
}
|
||||
|
||||
// In well-structured encrypted media, each track run will be immediately
|
||||
|
@ -127,7 +120,8 @@ class CompareMinTrackRunDataOffset {
|
|||
int64 b_lesser = std::min(b_aux, b.sample_start_offset);
|
||||
int64 b_greater = std::max(b_aux, b.sample_start_offset);
|
||||
|
||||
if (a_lesser == b_lesser) return a_greater < b_greater;
|
||||
if (a_lesser == b_lesser)
|
||||
return a_greater < b_greater;
|
||||
return a_lesser < b_lesser;
|
||||
}
|
||||
};
|
||||
|
@ -136,7 +130,7 @@ bool TrackRunIterator::Init() {
|
|||
runs_.clear();
|
||||
|
||||
for (std::vector<Track>::const_iterator trak = moov_->tracks.begin();
|
||||
trak != moov_->tracks.end(); ++trak) {
|
||||
trak != moov_->tracks.end(); ++trak) {
|
||||
const SampleDescription& stsd =
|
||||
trak->media.information.sample_table.description;
|
||||
if (stsd.type != kAudio && stsd.type != kVideo) {
|
||||
|
@ -144,21 +138,17 @@ bool TrackRunIterator::Init() {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Process edit list to remove CTS offset introduced in the presence of
|
||||
// B-frames (those that contain a single edit with a nonnegative media
|
||||
// time). Other uses of edit lists are not supported, as they are
|
||||
// both uncommon and better served by higher-level protocols.
|
||||
int64 edit_list_offset = 0;
|
||||
// Edit list is ignored.
|
||||
// We may consider supporting the single edit with a nonnegative media time
|
||||
// if it is required. Just need to pass the media_time to Muxer and
|
||||
// generate the edit list.
|
||||
const std::vector<EditListEntry>& edits = trak->edit.list.edits;
|
||||
if (!edits.empty()) {
|
||||
if (edits.size() > 1)
|
||||
DVLOG(1) << "Multi-entry edit box detected; some components ignored.";
|
||||
DVLOG(1) << "Multi-entry edit box detected.";
|
||||
|
||||
if (edits[0].media_time < 0) {
|
||||
DVLOG(1) << "Empty edit list entry ignored.";
|
||||
} else {
|
||||
edit_list_offset = -edits[0].media_time;
|
||||
}
|
||||
LOG(INFO) << "Edit list with media time " << edits[0].media_time
|
||||
<< " ignored.";
|
||||
}
|
||||
|
||||
DecodingTimeIterator decoding_time(
|
||||
|
@ -176,7 +166,7 @@ bool TrackRunIterator::Init() {
|
|||
const SampleSize& sample_size =
|
||||
trak->media.information.sample_table.sample_size;
|
||||
const std::vector<uint64>& chunk_offset_vector =
|
||||
trak->media.information.sample_table.chunk_offset.offsets;
|
||||
trak->media.information.sample_table.chunk_large_offset.offsets;
|
||||
|
||||
int64 run_start_dts = 0;
|
||||
int64 run_data_offset = 0;
|
||||
|
@ -188,7 +178,7 @@ bool TrackRunIterator::Init() {
|
|||
DCHECK(num_samples == decoding_time.NumSamples() &&
|
||||
num_samples == composition_offset.NumSamples() &&
|
||||
(num_chunks == 0 ||
|
||||
num_samples == chunk_info.NumSamples(1, num_chunks)) &&
|
||||
num_samples == chunk_info.NumSamples(1, num_chunks)) &&
|
||||
num_chunks >= chunk_info.LastFirstChunk());
|
||||
|
||||
if (num_samples > 0) {
|
||||
|
@ -233,13 +223,12 @@ bool TrackRunIterator::Init() {
|
|||
tri.samples.resize(samples_per_chunk);
|
||||
for (uint32 k = 0; k < samples_per_chunk; ++k) {
|
||||
SampleInfo& sample = tri.samples[k];
|
||||
sample.size =
|
||||
sample_size.sample_size != 0 ?
|
||||
sample_size.sample_size : sample_size.sizes[sample_index];
|
||||
sample.size = sample_size.sample_size != 0
|
||||
? sample_size.sample_size
|
||||
: sample_size.sizes[sample_index];
|
||||
sample.duration = decoding_time.sample_delta();
|
||||
sample.cts_offset =
|
||||
has_composition_offset ? composition_offset.sample_offset() : 0;
|
||||
sample.cts_offset += edit_list_offset;
|
||||
sample.is_keyframe = sync_sample.IsSyncSample();
|
||||
|
||||
run_start_dts += sample.duration;
|
||||
|
@ -297,27 +286,11 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
|
|||
continue;
|
||||
}
|
||||
size_t desc_idx = traf.header.sample_description_index;
|
||||
if (!desc_idx) desc_idx = trex->default_sample_description_index;
|
||||
if (!desc_idx)
|
||||
desc_idx = trex->default_sample_description_index;
|
||||
RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file
|
||||
desc_idx -= 1;
|
||||
|
||||
// Process edit list to remove CTS offset introduced in the presence of
|
||||
// B-frames (those that contain a single edit with a nonnegative media
|
||||
// time). Other uses of edit lists are not supported, as they are
|
||||
// both uncommon and better served by higher-level protocols.
|
||||
int64 edit_list_offset = 0;
|
||||
const std::vector<EditListEntry>& edits = trak->edit.list.edits;
|
||||
if (!edits.empty()) {
|
||||
if (edits.size() > 1)
|
||||
DVLOG(1) << "Multi-entry edit box detected; some components ignored.";
|
||||
|
||||
if (edits[0].media_time < 0) {
|
||||
DVLOG(1) << "Empty edit list entry ignored.";
|
||||
} else {
|
||||
edit_list_offset = -edits[0].media_time;
|
||||
}
|
||||
}
|
||||
|
||||
int64 run_start_dts = traf.decode_time.decode_time;
|
||||
int sample_count_sum = 0;
|
||||
|
||||
|
@ -356,7 +329,8 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
|
|||
if (tri.aux_info_default_size == 0) {
|
||||
const std::vector<uint8>& sizes =
|
||||
traf.auxiliary_size.sample_info_sizes;
|
||||
tri.aux_info_sizes.insert(tri.aux_info_sizes.begin(),
|
||||
tri.aux_info_sizes.insert(
|
||||
tri.aux_info_sizes.begin(),
|
||||
sizes.begin() + sample_count_sum,
|
||||
sizes.begin() + sample_count_sum + trun.sample_count);
|
||||
}
|
||||
|
@ -380,8 +354,7 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
|
|||
|
||||
tri.samples.resize(trun.sample_count);
|
||||
for (size_t k = 0; k < trun.sample_count; k++) {
|
||||
PopulateSampleInfo(*trex, traf.header, trun, edit_list_offset,
|
||||
k, &tri.samples[k]);
|
||||
PopulateSampleInfo(*trex, traf.header, trun, k, &tri.samples[k]);
|
||||
run_start_dts += tri.samples[k].duration;
|
||||
}
|
||||
runs_.push_back(tri);
|
||||
|
@ -401,7 +374,8 @@ void TrackRunIterator::AdvanceRun() {
|
|||
}
|
||||
|
||||
void TrackRunIterator::ResetRun() {
|
||||
if (!IsRunValid()) return;
|
||||
if (!IsRunValid())
|
||||
return;
|
||||
sample_dts_ = run_itr_->start_dts;
|
||||
sample_offset_ = run_itr_->sample_start_offset;
|
||||
sample_itr_ = run_itr_->samples.begin();
|
||||
|
@ -441,9 +415,7 @@ bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool TrackRunIterator::IsRunValid() const {
|
||||
return run_itr_ != runs_.end();
|
||||
}
|
||||
bool TrackRunIterator::IsRunValid() const { return run_itr_ != runs_.end(); }
|
||||
|
||||
bool TrackRunIterator::IsSampleValid() const {
|
||||
return IsRunValid() && (sample_itr_ != run_itr_->samples.end());
|
||||
|
@ -471,7 +443,8 @@ int64 TrackRunIterator::GetMaxClearOffset() {
|
|||
offset = std::min(offset, next_run->aux_info_start_offset);
|
||||
}
|
||||
}
|
||||
if (offset == kint64max) return 0;
|
||||
if (offset == kint64max)
|
||||
return 0;
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -552,9 +525,8 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
|
|||
const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
|
||||
DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
|
||||
|
||||
if (!cenc_info.subsamples.empty() &&
|
||||
(cenc_info.GetTotalSizeOfSubsamples() !=
|
||||
static_cast<size_t>(sample_size()))) {
|
||||
if (!cenc_info.subsamples.empty() && (cenc_info.GetTotalSizeOfSubsamples() !=
|
||||
static_cast<size_t>(sample_size()))) {
|
||||
LOG(ERROR) << "Incorrect CENC subsample size.";
|
||||
return scoped_ptr<DecryptConfig>();
|
||||
}
|
||||
|
@ -562,8 +534,7 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
|
|||
const std::vector<uint8>& kid = track_encryption().default_kid;
|
||||
return scoped_ptr<DecryptConfig>(new DecryptConfig(
|
||||
std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
|
||||
std::string(reinterpret_cast<const char*>(cenc_info.iv),
|
||||
arraysize(cenc_info.iv)),
|
||||
std::string(cenc_info.iv.begin(), cenc_info.iv.end()),
|
||||
0, // No offset to start of media data in MP4 using CENC.
|
||||
cenc_info.subsamples));
|
||||
}
|
||||
|
|
|
@ -17,34 +17,29 @@ static const int kSumAscending1 = 45;
|
|||
static const int kAudioScale = 48000;
|
||||
static const int kVideoScale = 25;
|
||||
|
||||
static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
|
||||
|
||||
static const uint8 kAuxInfo[] = {
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
|
||||
0x00, 0x02,
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04
|
||||
};
|
||||
// Sample 1: IV (no subsumples).
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
// Sample 2: IV.
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
|
||||
// Sample 2: Subsample count.
|
||||
0x00, 0x02,
|
||||
// Sample 2: Subsample 1.
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 2: Subsample 2.
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
|
||||
|
||||
static const char kIv1[] = {
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
static const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, };
|
||||
|
||||
static const uint8 kKeyId[] = {
|
||||
0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
|
||||
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
|
||||
};
|
||||
static const uint8 kKeyId[] = {0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
|
||||
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44};
|
||||
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
class TrackRunIteratorTest : public testing::Test {
|
||||
public:
|
||||
TrackRunIteratorTest() {
|
||||
CreateMovie();
|
||||
}
|
||||
TrackRunIteratorTest() { CreateMovie(); }
|
||||
|
||||
protected:
|
||||
Movie moov_;
|
||||
|
@ -87,7 +82,7 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
moof.tracks.resize(2);
|
||||
moof.tracks[0].decode_time.decode_time = 0;
|
||||
moof.tracks[0].header.track_id = 1;
|
||||
moof.tracks[0].header.has_default_sample_flags = true;
|
||||
moof.tracks[0].header.flags = kDefaultSampleFlagsPresentMask;
|
||||
moof.tracks[0].header.default_sample_duration = 1024;
|
||||
moof.tracks[0].header.default_sample_size = 4;
|
||||
moof.tracks[0].runs.resize(2);
|
||||
|
@ -99,7 +94,7 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
moof.tracks[0].runs[1].data_offset = 10000;
|
||||
|
||||
moof.tracks[1].header.track_id = 2;
|
||||
moof.tracks[1].header.has_default_sample_flags = false;
|
||||
moof.tracks[1].header.flags = 0;
|
||||
moof.tracks[1].decode_time.decode_time = 10;
|
||||
moof.tracks[1].runs.resize(1);
|
||||
moof.tracks[1].runs[0].sample_count = 10;
|
||||
|
@ -108,8 +103,7 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
SetAscending(&moof.tracks[1].runs[0].sample_durations);
|
||||
moof.tracks[1].runs[0].sample_flags.resize(10);
|
||||
for (size_t i = 1; i < moof.tracks[1].runs[0].sample_flags.size(); i++) {
|
||||
moof.tracks[1].runs[0].sample_flags[i] =
|
||||
kSampleIsDifferenceSampleFlagMask;
|
||||
moof.tracks[1].runs[0].sample_flags[i] = kNonKeySampleMask;
|
||||
}
|
||||
|
||||
return moof;
|
||||
|
@ -121,17 +115,16 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
&track->media.information.sample_table.description;
|
||||
ProtectionSchemeInfo* sinf;
|
||||
if (!stsd->video_entries.empty()) {
|
||||
sinf = &stsd->video_entries[0].sinf;
|
||||
sinf = &stsd->video_entries[0].sinf;
|
||||
} else {
|
||||
sinf = &stsd->audio_entries[0].sinf;
|
||||
sinf = &stsd->audio_entries[0].sinf;
|
||||
}
|
||||
|
||||
sinf->type.type = FOURCC_CENC;
|
||||
sinf->info.track_encryption.is_encrypted = true;
|
||||
sinf->info.track_encryption.default_iv_size = 8;
|
||||
sinf->info.track_encryption.default_kid.insert(
|
||||
sinf->info.track_encryption.default_kid.begin(),
|
||||
kKeyId, kKeyId + arraysize(kKeyId));
|
||||
sinf->info.track_encryption.default_kid.assign(kKeyId,
|
||||
kKeyId + arraysize(kKeyId));
|
||||
}
|
||||
|
||||
// Add aux info covering the first track run to a TrackFragment, and update
|
||||
|
@ -148,7 +141,7 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
void SetAscending(std::vector<uint32>* vec) {
|
||||
vec->resize(10);
|
||||
for (size_t i = 0; i < vec->size(); i++)
|
||||
(*vec)[i] = i+1;
|
||||
(*vec)[i] = i + 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -177,7 +170,8 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
|
|||
EXPECT_TRUE(iter_->is_keyframe());
|
||||
|
||||
// Advance to the last sample in the current run, and test its properties
|
||||
for (int i = 0; i < 9; i++) iter_->AdvanceSample();
|
||||
for (int i = 0; i < 9; i++)
|
||||
iter_->AdvanceSample();
|
||||
EXPECT_EQ(iter_->track_id(), 1u);
|
||||
EXPECT_EQ(iter_->sample_offset(), 100 + kSumAscending1);
|
||||
EXPECT_EQ(iter_->sample_size(), 10);
|
||||
|
@ -192,7 +186,8 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
|
|||
// Test last sample of next run
|
||||
iter_->AdvanceRun();
|
||||
EXPECT_TRUE(iter_->is_keyframe());
|
||||
for (int i = 0; i < 9; i++) iter_->AdvanceSample();
|
||||
for (int i = 0; i < 9; i++)
|
||||
iter_->AdvanceSample();
|
||||
EXPECT_EQ(iter_->track_id(), 2u);
|
||||
EXPECT_EQ(iter_->sample_offset(), 200 + kSumAscending1);
|
||||
EXPECT_EQ(iter_->sample_size(), 10);
|
||||
|
@ -207,7 +202,7 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
|
|||
EXPECT_EQ(iter_->dts(), 1024 * 10);
|
||||
iter_->AdvanceSample();
|
||||
EXPECT_EQ(moof.tracks[0].runs[1].data_offset +
|
||||
moof.tracks[0].header.default_sample_size,
|
||||
moof.tracks[0].header.default_sample_size,
|
||||
iter_->sample_offset());
|
||||
iter_->AdvanceRun();
|
||||
EXPECT_FALSE(iter_->IsRunValid());
|
||||
|
@ -216,11 +211,10 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
|
|||
TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
|
||||
moov_.extends.tracks[0].default_sample_duration = 50;
|
||||
moov_.extends.tracks[0].default_sample_size = 3;
|
||||
moov_.extends.tracks[0].default_sample_flags =
|
||||
kSampleIsDifferenceSampleFlagMask;
|
||||
moov_.extends.tracks[0].default_sample_flags = kNonKeySampleMask;
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
MovieFragment moof = CreateFragment();
|
||||
moof.tracks[0].header.has_default_sample_flags = false;
|
||||
moof.tracks[0].header.flags = 0;
|
||||
moof.tracks[0].header.default_sample_size = 0;
|
||||
moof.tracks[0].header.default_sample_duration = 0;
|
||||
moof.tracks[0].runs[0].sample_sizes.clear();
|
||||
|
@ -239,9 +233,8 @@ TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) {
|
|||
// defaults for all subsequent samples
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
MovieFragment moof = CreateFragment();
|
||||
moof.tracks[1].header.has_default_sample_flags = true;
|
||||
moof.tracks[1].header.default_sample_flags =
|
||||
kSampleIsDifferenceSampleFlagMask;
|
||||
moof.tracks[1].header.flags = kDefaultSampleFlagsPresentMask;
|
||||
moof.tracks[1].header.default_sample_flags = kNonKeySampleMask;
|
||||
moof.tracks[1].runs[0].sample_flags.resize(1);
|
||||
ASSERT_TRUE(iter_->Init(moof));
|
||||
iter_->AdvanceRun();
|
||||
|
@ -251,7 +244,7 @@ TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) {
|
|||
}
|
||||
|
||||
TEST_F(TrackRunIteratorTest, ReorderingTest) {
|
||||
// Test frame reordering and edit list support. The frames have the following
|
||||
// Test frame reordering. The frames have the following
|
||||
// decode timestamps:
|
||||
//
|
||||
// 0ms 40ms 120ms 240ms
|
||||
|
@ -261,26 +254,14 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) {
|
|||
//
|
||||
// 0ms 40ms 160ms 240ms
|
||||
// | 0 | 2 - - | 1 - |
|
||||
|
||||
// Create an edit list with one entry, with an initial start time of 80ms
|
||||
// (that is, 2 / kVideoTimescale) and a duration of zero (which is treated as
|
||||
// infinite according to 14496-12:2012). This will cause the first 80ms of the
|
||||
// media timeline - which will be empty, due to CTS biasing - to be discarded.
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
EditListEntry entry;
|
||||
entry.segment_duration = 0;
|
||||
entry.media_time = 2;
|
||||
entry.media_rate_integer = 1;
|
||||
entry.media_rate_fraction = 0;
|
||||
moov_.tracks[1].edit.list.edits.push_back(entry);
|
||||
|
||||
// Add CTS offsets. Without bias, the CTS offsets for the first three frames
|
||||
// would simply be [0, 3, -2]. Since CTS offsets should be non-negative for
|
||||
// maximum compatibility, these values are biased up to [2, 5, 0], and the
|
||||
// extra 80ms is removed via the edit list.
|
||||
// maximum compatibility, these values are biased up to [2, 5, 0].
|
||||
MovieFragment moof = CreateFragment();
|
||||
std::vector<int32>& cts_offsets =
|
||||
moof.tracks[1].runs[0].sample_composition_time_offsets;
|
||||
moof.tracks[1].runs[0].sample_composition_time_offsets;
|
||||
cts_offsets.resize(10);
|
||||
cts_offsets[0] = 2;
|
||||
cts_offsets[1] = 5;
|
||||
|
@ -290,15 +271,15 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) {
|
|||
ASSERT_TRUE(iter_->Init(moof));
|
||||
iter_->AdvanceRun();
|
||||
EXPECT_EQ(iter_->dts(), 0);
|
||||
EXPECT_EQ(iter_->cts(), 0);
|
||||
EXPECT_EQ(iter_->cts(), 2);
|
||||
EXPECT_EQ(iter_->duration(), 1);
|
||||
iter_->AdvanceSample();
|
||||
EXPECT_EQ(iter_->dts(), 1);
|
||||
EXPECT_EQ(iter_->cts(), 4);
|
||||
EXPECT_EQ(iter_->cts(), 6);
|
||||
EXPECT_EQ(iter_->duration(), 2);
|
||||
iter_->AdvanceSample();
|
||||
EXPECT_EQ(iter_->dts(), 3);
|
||||
EXPECT_EQ(iter_->cts(), 1);
|
||||
EXPECT_EQ(iter_->cts(), 3);
|
||||
EXPECT_EQ(iter_->duration(), 3);
|
||||
}
|
||||
|
||||
|
@ -340,8 +321,8 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
|
|||
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[0].runs[0].data_offset);
|
||||
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
|
||||
ASSERT_EQ(arraysize(kKeyId), config->key_id().size());
|
||||
EXPECT_TRUE(!memcmp(kKeyId, config->key_id().data(),
|
||||
config->key_id().size()));
|
||||
EXPECT_TRUE(
|
||||
!memcmp(kKeyId, config->key_id().data(), config->key_id().size()));
|
||||
ASSERT_EQ(arraysize(kIv1), config->iv().size());
|
||||
EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
|
||||
EXPECT_TRUE(config->subsamples().empty());
|
||||
|
|
Loading…
Reference in New Issue