diff --git a/media/base/decryptor_source.h b/media/base/decryptor_source.h new file mode 100644 index 0000000000..a909209156 --- /dev/null +++ b/media/base/decryptor_source.h @@ -0,0 +1,30 @@ +// 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. +// +// DecryptorSource is responsible for decryption key acquisition. + +#ifndef MEDIA_BASE_DECRYPTOR_SOURCE_H_ +#define MEDIA_BASE_DECRYPTOR_SOURCE_H_ + +#include "base/memory/scoped_ptr.h" +#include "media/base/container_names.h" +#include "media/base/status.h" + +namespace media { + +class DecryptorSource { + public: + DecryptorSource() {} + virtual ~DecryptorSource() {} + + virtual Status OnNeedKey(MediaContainerName container, + const std::string& init_data) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(DecryptorSource); +}; + +} + +#endif // MEDIA_BASE_DECRYPTOR_SOURCE_H_ diff --git a/media/base/demuxer.cc b/media/base/demuxer.cc new file mode 100644 index 0000000000..bfeccb3edb --- /dev/null +++ b/media/base/demuxer.cc @@ -0,0 +1,141 @@ +// 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/demuxer.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "media/base/container_names.h" +#include "media/base/decryptor_source.h" +#include "media/base/media_stream.h" +#include "media/base/stream_info.h" +#include "media/file/file.h" +#include "media/mp4/mp4_media_parser.h" + +namespace { +const int kBufSize = 0x40000; // 256KB. +} + +namespace media { + +Demuxer::Demuxer(const std::string& file_name, + DecryptorSource* decryptor_source) + : decryptor_source_(decryptor_source), + file_name_(file_name), + media_file_(NULL), + init_event_received_(false), + buffer_(new uint8[kBufSize]) {} + +Demuxer::~Demuxer() { + if (media_file_) + media_file_->Close(); + STLDeleteElements(&streams_); +} + +Status Demuxer::Initialize() { + DCHECK(media_file_ == NULL); + DCHECK(!init_event_received_); + + media_file_ = File::Open(file_name_.c_str(), "r"); + if (media_file_ == NULL) { + return Status(error::FILE_FAILURE, + "Cannot open file for read " + file_name_); + } + + // Determine media container. + int64 bytes_read = media_file_->Read(buffer_.get(), kBufSize); + if (bytes_read <= 0) + return Status(error::FILE_FAILURE, "Cannot read file " + file_name_); + MediaContainerName container = DetermineContainer(buffer_.get(), bytes_read); + + // Initialize media parser. + switch (container) { + case CONTAINER_MOV: + parser_.reset(new mp4::MP4MediaParser()); + break; + default: + NOTIMPLEMENTED(); + return Status(error::UNIMPLEMENTED, "Container not supported."); + } + + parser_->Init( + base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)), + base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)), + base::Bind(&Demuxer::KeyNeededEvent, base::Unretained(this))); + + if (!parser_->Parse(buffer_.get(), bytes_read)) + return Status(error::PARSER_FAILURE, + "Cannot parse media file " + file_name_); + + // TODO(kqyang): Does not look clean. Consider refactoring later. + Status status; + while (!init_event_received_) { + if (!(status = Parse()).ok()) break; + } + return status; +} + +void Demuxer::ParserInitEvent( + const std::vector >& streams) { + init_event_received_ = true; + + std::vector >::const_iterator it = streams.begin(); + for (; it != streams.end(); ++it) { + streams_.push_back(new MediaStream(*it, this)); + } +} + +bool Demuxer::NewSampleEvent( + uint32 track_id, const scoped_refptr& sample) { + std::vector::iterator it = streams_.begin(); + for (; it != streams_.end(); ++it) { + if (track_id == (*it)->info()->track_id()) { + return (*it)->PushSample(sample).ok(); + } + } + return false; +} + +void Demuxer::KeyNeededEvent( + MediaContainerName container, scoped_ptr init_data, + int init_data_size) { + NOTIMPLEMENTED(); +} + +Status Demuxer::Run() { + Status status; + + // Start the streams. + for (std::vector::iterator it = streams_.begin(); + it != streams_.end(); + ++it) { + status = (*it)->Start(MediaStream::kPush); + if (!status.ok()) + return status; + } + + while ((status = Parse()).ok()) continue; + return status.Matches(Status(error::EOF, "")) ? Status::OK : status; +} + +Status Demuxer::Parse() { + DCHECK(media_file_ != NULL); + DCHECK(parser_ != NULL); + DCHECK(buffer_ != NULL); + + int64 bytes_read = media_file_->Read(buffer_.get(), kBufSize); + if (bytes_read <= 0) { + return media_file_->Eof() ? + Status(error::EOF, "End of file") : + Status(error::FILE_FAILURE, "Cannot read file " + file_name_); + } + + return parser_->Parse(buffer_.get(), bytes_read) + ? Status::OK + : Status(error::PARSER_FAILURE, + "Cannot parse media file " + file_name_); +} + +} // namespace media diff --git a/media/base/demuxer.h b/media/base/demuxer.h new file mode 100644 index 0000000000..245b39f32c --- /dev/null +++ b/media/base/demuxer.h @@ -0,0 +1,79 @@ +// 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_DEMUXER_H_ +#define MEDIA_BASE_DEMUXER_H_ + +#include + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/container_names.h" +#include "media/base/status.h" + +namespace media { + +class Decryptor; +class DecryptorSource; +class File; +class MediaParser; +class MediaSample; +class MediaStream; +class StreamInfo; + +class Demuxer { + public: + // |file_name| specifies the input source. It uses prefix matching to create + // a proper File object. The user can extend File to support their custom + // File objects with its own prefix. + // decryptor_source generates decryptor(s) when init_data is available. + // Demuxer does not take over the ownership of decryptor_source. + Demuxer(const std::string& file_name, DecryptorSource* decryptor_source); + ~Demuxer(); + + // Initializes corresponding MediaParser, Decryptor, instantiates + // MediaStream(s) etc. + Status Initialize(); + + // Drives the remuxing from demuxer side (push): Reads the file and push + // the Data to Muxer until Eof. + Status Run(); + + // Reads from the source and send it to the parser. + Status Parse(); + + uint32 num_streams() const { + return streams_.size(); + } + + // Demuxer retains the ownership of streams. + MediaStream* streams(uint32 index) { + if (index >= streams_.size()) + return NULL; + return streams_[index]; + } + + private: + // Parser event handlers. + void ParserInitEvent(const std::vector >& streams); + bool NewSampleEvent(uint32 track_id, + const scoped_refptr& sample); + void KeyNeededEvent(MediaContainerName container, + scoped_ptr init_data, + int init_data_size); + + DecryptorSource* decryptor_source_; + std::string file_name_; + File* media_file_; + bool init_event_received_; + scoped_ptr parser_; + std::vector streams_; + scoped_ptr buffer_; + + DISALLOW_COPY_AND_ASSIGN(Demuxer); +}; + +} + +#endif // MEDIA_BASE_DEMUXER_H_ diff --git a/media/base/encryptor_source.h b/media/base/encryptor_source.h new file mode 100644 index 0000000000..3bccca242a --- /dev/null +++ b/media/base/encryptor_source.h @@ -0,0 +1,29 @@ +// 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. +// +// EncryptorSource is responsible for encryption key acquisition. + +#ifndef MEDIA_BASE_ENCRYPTOR_SOURCE_H_ +#define MEDIA_BASE_ENCRYPTOR_SOURCE_H_ + +#include "base/memory/scoped_ptr.h" +#include "media/base/container_names.h" +#include "media/base/status.h" + +namespace media { + +class EncryptorSource { + public: + EncryptorSource() {} + virtual ~EncryptorSource() {} + + virtual Status Init() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(EncryptorSource); +}; + +} + +#endif // MEDIA_BASE_ENCRYPTOR_SOURCE_H_ diff --git a/media/base/media_parser.h b/media/base/media_parser.h index 07ed637bef..8995798282 100644 --- a/media/base/media_parser.h +++ b/media/base/media_parser.h @@ -24,16 +24,13 @@ class 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 >&)> + // First 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; + // 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. diff --git a/media/base/media_sample.cc b/media/base/media_sample.cc index 11588264b9..2a1e318bba 100644 --- a/media/base/media_sample.cc +++ b/media/base/media_sample.cc @@ -55,7 +55,7 @@ scoped_refptr MediaSample::CreateEOSBuffer() { return make_scoped_refptr(new MediaSample(NULL, 0, NULL, 0, false)); } -std::string MediaSample::ToString() { +std::string MediaSample::ToString() const { if (end_of_stream()) { return "end of stream"; } diff --git a/media/base/media_sample.h b/media/base/media_sample.h index 6035f03ef7..8560ffd486 100644 --- a/media/base/media_sample.h +++ b/media/base/media_sample.h @@ -121,7 +121,7 @@ class MediaSample } // Returns a human-readable string describing |*this|. - std::string ToString(); + std::string ToString() const; protected: friend class base::RefCountedThreadSafe; diff --git a/media/base/media_stream.cc b/media/base/media_stream.cc new file mode 100644 index 0000000000..dee8e2f088 --- /dev/null +++ b/media/base/media_stream.cc @@ -0,0 +1,110 @@ +// 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_stream.h" + +#include "base/logging.h" +#include "media/base/demuxer.h" +#include "media/base/media_sample.h" +#include "media/base/muxer.h" +#include "media/base/stream_info.h" + +namespace media { + +MediaStream::MediaStream(scoped_refptr info, Demuxer* demuxer) + : info_(info), demuxer_(demuxer), muxer_(NULL), state_(kIdle) {} + +MediaStream::~MediaStream() {} + +Status MediaStream::PullSample(scoped_refptr* sample) { + DCHECK_EQ(state_, kPulling); + + // Trigger a new parse in demuxer if no more samples. + while (samples_.empty()) { + Status status = demuxer_->Parse(); + if (!status.ok()) + return status; + } + + *sample = samples_.front(); + samples_.pop_front(); + return Status::OK; +} + +Status MediaStream::PushSample(const scoped_refptr& sample) { + switch (state_) { + case kIdle: + case kPulling: + samples_.push_back(sample); + return Status::OK; + case kDisconnected: + return Status::OK; + case kPushing: + return muxer_->AddSample(this, sample); + default: + NOTREACHED() << "Unexpected State " << state_; + return Status::UNKNOWN; + } +} + +void MediaStream::Connect(Muxer* muxer) { + DCHECK(muxer != NULL); + DCHECK(muxer_ == NULL); + state_ = kConnected; + muxer_ = muxer; +} + +Status MediaStream::Start(MediaStreamOperation operation) { + DCHECK(demuxer_ != NULL); + DCHECK(operation == kPush || operation == kPull); + + + switch (state_) { + case kIdle: + // Disconnect the stream if it is not connected to a muxer. + state_ = kDisconnected; + samples_.clear(); + return Status::OK; + case kConnected: + state_ = (operation == kPush) ? kPushing : kPulling; + if (operation == kPush) { + // Push samples in the queue to muxer if there is any. + while (!samples_.empty()) { + Status status = muxer_->AddSample(this, samples_.front()); + if (!status.ok()) + return status; + samples_.pop_front(); + } + } else { + // We need to disconnect all its peer streams which are not connected + // to a muxer. + for (int i = 0; i < demuxer_->num_streams(); ++i) { + Status status = demuxer_->streams(i)->Start(operation); + if (!status.ok()) + return status; + } + } + return Status::OK; + case kPulling: + DCHECK(operation == kPull); + return Status::OK; + default: + NOTREACHED() << "Unexpected State " << state_; + return Status::UNKNOWN; + } +} + +scoped_refptr MediaStream::info() { + return info_; +} + +std::string MediaStream::ToString() const { + std::ostringstream s; + s << "state: " << state_ + << " samples in the queue: " << samples_.size() + << " " << info_->ToString(); + return s.str(); +} + +} // namespace media diff --git a/media/base/media_stream.h b/media/base/media_stream.h new file mode 100644 index 0000000000..50b6c9501b --- /dev/null +++ b/media/base/media_stream.h @@ -0,0 +1,79 @@ +// 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. +// +// MediaStream connects Demuxer to Muxer. + +#ifndef MEDIA_BASE_MEDIA_STREAM_H_ +#define MEDIA_BASE_MEDIA_STREAM_H_ + +#include + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/status.h" + +namespace media { + +class Demuxer; +class Muxer; +class MediaSample; +class StreamInfo; + +class MediaStream { + public: + enum MediaStreamOperation { + kPush, + kPull, + }; + // Create MediaStream from StreamInfo and Demuxer. MediaStream does not own + // demuxer. + MediaStream(scoped_refptr info, Demuxer* demuxer); + ~MediaStream(); + + // Connect the stream to Muxer. MediaStream does not own muxer. + void Connect(Muxer* muxer); + + // Start the stream for pushing or pulling. + Status Start(MediaStreamOperation operation); + + // Push sample to Muxer (triggered by Demuxer). + Status PushSample(const scoped_refptr& sample); + + // Pull sample from Demuxer (triggered by Muxer). + Status PullSample(scoped_refptr* sample); + + Demuxer* demuxer() { + return demuxer_; + } + Muxer* muxer() { + return muxer_; + } + scoped_refptr info(); + + // Returns a human-readable string describing |*this|. + std::string ToString() const; + + private: + // State transition diagram available @ http://goo.gl/ThJQbl. + enum State { + kIdle, + kConnected, + kDisconnected, + kPushing, + kPulling, + }; + + scoped_refptr info_; + Demuxer* demuxer_; + Muxer* muxer_; + State state_; + // An internal buffer to store samples temporarily. + std::deque > samples_; + + DISALLOW_COPY_AND_ASSIGN(MediaStream); +}; + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_STREAM_H_ diff --git a/media/base/muxer.cc b/media/base/muxer.cc new file mode 100644 index 0000000000..1723cecde9 --- /dev/null +++ b/media/base/muxer.cc @@ -0,0 +1,49 @@ +// 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/muxer.h" + +#include "media/base/encryptor_source.h" +#include "media/base/media_sample.h" +#include "media/base/media_stream.h" + +namespace media { + +Muxer::Muxer(const Options& options, EncryptorSource* encrytor_source) : + encryptor_source_(encrytor_source), encryptor_(NULL) {} + +Muxer::~Muxer() {} + +Status Muxer::AddStream(MediaStream* stream) { + stream->Connect(this); + streams_.push_back(stream); + return Status::OK; +} + +Status Muxer::Run() { + DCHECK(!streams_.empty()); + + Status status; + + // Start the streams. + for (std::vector::iterator it = streams_.begin(); + it != streams_.end(); + ++it) { + status = (*it)->Start(MediaStream::kPull); + if (!status.ok()) + return status; + } + + while (status.ok()) { + // TODO(kqyang): Need to do some proper synchronization between streams. + scoped_refptr sample; + status = streams_[0]->PullSample(&sample); + if (!status.ok()) + break; + status = AddSample(streams_[0], sample); + } + return status.Matches(Status(error::EOF, "")) ? Status::OK : status; +} + +} // namespace media diff --git a/media/base/muxer.h b/media/base/muxer.h new file mode 100644 index 0000000000..5d641377b9 --- /dev/null +++ b/media/base/muxer.h @@ -0,0 +1,104 @@ +// 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_MUXER_H_ +#define MEDIA_BASE_MUXER_H_ + +#include + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/status.h" + +namespace media { + +class Encryptor; +class EncryptorSource; +class MediaSample; +class MediaStream; + +class Muxer { + public: + struct Options { + // Generate a single segment for each media presentation. This option + // should be set for on demand profile. + bool single_segment; + + // Segment duration. If single_segment is specified, this parameter sets + // the duration of a subsegment. A subsegment can contain one to several + // fragments. + int segment_duration; + + // Fragment duration. Should not be larger than the segment duration. + int fragment_duration; + + // Force segments to begin with stream access points. Segment duration may + // not be exactly what asked by segment_duration. + bool segment_sap_aligned; + + // Force fragments to begin with stream access points. Fragment duration + // may not be exactly what asked by segment_duration. Imply + // segment_sap_aligned. + bool fragment_sap_aligned; + + // Set the number of subsegments in each SIDX box. If 0, a single SIDX box + // is used per segment. If -1, no SIDX box is used. Otherwise, the Muxer + // will pack N subsegments in the root SIDX of the segment, with + // segment_duration/N/fragment_duration fragments per subsegment. + int num_subsegments_per_sidx; + + // Output file name. If segment_template is not specified, the Muxer + // generates this single output file with all segments concatenated; + // Otherwise, it specifies the init segment name. + std::string output_file_name; + + // Specify output segment name pattern for generated segments. It can + // furthermore be configured by using a subset of the SegmentTemplate + // identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time. + // Optional. + std::string segment_template; + + // Specify a directory for temporary file creation. + std::string temp_directory_name; + }; + + // Muxer does not take over the ownership of encryptor_source. + Muxer(const Options& options, EncryptorSource* encryptor_source); + virtual ~Muxer(); + + // Adds video/audio stream. + // Returns OK on success. + virtual Status AddStream(MediaStream* stream); + + // Adds new media sample. + virtual Status AddSample(const MediaStream* stream, + scoped_refptr sample) = 0; + + // Final clean up. + virtual Status Finalize() = 0; + + // Drives the remuxing from muxer side (pull). + virtual Status Run(); + + uint32 num_streams() const { + return streams_.size(); + } + + MediaStream* streams(uint32 index) const { + if (index >= streams_.size()) + return NULL; + return streams_[index]; + } + + private: + EncryptorSource* encryptor_source_; + Encryptor* encryptor_; + std::vector streams_; + + DISALLOW_COPY_AND_ASSIGN(Muxer); +}; + +} // namespace media + +#endif // MEDIA_BASE_MUXER_H_ diff --git a/media/base/run_tests_with_atexit_manager.cc b/media/base/run_tests_with_atexit_manager.cc index 212abd5f6e..5e7dafaa24 100644 --- a/media/base/run_tests_with_atexit_manager.cc +++ b/media/base/run_tests_with_atexit_manager.cc @@ -3,10 +3,17 @@ // found in the LICENSE file. #include "base/at_exit.h" +#include "base/command_line.h" +#include "base/logging.h" #include "testing/gtest/include/gtest/gtest.h" int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); + + // Needed to enable VLOG/DVLOG through --vmodule or --v. + CommandLine::Init(argc, argv); + CHECK(logging::InitLogging(logging::LoggingSettings())); + base::AtExitManager exit; return RUN_ALL_TESTS(); } diff --git a/media/base/status.cc b/media/base/status.cc new file mode 100644 index 0000000000..63d648b73f --- /dev/null +++ b/media/base/status.cc @@ -0,0 +1,28 @@ +// 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/status.h" + +#include + +namespace media { + +const Status& Status::OK = Status(error::OK, ""); +const Status& Status::UNKNOWN = Status(error::UNKNOWN, ""); + +std::string Status::ToString() const { + if (error_code_ == error::OK) + return "OK"; + + std::ostringstream string_stream; + string_stream << error_code_ << ":" << error_message_; + return string_stream.str(); +} + +std::ostream& operator<<(std::ostream& os, const Status& x) { + os << x.ToString(); + return os; +} + +} // namespace media diff --git a/media/base/status.h b/media/base/status.h new file mode 100644 index 0000000000..21f6762834 --- /dev/null +++ b/media/base/status.h @@ -0,0 +1,156 @@ +// 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_STATUS_H_ +#define MEDIA_BASE_STATUS_H_ + +#include + +#include "base/logging.h" + +namespace media { + +namespace error { + +// Error codes for the packager APIs. +enum Code { + // Not an error; returned on success + OK, + + // Unknown error. An example of where this error may be returned is + // errors raised by APIs that do not return enough error information + // may be converted to this error. + UNKNOWN, + + // The operation was cancelled (typically by the caller). + CANCELLED, + + // Client specified an invalid argument. INVALID_ARGUMENT indicates + // arguments that are problematic regardless of the state of the system + // (e.g. a malformed file name). + INVALID_ARGUMENT, + + // Operation is not implemented or not supported/enabled. + UNIMPLEMENTED, + + // Cannot open file. + FILE_FAILURE, + + // End of file. + EOF, + + // Unable to parse the media file. + PARSER_FAILURE, + + // TODO(kqyang): define packager specific error codes. +}; + +} // namespace error + +class Status { + public: + // Creates a "successful" status. + Status() : error_code_(error::OK) {} + + // Create a status with the specified code, and error message. If "code == 0", + // error_message is ignored and a Status object identical to Status::OK is + // constructed. + Status(error::Code error_code, const std::string& error_message) : + error_code_(error_code) { + if (!ok()) + error_message_ = error_message; + } + + //Status(const Status&); + //Status& operator=(const Status& x); + ~Status() {} + + // Some pre-defined Status objects. + static const Status& OK; // Identical to 0-arg constructor. + static const Status& UNKNOWN; + + // Store the specified error in this Status object. + // If "error_code == error::OK", error_message is ignored and a Status object + // identical to Status::OK is constructed. + void SetError(error::Code error_code, const std::string& error_message) { + if (error_code == error::OK) { + Clear(); + } else { + error_code_ = error_code; + error_message_ = error_message; + } + } + + // If "ok()", stores "new_status" into *this. If "!ok()", preserves + // the current "error_code()/error_message()", + // + // Convenient way of keeping track of the first error encountered. + // Instead of: + // if (overall_status.ok()) overall_status = new_status + // Use: + // overall_status.Update(new_status); + void Update(const Status& new_status) { + if (ok()) + *this = new_status; + } + + // Clear this status object to contain the OK code and no error message. + void Clear() { + error_code_ = error::OK; + error_message_ = ""; + } + + // Accessor. + bool ok() const { + return error_code_ == error::OK; + } + error::Code error_code() const { + return error_code_; + } + const std::string& error_message() const { + return error_message_; + } + + bool operator==(const Status& x) const { + return error_code_ == x.error_code() && error_message_ == x.error_message(); + } + bool operator!=(const Status& x) const { + return !(*this == x); + } + + // Returns true iff this has the same error_code as "x". I.e., the two + // Status objects are identical except possibly for the error message. + bool Matches(const Status& x) const { + return error_code_ == x.error_code(); + } + + // Return a combination of the error code name and message. + std::string ToString() const; + + void Swap(Status* other) { + error::Code error_code = error_code_; + error_code_ = other->error_code_; + other->error_code_ = error_code; + error_message_.swap(other->error_message_); + } + + private: + error::Code error_code_; + std::string error_message_; + + // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler + // generated copy constructor and assignment operator. +}; + +extern std::ostream& operator<<(std::ostream& os, const Status& x); + +// Status success comparison. +// This is better than CHECK((val).ok()) because the embedded +// error string gets printed by the CHECK_EQ. +#define CHECK_OK(val) CHECK_EQ(Status::OK, (val)) +#define DCHECK_OK(val) DCHECK_EQ(Status::OK, (val)) + +} // namespace media + +#endif // MEDIA_BASE_STATUS_H_ diff --git a/media/base/status_test_util.h b/media/base/status_test_util.h new file mode 100644 index 0000000000..831f78371c --- /dev/null +++ b/media/base/status_test_util.h @@ -0,0 +1,14 @@ +// 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_STATUS_TEST_UTIL_H_ +#define MEDIA_BASE_STATUS_TEST_UTIL_H_ + +#include "media/base/status.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define EXPECT_OK(val) EXPECT_EQ(media::Status::OK, (val)) +#define ASSERT_OK(val) ASSERT_EQ(media::Status::OK, (val)) + +#endif // MEDIA_BASE_STATUS_TEST_UTIL_H_ diff --git a/media/base/status_test_util_unittest.cc b/media/base/status_test_util_unittest.cc new file mode 100644 index 0000000000..3e1e07ed6b --- /dev/null +++ b/media/base/status_test_util_unittest.cc @@ -0,0 +1,26 @@ +// 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/status_test_util.h" + +#include "testing/gtest/include/gtest/gtest-spi.h" + +TEST(StatusTestUtil, ExpectOkSuccess) { + EXPECT_OK(media::Status::OK); +} + +TEST(StatusTestUtil, AssertOkSuccess) { + ASSERT_OK(media::Status::OK); +} + +TEST(StatusTestUtil, ExpectOkFailure) { + media::Status status(media::error::UNKNOWN, "Status Unknown"); + EXPECT_NONFATAL_FAILURE(EXPECT_OK(status), "Status Unknown"); +} + +TEST(StatusTestUtil, AssertOkFailure) { + EXPECT_FATAL_FAILURE( + ASSERT_OK(media::Status(media::error::UNKNOWN, "Status Unknown")), + "Status Unknown"); +} diff --git a/media/base/status_unittest.cc b/media/base/status_unittest.cc new file mode 100644 index 0000000000..ba678384f1 --- /dev/null +++ b/media/base/status_unittest.cc @@ -0,0 +1,180 @@ +// 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/status.h" + +#include + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +static void CheckStatus(const Status& s, + error::Code code, + const std::string& message) { + EXPECT_EQ(code, s.error_code()); + EXPECT_EQ(message, s.error_message()); + + if (code == error::OK) { + EXPECT_TRUE(s.ok()); + EXPECT_EQ("OK", s.ToString()); + } else { + EXPECT_TRUE(!s.ok()); + EXPECT_THAT(s.ToString(), testing::HasSubstr(message)); + + std::ostringstream string_stream; + string_stream << code << ":" << message; + EXPECT_EQ(string_stream.str(), s.ToString()); + } +} + +TEST(Status, Empty) { + CheckStatus(Status(), error::OK, ""); +} + +TEST(Status, OK) { + CheckStatus(Status::OK, error::OK, ""); +} + +TEST(Status, ConstructorOK) { + CheckStatus(Status(error::OK, "msg"), error::OK, ""); +} + +TEST(Status, CheckOK) { + CHECK_OK(Status()); +} + +TEST(Status, CheckOKDeath) { + Status status(error::UNKNOWN, "Status Unknown"); + ASSERT_DEATH(CHECK_OK(status), "Status Unknown"); + ASSERT_DEATH(CHECK_OK(status) << "Foo1234", "Foo1234"); +} + +TEST(Status, SetError) { + Status status; + status.SetError(error::CANCELLED, "message"); + CheckStatus(status, error::CANCELLED, "message"); +} + +TEST(Status, SetErrorOK) { + Status status(error::CANCELLED, "message"); + status.SetError(error::OK, "msg"); + CheckStatus(status, error::OK, ""); +} + +TEST(Status, Unknown) { + CheckStatus(Status::UNKNOWN, error::UNKNOWN, ""); +} + +TEST(Status, Filled) { + CheckStatus(Status(error::CANCELLED, "message"), error::CANCELLED, "message"); +} + +TEST(Status, Clear) { + Status status(error::CANCELLED, "message"); + status.Clear(); + CheckStatus(status, error::OK, ""); +} + +TEST(Status, Copy) { + Status a(error::CANCELLED, "message"); + Status b(a); + ASSERT_EQ(a, b); +} + +TEST(Status, Assign) { + Status a(error::CANCELLED, "message"); + Status b; + b = a; + ASSERT_EQ(a, b); +} + +TEST(Status, AssignEmpty) { + Status a(error::CANCELLED, "message"); + Status b; + a = b; + ASSERT_EQ("OK", a.ToString()); + ASSERT_TRUE(b.ok()); + ASSERT_TRUE(a.ok()); +} + +TEST(Status, Update) { + Status s; + s.Update(Status::OK); + ASSERT_TRUE(s.ok()); + Status a(error::CANCELLED, "message"); + s.Update(a); + ASSERT_EQ(s, a); + Status b(error::UNIMPLEMENTED, "other message"); + s.Update(b); + ASSERT_EQ(s, a); + s.Update(Status::OK); + ASSERT_EQ(s, a); + ASSERT_FALSE(s.ok()); +} + +TEST(Status, Swap) { + Status a(error::CANCELLED, "message"); + Status b = a; + Status c; + c.Swap(&a); + ASSERT_EQ(c, b); + ASSERT_EQ(a, Status::OK); +} + +TEST(Status, MatchOK) { + ASSERT_TRUE(Status().Matches(Status::OK)); +} + +TEST(Status, MatchSame) { + Status a(error::UNKNOWN, "message"); + Status b(error::UNKNOWN, "message"); + ASSERT_TRUE(a.Matches(b)); +} + +TEST(Status, MatchCopy) { + Status a(error::UNKNOWN, "message"); + Status b = a; + ASSERT_TRUE(a.Matches(b)); +} + +TEST(Status, MatchDifferentCode) { + Status a(error::UNKNOWN, "message"); + Status b(error::CANCELLED, "message"); + ASSERT_TRUE(!a.Matches(b)); +} + +TEST(Status, MatchDifferentMessage) { + Status a(error::UNKNOWN, "message"); + Status b(error::UNKNOWN, "another"); + ASSERT_TRUE(a.Matches(b)); +} + +TEST(Status, EqualsOK) { + ASSERT_EQ(Status::OK, Status()); +} + +TEST(Status, EqualsSame) { + ASSERT_EQ(Status(error::UNKNOWN, "message"), + Status(error::UNKNOWN, "message")); +} + +TEST(Status, EqualsCopy) { + Status a(error::UNKNOWN, "message"); + Status b = a; + ASSERT_EQ(a, b); +} + +TEST(Status, EqualsDifferentCode) { + ASSERT_NE(Status(error::UNKNOWN, "message"), + Status(error::CANCELLED, "message")); +} + +TEST(Status, EqualsDifferentMessage) { + ASSERT_NE(Status(error::UNKNOWN, "message"), + Status(error::UNKNOWN, "another")); +} + +} // namespace media diff --git a/media/file/file.cc b/media/file/file.cc new file mode 100644 index 0000000000..c7182edd2e --- /dev/null +++ b/media/file/file.cc @@ -0,0 +1,64 @@ +// 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 "media/file/file.h" + +#include "media/file/local_file.h" + +namespace media { + +const char* kLocalFilePrefix = "file://"; + +typedef File* (*FileFactoryFunction)(const char* fname, const char* mode); + +struct SupportedTypeInfo { + const char* type; + int type_length; + const FileFactoryFunction factory_function; +}; + +static File* CreateLocalFile(const char* fname, const char* mode) { + return new LocalFile(fname, mode); +} + +static const SupportedTypeInfo kSupportedTypeInfo[] = { + { kLocalFilePrefix, strlen(kLocalFilePrefix), &CreateLocalFile }, +}; + +File* File::Create(const char* fname, const char* mode) { + for (size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) { + const SupportedTypeInfo& type_info = kSupportedTypeInfo[i]; + if (strncmp(type_info.type, fname, type_info.type_length) == 0) { + return type_info.factory_function(fname + type_info.type_length, mode); + } + } + // Otherwise we assume it is a local file + return CreateLocalFile(fname, mode); +} + +File* File::Open(const char* name, const char* mode) { + File* file = File::Create(name, mode); + if (!file) { + return NULL; + } + if (!file->Open()) { + delete file; + return NULL; + } + return file; +} + +// Return the file size or -1 on failure. +// Requires opening and closing the file. +int64 File::GetFileSize(const char* name) { + File* f = File::Open(name, "r"); + if (!f) { + return -1; + } + int64 res = f->Size(); + f->Close(); + return res; +} + +} // namespace media diff --git a/media/file/file.h b/media/file/file.h new file mode 100644 index 0000000000..6aa7e23951 --- /dev/null +++ b/media/file/file.h @@ -0,0 +1,85 @@ +// 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. +// +// Defines an abstract file interface. + +#ifndef PACKAGER_FILE_FILE_H_ +#define PACKAGER_FILE_FILE_H_ + +#include "base/basictypes.h" + +namespace media { + +extern const char* kLocalFilePrefix; + +class File { + public: + // Open the specified file, or return NULL on error. + // This is actually a file factory method, it opens a proper file, e.g. + // LocalFile, MemFile automatically based on prefix. + static File* Open(const char* name, const char* mode); + + // Flush() and de-allocate resources associated with this file, and + // delete this File object. THIS IS THE ONE TRUE WAY TO DEALLOCATE + // THIS OBJECT. + // Returns true on success. + // For writable files, returning false MAY INDICATE DATA LOSS. + virtual bool Close() = 0; + + // Reads data and returns it in buffer. Returns a value < 0 on error, + // or the number of bytes read otherwise. Returns zero on end-of-file, + // or if 'length' is zero. + virtual int64 Read(void* buffer, uint64 length) = 0; + + // Write 'length' bytes from 'buffer', returning the number of bytes + // that were actually written. Return < 0 on error. + // + // For a file open in append mode (i.e., "a" or "a+"), Write() + // always appends to the end of the file. For files opened in other + // write modes (i.e., "w", or "w+"), writes occur at the + // current file offset. + virtual int64 Write(const void* buffer, uint64 length) = 0; + + // Return the size of the file in bytes. + // A return value less than zero indicates a problem getting the + // size. + virtual int64 Size() = 0; + + // Flushes the file so that recently written data will survive an + // application crash (but not necessarily an OS crash). For + // instance, in localfile the data is flushed into the OS but not + // necessarily to disk. + virtual bool Flush() = 0; + + // Return whether we're currently at eof. + virtual bool Eof() = 0; + + // ************************************************************ + // * Static Methods: File-on-the-filesystem status + // ************************************************************ + + // Returns the size of a file in bytes, and opens and closes the file + // in the process. Returns -1 on failure. + static int64 GetFileSize(const char* fname); + + protected: + File() {} + // Do *not* call the destructor directly (with the "delete" keyword) + // nor use scoped_ptr; instead use Close(). + virtual ~File() {} + + // Internal open. Should not be used directly. + virtual bool Open() = 0; + + private: + // This is a file factory method, it creates a proper file, e.g. + // LocalFile, MemFile based on prefix. + static File* Create(const char* fname, const char* mode); + + DISALLOW_COPY_AND_ASSIGN(File); +}; + +} // namespace media + +#endif // PACKAGER_FILE_FILE_H_ diff --git a/media/file/file_unittest.cc b/media/file/file_unittest.cc new file mode 100644 index 0000000000..047f6ccf58 --- /dev/null +++ b/media/file/file_unittest.cc @@ -0,0 +1,120 @@ +// 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 "media/file/file.h" + +#include + +#include "base/file_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const int kDataSize = 1024; +const char* kTestLocalFileName = "/tmp/local_file_test"; +} + +namespace media { + +class LocalFileTest : public testing::Test { + protected: + virtual void SetUp() { + data_.resize(kDataSize); + for (int i = 0; i < kDataSize; ++i) + data_[i] = i % 256; + + // Test file path for file_util API. + test_file_path_ = base::FilePath(kTestLocalFileName); + + // Local file name with prefix for File API. + local_file_name_ = kLocalFilePrefix; + local_file_name_ += kTestLocalFileName; + } + + virtual void TearDown() { + // Remove test file if created. + base::DeleteFile(base::FilePath(kTestLocalFileName), false); + } + + std::string data_; + base::FilePath test_file_path_; + std::string local_file_name_; +}; + +TEST_F(LocalFileTest, ReadNotExist) { + // Remove test file if it exists. + base::DeleteFile(base::FilePath(kTestLocalFileName), false); + ASSERT_TRUE(File::Open(local_file_name_.c_str(), "r") == NULL); +} + +TEST_F(LocalFileTest, Size) { + ASSERT_EQ(kDataSize, + file_util::WriteFile(test_file_path_, data_.data(), kDataSize)); + ASSERT_EQ(kDataSize, File::GetFileSize(local_file_name_.c_str())); +} + +TEST_F(LocalFileTest, Write) { + // Write file using File API. + File* file = File::Open(local_file_name_.c_str(), "w"); + ASSERT_TRUE(file != NULL); + EXPECT_EQ(kDataSize, file->Write(&data_[0], kDataSize)); + EXPECT_EQ(kDataSize, file->Size()); + EXPECT_TRUE(file->Close()); + + // Read file using file_util API. + std::string read_data(kDataSize, 0); + ASSERT_EQ(kDataSize, + file_util::ReadFile(test_file_path_, &read_data[0], kDataSize)); + + // Compare data written and read. + EXPECT_EQ(data_, read_data); +} + +TEST_F(LocalFileTest, Read_And_Eof) { + // Write file using file_util API. + ASSERT_EQ(kDataSize, + file_util::WriteFile(test_file_path_, data_.data(), kDataSize)); + + // Read file using File API. + File* file = File::Open(local_file_name_.c_str(), "r"); + ASSERT_TRUE(file != NULL); + + // Read half of the file and verify that Eof is not true. + const int kFirstReadBytes = kDataSize / 2; + std::string read_data(kFirstReadBytes + kDataSize, 0); + EXPECT_EQ(kFirstReadBytes, file->Read(&read_data[0], kFirstReadBytes)); + EXPECT_FALSE(file->Eof()); + + // Read the remaining half of the file and verify Eof is true. + EXPECT_EQ(kDataSize - kFirstReadBytes, + file->Read(&read_data[kFirstReadBytes], kDataSize)); + EXPECT_TRUE(file->Eof()); + EXPECT_TRUE(file->Close()); + + // Compare data written and read. + read_data.resize(kDataSize); + EXPECT_EQ(data_, read_data); +} + +TEST_F(LocalFileTest, WriteRead) { + // Write file using File API, using file name directly (without prefix). + File* file = File::Open(kTestLocalFileName, "w"); + ASSERT_TRUE(file != NULL); + EXPECT_EQ(kDataSize, file->Write(&data_[0], kDataSize)); + EXPECT_EQ(kDataSize, file->Size()); + EXPECT_TRUE(file->Close()); + + // Read file using File API, using local file prefix + file name. + file = File::Open(local_file_name_.c_str(), "r"); + ASSERT_TRUE(file != NULL); + + // Read half of the file and verify that Eof is not true. + std::string read_data(kDataSize, 0); + EXPECT_EQ(kDataSize, file->Read(&read_data[0], kDataSize)); + EXPECT_TRUE(file->Close()); + + // Compare data written and read. + EXPECT_EQ(data_, read_data); +} + +} // namespace media diff --git a/media/file/local_file.cc b/media/file/local_file.cc new file mode 100644 index 0000000000..2dbde5b288 --- /dev/null +++ b/media/file/local_file.cc @@ -0,0 +1,70 @@ +// 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 "media/file/local_file.h" + +#include "base/file_util.h" +#include "base/logging.h" + +namespace media { + +LocalFile::LocalFile(const char* name, const char* mode) + : File(), file_name_(name), file_mode_(mode), internal_file_(NULL) {} + +bool LocalFile::Open() { + internal_file_ = + file_util::OpenFile(base::FilePath(file_name_), file_mode_.c_str()); + return (internal_file_ != NULL); +} + +bool LocalFile::Close() { + bool result = true; + if (internal_file_) { + result = file_util::CloseFile(internal_file_); + internal_file_ = NULL; + } + delete this; + return result; +} + +int64 LocalFile::Read(void* buffer, uint64 length) { + DCHECK(buffer != NULL); + DCHECK(internal_file_ != NULL); + return fread(buffer, sizeof(char), length, internal_file_); +} + +int64 LocalFile::Write(const void* buffer, uint64 length) { + DCHECK(buffer != NULL); + DCHECK(internal_file_ != NULL); + return fwrite(buffer, sizeof(char), length, internal_file_); +} + +int64 LocalFile::Size() { + DCHECK(internal_file_ != NULL); + + // Flush any buffered data, so we get the true file size. + if (!Flush()) { + LOG(ERROR) << "Cannot flush file."; + return -1; + } + + int64 file_size; + if (!file_util::GetFileSize(base::FilePath(file_name_), &file_size)) { + LOG(ERROR) << "Cannot get file size."; + return -1; + } + return file_size; +} + +bool LocalFile::Flush() { + DCHECK(internal_file_ != NULL); + return ((fflush(internal_file_) == 0) && !ferror(internal_file_)); +} + +bool LocalFile::Eof() { + DCHECK(internal_file_ != NULL); + return static_cast(feof(internal_file_)); +} + +} // namespace media diff --git a/media/file/local_file.h b/media/file/local_file.h new file mode 100644 index 0000000000..40b7d32c62 --- /dev/null +++ b/media/file/local_file.h @@ -0,0 +1,44 @@ +// 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. +// +// Implements LocalFile. + +#ifndef PACKAGER_FILE_LOCAL_FILE_H_ +#define PACKAGER_FILE_LOCAL_FILE_H_ + +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "media/file/file.h" + +namespace media { + +class LocalFile : public File { + public: + LocalFile(const char* name, const char* mode); + + // File implementations. + virtual bool Close() OVERRIDE; + virtual int64 Read(void* buffer, uint64 length) OVERRIDE; + virtual int64 Write(const void* buffer, uint64 length) OVERRIDE; + virtual int64 Size() OVERRIDE; + virtual bool Flush() OVERRIDE; + virtual bool Eof() OVERRIDE; + + protected: + virtual bool Open() OVERRIDE; + + private: + std::string file_name_; + std::string file_mode_; + FILE* internal_file_; + + DISALLOW_COPY_AND_ASSIGN(LocalFile); +}; + +} // namespace media + +#endif // PACKAGER_FILE_LOCAL_FILE_H_ + diff --git a/media/mp4/mp4_media_parser.cc b/media/mp4/mp4_media_parser.cc index 4d8ae21a89..39c03963fb 100644 --- a/media/mp4/mp4_media_parser.cc +++ b/media/mp4/mp4_media_parser.cc @@ -304,8 +304,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { } } - init_cb_.Run(true, streams); - + init_cb_.Run(streams); EmitNeedKeyIfNecessary(moov_->pssh); runs_.reset(new TrackRunIterator(moov_.get())); RCHECK(runs_->Init()); diff --git a/media/mp4/mp4_media_parser_unittest.cc b/media/mp4/mp4_media_parser_unittest.cc index e58fd4fde8..3124624747 100644 --- a/media/mp4/mp4_media_parser_unittest.cc +++ b/media/mp4/mp4_media_parser_unittest.cc @@ -48,9 +48,8 @@ class MP4MediaParserTest : public testing::Test { return true; } - void InitF(bool init_ok, std::vector >& streams) { - DVLOG(1) << "InitF: ok=" << init_ok; - if (init_ok && streams.size() > 0) + void InitF(const std::vector >& streams) { + if (streams.size() > 0) configs_received_ = true; } diff --git a/media/test/packager_test.cc b/media/test/packager_test.cc new file mode 100644 index 0000000000..318a76ffe9 --- /dev/null +++ b/media/test/packager_test.cc @@ -0,0 +1,68 @@ +// 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/demuxer.h" +#include "media/base/media_sample.h" +#include "media/base/media_stream.h" +#include "media/base/muxer.h" +#include "media/base/status_test_util.h" +#include "media/base/stream_info.h" +#include "media/base/test_data_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const char* kMediaFiles[] = {"bear-1280x720.mp4", "bear-1280x720-av_frag.mp4"}; +} + +namespace media { + +class TestingMuxer : public Muxer { + public: + TestingMuxer(const Options& options, EncryptorSource* encryptor_source) + : Muxer(options, encryptor_source) {} + + virtual Status AddSample(const MediaStream* stream, + scoped_refptr sample) { + DVLOG(1) << "Add Sample: " << sample->ToString(); + DVLOG(2) << "To Stream: " << stream->ToString(); + return Status::OK; + } + + virtual Status Finalize() { + DVLOG(1) << "Finalize is called."; + return Status::OK; + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestingMuxer); +}; + +class PackagerTest : public ::testing::TestWithParam {}; + +TEST_P(PackagerTest, Remux) { + Demuxer demuxer( + GetTestDataFilePath(GetParam()).value(), NULL); + ASSERT_OK(demuxer.Initialize()); + + LOG(INFO) << "Num Streams: " << demuxer.num_streams(); + for (int i = 0; i < demuxer.num_streams(); ++i) { + LOG(INFO) << "Streams " << i << " " << demuxer.streams(i)->ToString(); + } + + Muxer::Options options; + TestingMuxer muxer(options, NULL); + + ASSERT_OK(muxer.AddStream(demuxer.streams(0))); + + // Starts remuxing process. + ASSERT_OK(demuxer.Run()); + + ASSERT_OK(muxer.Finalize()); +} + +INSTANTIATE_TEST_CASE_P(PackagerE2ETest, + PackagerTest, + ::testing::ValuesIn(kMediaFiles)); + +} // namespace media diff --git a/packager.gyp b/packager.gyp index 2fd7342975..60ffd2e9d9 100644 --- a/packager.gyp +++ b/packager.gyp @@ -1,6 +1,7 @@ # 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. +# TODO(kqyang): this file should be in media directory. { 'target_defaults': { 'include_dirs': [ @@ -8,5 +9,172 @@ ], }, 'targets': [ + { + 'target_name': 'media_base', + 'type': 'static_library', + 'sources': [ + 'media/base/audio_stream_info.cc', + 'media/base/audio_stream_info.h', + 'media/base/bit_reader.cc', + 'media/base/bit_reader.h', + 'media/base/buffers.h', + 'media/base/byte_queue.cc', + 'media/base/byte_queue.h', + 'media/base/container_names.cc', + 'media/base/container_names.h', + # TODO(kqyang): demuxer should not be here, it looks like some kinds of + # circular dependencies. + 'media/base/demuxer.cc', + 'media/base/demuxer.h', + 'media/base/decrypt_config.cc', + 'media/base/decrypt_config.h', + 'media/base/decryptor_source.h', + 'media/base/encryptor_source.h', + 'media/base/limits.h', + 'media/base/media_parser.h', + 'media/base/media_sample.cc', + 'media/base/media_sample.h', + 'media/base/media_stream.cc', + 'media/base/media_stream.h', + 'media/base/muxer.cc', + 'media/base/muxer.h', + 'media/base/status.cc', + 'media/base/status.h', + 'media/base/stream_info.cc', + 'media/base/stream_info.h', + 'media/base/text_track.h', + 'media/base/video_stream_info.cc', + 'media/base/video_stream_info.h', + ], + 'dependencies': [ + 'base/base.gyp:base', + ], + }, + { + 'target_name': 'media_test_support', + 'type': 'static_library', + 'sources': [ + # TODO(kqyang): move these files to test directory. + 'media/base/run_tests_with_atexit_manager.cc', + 'media/base/test_data_util.cc', + 'media/base/test_data_util.h', + ], + 'dependencies': [ + 'base/base.gyp:base', + 'testing/gtest.gyp:gtest', + ], + }, + { + 'target_name': 'media_base_unittest', + 'type': 'executable', + 'sources': [ + 'media/base/bit_reader_unittest.cc', + 'media/base/container_names_unittest.cc', + 'media/base/status_test_util.h', + 'media/base/status_test_util_unittest.cc', + 'media/base/status_unittest.cc', + ], + 'dependencies': [ + 'media_base', + 'media_test_support', + 'testing/gtest.gyp:gtest', + 'testing/gmock.gyp:gmock', + ], + }, + { + 'target_name': 'mp4', + 'type': 'static_library', + 'sources': [ + 'media/mp4/aac.cc', + 'media/mp4/aac.h', + 'media/mp4/box_definitions.cc', + 'media/mp4/box_definitions.h', + 'media/mp4/box_reader.cc', + 'media/mp4/box_reader.h', + 'media/mp4/cenc.cc', + 'media/mp4/cenc.h', + 'media/mp4/chunk_info_iterator.cc', + 'media/mp4/chunk_info_iterator.h', + 'media/mp4/composition_offset_iterator.cc', + 'media/mp4/composition_offset_iterator.h', + 'media/mp4/decoding_time_iterator.cc', + 'media/mp4/decoding_time_iterator.h', + 'media/mp4/es_descriptor.cc', + 'media/mp4/es_descriptor.h', + 'media/mp4/fourccs.h', + 'media/mp4/mp4_media_parser.cc', + 'media/mp4/mp4_media_parser.h', + 'media/mp4/offset_byte_queue.cc', + 'media/mp4/offset_byte_queue.h', + 'media/mp4/rcheck.h', + 'media/mp4/sync_sample_iterator.cc', + 'media/mp4/sync_sample_iterator.h', + 'media/mp4/track_run_iterator.cc', + 'media/mp4/track_run_iterator.h', + ], + 'dependencies': [ + 'media_base', + ], + }, + { + 'target_name': 'mp4_unittest', + 'type': 'executable', + 'sources': [ + 'media/mp4/aac_unittest.cc', + 'media/mp4/box_reader_unittest.cc', + 'media/mp4/chunk_info_iterator_unittest.cc', + 'media/mp4/composition_offset_iterator_unittest.cc', + 'media/mp4/decoding_time_iterator_unittest.cc', + 'media/mp4/es_descriptor_unittest.cc', + 'media/mp4/mp4_media_parser_unittest.cc', + 'media/mp4/offset_byte_queue_unittest.cc', + 'media/mp4/sync_sample_iterator_unittest.cc', + 'media/mp4/track_run_iterator_unittest.cc', + ], + 'dependencies': [ + 'media_test_support', + 'mp4', + 'testing/gtest.gyp:gtest', + 'testing/gmock.gyp:gmock', + ] + }, + { + 'target_name': 'file', + 'type': 'static_library', + 'sources': [ + 'media/file/file.cc', + 'media/file/file.h', + 'media/file/local_file.cc', + 'media/file/local_file.h', + ], + 'dependencies': [ + 'base/base.gyp:base', + ], + }, + { + 'target_name': 'file_unittest', + 'type': 'executable', + 'sources': [ + 'media/file/file_unittest.cc', + ], + 'dependencies': [ + 'file', + 'testing/gtest.gyp:gtest', + 'testing/gtest.gyp:gtest_main', + ], + }, + { + 'target_name': 'packager_test', + 'type': 'executable', + 'sources': [ + 'media/test/packager_test.cc', + ], + 'dependencies': [ + 'file', + 'media_test_support', + 'mp4', + 'testing/gtest.gyp:gtest', + ], + }, ], }