From 338e3342b0f03418965b37890c9801c841c87470 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Mon, 23 Sep 2013 21:17:12 -0700 Subject: [PATCH] Create MP4MediaParser and its dependents. Create new classes: StreamInfo, AudioStreamInfo, VideoStreamInfo, MediaParser, MediaSample, MP4MediaParser. Change-Id: I19c2fc73964d9f8fc90acaddd1783f4e6ff87d07 --- media/base/audio_stream_info.cc | 53 +++ media/base/audio_stream_info.h | 76 ++++ media/base/bit_reader.h | 3 +- media/base/buffers.h | 6 +- media/base/byte_queue.h | 3 +- media/base/container_names.cc | 4 - media/base/container_names.h | 8 +- media/base/container_names_unittest.cc | 4 - media/base/decrypt_config.h | 3 +- media/base/media_parser.h | 70 ++++ media/base/media_sample.cc | 74 ++++ media/base/media_sample.h | 162 +++++++++ media/base/run_tests_with_atexit_manager.cc | 12 + media/base/stream_info.cc | 37 ++ media/base/stream_info.h | 70 ++++ media/base/test_data_util.cc | 7 +- media/base/test_data_util.h | 6 +- media/base/video_stream_info.cc | 48 +++ media/base/video_stream_info.h | 63 ++++ media/mp4/aac.cc | 67 ++-- media/mp4/aac.h | 25 +- media/mp4/aac_unittest.cc | 14 +- media/mp4/avc.cc | 91 ----- media/mp4/avc.h | 30 -- media/mp4/avc_unittest.cc | 95 ----- media/mp4/box_definitions.h | 76 ++-- media/mp4/box_reader.cc | 22 +- media/mp4/box_reader.h | 18 +- media/mp4/box_reader_unittest.cc | 14 +- media/mp4/es_descriptor.cc | 4 +- media/mp4/es_descriptor.h | 9 +- ...4_stream_parser.cc => mp4_media_parser.cc} | 326 ++++++------------ ...mp4_stream_parser.h => mp4_media_parser.h} | 67 ++-- media/mp4/mp4_media_parser_unittest.cc | 128 +++++++ media/mp4/mp4_stream_parser_unittest.cc | 216 ------------ media/mp4/offset_byte_queue.h | 5 +- media/mp4/track_run_iterator.cc | 29 +- media/mp4/track_run_iterator.h | 17 +- media/mp4/track_run_iterator_unittest.cc | 57 ++- 39 files changed, 1077 insertions(+), 942 deletions(-) create mode 100644 media/base/audio_stream_info.cc create mode 100644 media/base/audio_stream_info.h create mode 100644 media/base/media_parser.h create mode 100644 media/base/media_sample.cc create mode 100644 media/base/media_sample.h create mode 100644 media/base/run_tests_with_atexit_manager.cc create mode 100644 media/base/stream_info.cc create mode 100644 media/base/stream_info.h create mode 100644 media/base/video_stream_info.cc create mode 100644 media/base/video_stream_info.h delete mode 100644 media/mp4/avc.cc delete mode 100644 media/mp4/avc.h delete mode 100644 media/mp4/avc_unittest.cc rename media/mp4/{mp4_stream_parser.cc => mp4_media_parser.cc} (51%) rename media/mp4/{mp4_stream_parser.h => mp4_media_parser.h} (50%) create mode 100644 media/mp4/mp4_media_parser_unittest.cc delete mode 100644 media/mp4/mp4_stream_parser_unittest.cc diff --git a/media/base/audio_stream_info.cc b/media/base/audio_stream_info.cc new file mode 100644 index 0000000000..49d2f49166 --- /dev/null +++ b/media/base/audio_stream_info.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/audio_stream_info.h" + +#include + +#include "media/base/limits.h" + +namespace media { + +AudioStreamInfo::AudioStreamInfo(int track_id, + int time_scale, + AudioCodec codec, + int bytes_per_channel, + int num_channels, + int samples_per_second, + const uint8* extra_data, + size_t extra_data_size, + bool is_encrypted) + : StreamInfo(kStreamAudio, + track_id, + time_scale, + extra_data, + extra_data_size, + is_encrypted), + codec_(codec), + bytes_per_channel_(bytes_per_channel), + num_channels_(num_channels), + samples_per_second_(samples_per_second) {} + +AudioStreamInfo::~AudioStreamInfo() {} + +bool AudioStreamInfo::IsValidConfig() const { + return codec_ != kUnknownAudioCodec && num_channels_ != 0 && + num_channels_ <= limits::kMaxChannels && bytes_per_channel_ > 0 && + bytes_per_channel_ <= limits::kMaxBytesPerSample && + samples_per_second_ > 0 && + samples_per_second_ <= limits::kMaxSampleRate; +} + +std::string AudioStreamInfo::ToString() { + std::ostringstream s; + s << "codec: " << codec_ + << " bytes_per_channel: " << bytes_per_channel_ + << " num_channels: " << num_channels_ + << " samples_per_second: " << samples_per_second_ + << " " << StreamInfo::ToString(); + return s.str(); +} + +} // namespace media diff --git a/media/base/audio_stream_info.h b/media/base/audio_stream_info.h new file mode 100644 index 0000000000..14dd00bf32 --- /dev/null +++ b/media/base/audio_stream_info.h @@ -0,0 +1,76 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_AUDIO_STREAM_INFO_H_ +#define MEDIA_BASE_AUDIO_STREAM_INFO_H_ + +#include + +#include "media/base/stream_info.h" + +namespace media { + +enum AudioCodec { + kUnknownAudioCodec = 0, + kCodecAAC, + kCodecMP3, + kCodecPCM, + kCodecVorbis, + kCodecFLAC, + kCodecAMR_NB, + kCodecAMR_WB, + kCodecPCM_MULAW, + kCodecGSM_MS, + kCodecPCM_S16BE, + kCodecPCM_S24BE, + kCodecOpus, + kCodecEAC3, + + kNumAudioCodec +}; + +class AudioStreamInfo : public StreamInfo { + public: + // Constructs an initialized object. It is acceptable to pass in NULL for + // |extra_data|, otherwise the memory is copied. + AudioStreamInfo(int track_id, + int time_scale, + AudioCodec codec, + int bytes_per_channel, + int num_channels, + int samples_per_second, + const uint8* extra_data, + size_t extra_data_size, + bool is_encrypted); + + virtual ~AudioStreamInfo(); + + // Returns true if this object has appropriate configuration values, false + // otherwise. + virtual bool IsValidConfig() const; + + // Returns a human-readable string describing |*this|. + virtual std::string ToString(); + + AudioCodec codec() const { return codec_; } + int bits_per_channel() const { return bytes_per_channel_ * 8; } + int bytes_per_channel() const { return bytes_per_channel_; } + int num_channels() const { return num_channels_; } + int samples_per_second() const { return samples_per_second_; } + int bytes_per_frame() const { return num_channels_ * bytes_per_channel_; } + + private: + AudioCodec codec_; + int bytes_per_channel_; + int num_channels_; + int samples_per_second_; + + // 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. +}; + +} // namespace media + +#endif // MEDIA_BASE_AUDIO_STREAM_INFO_H_ diff --git a/media/base/bit_reader.h b/media/base/bit_reader.h index 8c15891c91..4cabc17c1d 100644 --- a/media/base/bit_reader.h +++ b/media/base/bit_reader.h @@ -9,12 +9,11 @@ #include "base/basictypes.h" #include "base/logging.h" -#include "media/base/media_export.h" namespace media { // A class to read bit streams. -class MEDIA_EXPORT BitReader { +class BitReader { public: // Initialize the reader to start reading at |data|, |size| being size // of |data| in bytes. diff --git a/media/base/buffers.h b/media/base/buffers.h index 6a6c7303d1..e5d8bd8c09 100644 --- a/media/base/buffers.h +++ b/media/base/buffers.h @@ -22,21 +22,19 @@ #define MEDIA_BASE_BUFFERS_H_ #include "base/basictypes.h" -#include "base/memory/ref_counted.h" #include "base/time/time.h" -#include "media/base/media_export.h" namespace media { // TODO(scherkus): Move the contents of this file elsewhere. // Indicates an invalid or missing timestamp. -MEDIA_EXPORT extern inline base::TimeDelta kNoTimestamp() { +extern inline base::TimeDelta kNoTimestamp() { return base::TimeDelta::FromMicroseconds(kint64min); } // Represents an infinite stream duration. -MEDIA_EXPORT extern inline base::TimeDelta kInfiniteDuration() { +extern inline base::TimeDelta kInfiniteDuration() { return base::TimeDelta::FromMicroseconds(kint64max); } diff --git a/media/base/byte_queue.h b/media/base/byte_queue.h index f25328d362..ce6bb4b4e4 100644 --- a/media/base/byte_queue.h +++ b/media/base/byte_queue.h @@ -7,7 +7,6 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "media/base/media_export.h" namespace media { @@ -16,7 +15,7 @@ namespace media { // Pop(). The contents of the queue can be observed via the Peek() method. // This class manages the underlying storage of the queue and tries to minimize // the number of buffer copies when data is appended and removed. -class MEDIA_EXPORT ByteQueue { +class ByteQueue { public: ByteQueue(); ~ByteQueue(); diff --git a/media/base/container_names.cc b/media/base/container_names.cc index f062929d54..331fbde29d 100644 --- a/media/base/container_names.cc +++ b/media/base/container_names.cc @@ -13,8 +13,6 @@ namespace media { -namespace container_names { - #define TAG(a, b, c, d) \ ((static_cast(a) << 24) | (static_cast(b) << 16) | \ (static_cast(c) << 8) | (static_cast(d))) @@ -1666,6 +1664,4 @@ MediaContainerName DetermineContainer(const uint8* buffer, int buffer_size) { return CONTAINER_UNKNOWN; } -} // namespace container_names - } // namespace media diff --git a/media/base/container_names.h b/media/base/container_names.h index 7b7b099a00..250483b4ee 100644 --- a/media/base/container_names.h +++ b/media/base/container_names.h @@ -6,12 +6,9 @@ #define MEDIA_BASE_CONTAINER_NAMES_H_ #include "base/basictypes.h" -#include "media/base/media_export.h" namespace media { -namespace container_names { - // This is the set of input container formats detected for logging purposes. Not // all of these are enabled (and it varies by product). Any additions need to be // done at the end of the list (before CONTAINER_MAX). This list must be kept in @@ -60,10 +57,7 @@ enum MediaContainerName { }; // Determine the container type. -MEDIA_EXPORT MediaContainerName DetermineContainer(const uint8* buffer, - int buffer_size); - -} // namespace container_names +MediaContainerName DetermineContainer(const uint8* buffer, int buffer_size); } // namespace media diff --git a/media/base/container_names_unittest.cc b/media/base/container_names_unittest.cc index 21f80af6d9..acfead8b5f 100644 --- a/media/base/container_names_unittest.cc +++ b/media/base/container_names_unittest.cc @@ -9,8 +9,6 @@ namespace media { -namespace container_names { - // Using a macros to simplify tests. Since EXPECT_EQ outputs the second argument // as a string when it fails, this lets the output identify what item actually // failed. @@ -215,6 +213,4 @@ TEST(ContainerNamesTest, FileCheckUNKNOWN) { TestFile(CONTAINER_UNKNOWN, GetTestDataFilePath("webm_vp8_track_entry")); } -} // namespace container_names - } // namespace media diff --git a/media/base/decrypt_config.h b/media/base/decrypt_config.h index be0bb4d61b..0597043748 100644 --- a/media/base/decrypt_config.h +++ b/media/base/decrypt_config.h @@ -10,7 +10,6 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "media/base/media_export.h" namespace media { @@ -29,7 +28,7 @@ struct SubsampleEntry { }; // Contains all information that a decryptor needs to decrypt a media sample. -class MEDIA_EXPORT DecryptConfig { +class DecryptConfig { public: // Keys are always 128 bits. static const int kDecryptionKeySize = 16; diff --git a/media/base/media_parser.h b/media/base/media_parser.h new file mode 100644 index 0000000000..07ed637bef --- /dev/null +++ b/media/base/media_parser.h @@ -0,0 +1,70 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_PARSER_H_ +#define MEDIA_BASE_MEDIA_PARSER_H_ + +#include +#include + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/container_names.h" + +namespace media { + +class MediaSample; +class StreamInfo; + +class MediaParser { + public: + MediaParser() {} + virtual ~MediaParser() {} + + // Indicates completion of parser initialization. + // First parameter - Indicates initialization success. Set to true if + // initialization was successful. False if an error + // occurred. + // Second parameter - A vector of all the elementary streams within this file. + typedef base::Callback >&)> + InitCB; + + // New stream sample have been parsed. + // First parameter - The track id of the new sample. + // Second parameter - The new media sample; + // Return value - True indicates that the sample is accepted. + // False if something was wrong with the sample and a parsing + // error should be signaled. + typedef base::Callback< + bool(uint32 track_id, const scoped_refptr&)> + NewSampleCB; + + // A new potentially encrypted stream has been parsed. + // First Parameter - Container name. + // Second parameter - The initialization data associated with the stream. + // Third parameter - Number of bytes of the initialization data. + typedef base::Callback, int)> + NeedKeyCB; + + // Initialize the parser with necessary callbacks. Must be called before any + // data is passed to Parse(). |init_cb| will be called once enough data has + // been parsed to determine the initial stream configurations. + virtual void Init(const InitCB& init_cb, + const NewSampleCB& new_sample_cb, + const NeedKeyCB& need_key_cb) = 0; + + // Called when there is new data to parse. + // + // Returns true if the parse succeeds. + // TODO(kqyang): change to return Status. + virtual bool Parse(const uint8* buf, int size) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(MediaParser); +}; + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_PARSER_H_ diff --git a/media/base/media_sample.cc b/media/base/media_sample.cc new file mode 100644 index 0000000000..11588264b9 --- /dev/null +++ b/media/base/media_sample.cc @@ -0,0 +1,74 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/media_sample.h" + +#include "base/logging.h" +#include "media/base/decrypt_config.h" + +namespace media { + +MediaSample::MediaSample(const uint8* data, + int size, + const uint8* side_data, + int side_data_size, + bool is_key_frame) + : dts_(0), pts_(0), duration_(0), is_key_frame_(is_key_frame) { + if (!data) { + CHECK_EQ(size, 0); + CHECK(!side_data); + return; + } + + data_.assign(data, data + size); + if (side_data) + side_data_.assign(side_data, side_data + side_data_size); +} + +MediaSample::~MediaSample() {} + +// static +scoped_refptr MediaSample::CopyFrom(const uint8* data, + int data_size, + bool is_key_frame) { + // If you hit this CHECK you likely have a bug in a demuxer. Go fix it. + CHECK(data); + return make_scoped_refptr( + new MediaSample(data, data_size, NULL, 0, is_key_frame)); +} + +// static +scoped_refptr MediaSample::CopyFrom(const uint8* data, + int data_size, + const uint8* side_data, + int side_data_size, + bool is_key_frame) { + // If you hit this CHECK you likely have a bug in a demuxer. Go fix it. + CHECK(data); + return make_scoped_refptr(new MediaSample( + data, data_size, side_data, side_data_size, is_key_frame)); +} + +// static +scoped_refptr MediaSample::CreateEOSBuffer() { + return make_scoped_refptr(new MediaSample(NULL, 0, NULL, 0, false)); +} + +std::string MediaSample::ToString() { + if (end_of_stream()) { + return "end of stream"; + } + + std::ostringstream s; + s << "dts: " << dts_ + << " pts: " << pts_ + << " duration: " << duration_ + << " is_key_frame: " << is_key_frame_ + << " size: " << data_.size() + << " side_data_size: " << side_data_.size() + << " encrypted: " << (decrypt_config_ != NULL); + return s.str(); +} + +} // namespace media diff --git a/media/base/media_sample.h b/media/base/media_sample.h new file mode 100644 index 0000000000..6035f03ef7 --- /dev/null +++ b/media/base/media_sample.h @@ -0,0 +1,162 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_SAMPLE_H_ +#define MEDIA_BASE_MEDIA_SAMPLE_H_ + +#include +#include +#include + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/decrypt_config.h" + +namespace media { + +class DecryptConfig; + +// Holds media sample. Also includes decoder specific functionality for +// decryption. +class MediaSample + : public base::RefCountedThreadSafe { + public: + // Create a MediaSample whose |data_| is copied from |data|. + // |data| must not be NULL and |size| >= 0. + static scoped_refptr CopyFrom(const uint8* data, + int size, + bool is_key_frame); + + // Create a MediaSample whose |data_| is copied from |data| and |side_data_| + // is copied from |side_data|. Data pointers must not be NULL and sizes + // must be >= 0. + static scoped_refptr CopyFrom(const uint8* data, + int size, + const uint8* side_data, + int side_data_size, + bool is_key_frame); + + // Create a MediaSample indicating we've reached end of stream. + // + // Calling any method other than end_of_stream() on the resulting buffer + // is disallowed. + // TODO(kqyang): do we need it? + static scoped_refptr CreateEOSBuffer(); + + int64 dts() const { + DCHECK(!end_of_stream()); + return dts_; + } + + void set_dts(int64 dts) { + DCHECK(!end_of_stream()); + dts_ = dts; + } + + int64 pts() const { + DCHECK(!end_of_stream()); + return pts_; + } + + void set_pts(int64 pts) { + DCHECK(!end_of_stream()); + pts_ = pts; + } + + int64 duration() const { + DCHECK(!end_of_stream()); + return duration_; + } + + void set_duration(int64 duration) { + DCHECK(!end_of_stream()); + duration_ = duration; + } + + bool is_key_frame() const { + DCHECK(!end_of_stream()); + return is_key_frame_; + } + + const uint8* data() const { + DCHECK(!end_of_stream()); + return data_.data(); + } + + uint8* writable_data() { + DCHECK(!end_of_stream()); + return data_.data(); + } + + int data_size() const { + DCHECK(!end_of_stream()); + return data_.size(); + } + + const uint8* side_data() const { + DCHECK(!end_of_stream()); + return side_data_.data(); + } + + int side_data_size() const { + DCHECK(!end_of_stream()); + return side_data_.size(); + } + + const DecryptConfig* decrypt_config() const { + DCHECK(!end_of_stream()); + return decrypt_config_.get(); + } + + void set_decrypt_config(scoped_ptr decrypt_config) { + DCHECK(!end_of_stream()); + decrypt_config_ = decrypt_config.Pass(); + } + + // If there's no data in this buffer, it represents end of stream. + bool end_of_stream() const { + return data_.size() == 0; + } + + // Returns a human-readable string describing |*this|. + std::string ToString(); + + protected: + friend class base::RefCountedThreadSafe; + + // Allocates a buffer of size |size| >= 0 and copies |data| into it. Buffer + // will be padded and aligned as necessary. If |data| is NULL then |data_| is + // set to NULL and |buffer_size_| to 0. + MediaSample(const uint8* data, + int size, + const uint8* side_data, + int side_data_size, + bool is_key_frame); + virtual ~MediaSample(); + + private: + // Decoding time stamp. + int64 dts_; + // Presentation time stamp. + int64 pts_; + int64 duration_; + bool is_key_frame_; + + // Main buffer data. + std::vector data_; + // Contain additional buffers to complete the main one. Needed by WebM + // http://www.matroska.org/technical/specs/index.html BlockAdditional[A5]. + // Not used by mp4 and other containers. + std::vector side_data_; + scoped_ptr decrypt_config_; + + DISALLOW_COPY_AND_ASSIGN(MediaSample); +}; + +typedef std::deque > BufferQueue; + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_SAMPLE_H_ diff --git a/media/base/run_tests_with_atexit_manager.cc b/media/base/run_tests_with_atexit_manager.cc new file mode 100644 index 0000000000..212abd5f6e --- /dev/null +++ b/media/base/run_tests_with_atexit_manager.cc @@ -0,0 +1,12 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license tha can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "testing/gtest/include/gtest/gtest.h" + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + base::AtExitManager exit; + return RUN_ALL_TESTS(); +} diff --git a/media/base/stream_info.cc b/media/base/stream_info.cc new file mode 100644 index 0000000000..480fec98f4 --- /dev/null +++ b/media/base/stream_info.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/stream_info.h" + +#include "base/logging.h" + +namespace media { + +StreamInfo::StreamInfo(StreamType stream_type, + int track_id, + int time_scale, + const uint8* extra_data, + size_t extra_data_size, + bool is_encrypted) + : stream_type_(stream_type), + track_id_(track_id), + time_scale_(time_scale), + is_encrypted_(is_encrypted) { + + CHECK((extra_data_size != 0) == (extra_data != NULL)); + extra_data_.assign(extra_data, extra_data + extra_data_size); +} + +StreamInfo::~StreamInfo() {} + +std::string StreamInfo::ToString() { + std::ostringstream s; + s << "type: " << (stream_type_ == kStreamAudio ? "Audio" : "Video") + << " track_id: " << track_id_ + << " time_scale: " << time_scale_ + << " is_encrypted: " << is_encrypted_; + return s.str(); +} + +} // namespace media diff --git a/media/base/stream_info.h b/media/base/stream_info.h new file mode 100644 index 0000000000..21a2b4316a --- /dev/null +++ b/media/base/stream_info.h @@ -0,0 +1,70 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_STREAM_INFO_H_ +#define MEDIA_BASE_STREAM_INFO_H_ + +#include +#include + +#include "base/memory/ref_counted.h" + +namespace media { + +enum StreamType { + kStreamAudio, + kStreamVideo, +}; + +class StreamInfo : public base::RefCountedThreadSafe { + public: + StreamInfo(StreamType stream_type, + int track_id, + int time_scale, + const uint8* extra_data, + size_t extra_data_size, + bool is_encrypted); + virtual ~StreamInfo(); + + // Returns true if this object has appropriate configuration values, false + // otherwise. + virtual bool IsValidConfig() const = 0; + + // Returns a human-readable string describing |*this|. + virtual std::string ToString(); + + StreamType stream_type() const { return stream_type_; } + int track_id() const { return track_id_; } + int time_scale() const { return time_scale_; } + + 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(); + } + + private: + // Whether the stream is Audio or Video. + StreamType stream_type_; + int track_id_; + int time_scale_; + // Whether the stream is potentially encrypted. + // Note that in a potentially encrypted stream, individual buffers + // can be encrypted or not encrypted. + bool is_encrypted_; + // Optional byte data required for some audio/video decoders such as Vorbis + // codebooks. + std::vector extra_data_; + + // 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. +}; + +} // namespace media + +#endif // MEDIA_BASE_STREAM_INFO_H_ diff --git a/media/base/test_data_util.cc b/media/base/test_data_util.cc index 55e82fc863..4e1b7a924d 100644 --- a/media/base/test_data_util.cc +++ b/media/base/test_data_util.cc @@ -7,7 +7,6 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/path_service.h" -#include "media/base/decoder_buffer.h" namespace media { @@ -21,7 +20,7 @@ base::FilePath GetTestDataFilePath(const std::string& name) { return file_path; } -scoped_refptr ReadTestDataFile(const std::string& name) { +std::vector ReadTestDataFile(const std::string& name) { base::FilePath file_path; CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); @@ -34,11 +33,11 @@ scoped_refptr ReadTestDataFile(const std::string& name) { << "Failed to get file size for '" << name << "'"; int file_size = static_cast(tmp); + std::vector buffer(file_size); - scoped_refptr buffer(new DecoderBuffer(file_size)); CHECK_EQ(file_size, file_util::ReadFile( - file_path, reinterpret_cast(buffer->writable_data()), + file_path, reinterpret_cast(buffer.data()), file_size)) << "Failed to read '" << name << "'"; return buffer; diff --git a/media/base/test_data_util.h b/media/base/test_data_util.h index 8d51e96c73..cbf280f3f3 100644 --- a/media/base/test_data_util.h +++ b/media/base/test_data_util.h @@ -9,13 +9,9 @@ #include "base/basictypes.h" #include "base/files/file_path.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" namespace media { -class DecoderBuffer; - // Returns a file path for a file in the media/test/data directory. base::FilePath GetTestDataFilePath(const std::string& name); @@ -25,7 +21,7 @@ base::FilePath GetTestDataFilePath(const std::string& name); // // |name| - The name of the file. // |buffer| - The contents of the file. -scoped_refptr ReadTestDataFile(const std::string& name); +std::vector ReadTestDataFile(const std::string& name); } // namespace media diff --git a/media/base/video_stream_info.cc b/media/base/video_stream_info.cc new file mode 100644 index 0000000000..589cd49fa4 --- /dev/null +++ b/media/base/video_stream_info.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/video_stream_info.h" + +#include + +#include "media/base/limits.h" + +namespace media { + +VideoStreamInfo::VideoStreamInfo(int track_id, + int time_scale, + VideoCodec codec, + int width, + int height, + const uint8* extra_data, + size_t extra_data_size, + bool is_encrypted) + : StreamInfo(kStreamVideo, + track_id, + time_scale, + extra_data, + extra_data_size, + is_encrypted), + codec_(codec), + width_(width), + height_(height) {} + +VideoStreamInfo::~VideoStreamInfo() {} + +bool VideoStreamInfo::IsValidConfig() const { + return codec_ != kUnknownVideoCodec && + width_ > 0 && width_ <= limits::kMaxDimension && + height_ > 0 && height_ <= limits::kMaxDimension; +} + +std::string VideoStreamInfo::ToString() { + std::ostringstream s; + s << "codec: " << codec_ + << " width: " << width_ + << " height: " << height_ + << " " << StreamInfo::ToString(); + return s.str(); +} + +} // namespace media diff --git a/media/base/video_stream_info.h b/media/base/video_stream_info.h new file mode 100644 index 0000000000..9e5e7d5c1b --- /dev/null +++ b/media/base/video_stream_info.h @@ -0,0 +1,63 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_VIDEO_STREAM_INFO_H_ +#define MEDIA_BASE_VIDEO_STREAM_INFO_H_ + +#include "media/base/stream_info.h" + +namespace media { + +enum VideoCodec { + kUnknownVideoCodec = 0, + kCodecH264, + kCodecVC1, + kCodecMPEG2, + kCodecMPEG4, + kCodecTheora, + kCodecVP8, + kCodecVP9, + + kNumVideoCodec +}; + +class VideoStreamInfo : public StreamInfo { + public: + // Constructs an initialized object. It is acceptable to pass in NULL for + // |extra_data|, otherwise the memory is copied. + VideoStreamInfo(int track_id, + int time_scale, + VideoCodec codec, + int width, + int height, + const uint8* extra_data, + size_t extra_data_size, + bool is_encrypted); + + virtual ~VideoStreamInfo(); + + // Returns true if this object has appropriate configuration values, false + // otherwise. + virtual bool IsValidConfig() const; + + // Returns a human-readable string describing |*this|. + virtual std::string ToString(); + + VideoCodec codec() const { return codec_; } + int width() const { return width_; } + int height() const { return height_; } + + private: + VideoCodec codec_; + int width_; + int height_; + + // 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. +}; + +} // namespace media + +#endif // MEDIA_BASE_VIDEO_STREAM_INFO_H_ diff --git a/media/mp4/aac.cc b/media/mp4/aac.cc index 6604c505a0..87866c71fe 100644 --- a/media/mp4/aac.cc +++ b/media/mp4/aac.cc @@ -10,43 +10,26 @@ #include "media/base/bit_reader.h" #include "media/mp4/rcheck.h" -// The following conversion table is extracted from ISO 14496 Part 3 - -// Table 1.16 - Sampling Frequency Index. -static const int kFrequencyMap[] = { +namespace { + +// Sampling Frequency Index table, from ISO 14496-3 Table 1.16 +static const int kSampleRates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 }; +// Channel Configuration table, from ISO 14496-3 Table 1.17 +const uint32 kChannelConfigs[] = {0, 1, 2, 3, 4, 5, 6, 8}; + +} // namespace + namespace media { -static ChannelLayout ConvertChannelConfigToLayout(uint8 channel_config) { - switch (channel_config) { - case 1: - return CHANNEL_LAYOUT_MONO; - case 2: - return CHANNEL_LAYOUT_STEREO; - case 3: - return CHANNEL_LAYOUT_SURROUND; - case 4: - return CHANNEL_LAYOUT_4_0; - case 5: - return CHANNEL_LAYOUT_5_0; - case 6: - return CHANNEL_LAYOUT_5_1; - case 8: - return CHANNEL_LAYOUT_7_1; - default: - break; - } - - return CHANNEL_LAYOUT_UNSUPPORTED; -} - namespace mp4 { AAC::AAC() : profile_(0), frequency_index_(0), channel_config_(0), frequency_(0), - extension_frequency_(0), channel_layout_(CHANNEL_LAYOUT_UNSUPPORTED) { + extension_frequency_(0), num_channels_(0) { } AAC::~AAC() { @@ -122,24 +105,26 @@ bool AAC::Parse(const std::vector& data) { } if (frequency_ == 0) { - RCHECK(frequency_index_ < arraysize(kFrequencyMap)); - frequency_ = kFrequencyMap[frequency_index_]; + RCHECK(frequency_index_ < arraysize(kSampleRates)); + frequency_ = kSampleRates[frequency_index_]; } if (extension_frequency_ == 0 && extension_frequency_index != 0xff) { - RCHECK(extension_frequency_index < arraysize(kFrequencyMap)); - extension_frequency_ = kFrequencyMap[extension_frequency_index]; + RCHECK(extension_frequency_index < arraysize(kSampleRates)); + extension_frequency_ = kSampleRates[extension_frequency_index]; } + // TODO(kqyang): should we care about whether Parametric Stereo is on? // When Parametric Stereo is on, mono will be played as stereo. if (ps_present && channel_config_ == 1) - channel_layout_ = CHANNEL_LAYOUT_STEREO; - else - channel_layout_ = ConvertChannelConfigToLayout(channel_config_); + num_channels_ = 2; // CHANNEL_LAYOUT_STEREO + else { + RCHECK(channel_config_ < arraysize(kChannelConfigs)); + num_channels_ = kChannelConfigs[channel_config_]; + } - return frequency_ != 0 && channel_layout_ != CHANNEL_LAYOUT_UNSUPPORTED && - profile_ >= 1 && profile_ <= 4 && frequency_index_ != 0xf && - channel_config_ <= 7; + return frequency_ != 0 && num_channels_ != 0 && profile_ >= 1 && + profile_ <= 4 && frequency_index_ != 0xf && channel_config_ <= 7; } int AAC::GetOutputSamplesPerSecond(bool sbr_in_mimetype) const { @@ -157,17 +142,17 @@ int AAC::GetOutputSamplesPerSecond(bool sbr_in_mimetype) const { return std::min(2 * frequency_, 48000); } -ChannelLayout AAC::GetChannelLayout(bool sbr_in_mimetype) const { +int AAC::GetNumChannels(bool sbr_in_mimetype) const { // Check for implicit signalling of HE-AAC and indicate stereo output // if the mono channel configuration is signalled. // See ISO-14496-3 Section 1.6.6.1.2 for details about this special casing. if (sbr_in_mimetype && channel_config_ == 1) - return CHANNEL_LAYOUT_STEREO; + return 2; // CHANNEL_LAYOUT_STEREO - return channel_layout_; + return num_channels_; } -bool AAC::ConvertEsdsToADTS(std::vector* buffer) const { +bool AAC::ConvertToADTS(std::vector* buffer) const { size_t size = buffer->size() + kADTSHeaderSize; DCHECK(profile_ >= 1 && profile_ <= 4 && frequency_index_ != 0xf && diff --git a/media/mp4/aac.h b/media/mp4/aac.h index 1a546b743f..19fa9c51b6 100644 --- a/media/mp4/aac.h +++ b/media/mp4/aac.h @@ -8,8 +8,6 @@ #include #include "base/basictypes.h" -#include "media/base/channel_layout.h" -#include "media/base/media_export.h" namespace media { @@ -21,7 +19,9 @@ namespace mp4 { // embedded in the esds box in an ISO BMFF file. // Please refer to ISO 14496 Part 3 Table 1.13 - Syntax of AudioSpecificConfig // for more details. -class MEDIA_EXPORT AAC { +// TODO(kqyang): the class name is not appropriate, it should be +// AACAudioSpecificConfig instead. +class AAC { public: AAC(); ~AAC(); @@ -35,22 +35,18 @@ class MEDIA_EXPORT AAC { // Gets the output sample rate for the AAC stream. // |sbr_in_mimetype| should be set to true if the SBR mode is // signalled in the mimetype. (ie mp4a.40.5 in the codecs parameter). - // Returns the samples_per_second value that should used in an - // AudioDecoderConfig. int GetOutputSamplesPerSecond(bool sbr_in_mimetype) const; - // Gets the channel layout for the AAC stream. + // Gets number of channels for the AAC stream. // |sbr_in_mimetype| should be set to true if the SBR mode is // signalled in the mimetype. (ie mp4a.40.5 in the codecs parameter). - // Returns the channel_layout value that should used in an - // AudioDecoderConfig. - ChannelLayout GetChannelLayout(bool sbr_in_mimetype) const; + int GetNumChannels(bool sbr_in_mimetype) const; // This function converts a raw AAC frame into an AAC frame with an ADTS // header. On success, the function returns true and stores the converted data // in the buffer. The function returns false on failure and leaves the buffer // unchanged. - bool ConvertEsdsToADTS(std::vector* buffer) const; + bool ConvertToADTS(std::vector* buffer) const; #if defined(OS_ANDROID) // Returns the codec specific data needed by android MediaCodec. @@ -78,13 +74,12 @@ class MEDIA_EXPORT AAC { std::vector codec_specific_data_; #endif - // The following variables store audio configuration information that - // can be used by Chromium. They are based on the AAC specific - // configuration but can be overridden by extensions in elementary - // stream descriptor. + // The following variables store audio configuration information. + // They are based on the AAC specific configuration but can be overridden + // by extensions in elementary stream descriptor. int frequency_; int extension_frequency_; - ChannelLayout channel_layout_; + int num_channels_; }; } // namespace mp4 diff --git a/media/mp4/aac_unittest.cc b/media/mp4/aac_unittest.cc index d9ce22db3f..58d0c53cbf 100644 --- a/media/mp4/aac_unittest.cc +++ b/media/mp4/aac_unittest.cc @@ -19,7 +19,7 @@ TEST(AACTest, BasicProfileTest) { EXPECT_TRUE(aac.Parse(data)); EXPECT_EQ(aac.GetOutputSamplesPerSecond(false), 44100); - EXPECT_EQ(aac.GetChannelLayout(false), CHANNEL_LAYOUT_STEREO); + EXPECT_EQ(aac.GetNumChannels(false), 2); } TEST(AACTest, ExtensionTest) { @@ -32,7 +32,7 @@ TEST(AACTest, ExtensionTest) { EXPECT_TRUE(aac.Parse(data)); EXPECT_EQ(aac.GetOutputSamplesPerSecond(false), 48000); EXPECT_EQ(aac.GetOutputSamplesPerSecond(true), 48000); - EXPECT_EQ(aac.GetChannelLayout(false), CHANNEL_LAYOUT_STEREO); + EXPECT_EQ(aac.GetNumChannels(false), 2); } // Test implicit SBR with mono channel config. @@ -50,11 +50,11 @@ TEST(AACTest, ImplicitSBR_ChannelConfig0) { // Test w/o implict SBR. EXPECT_EQ(aac.GetOutputSamplesPerSecond(false), 24000); - EXPECT_EQ(aac.GetChannelLayout(false), CHANNEL_LAYOUT_MONO); + EXPECT_EQ(aac.GetNumChannels(false), 1); // Test implicit SBR. EXPECT_EQ(aac.GetOutputSamplesPerSecond(true), 48000); - EXPECT_EQ(aac.GetChannelLayout(true), CHANNEL_LAYOUT_STEREO); + EXPECT_EQ(aac.GetNumChannels(true), 2); } // Tests implicit SBR with a stereo channel config. @@ -69,11 +69,11 @@ TEST(AACTest, ImplicitSBR_ChannelConfig1) { // Test w/o implict SBR. EXPECT_EQ(aac.GetOutputSamplesPerSecond(false), 24000); - EXPECT_EQ(aac.GetChannelLayout(false), CHANNEL_LAYOUT_STEREO); + EXPECT_EQ(aac.GetNumChannels(false), 2); // Test implicit SBR. EXPECT_EQ(aac.GetOutputSamplesPerSecond(true), 48000); - EXPECT_EQ(aac.GetChannelLayout(true), CHANNEL_LAYOUT_STEREO); + EXPECT_EQ(aac.GetNumChannels(true), 2); } TEST(AACTest, SixChannelTest) { @@ -85,7 +85,7 @@ TEST(AACTest, SixChannelTest) { EXPECT_TRUE(aac.Parse(data)); EXPECT_EQ(aac.GetOutputSamplesPerSecond(false), 48000); - EXPECT_EQ(aac.GetChannelLayout(false), CHANNEL_LAYOUT_5_1); + EXPECT_EQ(aac.GetNumChannels(false), 6); } TEST(AACTest, DataTooShortTest) { diff --git a/media/mp4/avc.cc b/media/mp4/avc.cc deleted file mode 100644 index ae28ffd256..0000000000 --- a/media/mp4/avc.cc +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/mp4/avc.h" - -#include -#include - -#include "media/mp4/box_definitions.h" -#include "media/mp4/box_reader.h" - -namespace media { -namespace mp4 { - -static const uint8 kAnnexBStartCode[] = {0, 0, 0, 1}; -static const int kAnnexBStartCodeSize = 4; - -static bool ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector* buf) { - const int kLengthSize = 4; - size_t pos = 0; - while (pos + kLengthSize < buf->size()) { - int nal_size = (*buf)[pos]; - nal_size = (nal_size << 8) + (*buf)[pos+1]; - nal_size = (nal_size << 8) + (*buf)[pos+2]; - nal_size = (nal_size << 8) + (*buf)[pos+3]; - std::copy(kAnnexBStartCode, kAnnexBStartCode + kAnnexBStartCodeSize, - buf->begin() + pos); - pos += kLengthSize + nal_size; - } - return pos == buf->size(); -} - -// static -bool AVC::ConvertFrameToAnnexB(int length_size, std::vector* buffer) { - RCHECK(length_size == 1 || length_size == 2 || length_size == 4); - - if (length_size == 4) - return ConvertAVCToAnnexBInPlaceForLengthSize4(buffer); - - std::vector temp; - temp.swap(*buffer); - buffer->reserve(temp.size() + 32); - - size_t pos = 0; - while (pos + length_size < temp.size()) { - int nal_size = temp[pos]; - if (length_size == 2) nal_size = (nal_size << 8) + temp[pos+1]; - pos += length_size; - - RCHECK(pos + nal_size <= temp.size()); - buffer->insert(buffer->end(), kAnnexBStartCode, - kAnnexBStartCode + kAnnexBStartCodeSize); - buffer->insert(buffer->end(), temp.begin() + pos, - temp.begin() + pos + nal_size); - pos += nal_size; - } - return pos == temp.size(); -} - -// static -bool AVC::ConvertConfigToAnnexB( - const AVCDecoderConfigurationRecord& avc_config, - std::vector* buffer) { - DCHECK(buffer->empty()); - buffer->clear(); - int total_size = 0; - for (size_t i = 0; i < avc_config.sps_list.size(); i++) - total_size += avc_config.sps_list[i].size() + kAnnexBStartCodeSize; - for (size_t i = 0; i < avc_config.pps_list.size(); i++) - total_size += avc_config.pps_list[i].size() + kAnnexBStartCodeSize; - buffer->reserve(total_size); - - for (size_t i = 0; i < avc_config.sps_list.size(); i++) { - buffer->insert(buffer->end(), kAnnexBStartCode, - kAnnexBStartCode + kAnnexBStartCodeSize); - buffer->insert(buffer->end(), avc_config.sps_list[i].begin(), - avc_config.sps_list[i].end()); - } - - for (size_t i = 0; i < avc_config.pps_list.size(); i++) { - buffer->insert(buffer->end(), kAnnexBStartCode, - kAnnexBStartCode + kAnnexBStartCodeSize); - buffer->insert(buffer->end(), avc_config.pps_list[i].begin(), - avc_config.pps_list[i].end()); - } - return true; -} - -} // namespace mp4 -} // namespace media diff --git a/media/mp4/avc.h b/media/mp4/avc.h deleted file mode 100644 index 3d815a1739..0000000000 --- a/media/mp4/avc.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_MP4_AVC_H_ -#define MEDIA_MP4_AVC_H_ - -#include - -#include "base/basictypes.h" -#include "media/base/media_export.h" - -namespace media { -namespace mp4 { - -struct AVCDecoderConfigurationRecord; - -class MEDIA_EXPORT AVC { - public: - static bool ConvertFrameToAnnexB(int length_size, std::vector* buffer); - - static bool ConvertConfigToAnnexB( - const AVCDecoderConfigurationRecord& avc_config, - std::vector* buffer); -}; - -} // namespace mp4 -} // namespace media - -#endif // MEDIA_MP4_AVC_H_ diff --git a/media/mp4/avc_unittest.cc b/media/mp4/avc_unittest.cc deleted file mode 100644 index 766a979196..0000000000 --- a/media/mp4/avc_unittest.cc +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "base/basictypes.h" -#include "media/base/stream_parser_buffer.h" -#include "media/mp4/avc.h" -#include "media/mp4/box_definitions.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gtest/include/gtest/gtest-param-test.h" - -namespace media { -namespace mp4 { - -static const uint8 kNALU1[] = { 0x01, 0x02, 0x03 }; -static const uint8 kNALU2[] = { 0x04, 0x05, 0x06, 0x07 }; -static const uint8 kExpected[] = { - 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, - 0x00, 0x00, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07 }; - -static const uint8 kExpectedParamSets[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x12, - 0x00, 0x00, 0x00, 0x01, 0x67, 0x34, - 0x00, 0x00, 0x00, 0x01, 0x68, 0x56, 0x78}; - -class AVCConversionTest : public testing::TestWithParam { - protected: - void MakeInputForLength(int length_size, std::vector* buf) { - buf->clear(); - for (int i = 1; i < length_size; i++) - buf->push_back(0); - buf->push_back(sizeof(kNALU1)); - buf->insert(buf->end(), kNALU1, kNALU1 + sizeof(kNALU1)); - - for (int i = 1; i < length_size; i++) - buf->push_back(0); - buf->push_back(sizeof(kNALU2)); - buf->insert(buf->end(), kNALU2, kNALU2 + sizeof(kNALU2)); - } -}; - -TEST_P(AVCConversionTest, ParseCorrectly) { - std::vector buf; - MakeInputForLength(GetParam(), &buf); - EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf)); - EXPECT_EQ(buf.size(), sizeof(kExpected)); - EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected))); -} - -TEST_P(AVCConversionTest, ParsePartial) { - std::vector buf; - MakeInputForLength(GetParam(), &buf); - buf.pop_back(); - EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf)); - // This tests a buffer ending in the middle of a NAL length. For length size - // of one, this can't happen, so we skip that case. - if (GetParam() != 1) { - MakeInputForLength(GetParam(), &buf); - buf.erase(buf.end() - (sizeof(kNALU2) + 1), buf.end()); - EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf)); - } -} - -TEST_P(AVCConversionTest, ParseEmpty) { - std::vector buf; - EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf)); - EXPECT_EQ(0u, buf.size()); -} - -INSTANTIATE_TEST_CASE_P(AVCConversionTestValues, - AVCConversionTest, - ::testing::Values(1, 2, 4)); - -TEST_F(AVCConversionTest, ConvertConfigToAnnexB) { - AVCDecoderConfigurationRecord avc_config; - avc_config.sps_list.resize(2); - avc_config.sps_list[0].push_back(0x67); - avc_config.sps_list[0].push_back(0x12); - avc_config.sps_list[1].push_back(0x67); - avc_config.sps_list[1].push_back(0x34); - avc_config.pps_list.resize(1); - avc_config.pps_list[0].push_back(0x68); - avc_config.pps_list[0].push_back(0x56); - avc_config.pps_list[0].push_back(0x78); - - std::vector buf; - EXPECT_TRUE(AVC::ConvertConfigToAnnexB(avc_config, &buf)); - EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0], - sizeof(kExpectedParamSets))); -} - -} // namespace mp4 -} // namespace media diff --git a/media/mp4/box_definitions.h b/media/mp4/box_definitions.h index eab8c4f410..6d03c3e635 100644 --- a/media/mp4/box_definitions.h +++ b/media/mp4/box_definitions.h @@ -8,11 +8,7 @@ #include #include -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "media/base/media_export.h" #include "media/mp4/aac.h" -#include "media/mp4/avc.h" #include "media/mp4/box_reader.h" #include "media/mp4/fourccs.h" @@ -32,27 +28,27 @@ enum TrackType { virtual bool Parse(BoxReader* reader) OVERRIDE; \ virtual FourCC BoxType() const OVERRIDE; \ -struct MEDIA_EXPORT FileType : Box { +struct FileType : Box { DECLARE_BOX_METHODS(FileType); FourCC major_brand; uint32 minor_version; }; -struct MEDIA_EXPORT ProtectionSystemSpecificHeader : Box { +struct ProtectionSystemSpecificHeader : Box { DECLARE_BOX_METHODS(ProtectionSystemSpecificHeader); std::vector system_id; std::vector raw_box; }; -struct MEDIA_EXPORT SampleAuxiliaryInformationOffset : Box { +struct SampleAuxiliaryInformationOffset : Box { DECLARE_BOX_METHODS(SampleAuxiliaryInformationOffset); std::vector offsets; }; -struct MEDIA_EXPORT SampleAuxiliaryInformationSize : Box { +struct SampleAuxiliaryInformationSize : Box { DECLARE_BOX_METHODS(SampleAuxiliaryInformationSize); uint8 default_sample_info_size; @@ -60,20 +56,20 @@ struct MEDIA_EXPORT SampleAuxiliaryInformationSize : Box { std::vector sample_info_sizes; }; -struct MEDIA_EXPORT OriginalFormat : Box { +struct OriginalFormat : Box { DECLARE_BOX_METHODS(OriginalFormat); FourCC format; }; -struct MEDIA_EXPORT SchemeType : Box { +struct SchemeType : Box { DECLARE_BOX_METHODS(SchemeType); FourCC type; uint32 version; }; -struct MEDIA_EXPORT TrackEncryption : Box { +struct TrackEncryption : Box { DECLARE_BOX_METHODS(TrackEncryption); // Note: this definition is specific to the CENC protection type. @@ -82,13 +78,13 @@ struct MEDIA_EXPORT TrackEncryption : Box { std::vector default_kid; }; -struct MEDIA_EXPORT SchemeInfo : Box { +struct SchemeInfo : Box { DECLARE_BOX_METHODS(SchemeInfo); TrackEncryption track_encryption; }; -struct MEDIA_EXPORT ProtectionSchemeInfo : Box { +struct ProtectionSchemeInfo : Box { DECLARE_BOX_METHODS(ProtectionSchemeInfo); OriginalFormat format; @@ -96,7 +92,7 @@ struct MEDIA_EXPORT ProtectionSchemeInfo : Box { SchemeInfo info; }; -struct MEDIA_EXPORT MovieHeader : Box { +struct MovieHeader : Box { DECLARE_BOX_METHODS(MovieHeader); uint64 creation_time; @@ -108,7 +104,7 @@ struct MEDIA_EXPORT MovieHeader : Box { uint32 next_track_id; }; -struct MEDIA_EXPORT TrackHeader : Box { +struct TrackHeader : Box { DECLARE_BOX_METHODS(TrackHeader); uint64 creation_time; @@ -122,32 +118,32 @@ struct MEDIA_EXPORT TrackHeader : Box { uint32 height; }; -struct MEDIA_EXPORT EditListEntry { +struct EditListEntry { uint64 segment_duration; int64 media_time; int16 media_rate_integer; int16 media_rate_fraction; }; -struct MEDIA_EXPORT EditList : Box { +struct EditList : Box { DECLARE_BOX_METHODS(EditList); std::vector edits; }; -struct MEDIA_EXPORT Edit : Box { +struct Edit : Box { DECLARE_BOX_METHODS(Edit); EditList list; }; -struct MEDIA_EXPORT HandlerReference : Box { +struct HandlerReference : Box { DECLARE_BOX_METHODS(HandlerReference); TrackType type; }; -struct MEDIA_EXPORT AVCDecoderConfigurationRecord : Box { +struct AVCDecoderConfigurationRecord : Box { DECLARE_BOX_METHODS(AVCDecoderConfigurationRecord); uint8 version; @@ -163,14 +159,14 @@ struct MEDIA_EXPORT AVCDecoderConfigurationRecord : Box { std::vector pps_list; }; -struct MEDIA_EXPORT PixelAspectRatioBox : Box { +struct PixelAspectRatioBox : Box { DECLARE_BOX_METHODS(PixelAspectRatioBox); uint32 h_spacing; uint32 v_spacing; }; -struct MEDIA_EXPORT VideoSampleEntry : Box { +struct VideoSampleEntry : Box { DECLARE_BOX_METHODS(VideoSampleEntry); FourCC format; @@ -185,14 +181,14 @@ struct MEDIA_EXPORT VideoSampleEntry : Box { AVCDecoderConfigurationRecord avcc; }; -struct MEDIA_EXPORT ElementaryStreamDescriptor : Box { +struct ElementaryStreamDescriptor : Box { DECLARE_BOX_METHODS(ElementaryStreamDescriptor); uint8 object_type; AAC aac; }; -struct MEDIA_EXPORT AudioSampleEntry : Box { +struct AudioSampleEntry : Box { DECLARE_BOX_METHODS(AudioSampleEntry); FourCC format; @@ -205,7 +201,7 @@ struct MEDIA_EXPORT AudioSampleEntry : Box { ElementaryStreamDescriptor esds; }; -struct MEDIA_EXPORT SampleDescription : Box { +struct SampleDescription : Box { DECLARE_BOX_METHODS(SampleDescription); TrackType type; @@ -213,7 +209,7 @@ struct MEDIA_EXPORT SampleDescription : Box { std::vector audio_entries; }; -struct MEDIA_EXPORT SampleTable : Box { +struct SampleTable : Box { DECLARE_BOX_METHODS(SampleTable); // Media Source specific: we ignore many of the sub-boxes in this box, @@ -223,7 +219,7 @@ struct MEDIA_EXPORT SampleTable : Box { SampleDescription description; }; -struct MEDIA_EXPORT MediaHeader : Box { +struct MediaHeader : Box { DECLARE_BOX_METHODS(MediaHeader); uint64 creation_time; @@ -232,13 +228,13 @@ struct MEDIA_EXPORT MediaHeader : Box { uint64 duration; }; -struct MEDIA_EXPORT MediaInformation : Box { +struct MediaInformation : Box { DECLARE_BOX_METHODS(MediaInformation); SampleTable sample_table; }; -struct MEDIA_EXPORT Media : Box { +struct Media : Box { DECLARE_BOX_METHODS(Media); MediaHeader header; @@ -246,7 +242,7 @@ struct MEDIA_EXPORT Media : Box { MediaInformation information; }; -struct MEDIA_EXPORT Track : Box { +struct Track : Box { DECLARE_BOX_METHODS(Track); TrackHeader header; @@ -254,13 +250,13 @@ struct MEDIA_EXPORT Track : Box { Edit edit; }; -struct MEDIA_EXPORT MovieExtendsHeader : Box { +struct MovieExtendsHeader : Box { DECLARE_BOX_METHODS(MovieExtendsHeader); uint64 fragment_duration; }; -struct MEDIA_EXPORT TrackExtends : Box { +struct TrackExtends : Box { DECLARE_BOX_METHODS(TrackExtends); uint32 track_id; @@ -270,14 +266,14 @@ struct MEDIA_EXPORT TrackExtends : Box { uint32 default_sample_flags; }; -struct MEDIA_EXPORT MovieExtends : Box { +struct MovieExtends : Box { DECLARE_BOX_METHODS(MovieExtends); MovieExtendsHeader header; std::vector tracks; }; -struct MEDIA_EXPORT Movie : Box { +struct Movie : Box { DECLARE_BOX_METHODS(Movie); bool fragmented; @@ -287,19 +283,19 @@ struct MEDIA_EXPORT Movie : Box { std::vector pssh; }; -struct MEDIA_EXPORT TrackFragmentDecodeTime : Box { +struct TrackFragmentDecodeTime : Box { DECLARE_BOX_METHODS(TrackFragmentDecodeTime); uint64 decode_time; }; -struct MEDIA_EXPORT MovieFragmentHeader : Box { +struct MovieFragmentHeader : Box { DECLARE_BOX_METHODS(MovieFragmentHeader); uint32 sequence_number; }; -struct MEDIA_EXPORT TrackFragmentHeader : Box { +struct TrackFragmentHeader : Box { DECLARE_BOX_METHODS(TrackFragmentHeader); uint32 track_id; @@ -314,7 +310,7 @@ struct MEDIA_EXPORT TrackFragmentHeader : Box { bool has_default_sample_flags; }; -struct MEDIA_EXPORT TrackFragmentRun : Box { +struct TrackFragmentRun : Box { DECLARE_BOX_METHODS(TrackFragmentRun); uint32 sample_count; @@ -325,7 +321,7 @@ struct MEDIA_EXPORT TrackFragmentRun : Box { std::vector sample_composition_time_offsets; }; -struct MEDIA_EXPORT TrackFragment : Box { +struct TrackFragment : Box { DECLARE_BOX_METHODS(TrackFragment); TrackFragmentHeader header; @@ -335,7 +331,7 @@ struct MEDIA_EXPORT TrackFragment : Box { SampleAuxiliaryInformationSize auxiliary_size; }; -struct MEDIA_EXPORT MovieFragment : Box { +struct MovieFragment : Box { DECLARE_BOX_METHODS(MovieFragment); MovieFragmentHeader header; diff --git a/media/mp4/box_reader.cc b/media/mp4/box_reader.cc index c788772035..6bf438bc0d 100644 --- a/media/mp4/box_reader.cc +++ b/media/mp4/box_reader.cc @@ -79,10 +79,8 @@ bool BufferReader::Read4sInto8s(int64* v) { } -BoxReader::BoxReader(const uint8* buf, const int size, - const LogCB& log_cb) +BoxReader::BoxReader(const uint8* buf, const int size) : BufferReader(buf, size), - log_cb_(log_cb), type_(FOURCC_NULL), version_(0), flags_(0), @@ -101,13 +99,12 @@ BoxReader::~BoxReader() { // static BoxReader* BoxReader::ReadTopLevelBox(const uint8* buf, const int buf_size, - const LogCB& log_cb, bool* err) { - scoped_ptr reader(new BoxReader(buf, buf_size, log_cb)); + scoped_ptr reader(new BoxReader(buf, buf_size)); if (!reader->ReadHeader(err)) return NULL; - if (!IsValidTopLevelBox(reader->type(), log_cb)) { + if (!IsValidTopLevelBox(reader->type())) { *err = true; return NULL; } @@ -121,13 +118,12 @@ BoxReader* BoxReader::ReadTopLevelBox(const uint8* buf, // static bool BoxReader::StartTopLevelBox(const uint8* buf, const int buf_size, - const LogCB& log_cb, FourCC* type, int* box_size, bool* err) { - BoxReader reader(buf, buf_size, log_cb); + BoxReader reader(buf, buf_size); if (!reader.ReadHeader(err)) return false; - if (!IsValidTopLevelBox(reader.type(), log_cb)) { + if (!IsValidTopLevelBox(reader.type())) { *err = true; return false; } @@ -137,8 +133,7 @@ bool BoxReader::StartTopLevelBox(const uint8* buf, } // static -bool BoxReader::IsValidTopLevelBox(const FourCC& type, - const LogCB& log_cb) { +bool BoxReader::IsValidTopLevelBox(const FourCC& type) { switch (type) { case FOURCC_FTYP: case FOURCC_PDIN: @@ -158,8 +153,7 @@ bool BoxReader::IsValidTopLevelBox(const FourCC& type, return true; default: // Hex is used to show nonprintable characters and aid in debugging - MEDIA_LOG(log_cb) << "Unrecognized top-level box type 0x" - << std::hex << type; + LOG(ERROR) << "Unrecognized top-level box type 0x" << std::hex << type; return false; } } @@ -170,7 +164,7 @@ bool BoxReader::ScanChildren() { bool err = false; while (pos() < size()) { - BoxReader child(&buf_[pos_], size_ - pos_, log_cb_); + BoxReader child(&buf_[pos_], size_ - pos_); if (!child.ReadHeader(&err)) break; children_.insert(std::pair(child.type(), child)); diff --git a/media/mp4/box_reader.h b/media/mp4/box_reader.h index 43f11d56fe..759d89a75c 100644 --- a/media/mp4/box_reader.h +++ b/media/mp4/box_reader.h @@ -10,8 +10,6 @@ #include "base/compiler_specific.h" #include "base/logging.h" -#include "media/base/media_export.h" -#include "media/base/media_log.h" #include "media/mp4/fourccs.h" #include "media/mp4/rcheck.h" @@ -20,13 +18,13 @@ namespace mp4 { class BoxReader; -struct MEDIA_EXPORT Box { +struct Box { virtual ~Box(); virtual bool Parse(BoxReader* reader) = 0; virtual FourCC BoxType() const = 0; }; -class MEDIA_EXPORT BufferReader { +class BufferReader { public: BufferReader(const uint8* buf, const int size) : buf_(buf), size_(size), pos_(0) {} @@ -67,7 +65,7 @@ class MEDIA_EXPORT BufferReader { template bool Read(T* t) WARN_UNUSED_RESULT; }; -class MEDIA_EXPORT BoxReader : public BufferReader { +class BoxReader : public BufferReader { public: ~BoxReader(); @@ -79,7 +77,6 @@ class MEDIA_EXPORT BoxReader : public BufferReader { // |buf| is retained but not owned, and must outlive the BoxReader instance. static BoxReader* ReadTopLevelBox(const uint8* buf, const int buf_size, - const LogCB& log_cb, bool* err); // Read the box header from the current buffer. This function returns true if @@ -90,7 +87,6 @@ class MEDIA_EXPORT BoxReader : public BufferReader { // |buf| is not retained. static bool StartTopLevelBox(const uint8* buf, const int buf_size, - const LogCB& log_cb, FourCC* type, int* box_size, bool* err) WARN_UNUSED_RESULT; @@ -98,8 +94,7 @@ class MEDIA_EXPORT BoxReader : public BufferReader { // Returns true if |type| is recognized to be a top-level box, false // otherwise. This returns true for some boxes which we do not parse. // Helpful in debugging misaligned appends. - static bool IsValidTopLevelBox(const FourCC& type, - const LogCB& log_cb); + static bool IsValidTopLevelBox(const FourCC& type); // Scan through all boxes within the current box, starting at the current // buffer position. Must be called before any of the *Child functions work. @@ -137,7 +132,7 @@ class MEDIA_EXPORT BoxReader : public BufferReader { uint32 flags() const { return flags_; } private: - BoxReader(const uint8* buf, const int size, const LogCB& log_cb); + BoxReader(const uint8* buf, const int size); // Must be called immediately after init. If the return is false, this // indicates that the box header and its contents were not available in the @@ -148,7 +143,6 @@ class MEDIA_EXPORT BoxReader : public BufferReader { // true, the error is unrecoverable and the stream should be aborted. bool ReadHeader(bool* err); - LogCB log_cb_; FourCC type_; uint8 version_; uint32 flags_; @@ -197,7 +191,7 @@ bool BoxReader::ReadAllChildren(std::vector* children) { bool err = false; while (pos() < size()) { - BoxReader child_reader(&buf_[pos_], size_ - pos_, log_cb_); + BoxReader child_reader(&buf_[pos_], size_ - pos_); if (!child_reader.ReadHeader(&err)) break; T child; RCHECK(child.Parse(&child_reader)); diff --git a/media/mp4/box_reader_unittest.cc b/media/mp4/box_reader_unittest.cc index 99d9975fd2..40af435f1b 100644 --- a/media/mp4/box_reader_unittest.cc +++ b/media/mp4/box_reader_unittest.cc @@ -86,7 +86,7 @@ TEST_F(BoxReaderTest, ExpectedOperationTest) { std::vector buf = GetBuf(); bool err; scoped_ptr reader( - BoxReader::ReadTopLevelBox(&buf[0], buf.size(), LogCB(), &err)); + BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); EXPECT_FALSE(err); EXPECT_TRUE(reader.get()); @@ -114,7 +114,7 @@ TEST_F(BoxReaderTest, OuterTooShortTest) { // Create a soft failure by truncating the outer box. scoped_ptr r( - BoxReader::ReadTopLevelBox(&buf[0], buf.size() - 2, LogCB(), &err)); + BoxReader::ReadTopLevelBox(&buf[0], buf.size() - 2, &err)); EXPECT_FALSE(err); EXPECT_FALSE(r.get()); @@ -127,7 +127,7 @@ TEST_F(BoxReaderTest, InnerTooLongTest) { // Make an inner box too big for its outer box. buf[25] = 1; scoped_ptr reader( - BoxReader::ReadTopLevelBox(&buf[0], buf.size(), LogCB(), &err)); + BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); SkipBox box; EXPECT_FALSE(box.Parse(reader.get())); @@ -140,7 +140,7 @@ TEST_F(BoxReaderTest, WrongFourCCTest) { // Set an unrecognized top-level FourCC. buf[5] = 1; scoped_ptr reader( - BoxReader::ReadTopLevelBox(&buf[0], buf.size(), LogCB(), &err)); + BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); EXPECT_FALSE(reader.get()); EXPECT_TRUE(err); } @@ -149,7 +149,7 @@ TEST_F(BoxReaderTest, ScanChildrenTest) { std::vector buf = GetBuf(); bool err; scoped_ptr reader( - BoxReader::ReadTopLevelBox(&buf[0], buf.size(), LogCB(), &err)); + BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); EXPECT_TRUE(reader->SkipBytes(16) && reader->ScanChildren()); @@ -173,7 +173,7 @@ TEST_F(BoxReaderTest, ReadAllChildrenTest) { buf[3] = 0x38; bool err; scoped_ptr reader( - BoxReader::ReadTopLevelBox(&buf[0], buf.size(), LogCB(), &err)); + BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); std::vector kids; EXPECT_TRUE(reader->SkipBytes(16) && reader->ReadAllChildren(&kids)); @@ -190,7 +190,7 @@ TEST_F(BoxReaderTest, SkippingBloc) { bool err; scoped_ptr reader( - BoxReader::ReadTopLevelBox(&buf[0], buf.size(), LogCB(), &err)); + BoxReader::ReadTopLevelBox(&buf[0], buf.size(), &err)); EXPECT_FALSE(err); EXPECT_TRUE(reader); diff --git a/media/mp4/es_descriptor.cc b/media/mp4/es_descriptor.cc index 8517b82cbd..9c337b8a54 100644 --- a/media/mp4/es_descriptor.cc +++ b/media/mp4/es_descriptor.cc @@ -32,7 +32,7 @@ namespace media { namespace mp4 { // static -bool ESDescriptor::IsAAC(uint8 object_type) { +bool ESDescriptor::IsAAC(ObjectType object_type) { return object_type == kISO_14496_3 || object_type == kISO_13818_7_AAC_LC; } @@ -72,7 +72,7 @@ bool ESDescriptor::Parse(const std::vector& data) { return true; } -uint8 ESDescriptor::object_type() const { +ObjectType ESDescriptor::object_type() const { return object_type_; } diff --git a/media/mp4/es_descriptor.h b/media/mp4/es_descriptor.h index 36e1bf2a3b..b6412c978a 100644 --- a/media/mp4/es_descriptor.h +++ b/media/mp4/es_descriptor.h @@ -8,7 +8,6 @@ #include #include "base/basictypes.h" -#include "media/base/media_export.h" namespace media { @@ -28,17 +27,17 @@ enum ObjectType { // This class parse object type and decoder specific information from an // elementary stream descriptor, which is usually contained in an esds box. // Please refer to ISO 14496 Part 1 7.2.6.5 for more details. -class MEDIA_EXPORT ESDescriptor { +class ESDescriptor { public: // Utility function to check if the given object type is AAC. - static bool IsAAC(uint8 object_type); + static bool IsAAC(ObjectType object_type); ESDescriptor(); ~ESDescriptor(); bool Parse(const std::vector& data); - uint8 object_type() const; + ObjectType object_type() const; const std::vector& decoder_specific_info() const; private: @@ -51,7 +50,7 @@ class MEDIA_EXPORT ESDescriptor { bool ParseDecoderConfigDescriptor(BitReader* reader); bool ParseDecoderSpecificInfo(BitReader* reader); - uint8 object_type_; + ObjectType object_type_; std::vector decoder_specific_info_; }; diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_media_parser.cc similarity index 51% rename from media/mp4/mp4_stream_parser.cc rename to media/mp4/mp4_media_parser.cc index fc4ee8abd1..ba3c93c4b4 100644 --- a/media/mp4/mp4_stream_parser.cc +++ b/media/mp4/mp4_media_parser.cc @@ -2,29 +2,38 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/mp4/mp4_stream_parser.h" +#include "media/mp4/mp4_media_parser.h" #include "base/callback.h" #include "base/callback_helpers.h" #include "base/logging.h" +#include "base/memory/ref_counted.h" #include "base/time/time.h" -#include "media/base/audio_decoder_config.h" -#include "media/base/stream_parser_buffer.h" -#include "media/base/video_decoder_config.h" -#include "media/base/video_util.h" +#include "media/base/audio_stream_info.h" +#include "media/base/buffers.h" +#include "media/base/media_sample.h" +#include "media/base/video_stream_info.h" #include "media/mp4/box_definitions.h" #include "media/mp4/box_reader.h" #include "media/mp4/es_descriptor.h" #include "media/mp4/rcheck.h" +#include "media/mp4/track_run_iterator.h" + +namespace { + +base::TimeDelta TimeDeltaFromRational(int64 numer, int64 denom) { + DCHECK_LT((numer > 0 ? numer : -numer), + kint64max / base::Time::kMicrosecondsPerSecond); + return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond * + numer / denom); +} + +} // namespace namespace media { namespace mp4 { -// TODO(xhwang): Figure out the init data type appropriately once it's spec'ed. -static const char kMp4InitDataType[] = "video/mp4"; - -MP4StreamParser::MP4StreamParser(const std::set& audio_object_types, - bool has_sbr) +MP4MediaParser::MP4MediaParser() : state_(kWaitingForInit), moof_head_(0), mdat_tail_(0), @@ -32,55 +41,37 @@ MP4StreamParser::MP4StreamParser(const std::set& audio_object_types, has_video_(false), audio_track_id_(0), video_track_id_(0), - audio_object_types_(audio_object_types), - has_sbr_(has_sbr), + // TODO(kqyang): do we need to care about it?? + has_sbr_(false), is_audio_track_encrypted_(false), is_video_track_encrypted_(false) { } -MP4StreamParser::~MP4StreamParser() {} +MP4MediaParser::~MP4MediaParser() {} -void MP4StreamParser::Init(const InitCB& init_cb, - const NewConfigCB& config_cb, - const NewBuffersCB& new_buffers_cb, - const NewTextBuffersCB& /* text_cb */ , - const NeedKeyCB& need_key_cb, - const AddTextTrackCB& /* add_text_track_cb */ , - const NewMediaSegmentCB& new_segment_cb, - const base::Closure& end_of_segment_cb, - const LogCB& log_cb) { +void MP4MediaParser::Init(const InitCB& init_cb, + const NewSampleCB& new_sample_cb, + const NeedKeyCB& need_key_cb) { DCHECK_EQ(state_, kWaitingForInit); DCHECK(init_cb_.is_null()); DCHECK(!init_cb.is_null()); - DCHECK(!config_cb.is_null()); - DCHECK(!new_buffers_cb.is_null()); + DCHECK(!new_sample_cb.is_null()); DCHECK(!need_key_cb.is_null()); - DCHECK(!end_of_segment_cb.is_null()); ChangeState(kParsingBoxes); init_cb_ = init_cb; - config_cb_ = config_cb; - new_buffers_cb_ = new_buffers_cb; + new_sample_cb_ = new_sample_cb; need_key_cb_ = need_key_cb; - new_segment_cb_ = new_segment_cb; - end_of_segment_cb_ = end_of_segment_cb; - log_cb_ = log_cb; } -void MP4StreamParser::Reset() { +void MP4MediaParser::Reset() { queue_.Reset(); runs_.reset(); moof_head_ = 0; mdat_tail_ = 0; } -void MP4StreamParser::Flush() { - DCHECK_NE(state_, kWaitingForInit); - Reset(); - ChangeState(kParsingBoxes); -} - -bool MP4StreamParser::Parse(const uint8* buf, int size) { +bool MP4MediaParser::Parse(const uint8* buf, int size) { DCHECK_NE(state_, kWaitingForInit); if (state_ == kError) @@ -88,9 +79,6 @@ bool MP4StreamParser::Parse(const uint8* buf, int size) { queue_.Push(buf, size); - BufferQueue audio_buffers; - BufferQueue video_buffers; - bool result, err = false; do { @@ -98,7 +86,7 @@ bool MP4StreamParser::Parse(const uint8* buf, int size) { result = ParseBox(&err); } else { DCHECK_EQ(kEmittingSamples, state_); - result = EnqueueSample(&audio_buffers, &video_buffers, &err); + result = EnqueueSample(&err); if (result) { int64 max_clear = runs_->GetMaxClearOffset() + moof_head_; err = !ReadAndDiscardMDATsUntil(max_clear); @@ -106,9 +94,6 @@ bool MP4StreamParser::Parse(const uint8* buf, int size) { } } while (result && !err); - if (!err) - err = !SendAndFlushSamples(&audio_buffers, &video_buffers); - if (err) { DLOG(ERROR) << "Error while parsing MP4"; moov_.reset(); @@ -120,14 +105,13 @@ bool MP4StreamParser::Parse(const uint8* buf, int size) { return true; } -bool MP4StreamParser::ParseBox(bool* err) { +bool MP4MediaParser::ParseBox(bool* err) { const uint8* buf; int size; queue_.Peek(&buf, &size); if (!size) return false; - scoped_ptr reader( - BoxReader::ReadTopLevelBox(buf, size, log_cb_, err)); + scoped_ptr reader(BoxReader::ReadTopLevelBox(buf, size, err)); if (reader.get() == NULL) return false; if (reader->type() == FOURCC_MOOV) { @@ -145,8 +129,8 @@ bool MP4StreamParser::ParseBox(bool* err) { // before the head of the 'moof', so keeping this box around is sufficient.) return !(*err); } else { - MEDIA_LOG(log_cb_) << "Skipping unrecognized top-level box: " - << FourCCToString(reader->type()); + LOG(WARNING) << "Skipping unrecognized top-level box: " + << FourCCToString(reader->type()); } queue_.Pop(reader->size()); @@ -154,7 +138,7 @@ bool MP4StreamParser::ParseBox(bool* err) { } -bool MP4StreamParser::ParseMoov(BoxReader* reader) { +bool MP4MediaParser::ParseMoov(BoxReader* reader) { moov_.reset(new Movie); RCHECK(moov_->Parse(reader)); runs_.reset(); @@ -162,8 +146,7 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { has_audio_ = false; has_video_ = false; - AudioDecoderConfig audio_config; - VideoDecoderConfig video_config; + std::vector > streams; for (std::vector::const_iterator track = moov_->tracks.begin(); track != moov_->tracks.end(); ++track) { @@ -187,7 +170,11 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { RCHECK(desc_idx > 0); desc_idx -= 1; // BMFF descriptor index is one-based - if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) { + if (track->media.handler.type == kAudio) { + // TODO(kqyang): do we need to support multiple audio or video streams in + // a single file? + RCHECK(!has_audio_); + RCHECK(!samp_descr.audio_entries.empty()); // It is not uncommon to find otherwise-valid files with incorrect sample @@ -200,68 +187,61 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { if (!(entry.format == FOURCC_MP4A || entry.format == FOURCC_EAC3 || (entry.format == FOURCC_ENCA && entry.sinf.format.format == FOURCC_MP4A))) { - MEDIA_LOG(log_cb_) << "Unsupported audio format 0x" - << std::hex << entry.format << " in stsd box."; + LOG(ERROR) << "Unsupported audio format 0x" + << std::hex << entry.format << " in stsd box."; return false; } - uint8 audio_type = entry.esds.object_type; + ObjectType audio_type = static_cast(entry.esds.object_type); DVLOG(1) << "audio_type " << std::hex << audio_type; if (audio_type == kForbidden && entry.format == FOURCC_EAC3) { audio_type = kEAC3; } - if (audio_object_types_.find(audio_type) == audio_object_types_.end()) { - MEDIA_LOG(log_cb_) << "audio object type 0x" << std::hex << audio_type - << " does not match what is specified in the" - << " mimetype."; - return false; - } AudioCodec codec = kUnknownAudioCodec; - ChannelLayout channel_layout = CHANNEL_LAYOUT_NONE; + int num_channels = 0; int sample_per_second = 0; std::vector extra_data; // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or // supported MPEG2 AAC varients. if (ESDescriptor::IsAAC(audio_type)) { codec = kCodecAAC; - channel_layout = aac.GetChannelLayout(has_sbr_); + num_channels = aac.GetNumChannels(has_sbr_); sample_per_second = aac.GetOutputSamplesPerSecond(has_sbr_); #if defined(OS_ANDROID) extra_data = aac.codec_specific_data(); #endif } else if (audio_type == kEAC3) { codec = kCodecEAC3; - channel_layout = GuessChannelLayout(entry.channelcount); + num_channels = entry.channelcount; sample_per_second = entry.samplerate; } else { - MEDIA_LOG(log_cb_) << "Unsupported audio object type 0x" << std::hex - << audio_type << " in esds."; - return false; - } - - SampleFormat sample_format; - if (entry.samplesize == 8) { - sample_format = kSampleFormatU8; - } else if (entry.samplesize == 16) { - sample_format = kSampleFormatS16; - } else if (entry.samplesize == 32) { - sample_format = kSampleFormatS32; - } else { - LOG(ERROR) << "Unsupported sample size."; + LOG(ERROR) << "Unsupported audio object type 0x" + << std::hex << audio_type << " in esds."; return false; } is_audio_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; DVLOG(1) << "is_audio_track_encrypted_: " << is_audio_track_encrypted_; - audio_config.Initialize( - codec, sample_format, channel_layout, sample_per_second, - extra_data.size() ? &extra_data[0] : NULL, extra_data.size(), - is_audio_track_encrypted_, false); + streams.push_back( + new AudioStreamInfo(track->header.track_id, + track->media.header.timescale, + codec, + entry.samplesize / 8, + num_channels, + sample_per_second, + extra_data.size() ? &extra_data[0] : NULL, + extra_data.size(), + is_audio_track_encrypted_)); has_audio_ = true; audio_track_id_ = track->header.track_id; } - if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { + + if (track->media.handler.type == kVideo) { + // TODO(kqyang): do we need to support multiple audio or video streams in + // a single file? + RCHECK(!has_video_); + RCHECK(!samp_descr.video_entries.empty()); if (desc_idx >= samp_descr.video_entries.size()) desc_idx = 0; @@ -270,31 +250,28 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { if (!(entry.format == FOURCC_AVC1 || (entry.format == FOURCC_ENCV && entry.sinf.format.format == FOURCC_AVC1))) { - MEDIA_LOG(log_cb_) << "Unsupported video format 0x" - << std::hex << entry.format << " in stsd box."; + LOG(ERROR) << "Unsupported video format 0x" + << std::hex << entry.format << " in stsd box."; return false; } - // TODO(strobe): Recover correct crop box - gfx::Size coded_size(entry.width, entry.height); - gfx::Rect visible_rect(coded_size); - gfx::Size natural_size = GetNaturalSize(visible_rect.size(), - entry.pixel_aspect.h_spacing, - entry.pixel_aspect.v_spacing); is_video_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; DVLOG(1) << "is_video_track_encrypted_: " << is_video_track_encrypted_; - video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12, - coded_size, visible_rect, natural_size, - // No decoder-specific buffer needed for AVC; - // SPS/PPS are embedded in the video stream - NULL, 0, is_video_track_encrypted_, true); + streams.push_back( + new VideoStreamInfo(track->header.track_id, + track->media.header.timescale, + kCodecH264, + entry.width, + entry.height, + // No decoder-specific buffer needed for AVC. + NULL, 0, + is_video_track_encrypted_)); has_video_ = true; video_track_id_ = track->header.track_id; } } - RCHECK(config_cb_.Run(audio_config, video_config)); - + // TODO(kqyang): figure out how to get duration for every tracks/streams. base::TimeDelta duration; if (moov_->extends.header.fragment_duration > 0) { duration = TimeDeltaFromRational(moov_->extends.header.fragment_duration, @@ -307,27 +284,25 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { duration = kInfiniteDuration(); } - if (!init_cb_.is_null()) - base::ResetAndReturn(&init_cb_).Run(true, duration); + init_cb_.Run(true, streams); EmitNeedKeyIfNecessary(moov_->pssh); return true; } -bool MP4StreamParser::ParseMoof(BoxReader* reader) { +bool MP4MediaParser::ParseMoof(BoxReader* reader) { RCHECK(moov_.get()); // Must already have initialization segment MovieFragment moof; RCHECK(moof.Parse(reader)); if (!runs_) - runs_.reset(new TrackRunIterator(moov_.get(), log_cb_)); + runs_.reset(new TrackRunIterator(moov_.get())); RCHECK(runs_->Init(moof)); EmitNeedKeyIfNecessary(moof.pssh); - new_segment_cb_.Run(); ChangeState(kEmittingSamples); return true; } -void MP4StreamParser::EmitNeedKeyIfNecessary( +void MP4MediaParser::EmitNeedKeyIfNecessary( const std::vector& headers) { // TODO(strobe): ensure that the value of init_data (all PSSH headers // concatenated in arbitrary order) matches the EME spec. @@ -346,78 +321,17 @@ void MP4StreamParser::EmitNeedKeyIfNecessary( headers[i].raw_box.size()); pos += headers[i].raw_box.size(); } - need_key_cb_.Run(kMp4InitDataType, init_data.Pass(), total_size); + need_key_cb_.Run(CONTAINER_MOV, init_data.Pass(), total_size); } -bool MP4StreamParser::PrepareAVCBuffer( - const AVCDecoderConfigurationRecord& avc_config, - std::vector* frame_buf, - std::vector* subsamples) const { - // Convert the AVC NALU length fields to Annex B headers, as expected by - // decoding libraries. Since this may enlarge the size of the buffer, we also - // update the clear byte count for each subsample if encryption is used to - // account for the difference in size between the length prefix and Annex B - // start code. - RCHECK(AVC::ConvertFrameToAnnexB(avc_config.length_size, frame_buf)); - if (!subsamples->empty()) { - const int nalu_size_diff = 4 - avc_config.length_size; - size_t expected_size = runs_->sample_size() + - subsamples->size() * nalu_size_diff; - RCHECK(frame_buf->size() == expected_size); - for (size_t i = 0; i < subsamples->size(); i++) - (*subsamples)[i].clear_bytes += nalu_size_diff; - } - - if (runs_->is_keyframe()) { - // If this is a keyframe, we (re-)inject SPS and PPS headers at the start of - // a frame. If subsample info is present, we also update the clear byte - // count for that first subsample. - std::vector param_sets; - RCHECK(AVC::ConvertConfigToAnnexB(avc_config, ¶m_sets)); - frame_buf->insert(frame_buf->begin(), - param_sets.begin(), param_sets.end()); - if (!subsamples->empty()) - (*subsamples)[0].clear_bytes += param_sets.size(); - } - return true; -} - -bool MP4StreamParser::PrepareAACBuffer( - const AAC& aac_config, std::vector* frame_buf, - std::vector* subsamples) const { - // Append an ADTS header to every audio sample. - RCHECK(aac_config.ConvertEsdsToADTS(frame_buf)); - - // As above, adjust subsample information to account for the headers. AAC is - // not required to use subsample encryption, so we may need to add an entry. - if (subsamples->empty()) { - SubsampleEntry entry; - entry.clear_bytes = AAC::kADTSHeaderSize; - entry.cypher_bytes = frame_buf->size() - AAC::kADTSHeaderSize; - subsamples->push_back(entry); - } else { - (*subsamples)[0].clear_bytes += AAC::kADTSHeaderSize; - } - return true; -} - -bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, - BufferQueue* video_buffers, - bool* err) { +bool MP4MediaParser::EnqueueSample(bool* err) { if (!runs_->IsRunValid()) { - // Flush any buffers we've gotten in this chunk so that buffers don't - // cross NewSegment() calls - *err = !SendAndFlushSamples(audio_buffers, video_buffers); - if (*err) - return false; - // Remain in kEnqueueingSamples state, discarding data, until the end of // the current 'mdat' box has been appended to the queue. if (!queue_.Trim(mdat_tail_)) return false; ChangeState(kParsingBoxes); - end_of_segment_cb_.Run(); return true; } @@ -468,26 +382,6 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, subsamples = decrypt_config->subsamples(); } - std::vector frame_buf(buf, buf + runs_->sample_size()); - if (video) { - if (!PrepareAVCBuffer(runs_->video_description().avcc, - &frame_buf, &subsamples)) { - MEDIA_LOG(log_cb_) << "Failed to prepare AVC sample for decode"; - *err = true; - return false; - } - } - - if (audio) { - if (ESDescriptor::IsAAC(runs_->audio_description().esds.object_type) && - !PrepareAACBuffer(runs_->audio_description().esds.aac, - &frame_buf, &subsamples)) { - MEDIA_LOG(log_cb_) << "Failed to prepare AAC sample for decode"; - *err = true; - return false; - } - } - if (decrypt_config) { if (!subsamples.empty()) { // Create a new config with the updated subsamples. @@ -498,54 +392,33 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, subsamples)); } // else, use the existing config. - } else if ((audio && is_audio_track_encrypted_) || - (video && is_video_track_encrypted_)) { - // The media pipeline requires a DecryptConfig with an empty |iv|. - // TODO(ddorwin): Refactor so we do not need a fake key ID ("1"); - decrypt_config.reset( - new DecryptConfig("1", "", 0, std::vector())); } - scoped_refptr stream_buf = - StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), - runs_->is_keyframe()); + std::vector frame_buf(buf, buf + runs_->sample_size()); + scoped_refptr stream_sample = MediaSample::CopyFrom( + &frame_buf[0], frame_buf.size(), runs_->is_keyframe()); if (decrypt_config) - stream_buf->set_decrypt_config(decrypt_config.Pass()); + stream_sample->set_decrypt_config(decrypt_config.Pass()); - stream_buf->set_duration(runs_->duration()); - stream_buf->set_timestamp(runs_->cts()); - stream_buf->SetDecodeTimestamp(runs_->dts()); + stream_sample->set_dts(runs_->dts()); + stream_sample->set_pts(runs_->cts()); + stream_sample->set_duration(runs_->duration()); DVLOG(3) << "Pushing frame: aud=" << audio << ", key=" << runs_->is_keyframe() - << ", dur=" << runs_->duration().InMilliseconds() - << ", dts=" << runs_->dts().InMilliseconds() - << ", cts=" << runs_->cts().InMilliseconds() + << ", dur=" << runs_->duration() + << ", dts=" << runs_->dts() + << ", cts=" << runs_->cts() << ", size=" << runs_->sample_size(); - if (audio) { - audio_buffers->push_back(stream_buf); - } else { - video_buffers->push_back(stream_buf); - } + new_sample_cb_.Run(runs_->track_id(), stream_sample); runs_->AdvanceSample(); return true; } -bool MP4StreamParser::SendAndFlushSamples(BufferQueue* audio_buffers, - BufferQueue* video_buffers) { - if (audio_buffers->empty() && video_buffers->empty()) - return true; - - bool success = new_buffers_cb_.Run(*audio_buffers, *video_buffers); - audio_buffers->clear(); - video_buffers->clear(); - return success; -} - -bool MP4StreamParser::ReadAndDiscardMDATsUntil(const int64 offset) { +bool MP4MediaParser::ReadAndDiscardMDATsUntil(const int64 offset) { bool err = false; while (mdat_tail_ < offset) { const uint8* buf; @@ -554,13 +427,12 @@ bool MP4StreamParser::ReadAndDiscardMDATsUntil(const int64 offset) { FourCC type; int box_sz; - if (!BoxReader::StartTopLevelBox(buf, size, log_cb_, - &type, &box_sz, &err)) + if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err)) break; if (type != FOURCC_MDAT) { - MEDIA_LOG(log_cb_) << "Unexpected box type while parsing MDATs: " - << FourCCToString(type); + LOG(ERROR) << "Unexpected box type while parsing MDATs: " + << FourCCToString(type); } mdat_tail_ += box_sz; } @@ -568,7 +440,7 @@ bool MP4StreamParser::ReadAndDiscardMDATsUntil(const int64 offset) { return !err; } -void MP4StreamParser::ChangeState(State new_state) { +void MP4MediaParser::ChangeState(State new_state) { DVLOG(2) << "Changing state: " << new_state; state_ = new_state; } diff --git a/media/mp4/mp4_stream_parser.h b/media/mp4/mp4_media_parser.h similarity index 50% rename from media/mp4/mp4_stream_parser.h rename to media/mp4/mp4_media_parser.h index 81139d5270..54da502864 100644 --- a/media/mp4/mp4_stream_parser.h +++ b/media/mp4/mp4_media_parser.h @@ -1,42 +1,40 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_MP4_MP4_STREAM_PARSER_H_ -#define MEDIA_MP4_MP4_STREAM_PARSER_H_ +#ifndef MEDIA_MP4_MP4_MEDIA_PARSER_H_ +#define MEDIA_MP4_MP4_MEDIA_PARSER_H_ -#include #include #include "base/basictypes.h" -#include "base/callback.h" +#include "base/callback_forward.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "media/base/media_export.h" -#include "media/base/stream_parser.h" +#include "media/base/media_parser.h" #include "media/mp4/offset_byte_queue.h" -#include "media/mp4/track_run_iterator.h" namespace media { + +class SubsampleEntry; + namespace mp4 { struct Movie; +class AAC; +class AVCDecoderConfigurationRecord; class BoxReader; +class ProtectionSystemSpecificHeader; +class TrackRunIterator; -class MEDIA_EXPORT MP4StreamParser : public StreamParser { +class MP4MediaParser : public MediaParser { public: - MP4StreamParser(const std::set& audio_object_types, bool has_sbr); - virtual ~MP4StreamParser(); + MP4MediaParser(); + virtual ~MP4MediaParser(); - virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb, - const NewBuffersCB& new_buffers_cb, - const NewTextBuffersCB& text_cb, - const NeedKeyCB& need_key_cb, - const AddTextTrackCB& add_text_track_cb, - const NewMediaSegmentCB& new_segment_cb, - const base::Closure& end_of_segment_cb, - const LogCB& log_cb) OVERRIDE; - virtual void Flush() OVERRIDE; + virtual void Init(const InitCB& init_cb, + const NewSampleCB& new_sample_cb, + const NeedKeyCB& need_key_cb) OVERRIDE; virtual bool Parse(const uint8* buf, int size) OVERRIDE; private: @@ -65,28 +63,15 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser { void ChangeState(State new_state); bool EmitConfigs(); - bool PrepareAVCBuffer(const AVCDecoderConfigurationRecord& avc_config, - std::vector* frame_buf, - std::vector* subsamples) const; - bool PrepareAACBuffer(const AAC& aac_config, - std::vector* frame_buf, - std::vector* subsamples) const; - bool EnqueueSample(BufferQueue* audio_buffers, - BufferQueue* video_buffers, - bool* err); - bool SendAndFlushSamples(BufferQueue* audio_buffers, - BufferQueue* video_buffers); + + bool EnqueueSample(bool* err); void Reset(); State state_; InitCB init_cb_; - NewConfigCB config_cb_; - NewBuffersCB new_buffers_cb_; + NewSampleCB new_sample_cb_; NeedKeyCB need_key_cb_; - NewMediaSegmentCB new_segment_cb_; - base::Closure end_of_segment_cb_; - LogCB log_cb_; OffsetByteQueue queue_; @@ -100,23 +85,21 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser { // Valid iff it is greater than the head of the queue. int64 mdat_tail_; - scoped_ptr moov_; - scoped_ptr runs_; + scoped_ptr moov_; + scoped_ptr runs_; bool has_audio_; bool has_video_; uint32 audio_track_id_; uint32 video_track_id_; - // The object types allowed for audio tracks. - std::set audio_object_types_; bool has_sbr_; bool is_audio_track_encrypted_; bool is_video_track_encrypted_; - DISALLOW_COPY_AND_ASSIGN(MP4StreamParser); + DISALLOW_COPY_AND_ASSIGN(MP4MediaParser); }; } // namespace mp4 } // namespace media -#endif // MEDIA_MP4_MP4_STREAM_PARSER_H_ +#endif // MEDIA_MP4_MP4_MEDIA_PARSER_H_ diff --git a/media/mp4/mp4_media_parser_unittest.cc b/media/mp4/mp4_media_parser_unittest.cc new file mode 100644 index 0000000000..03c12b5158 --- /dev/null +++ b/media/mp4/mp4_media_parser_unittest.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/mp4/mp4_media_parser.h" + +#include +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/time/time.h" +#include "media/base/media_sample.h" +#include "media/base/stream_info.h" +#include "media/base/test_data_util.h" +#include "media/mp4/es_descriptor.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { +namespace mp4 { + +class MP4MediaParserTest : public testing::Test { + public: + MP4MediaParserTest() : configs_received_(false) { + parser_.reset(new MP4MediaParser()); + } + + protected: + scoped_ptr parser_; + bool configs_received_; + + bool AppendData(const uint8* data, size_t length) { + return parser_->Parse(data, length); + } + + bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) { + const uint8* start = data; + const uint8* end = data + length; + while (start < end) { + size_t append_size = std::min(piece_size, + static_cast(end - start)); + if (!AppendData(start, append_size)) + return false; + start += append_size; + } + return true; + } + + void InitF(bool init_ok, std::vector >& streams) { + DVLOG(1) << "InitF: ok=" << init_ok; + if (init_ok && streams.size() > 0) + configs_received_ = true; + } + + bool NewSampleF(uint32 track_id, const scoped_refptr& sample) { + DVLOG(2) << "Track Id: " << track_id << " " + << sample->ToString(); + return true; + } + + void KeyNeededF(MediaContainerName container, + scoped_ptr init_data, + int init_data_size) { + DVLOG(1) << "KeyNeededF: " << init_data_size; + EXPECT_TRUE(init_data.get()); + EXPECT_GT(init_data_size, 0); + } + + void InitializeParser() { + parser_->Init( + base::Bind(&MP4MediaParserTest::InitF, base::Unretained(this)), + base::Bind(&MP4MediaParserTest::NewSampleF, base::Unretained(this)), + base::Bind(&MP4MediaParserTest::KeyNeededF, base::Unretained(this))); + } + + bool ParseMP4File(const std::string& filename, int append_bytes) { + InitializeParser(); + + std::vector buffer = ReadTestDataFile(filename); + EXPECT_TRUE(AppendDataInPieces(buffer.data(), + buffer.size(), + append_bytes)); + return true; + } +}; + +TEST_F(MP4MediaParserTest, UnalignedAppend) { + // Test small, non-segment-aligned appends (small enough to exercise + // incremental append system) + ParseMP4File("bear-1280x720-av_frag.mp4", 512); +} + +TEST_F(MP4MediaParserTest, BytewiseAppend) { + // Ensure no incremental errors occur when parsing + ParseMP4File("bear-1280x720-av_frag.mp4", 1); +} + +TEST_F(MP4MediaParserTest, MultiFragmentAppend) { + // Large size ensures multiple fragments are appended in one call (size is + // larger than this particular test file) + ParseMP4File("bear-1280x720-av_frag.mp4", 768432); +} + +TEST_F(MP4MediaParserTest, Reinitialization) { + InitializeParser(); + + std::vector buffer = + ReadTestDataFile("bear-1280x720-av_frag.mp4"); + EXPECT_TRUE(AppendDataInPieces(buffer.data(), + buffer.size(), + 512)); + EXPECT_TRUE(AppendDataInPieces(buffer.data(), + buffer.size(), + 512)); +} + +TEST_F(MP4MediaParserTest, MPEG2_AAC_LC) { + parser_.reset(new MP4MediaParser()); + ParseMP4File("bear-mpeg2-aac-only_frag.mp4", 512); +} + +// TODO(strobe): Create and test media which uses CENC auxiliary info stored +// inside a private box + +} // namespace mp4 +} // namespace media diff --git a/media/mp4/mp4_stream_parser_unittest.cc b/media/mp4/mp4_stream_parser_unittest.cc deleted file mode 100644 index fa880ac38c..0000000000 --- a/media/mp4/mp4_stream_parser_unittest.cc +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/time/time.h" -#include "media/base/audio_decoder_config.h" -#include "media/base/decoder_buffer.h" -#include "media/base/stream_parser_buffer.h" -#include "media/base/test_data_util.h" -#include "media/base/video_decoder_config.h" -#include "media/mp4/es_descriptor.h" -#include "media/mp4/mp4_stream_parser.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::TimeDelta; - -namespace media { -namespace mp4 { - -// TODO(xhwang): Figure out the init data type appropriately once it's spec'ed. -static const char kMp4InitDataType[] = "video/mp4"; - -class MP4StreamParserTest : public testing::Test { - public: - MP4StreamParserTest() - : configs_received_(false) { - std::set audio_object_types; - audio_object_types.insert(kISO_14496_3); - parser_.reset(new MP4StreamParser(audio_object_types, false)); - } - - protected: - scoped_ptr parser_; - bool configs_received_; - - bool AppendData(const uint8* data, size_t length) { - return parser_->Parse(data, length); - } - - bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) { - const uint8* start = data; - const uint8* end = data + length; - while (start < end) { - size_t append_size = std::min(piece_size, - static_cast(end - start)); - if (!AppendData(start, append_size)) - return false; - start += append_size; - } - return true; - } - - void InitF(bool init_ok, base::TimeDelta duration) { - DVLOG(1) << "InitF: ok=" << init_ok - << ", dur=" << duration.InMilliseconds(); - } - - bool NewConfigF(const AudioDecoderConfig& ac, const VideoDecoderConfig& vc) { - DVLOG(1) << "NewConfigF: audio=" << ac.IsValidConfig() - << ", video=" << vc.IsValidConfig(); - configs_received_ = true; - return true; - } - - - void DumpBuffers(const std::string& label, - const StreamParser::BufferQueue& buffers) { - DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size(); - for (StreamParser::BufferQueue::const_iterator buf = buffers.begin(); - buf != buffers.end(); buf++) { - DVLOG(3) << " n=" << buf - buffers.begin() - << ", size=" << (*buf)->data_size() - << ", dur=" << (*buf)->duration().InMilliseconds(); - } - } - - bool NewBuffersF(const StreamParser::BufferQueue& audio_buffers, - const StreamParser::BufferQueue& video_buffers) { - DumpBuffers("audio_buffers", audio_buffers); - DumpBuffers("video_buffers", video_buffers); - return true; - } - - bool NewTextBuffersF(TextTrack* text_track, - const StreamParser::BufferQueue& buffers) { - return true; - } - - void KeyNeededF(const std::string& type, - scoped_ptr init_data, int init_data_size) { - DVLOG(1) << "KeyNeededF: " << init_data_size; - EXPECT_EQ(kMp4InitDataType, type); - EXPECT_TRUE(init_data.get()); - EXPECT_GT(init_data_size, 0); - } - - scoped_ptr AddTextTrackF( - TextKind kind, - const std::string& label, - const std::string& language) { - return scoped_ptr(); - } - - void NewSegmentF() { - DVLOG(1) << "NewSegmentF"; - } - - void EndOfSegmentF() { - DVLOG(1) << "EndOfSegmentF()"; - } - - void InitializeParser() { - parser_->Init( - base::Bind(&MP4StreamParserTest::InitF, base::Unretained(this)), - base::Bind(&MP4StreamParserTest::NewConfigF, base::Unretained(this)), - base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)), - base::Bind(&MP4StreamParserTest::NewTextBuffersF, - base::Unretained(this)), - base::Bind(&MP4StreamParserTest::KeyNeededF, base::Unretained(this)), - base::Bind(&MP4StreamParserTest::AddTextTrackF, base::Unretained(this)), - base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)), - base::Bind(&MP4StreamParserTest::EndOfSegmentF, - base::Unretained(this)), - LogCB()); - } - - bool ParseMP4File(const std::string& filename, int append_bytes) { - InitializeParser(); - - scoped_refptr buffer = ReadTestDataFile(filename); - EXPECT_TRUE(AppendDataInPieces(buffer->data(), - buffer->data_size(), - append_bytes)); - return true; - } -}; - -TEST_F(MP4StreamParserTest, UnalignedAppend) { - // Test small, non-segment-aligned appends (small enough to exercise - // incremental append system) - ParseMP4File("bear-1280x720-av_frag.mp4", 512); -} - -TEST_F(MP4StreamParserTest, BytewiseAppend) { - // Ensure no incremental errors occur when parsing - ParseMP4File("bear-1280x720-av_frag.mp4", 1); -} - -TEST_F(MP4StreamParserTest, MultiFragmentAppend) { - // Large size ensures multiple fragments are appended in one call (size is - // larger than this particular test file) - ParseMP4File("bear-1280x720-av_frag.mp4", 768432); -} - -TEST_F(MP4StreamParserTest, Flush) { - // Flush while reading sample data, then start a new stream. - InitializeParser(); - - scoped_refptr buffer = - ReadTestDataFile("bear-1280x720-av_frag.mp4"); - EXPECT_TRUE(AppendDataInPieces(buffer->data(), 65536, 512)); - parser_->Flush(); - EXPECT_TRUE(AppendDataInPieces(buffer->data(), - buffer->data_size(), - 512)); -} - -TEST_F(MP4StreamParserTest, Reinitialization) { - InitializeParser(); - - scoped_refptr buffer = - ReadTestDataFile("bear-1280x720-av_frag.mp4"); - EXPECT_TRUE(AppendDataInPieces(buffer->data(), - buffer->data_size(), - 512)); - EXPECT_TRUE(AppendDataInPieces(buffer->data(), - buffer->data_size(), - 512)); -} - -TEST_F(MP4StreamParserTest, MPEG2_AAC_LC) { - std::set audio_object_types; - audio_object_types.insert(kISO_13818_7_AAC_LC); - parser_.reset(new MP4StreamParser(audio_object_types, false)); - ParseMP4File("bear-mpeg2-aac-only_frag.mp4", 512); -} - -// Test that a moov box is not always required after Flush() is called. -TEST_F(MP4StreamParserTest, NoMoovAfterFlush) { - InitializeParser(); - - scoped_refptr buffer = - ReadTestDataFile("bear-1280x720-av_frag.mp4"); - EXPECT_TRUE(AppendDataInPieces(buffer->data(), - buffer->data_size(), - 512)); - parser_->Flush(); - - const int kFirstMoofOffset = 1307; - EXPECT_TRUE(AppendDataInPieces(buffer->data() + kFirstMoofOffset, - buffer->data_size() - kFirstMoofOffset, - 512)); -} - -// TODO(strobe): Create and test media which uses CENC auxiliary info stored -// inside a private box - -} // namespace mp4 -} // namespace media diff --git a/media/mp4/offset_byte_queue.h b/media/mp4/offset_byte_queue.h index 9349b96088..f64f2999ad 100644 --- a/media/mp4/offset_byte_queue.h +++ b/media/mp4/offset_byte_queue.h @@ -7,7 +7,6 @@ #include "base/basictypes.h" #include "media/base/byte_queue.h" -#include "media/base/media_export.h" namespace media { @@ -15,7 +14,7 @@ namespace media { // monotonically-increasing offset. All buffer access is done by passing these // offsets into this class, going some way towards preventing the proliferation // of many different meanings of "offset", "head", etc. -class MEDIA_EXPORT OffsetByteQueue { +class OffsetByteQueue { public: OffsetByteQueue(); ~OffsetByteQueue(); @@ -63,4 +62,4 @@ class MEDIA_EXPORT OffsetByteQueue { } // namespace media -#endif // MEDIA_MP4_MP4_STREAM_PARSER_H_ +#endif // MEDIA_MP4_OFFSET_BYTE_QUEUE_H_ diff --git a/media/mp4/track_run_iterator.cc b/media/mp4/track_run_iterator.cc index f16a8bffd1..d547677c71 100644 --- a/media/mp4/track_run_iterator.cc +++ b/media/mp4/track_run_iterator.cc @@ -6,8 +6,6 @@ #include -#include "media/base/buffers.h" -#include "media/base/stream_parser_buffer.h" #include "media/mp4/rcheck.h" namespace { @@ -56,16 +54,8 @@ TrackRunInfo::TrackRunInfo() } TrackRunInfo::~TrackRunInfo() {} -TimeDelta TimeDeltaFromRational(int64 numer, int64 denom) { - DCHECK_LT((numer > 0 ? numer : -numer), - kint64max / base::Time::kMicrosecondsPerSecond); - return TimeDelta::FromMicroseconds( - base::Time::kMicrosecondsPerSecond * numer / denom); -} - -TrackRunIterator::TrackRunIterator(const Movie* moov, - const LogCB& log_cb) - : moov_(moov), log_cb_(log_cb), sample_offset_(0) { +TrackRunIterator::TrackRunIterator(const Movie* moov) + : moov_(moov), sample_offset_(0) { CHECK(moov); } @@ -388,20 +378,19 @@ int TrackRunIterator::sample_size() const { return sample_itr_->size; } -TimeDelta TrackRunIterator::dts() const { +int64 TrackRunIterator::dts() const { DCHECK(IsSampleValid()); - return TimeDeltaFromRational(sample_dts_, run_itr_->timescale); + return sample_dts_; } -TimeDelta TrackRunIterator::cts() const { +int64 TrackRunIterator::cts() const { DCHECK(IsSampleValid()); - return TimeDeltaFromRational(sample_dts_ + sample_itr_->cts_offset, - run_itr_->timescale); + return sample_dts_ + sample_itr_->cts_offset; } -TimeDelta TrackRunIterator::duration() const { +int64 TrackRunIterator::duration() const { DCHECK(IsSampleValid()); - return TimeDeltaFromRational(sample_itr_->duration, run_itr_->timescale); + return sample_itr_->duration; } bool TrackRunIterator::is_keyframe() const { @@ -424,7 +413,7 @@ scoped_ptr TrackRunIterator::GetDecryptConfig() { if (!cenc_info.subsamples.empty() && (cenc_info.GetTotalSizeOfSubsamples() != static_cast(sample_size()))) { - MEDIA_LOG(log_cb_) << "Incorrect CENC subsample size."; + LOG(ERROR) << "Incorrect CENC subsample size."; return scoped_ptr(); } diff --git a/media/mp4/track_run_iterator.h b/media/mp4/track_run_iterator.h index a21c5ba0c2..0ab1e32ebb 100644 --- a/media/mp4/track_run_iterator.h +++ b/media/mp4/track_run_iterator.h @@ -8,9 +8,6 @@ #include #include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "media/base/media_export.h" -#include "media/base/media_log.h" #include "media/mp4/box_definitions.h" #include "media/mp4/cenc.h" @@ -20,17 +17,14 @@ class DecryptConfig; namespace mp4 { -using base::TimeDelta; -base::TimeDelta MEDIA_EXPORT TimeDeltaFromRational(int64 numer, int64 denom); - struct SampleInfo; struct TrackRunInfo; -class MEDIA_EXPORT TrackRunIterator { +class TrackRunIterator { public: // Create a new TrackRunIterator. A reference to |moov| will be retained for // the lifetime of this object. - TrackRunIterator(const Movie* moov, const LogCB& log_cb); + explicit TrackRunIterator(const Movie* moov); ~TrackRunIterator(); // Sets up the iterator to handle all the runs from the current fragment. @@ -74,9 +68,9 @@ class MEDIA_EXPORT TrackRunIterator { // Properties of the current sample. Only valid if IsSampleValid(). int64 sample_offset() const; int sample_size() const; - TimeDelta dts() const; - TimeDelta cts() const; - TimeDelta duration() const; + int64 dts() const; + int64 cts() const; + int64 duration() const; bool is_keyframe() const; // Only call when is_encrypted() is true and AuxInfoNeedsToBeCached() is @@ -88,7 +82,6 @@ class MEDIA_EXPORT TrackRunIterator { const TrackEncryption& track_encryption() const; const Movie* moov_; - LogCB log_cb_; std::vector runs_; std::vector::const_iterator run_itr_; diff --git a/media/mp4/track_run_iterator_unittest.cc b/media/mp4/track_run_iterator_unittest.cc index 499a2e1a69..4a1b614046 100644 --- a/media/mp4/track_run_iterator_unittest.cc +++ b/media/mp4/track_run_iterator_unittest.cc @@ -48,7 +48,6 @@ class TrackRunIteratorTest : public testing::Test { protected: Movie moov_; - LogCB log_cb_; scoped_ptr iter_; void CreateMovie() { @@ -154,14 +153,14 @@ class TrackRunIteratorTest : public testing::Test { }; TEST_F(TrackRunIteratorTest, NoRunsTest) { - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); ASSERT_TRUE(iter_->Init(MovieFragment())); EXPECT_FALSE(iter_->IsRunValid()); EXPECT_FALSE(iter_->IsSampleValid()); } TEST_F(TrackRunIteratorTest, BasicOperationTest) { - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); MovieFragment moof = CreateFragment(); // Test that runs are sorted correctly, and that properties of the initial @@ -172,9 +171,9 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) { EXPECT_EQ(iter_->track_id(), 1u); EXPECT_EQ(iter_->sample_offset(), 100); EXPECT_EQ(iter_->sample_size(), 1); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kAudioScale)); - EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kAudioScale)); - EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale)); + EXPECT_EQ(iter_->dts(), 0); + EXPECT_EQ(iter_->cts(), 0); + EXPECT_EQ(iter_->duration(), 1024); EXPECT_TRUE(iter_->is_keyframe()); // Advance to the last sample in the current run, and test its properties @@ -182,8 +181,8 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) { EXPECT_EQ(iter_->track_id(), 1u); EXPECT_EQ(iter_->sample_offset(), 100 + kSumAscending1); EXPECT_EQ(iter_->sample_size(), 10); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 9, kAudioScale)); - EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale)); + EXPECT_EQ(iter_->dts(), 1024 * 9); + EXPECT_EQ(iter_->duration(), 1024); EXPECT_TRUE(iter_->is_keyframe()); // Test end-of-run @@ -198,14 +197,14 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) { EXPECT_EQ(iter_->sample_offset(), 200 + kSumAscending1); EXPECT_EQ(iter_->sample_size(), 10); int64 base_dts = kSumAscending1 + moof.tracks[1].decode_time.decode_time; - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(base_dts, kVideoScale)); - EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(10, kVideoScale)); + EXPECT_EQ(iter_->dts(), base_dts); + EXPECT_EQ(iter_->duration(), 10); EXPECT_FALSE(iter_->is_keyframe()); // Test final run iter_->AdvanceRun(); EXPECT_EQ(iter_->track_id(), 1u); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 10, kAudioScale)); + EXPECT_EQ(iter_->dts(), 1024 * 10); iter_->AdvanceSample(); EXPECT_EQ(moof.tracks[0].runs[1].data_offset + moof.tracks[0].header.default_sample_size, @@ -219,7 +218,7 @@ TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) { moov_.extends.tracks[0].default_sample_size = 3; moov_.extends.tracks[0].default_sample_flags = kSampleIsDifferenceSampleFlagMask; - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); MovieFragment moof = CreateFragment(); moof.tracks[0].header.has_default_sample_flags = false; moof.tracks[0].header.default_sample_size = 0; @@ -230,15 +229,15 @@ TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) { EXPECT_FALSE(iter_->is_keyframe()); EXPECT_EQ(iter_->sample_size(), 3); EXPECT_EQ(iter_->sample_offset(), moof.tracks[0].runs[0].data_offset + 3); - EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(50, kAudioScale)); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(50, kAudioScale)); + EXPECT_EQ(iter_->duration(), 50); + EXPECT_EQ(iter_->dts(), 50); } TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) { // Ensure that keyframes are flagged correctly in the face of BMFF boxes which // explicitly specify the flags for the first sample in a run and rely on // defaults for all subsequent samples - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); MovieFragment moof = CreateFragment(); moof.tracks[1].header.has_default_sample_flags = true; moof.tracks[1].header.default_sample_flags = @@ -267,7 +266,7 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) { // (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_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); EditListEntry entry; entry.segment_duration = 0; entry.media_time = 2; @@ -290,21 +289,21 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) { ASSERT_TRUE(iter_->Init(moof)); iter_->AdvanceRun(); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kVideoScale)); - EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kVideoScale)); - EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1, kVideoScale)); + EXPECT_EQ(iter_->dts(), 0); + EXPECT_EQ(iter_->cts(), 0); + EXPECT_EQ(iter_->duration(), 1); iter_->AdvanceSample(); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1, kVideoScale)); - EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(4, kVideoScale)); - EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(2, kVideoScale)); + EXPECT_EQ(iter_->dts(), 1); + EXPECT_EQ(iter_->cts(), 4); + EXPECT_EQ(iter_->duration(), 2); iter_->AdvanceSample(); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(3, kVideoScale)); - EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(1, kVideoScale)); - EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(3, kVideoScale)); + EXPECT_EQ(iter_->dts(), 3); + EXPECT_EQ(iter_->cts(), 1); + EXPECT_EQ(iter_->duration(), 3); } TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) { - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); MovieFragment moof = CreateFragment(); moof.tracks[1].auxiliary_offset.offsets.push_back(50); moof.tracks[1].auxiliary_size.default_sample_info_size = 2; @@ -317,7 +316,7 @@ TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) { TEST_F(TrackRunIteratorTest, DecryptConfigTest) { AddEncryption(&moov_.tracks[1]); - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); MovieFragment moof = CreateFragment(); AddAuxInfoHeaders(50, &moof.tracks[1]); @@ -357,7 +356,7 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTest) { TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) { AddEncryption(&moov_.tracks[0]); AddEncryption(&moov_.tracks[1]); - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); MovieFragment moof = CreateFragment(); moof.tracks[0].runs.resize(1); @@ -399,7 +398,7 @@ TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) { TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) { AddEncryption(&moov_.tracks[0]); AddEncryption(&moov_.tracks[1]); - iter_.reset(new TrackRunIterator(&moov_, log_cb_)); + iter_.reset(new TrackRunIterator(&moov_)); MovieFragment moof = CreateFragment(); AddAuxInfoHeaders(20000, &moof.tracks[0]);