From dc8870231590b49ef90b8dd2858f840eb7701ab9 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Tue, 12 Nov 2013 12:32:44 -0800 Subject: [PATCH] Removed EditList, added NAL and several other cleanup. Change-Id: I2658eae0789f1c4e8d0534a6ff70267058bee2fc --- media/base/demuxer.cc | 31 +++---- media/base/demuxer.h | 15 +--- media/base/media_sample.h | 13 ++- media/base/media_stream.cc | 12 +-- media/base/media_stream.h | 10 +-- media/base/status.h | 38 ++++---- media/base/stream_info.h | 9 +- media/base/test_data_util.cc | 14 +-- media/base/video_stream_info.cc | 12 ++- media/base/video_stream_info.h | 14 ++- media/file/file_unittest.cc | 2 - media/mp4/mp4_media_parser.cc | 107 ++++++++++++----------- media/mp4/track_run_iterator.cc | 93 +++++++------------- media/mp4/track_run_iterator_unittest.cc | 97 +++++++++----------- 14 files changed, 203 insertions(+), 264 deletions(-) diff --git a/media/base/demuxer.cc b/media/base/demuxer.cc index bfeccb3edb..e0e14f5a5b 100644 --- a/media/base/demuxer.cc +++ b/media/base/demuxer.cc @@ -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& sample) { +bool Demuxer::NewSampleEvent(uint32 track_id, + const scoped_refptr& sample) { std::vector::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 init_data, - int init_data_size) { +void Demuxer::KeyNeededEvent(MediaContainerName container, + scoped_ptr 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) diff --git a/media/base/demuxer.h b/media/base/demuxer.h index 245b39f32c..26274a0879 100644 --- a/media/base/demuxer.h +++ b/media/base/demuxer.h @@ -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& streams() { return streams_; } private: // Parser event handlers. @@ -73,7 +67,6 @@ class Demuxer { DISALLOW_COPY_AND_ASSIGN(Demuxer); }; - } #endif // MEDIA_BASE_DEMUXER_H_ diff --git a/media/base/media_sample.h b/media/base/media_sample.h index 8560ffd486..69236dc2cc 100644 --- a/media/base/media_sample.h +++ b/media/base/media_sample.h @@ -20,8 +20,7 @@ class DecryptConfig; // Holds media sample. Also includes decoder specific functionality for // decryption. -class MediaSample - : public base::RefCountedThreadSafe { +class MediaSample : public base::RefCountedThreadSafe { 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; diff --git a/media/base/media_stream.cc b/media/base/media_stream.cc index dee8e2f088..a3c908d6f3 100644 --- a/media/base/media_stream.cc +++ b/media/base/media_stream.cc @@ -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 MediaStream::info() { - return info_; -} +const scoped_refptr 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(); } diff --git a/media/base/media_stream.h b/media/base/media_stream.h index 50b6c9501b..feadc6d67c 100644 --- a/media/base/media_stream.h +++ b/media/base/media_stream.h @@ -43,13 +43,9 @@ class MediaStream { // Pull sample from Demuxer (triggered by Muxer). Status PullSample(scoped_refptr* sample); - Demuxer* demuxer() { - return demuxer_; - } - Muxer* muxer() { - return muxer_; - } - scoped_refptr info(); + Demuxer* demuxer() { return demuxer_; } + Muxer* muxer() { return muxer_; } + const scoped_refptr info() const; // Returns a human-readable string describing |*this|. std::string ToString() const; diff --git a/media/base/status.h b/media/base/status.h index 21f6762834..5c9872119c 100644 --- a/media/base/status.h +++ b/media/base/status.h @@ -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; diff --git a/media/base/stream_info.h b/media/base/stream_info.h index 055941e23d..109a8c0eb4 100644 --- a/media/base/stream_info.h +++ b/media/base/stream_info.h @@ -17,7 +17,7 @@ enum StreamType { kStreamVideo, }; -class StreamInfo : public base::RefCountedThreadSafe { +class StreamInfo : public base::RefCountedThreadSafe { public: StreamInfo(StreamType stream_type, int track_id, @@ -46,12 +46,7 @@ class StreamInfo : public base::RefCountedThreadSafe { 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& extra_data() const { return extra_data_; } void set_duration(int duration) { duration_ = duration; } diff --git a/media/base/test_data_util.cc b/media/base/test_data_util.cc index 4e1b7a924d..fba3f75e30 100644 --- a/media/base/test_data_util.cc +++ b/media/base/test_data_util.cc @@ -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 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 ReadTestDataFile(const std::string& name) { CHECK_EQ(file_size, file_util::ReadFile( - file_path, reinterpret_cast(buffer.data()), - file_size)) << "Failed to read '" << name << "'"; + file_path, reinterpret_cast(&buffer[0]), file_size)) + << "Failed to read '" << name << "'"; return buffer; } diff --git a/media/base/video_stream_info.cc b/media/base/video_stream_info.cc index 37a903d85c..6f610e5891 100644 --- a/media/base/video_stream_info.cc +++ b/media/base/video_stream_info.cc @@ -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(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; diff --git a/media/base/video_stream_info.h b/media/base/video_stream_info.h index 4772d921c9..c4c19aeee8 100644 --- a/media/base/video_stream_info.h +++ b/media/base/video_stream_info.h @@ -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. diff --git a/media/file/file_unittest.cc b/media/file/file_unittest.cc index 047f6ccf58..8e9e557098 100644 --- a/media/file/file_unittest.cc +++ b/media/file/file_unittest.cc @@ -4,8 +4,6 @@ #include "media/file/file.h" -#include - #include "base/file_util.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/media/mp4/mp4_media_parser.cc b/media/mp4/mp4_media_parser.cc index 39c03963fb..8f58172714 100644 --- a/media/mp4/mp4_media_parser.cc +++ b/media/mp4/mp4_media_parser.cc @@ -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 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(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 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 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 decrypt_config; std::vector 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. } diff --git a/media/mp4/track_run_iterator.cc b/media/mp4/track_run_iterator.cc index 2dd63a2e26..835cb063e8 100644 --- a/media/mp4/track_run_iterator.cc +++ b/media/mp4/track_run_iterator.cc @@ -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::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& 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& 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& 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& 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 TrackRunIterator::GetDecryptConfig() { const FrameCENCInfo& cenc_info = cenc_info_[sample_idx]; DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached()); - if (!cenc_info.subsamples.empty() && - (cenc_info.GetTotalSizeOfSubsamples() != - static_cast(sample_size()))) { + if (!cenc_info.subsamples.empty() && (cenc_info.GetTotalSizeOfSubsamples() != + static_cast(sample_size()))) { LOG(ERROR) << "Incorrect CENC subsample size."; return scoped_ptr(); } @@ -562,8 +534,7 @@ scoped_ptr TrackRunIterator::GetDecryptConfig() { const std::vector& kid = track_encryption().default_kid; return scoped_ptr(new DecryptConfig( std::string(reinterpret_cast(&kid[0]), kid.size()), - std::string(reinterpret_cast(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)); } diff --git a/media/mp4/track_run_iterator_unittest.cc b/media/mp4/track_run_iterator_unittest.cc index 4a1b614046..b1b6e3bc63 100644 --- a/media/mp4/track_run_iterator_unittest.cc +++ b/media/mp4/track_run_iterator_unittest.cc @@ -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* 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& 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 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());