Demuxer, MediaStream, File and Status implementations.

Also includes a dummy packager_test with TestingMuxer.

Change-Id: I6a6e6dd77e343ef742adc1846ede203993628943
This commit is contained in:
Kongqun Yang 2013-10-11 14:44:55 -07:00
parent 3f3d9a6b76
commit edf74fc89f
26 changed files with 1659 additions and 13 deletions

View File

@ -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_

141
media/base/demuxer.cc Normal file
View File

@ -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<scoped_refptr<StreamInfo> >& streams) {
init_event_received_ = true;
std::vector<scoped_refptr<StreamInfo> >::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<MediaSample>& sample) {
std::vector<MediaStream*>::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<uint8[]> init_data,
int init_data_size) {
NOTIMPLEMENTED();
}
Status Demuxer::Run() {
Status status;
// Start the streams.
for (std::vector<MediaStream*>::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

79
media/base/demuxer.h Normal file
View File

@ -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 <vector>
#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<scoped_refptr<StreamInfo> >& streams);
bool NewSampleEvent(uint32 track_id,
const scoped_refptr<MediaSample>& sample);
void KeyNeededEvent(MediaContainerName container,
scoped_ptr<uint8[]> init_data,
int init_data_size);
DecryptorSource* decryptor_source_;
std::string file_name_;
File* media_file_;
bool init_event_received_;
scoped_ptr<MediaParser> parser_;
std::vector<MediaStream*> streams_;
scoped_ptr<uint8[]> buffer_;
DISALLOW_COPY_AND_ASSIGN(Demuxer);
};
}
#endif // MEDIA_BASE_DEMUXER_H_

View File

@ -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_

View File

@ -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<void(bool, std::vector<scoped_refptr<StreamInfo> >&)>
// First parameter - A vector of all the elementary streams within this file.
typedef base::Callback<void(const std::vector<scoped_refptr<StreamInfo> >&)>
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.

View File

@ -55,7 +55,7 @@ scoped_refptr<MediaSample> 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";
}

View File

@ -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<MediaSample>;

110
media/base/media_stream.cc Normal file
View File

@ -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<StreamInfo> info, Demuxer* demuxer)
: info_(info), demuxer_(demuxer), muxer_(NULL), state_(kIdle) {}
MediaStream::~MediaStream() {}
Status MediaStream::PullSample(scoped_refptr<MediaSample>* 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<MediaSample>& 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<StreamInfo> 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

79
media/base/media_stream.h Normal file
View File

@ -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 <deque>
#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<StreamInfo> 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<MediaSample>& sample);
// Pull sample from Demuxer (triggered by Muxer).
Status PullSample(scoped_refptr<MediaSample>* sample);
Demuxer* demuxer() {
return demuxer_;
}
Muxer* muxer() {
return muxer_;
}
scoped_refptr<StreamInfo> info();
// 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<StreamInfo> info_;
Demuxer* demuxer_;
Muxer* muxer_;
State state_;
// An internal buffer to store samples temporarily.
std::deque<scoped_refptr<MediaSample> > samples_;
DISALLOW_COPY_AND_ASSIGN(MediaStream);
};
} // namespace media
#endif // MEDIA_BASE_MEDIA_STREAM_H_

49
media/base/muxer.cc Normal file
View File

@ -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<MediaStream*>::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<MediaSample> 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

104
media/base/muxer.h Normal file
View File

@ -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 <vector>
#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<MediaSample> 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<MediaStream*> streams_;
DISALLOW_COPY_AND_ASSIGN(Muxer);
};
} // namespace media
#endif // MEDIA_BASE_MUXER_H_

View File

@ -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();
}

28
media/base/status.cc Normal file
View File

@ -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 <sstream>
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

156
media/base/status.h Normal file
View File

@ -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 <string>
#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_

View File

@ -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_

View File

@ -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");
}

View File

@ -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 <sstream>
#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

64
media/file/file.cc Normal file
View File

@ -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

85
media/file/file.h Normal file
View File

@ -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_

120
media/file/file_unittest.cc Normal file
View File

@ -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 <vector>
#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

70
media/file/local_file.cc Normal file
View File

@ -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<bool>(feof(internal_file_));
}
} // namespace media

44
media/file/local_file.h Normal file
View File

@ -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 <string>
#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_

View File

@ -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());

View File

@ -48,9 +48,8 @@ class MP4MediaParserTest : public testing::Test {
return true;
}
void InitF(bool init_ok, std::vector<scoped_refptr<StreamInfo> >& streams) {
DVLOG(1) << "InitF: ok=" << init_ok;
if (init_ok && streams.size() > 0)
void InitF(const std::vector<scoped_refptr<StreamInfo> >& streams) {
if (streams.size() > 0)
configs_received_ = true;
}

View File

@ -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<MediaSample> 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<const char*> {};
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

View File

@ -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',
],
},
],
}