Commit 8abccfde authored by damienv's avatar damienv Committed by Commit bot

Audio/video data streaming over shared memory.

BUG=408189

Review URL: https://codereview.chromium.org/557333003

Cr-Commit-Position: refs/heads/master@{#294391}
parent dc5b3dc2
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "chromecast/media/cma/ipc/media_message.h"
#include "media/base/audio_decoder_config.h"
namespace chromecast {
namespace media {
namespace {
const size_t kMaxExtraDataSize = 16 * 1024;
}
// static
void AudioDecoderConfigMarshaller::Write(
const ::media::AudioDecoderConfig& config, MediaMessage* msg) {
CHECK(msg->WritePod(config.codec()));
CHECK(msg->WritePod(config.channel_layout()));
CHECK(msg->WritePod(config.samples_per_second()));
CHECK(msg->WritePod(config.sample_format()));
CHECK(msg->WritePod(config.is_encrypted()));
CHECK(msg->WritePod(config.extra_data_size()));
if (config.extra_data_size() > 0)
CHECK(msg->WriteBuffer(config.extra_data(), config.extra_data_size()));
}
// static
::media::AudioDecoderConfig AudioDecoderConfigMarshaller::Read(
MediaMessage* msg) {
::media::AudioCodec codec;
::media::SampleFormat sample_format;
::media::ChannelLayout channel_layout;
int samples_per_second;
bool is_encrypted;
size_t extra_data_size;
scoped_ptr<uint8[]> extra_data;
CHECK(msg->ReadPod(&codec));
CHECK(msg->ReadPod(&channel_layout));
CHECK(msg->ReadPod(&samples_per_second));
CHECK(msg->ReadPod(&sample_format));
CHECK(msg->ReadPod(&is_encrypted));
CHECK(msg->ReadPod(&extra_data_size));
CHECK_GE(codec, ::media::kUnknownAudioCodec);
CHECK_LE(codec, ::media::kAudioCodecMax);
CHECK_GE(channel_layout, ::media::CHANNEL_LAYOUT_NONE);
CHECK_LE(channel_layout, ::media::CHANNEL_LAYOUT_MAX);
CHECK_GE(sample_format, ::media::kUnknownSampleFormat);
CHECK_LE(sample_format, ::media::kSampleFormatMax);
CHECK_LT(extra_data_size, kMaxExtraDataSize);
if (extra_data_size > 0) {
extra_data.reset(new uint8[extra_data_size]);
CHECK(msg->ReadBuffer(extra_data.get(), extra_data_size));
}
return ::media::AudioDecoderConfig(
codec, sample_format,
channel_layout, samples_per_second,
extra_data.get(), extra_data_size,
is_encrypted);
}
} // namespace media
} // namespace chromecast
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_CMA_IPC_STREAMER_AUDIO_DECODER_CONFIG_MARSHALLER_H_
#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_AUDIO_DECODER_CONFIG_MARSHALLER_H_
#include "media/base/audio_decoder_config.h"
namespace chromecast {
namespace media {
class MediaMessage;
class AudioDecoderConfigMarshaller {
public:
// Writes the serialized structure of |config| into |msg|.
static void Write(
const ::media::AudioDecoderConfig& config, MediaMessage* msg);
// Returns an AudioDecoderConfig from its serialized structure.
static ::media::AudioDecoderConfig Read(MediaMessage* msg);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_AUDIO_DECODER_CONFIG_MARSHALLER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
#include "chromecast/media/cma/base/coded_frame_provider.h"
#include "chromecast/media/cma/base/decoder_buffer_base.h"
#include "chromecast/media/cma/ipc/media_memory_chunk.h"
#include "chromecast/media/cma/ipc/media_message.h"
#include "chromecast/media/cma/ipc/media_message_fifo.h"
#include "chromecast/media/cma/ipc/media_message_type.h"
#include "chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h"
#include "chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h"
#include "chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h"
namespace chromecast {
namespace media {
AvStreamerProxy::AvStreamerProxy()
: is_running_(false),
pending_read_(false),
pending_av_data_(false),
weak_factory_(this),
weak_this_(weak_factory_.GetWeakPtr()) {
thread_checker_.DetachFromThread();
}
AvStreamerProxy::~AvStreamerProxy() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void AvStreamerProxy::SetCodedFrameProvider(
scoped_ptr<CodedFrameProvider> frame_provider) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!frame_provider_);
frame_provider_.reset(frame_provider.release());
}
void AvStreamerProxy::SetMediaMessageFifo(
scoped_ptr<MediaMessageFifo> fifo) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!fifo_);
fifo_.reset(fifo.release());
}
void AvStreamerProxy::Start() {
DCHECK(!is_running_);
is_running_ = true;
RequestBufferIfNeeded();
}
void AvStreamerProxy::StopAndFlush(const base::Closure& done_cb) {
is_running_ = false;
pending_av_data_ = false;
pending_audio_config_ = ::media::AudioDecoderConfig();
pending_video_config_ = ::media::VideoDecoderConfig();
pending_buffer_ = scoped_refptr<DecoderBufferBase>();
pending_read_ = false;
frame_provider_->Flush(done_cb);
}
void AvStreamerProxy::OnFifoReadEvent() {
DCHECK(thread_checker_.CalledOnValidThread());
// Some enough space might have been released
// to accommodate the pending data.
if (pending_av_data_)
ProcessPendingData();
}
void AvStreamerProxy::RequestBufferIfNeeded() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!is_running_ || pending_read_ || pending_av_data_)
return;
// |frame_provider_| is assumed to run on the same message loop.
// Add a BindToCurrentLoop if that's not the case in the future.
pending_read_ = true;
frame_provider_->Read(base::Bind(&AvStreamerProxy::OnNewBuffer, weak_this_));
}
void AvStreamerProxy::OnNewBuffer(
const scoped_refptr<DecoderBufferBase>& buffer,
const ::media::AudioDecoderConfig& audio_config,
const ::media::VideoDecoderConfig& video_config) {
DCHECK(thread_checker_.CalledOnValidThread());
pending_read_ = false;
if (buffer->end_of_stream())
is_running_ = false;
DCHECK(!pending_av_data_);
pending_av_data_ = true;
pending_buffer_ = buffer;
pending_audio_config_ = audio_config;
pending_video_config_ = video_config;
ProcessPendingData();
}
void AvStreamerProxy::ProcessPendingData() {
if (pending_audio_config_.IsValidConfig()) {
if (!SendAudioDecoderConfig(pending_audio_config_))
return;
pending_audio_config_ = ::media::AudioDecoderConfig();
}
if (pending_video_config_.IsValidConfig()) {
if (!SendVideoDecoderConfig(pending_video_config_))
return;
pending_video_config_ = ::media::VideoDecoderConfig();
}
if (pending_buffer_.get()) {
if (!SendBuffer(pending_buffer_))
return;
pending_buffer_ = scoped_refptr<DecoderBufferBase>();
}
pending_av_data_ = false;
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(&AvStreamerProxy::RequestBufferIfNeeded, weak_this_));
}
bool AvStreamerProxy::SendAudioDecoderConfig(
const ::media::AudioDecoderConfig& config) {
// Create a dummy message to calculate first the message size.
scoped_ptr<MediaMessage> dummy_msg(
MediaMessage::CreateDummyMessage(AudioConfigMediaMsg));
AudioDecoderConfigMarshaller::Write(config, dummy_msg.get());
// Create the real message and write the actual content.
scoped_ptr<MediaMessage> msg(
MediaMessage::CreateMessage(
AudioConfigMediaMsg,
base::Bind(&MediaMessageFifo::ReserveMemory,
base::Unretained(fifo_.get())),
dummy_msg->content_size()));
if (!msg)
return false;
AudioDecoderConfigMarshaller::Write(config, msg.get());
return true;
}
bool AvStreamerProxy::SendVideoDecoderConfig(
const ::media::VideoDecoderConfig& config) {
// Create a dummy message to calculate first the message size.
scoped_ptr<MediaMessage> dummy_msg(
MediaMessage::CreateDummyMessage(VideoConfigMediaMsg));
VideoDecoderConfigMarshaller::Write(config, dummy_msg.get());
// Create the real message and write the actual content.
scoped_ptr<MediaMessage> msg(
MediaMessage::CreateMessage(
VideoConfigMediaMsg,
base::Bind(&MediaMessageFifo::ReserveMemory,
base::Unretained(fifo_.get())),
dummy_msg->content_size()));
if (!msg)
return false;
VideoDecoderConfigMarshaller::Write(config, msg.get());
return true;
}
bool AvStreamerProxy::SendBuffer(
const scoped_refptr<DecoderBufferBase>& buffer) {
// Create a dummy message to calculate first the message size.
scoped_ptr<MediaMessage> dummy_msg(
MediaMessage::CreateDummyMessage(FrameMediaMsg));
DecoderBufferBaseMarshaller::Write(buffer, dummy_msg.get());
// Create the real message and write the actual content.
scoped_ptr<MediaMessage> msg(
MediaMessage::CreateMessage(
FrameMediaMsg,
base::Bind(&MediaMessageFifo::ReserveMemory,
base::Unretained(fifo_.get())),
dummy_msg->content_size()));
if (!msg)
return false;
DecoderBufferBaseMarshaller::Write(buffer, msg.get());
return true;
}
} // namespace media
} // namespace chromecast
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_CMA_IPC_STREAMER_AV_STREAMER_PROXY_H_
#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_AV_STREAMER_PROXY_H_
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/video_decoder_config.h"
namespace chromecast {
namespace media {
class CodedFrameProvider;
class DecoderBufferBase;
class MediaMessageFifo;
// AvStreamerProxy streams audio/video data from a coded frame provider
// to a media message fifo.
class AvStreamerProxy {
public:
AvStreamerProxy();
~AvStreamerProxy();
// AvStreamerProxy gets frames and audio/video config
// from the |frame_provider| and feed them into the |fifo|.
void SetCodedFrameProvider(scoped_ptr<CodedFrameProvider> frame_provider);
void SetMediaMessageFifo(scoped_ptr<MediaMessageFifo> fifo);
// Starts fetching A/V buffers.
void Start();
// Stops fetching A/V buffers and flush the pending buffers,
// invoking |flush_cb| when done.
void StopAndFlush(const base::Closure& flush_cb);
// Event invoked when some data have been read from the fifo.
// This means some data can now possibly be written into the fifo.
void OnFifoReadEvent();
private:
void RequestBufferIfNeeded();
void OnNewBuffer(const scoped_refptr<DecoderBufferBase>& buffer,
const ::media::AudioDecoderConfig& audio_config,
const ::media::VideoDecoderConfig& video_config);
void ProcessPendingData();
bool SendAudioDecoderConfig(const ::media::AudioDecoderConfig& config);
bool SendVideoDecoderConfig(const ::media::VideoDecoderConfig& config);
bool SendBuffer(const scoped_refptr<DecoderBufferBase>& buffer);
base::ThreadChecker thread_checker_;
scoped_ptr<CodedFrameProvider> frame_provider_;
// Fifo used to convey A/V configs and buffers.
scoped_ptr<MediaMessageFifo> fifo_;
// State.
bool is_running_;
// Indicates if there is a pending request to the coded frame provider.
bool pending_read_;
// Pending config & buffer.
// |pending_av_data_| is set as long as one of the pending audio/video
// config or buffer is valid.
bool pending_av_data_;
::media::AudioDecoderConfig pending_audio_config_;
::media::VideoDecoderConfig pending_video_config_;
scoped_refptr<DecoderBufferBase> pending_buffer_;
base::WeakPtrFactory<AvStreamerProxy> weak_factory_;
base::WeakPtr<AvStreamerProxy> weak_this_;
DISALLOW_COPY_AND_ASSIGN(AvStreamerProxy);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_AV_STREAMER_PROXY_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <list>
#include <vector>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "chromecast/media/cma/base/decoder_buffer_base.h"
#include "chromecast/media/cma/base/frame_generator_for_test.h"
#include "chromecast/media/cma/base/mock_frame_consumer.h"
#include "chromecast/media/cma/base/mock_frame_provider.h"
#include "chromecast/media/cma/ipc/media_memory_chunk.h"
#include "chromecast/media/cma/ipc/media_message_fifo.h"
#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h"
#include "chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/decoder_buffer.h"
#include "media/base/video_decoder_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromecast {
namespace media {
namespace {
class FifoMemoryChunk : public MediaMemoryChunk {
public:
FifoMemoryChunk(void* mem, size_t size)
: mem_(mem), size_(size) {}
virtual ~FifoMemoryChunk() {}
virtual void* data() const OVERRIDE { return mem_; }
virtual size_t size() const OVERRIDE { return size_; }
virtual bool valid() const OVERRIDE { return true; }
private:
void* mem_;
size_t size_;
DISALLOW_COPY_AND_ASSIGN(FifoMemoryChunk);
};
} // namespace
class AvStreamerTest : public testing::Test {
public:
AvStreamerTest();
virtual ~AvStreamerTest();
// Setups the test.
void Configure(
size_t frame_count,
const std::vector<bool>& provider_delayed_pattern,
const std::vector<bool>& consumer_delayed_pattern);
// Starts the test.
void Start();
protected:
scoped_ptr<uint64[]> fifo_mem_;
scoped_ptr<AvStreamerProxy> av_buffer_proxy_;
scoped_ptr<CodedFrameProviderHost> coded_frame_provider_host_;
scoped_ptr<MockFrameConsumer> frame_consumer_;
private:
void OnTestTimeout();
void OnTestCompleted();
void OnFifoRead();
void OnFifoWrite();
DISALLOW_COPY_AND_ASSIGN(AvStreamerTest);
};
AvStreamerTest::AvStreamerTest() {
}
AvStreamerTest::~AvStreamerTest() {
}
void AvStreamerTest::Configure(
size_t frame_count,
const std::vector<bool>& provider_delayed_pattern,
const std::vector<bool>& consumer_delayed_pattern) {
// Frame generation on the producer and consumer side.
std::vector<FrameGeneratorForTest::FrameSpec> frame_specs;
frame_specs.resize(frame_count);
for (size_t k = 0; k < frame_specs.size() - 1; k++) {
frame_specs[k].has_config = (k == 0);
frame_specs[k].timestamp = base::TimeDelta::FromMilliseconds(40) * k;
frame_specs[k].size = 512;
frame_specs[k].has_decrypt_config = ((k % 3) == 0);
}
frame_specs[frame_specs.size() - 1].is_eos = true;
scoped_ptr<FrameGeneratorForTest> frame_generator_provider(
new FrameGeneratorForTest(frame_specs));
scoped_ptr<FrameGeneratorForTest> frame_generator_consumer(
new FrameGeneratorForTest(frame_specs));
scoped_ptr<MockFrameProvider> frame_provider(new MockFrameProvider());
frame_provider->Configure(provider_delayed_pattern,
frame_generator_provider.Pass());
size_t fifo_size_div_8 = 512;
fifo_mem_.reset(new uint64[fifo_size_div_8]);
scoped_ptr<MediaMessageFifo> producer_fifo(
new MediaMessageFifo(
scoped_ptr<MediaMemoryChunk>(
new FifoMemoryChunk(&fifo_mem_[0], fifo_size_div_8 * 8)),
true));
scoped_ptr<MediaMessageFifo> consumer_fifo(
new MediaMessageFifo(
scoped_ptr<MediaMemoryChunk>(
new FifoMemoryChunk(&fifo_mem_[0], fifo_size_div_8 * 8)),
false));
producer_fifo->ObserveWriteActivity(
base::Bind(&AvStreamerTest::OnFifoWrite, base::Unretained(this)));
consumer_fifo->ObserveReadActivity(
base::Bind(&AvStreamerTest::OnFifoRead, base::Unretained(this)));
av_buffer_proxy_.reset(
new AvStreamerProxy());
av_buffer_proxy_->SetCodedFrameProvider(
scoped_ptr<CodedFrameProvider>(frame_provider.release()));
av_buffer_proxy_->SetMediaMessageFifo(producer_fifo.Pass());
coded_frame_provider_host_.reset(
new CodedFrameProviderHost(consumer_fifo.Pass()));
frame_consumer_.reset(
new MockFrameConsumer(coded_frame_provider_host_.get()));
frame_consumer_->Configure(
consumer_delayed_pattern,
false,
frame_generator_consumer.Pass());
}
void AvStreamerTest::Start() {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(&AvStreamerProxy::Start,
base::Unretained(av_buffer_proxy_.get())));
frame_consumer_->Start(
base::Bind(&AvStreamerTest::OnTestCompleted,
base::Unretained(this)));
}
void AvStreamerTest::OnTestTimeout() {
ADD_FAILURE() << "Test timed out";
if (base::MessageLoop::current())
base::MessageLoop::current()->QuitWhenIdle();
}
void AvStreamerTest::OnTestCompleted() {
base::MessageLoop::current()->QuitWhenIdle();
}
void AvStreamerTest::OnFifoWrite() {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(&CodedFrameProviderHost::OnFifoWriteEvent,
base::Unretained(coded_frame_provider_host_.get())));
}
void AvStreamerTest::OnFifoRead() {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(&AvStreamerProxy::OnFifoReadEvent,
base::Unretained(av_buffer_proxy_.get())));
}
TEST_F(AvStreamerTest, FastProviderSlowConsumer) {
bool provider_delayed_pattern[] = { false };
bool consumer_delayed_pattern[] = { true };
const size_t frame_count = 100u;
Configure(
frame_count,
std::vector<bool>(
provider_delayed_pattern,
provider_delayed_pattern + arraysize(provider_delayed_pattern)),
std::vector<bool>(
consumer_delayed_pattern,
consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
message_loop->PostTask(
FROM_HERE,
base::Bind(&AvStreamerTest::Start, base::Unretained(this)));
message_loop->Run();
};
TEST_F(AvStreamerTest, SlowProviderFastConsumer) {
bool provider_delayed_pattern[] = { true };
bool consumer_delayed_pattern[] = { false };
const size_t frame_count = 100u;
Configure(
frame_count,
std::vector<bool>(
provider_delayed_pattern,
provider_delayed_pattern + arraysize(provider_delayed_pattern)),
std::vector<bool>(
consumer_delayed_pattern,
consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
message_loop->PostTask(
FROM_HERE,
base::Bind(&AvStreamerTest::Start, base::Unretained(this)));
message_loop->Run();
};
TEST_F(AvStreamerTest, SlowFastProducerConsumer) {
// Pattern lengths are prime between each other
// so that a lot of combinations can be tested.
bool provider_delayed_pattern[] = {
true, true, true, true, true,
false, false, false, false
};
bool consumer_delayed_pattern[] = {
true, true, true, true, true, true, true,
false, false, false, false, false, false, false
};
const size_t frame_count = 100u;
Configure(
frame_count,
std::vector<bool>(
provider_delayed_pattern,
provider_delayed_pattern + arraysize(provider_delayed_pattern)),
std::vector<bool>(
consumer_delayed_pattern,
consumer_delayed_pattern + arraysize(consumer_delayed_pattern)));
scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
message_loop->PostTask(
FROM_HERE,
base::Bind(&AvStreamerTest::Start, base::Unretained(this)));
message_loop->Run();
};
} // namespace media
} // namespace chromecast
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/cma/ipc_streamer/coded_frame_provider_host.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "chromecast/media/cma/base/decoder_buffer_base.h"
#include "chromecast/media/cma/ipc/media_message.h"
#include "chromecast/media/cma/ipc/media_message_fifo.h"
#include "chromecast/media/cma/ipc/media_message_type.h"
#include "chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h"
#include "chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h"
#include "chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h"
#include "media/base/buffers.h"
#include "media/base/decrypt_config.h"
namespace chromecast {
namespace media {
CodedFrameProviderHost::CodedFrameProviderHost(
scoped_ptr<MediaMessageFifo> media_message_fifo)
: fifo_(media_message_fifo.Pass()),
weak_factory_(this),
weak_this_(weak_factory_.GetWeakPtr()) {
thread_checker_.DetachFromThread();
}
CodedFrameProviderHost::~CodedFrameProviderHost() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void CodedFrameProviderHost::Read(const ReadCB& read_cb) {
DCHECK(thread_checker_.CalledOnValidThread());
// Cannot be called if there is already a pending read.
DCHECK(read_cb_.is_null());
read_cb_ = read_cb;
ReadMessages();
}
void CodedFrameProviderHost::Flush(const base::Closure& flush_cb) {
DCHECK(thread_checker_.CalledOnValidThread());
audio_config_ = ::media::AudioDecoderConfig();
video_config_ = ::media::VideoDecoderConfig();
read_cb_.Reset();
fifo_->Flush();
flush_cb.Run();
}
void CodedFrameProviderHost::OnFifoWriteEvent() {
DCHECK(thread_checker_.CalledOnValidThread());
ReadMessages();
}
base::Closure CodedFrameProviderHost::GetFifoWriteEventCb() {
return base::Bind(&CodedFrameProviderHost::OnFifoWriteEvent, weak_this_);
}
void CodedFrameProviderHost::ReadMessages() {
// Read messages until a frame is provided (i.e. not just the audio/video
// configurations).
while (!read_cb_.is_null()) {
scoped_ptr<MediaMessage> msg(fifo_->Pop());
if (!msg)
break;
if (msg->type() == PaddingMediaMsg) {
// Ignore the message.
} else if (msg->type() == AudioConfigMediaMsg) {
audio_config_ = AudioDecoderConfigMarshaller::Read(msg.get());
} else if (msg->type() == VideoConfigMediaMsg) {
video_config_ = VideoDecoderConfigMarshaller::Read(msg.get());
} else if (msg->type() == FrameMediaMsg) {
scoped_refptr<DecoderBufferBase> buffer =
DecoderBufferBaseMarshaller::Read(msg.Pass());
base::ResetAndReturn(&read_cb_).Run(
buffer, audio_config_, video_config_);
audio_config_ = ::media::AudioDecoderConfig();
video_config_ = ::media::VideoDecoderConfig();
} else {
// Receiving an unexpected message.
// Possible use case (except software bugs): the renderer process has
// been compromised and an invalid message value has been written to
// the fifo. Crash the browser process in this case to avoid further
// security implications (so do not use NOTREACHED which crashes only
// in debug builds).
LOG(FATAL) << "Unknown media message";
}
}
}
} // namespace media
} // namespace chromecast
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_CMA_IPC_STREAMER_CODED_FRAME_PROVIDER_HOST_H_
#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_CODED_FRAME_PROVIDER_HOST_H_
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "chromecast/media/cma/base/coded_frame_provider.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/video_decoder_config.h"
namespace chromecast {
namespace media {
class MediaMessageFifo;
// CodedFrameProviderHost is a frame provider that gets the frames
// from a media message fifo.
class CodedFrameProviderHost : public CodedFrameProvider {
public:
// Note: if the media message fifo is located into shared memory,
// the caller must make sure the shared memory segment is valid
// during the whole lifetime of this object.
explicit CodedFrameProviderHost(
scoped_ptr<MediaMessageFifo> media_message_fifo);
virtual ~CodedFrameProviderHost();
// CodedFrameProvider implementation.
virtual void Read(const ReadCB& read_cb) OVERRIDE;
virtual void Flush(const base::Closure& flush_cb) OVERRIDE;
// Invoked when some data has been written into the fifo.
void OnFifoWriteEvent();
base::Closure GetFifoWriteEventCb();
private:
void ReadMessages();
base::ThreadChecker thread_checker_;
// Fifo holding the frames.
scoped_ptr<MediaMessageFifo> fifo_;
ReadCB read_cb_;
// Audio/video configuration for the next A/V buffer.
::media::AudioDecoderConfig audio_config_;
::media::VideoDecoderConfig video_config_;
base::WeakPtrFactory<CodedFrameProviderHost> weak_factory_;
base::WeakPtr<CodedFrameProviderHost> weak_this_;
DISALLOW_COPY_AND_ASSIGN(CodedFrameProviderHost);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_CODED_FRAME_PROVIDER_HOST_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h"
#include "base/logging.h"
#include "chromecast/media/cma/base/decoder_buffer_base.h"
#include "chromecast/media/cma/ipc/media_message.h"
#include "chromecast/media/cma/ipc/media_message_type.h"
#include "chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.h"
#include "media/base/decrypt_config.h"
namespace chromecast {
namespace media {
namespace {
const size_t kMaxFrameSize = 4 * 1024 * 1024;
class DecoderBufferFromMsg : public DecoderBufferBase {
public:
explicit DecoderBufferFromMsg(scoped_ptr<MediaMessage> msg);
void Initialize();
// DecoderBufferBase implementation.
virtual base::TimeDelta timestamp() const OVERRIDE;
virtual const uint8* data() const OVERRIDE;
virtual uint8* writable_data() const OVERRIDE;
virtual int data_size() const OVERRIDE;
virtual const ::media::DecryptConfig* decrypt_config() const OVERRIDE;
virtual bool end_of_stream() const OVERRIDE;
private:
virtual ~DecoderBufferFromMsg();
// Indicates whether this is an end of stream frame.
bool is_eos_;
// Frame timestamp.
base::TimeDelta pts_;
// CENC parameters.
scoped_ptr< ::media::DecryptConfig> decrypt_config_;
// Size of the frame.
int data_size_;
// Keeps the message since frame data is not copied.
scoped_ptr<MediaMessage> msg_;
uint8* data_;
DISALLOW_COPY_AND_ASSIGN(DecoderBufferFromMsg);
};
DecoderBufferFromMsg::DecoderBufferFromMsg(
scoped_ptr<MediaMessage> msg)
: msg_(msg.Pass()),
is_eos_(true),
data_(NULL) {
CHECK(msg_);
}
DecoderBufferFromMsg::~DecoderBufferFromMsg() {
}
void DecoderBufferFromMsg::Initialize() {
CHECK_EQ(msg_->type(), FrameMediaMsg);
CHECK(msg_->ReadPod(&is_eos_));
if (is_eos_)
return;
int64 pts_internal = 0;
CHECK(msg_->ReadPod(&pts_internal));
pts_ = base::TimeDelta::FromInternalValue(pts_internal);
bool has_decrypt_config = false;
CHECK(msg_->ReadPod(&has_decrypt_config));
if (has_decrypt_config)
decrypt_config_.reset(DecryptConfigMarshaller::Read(msg_.get()).release());
CHECK(msg_->ReadPod(&data_size_));
CHECK_GT(data_size_, 0);
CHECK_LT(data_size_, kMaxFrameSize);
// Get a pointer to the frame data inside the message.
// Avoid copying the frame data here.
data_ = static_cast<uint8*>(msg_->GetWritableBuffer(data_size_));
CHECK(data_);
if (decrypt_config_) {
uint32 subsample_total_size = 0;
for (size_t k = 0; k < decrypt_config_->subsamples().size(); k++) {
subsample_total_size += decrypt_config_->subsamples()[k].clear_bytes;
subsample_total_size += decrypt_config_->subsamples()[k].cypher_bytes;
}
CHECK_EQ(subsample_total_size, data_size_);
}
}
base::TimeDelta DecoderBufferFromMsg::timestamp() const {
return pts_;
}
const uint8* DecoderBufferFromMsg::data() const {
CHECK(msg_->IsSerializedMsgAvailable());
return data_;
}
uint8* DecoderBufferFromMsg::writable_data() const {
CHECK(msg_->IsSerializedMsgAvailable());
return data_;
}
int DecoderBufferFromMsg::data_size() const {
return data_size_;
}
const ::media::DecryptConfig* DecoderBufferFromMsg::decrypt_config() const {
return decrypt_config_.get();
}
bool DecoderBufferFromMsg::end_of_stream() const {
return is_eos_;
}
} // namespace
// static
void DecoderBufferBaseMarshaller::Write(
const scoped_refptr<DecoderBufferBase>& buffer,
MediaMessage* msg) {
CHECK(msg->WritePod(buffer->end_of_stream()));
if (buffer->end_of_stream())
return;
CHECK(msg->WritePod(buffer->timestamp().ToInternalValue()));
bool has_decrypt_config =
(buffer->decrypt_config() != NULL &&
buffer->decrypt_config()->iv().size() > 0);
CHECK(msg->WritePod(has_decrypt_config));
if (has_decrypt_config)
DecryptConfigMarshaller::Write(*buffer->decrypt_config(), msg);
CHECK(msg->WritePod(buffer->data_size()));
CHECK(msg->WriteBuffer(buffer->data(), buffer->data_size()));
}
// static
scoped_refptr<DecoderBufferBase> DecoderBufferBaseMarshaller::Read(
scoped_ptr<MediaMessage> msg) {
scoped_refptr<DecoderBufferFromMsg> buffer(
new DecoderBufferFromMsg(msg.Pass()));
buffer->Initialize();
return buffer;
}
} // namespace media
} // namespace chromecast
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECODER_BUFFER_BASE_MARSHALLER_H_
#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECODER_BUFFER_BASE_MARSHALLER_H_
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
namespace chromecast {
namespace media {
class DecoderBufferBase;
class MediaMessage;
class DecoderBufferBaseMarshaller {
public:
// Writes the serialized structure of |config| into |msg|.
static void Write(
const scoped_refptr<DecoderBufferBase>& buffer, MediaMessage* msg);
// Returns a decoder buffer from its serialized structure.
static scoped_refptr<DecoderBufferBase> Read(scoped_ptr<MediaMessage> msg);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECODER_BUFFER_BASE_MARSHALLER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/cma/ipc_streamer/decrypt_config_marshaller.h"
#include "base/logging.h"
#include "chromecast/media/cma/ipc/media_message.h"
#include "media/base/decrypt_config.h"
namespace chromecast {
namespace media {
namespace {
const size_t kMaxKeyIdSize = 256;
const size_t kMaxIvSize = 256;
const size_t kMaxSubsampleCount = 1024;
}
// static
void DecryptConfigMarshaller::Write(
const ::media::DecryptConfig& config, MediaMessage* msg) {
CHECK_GT(config.key_id().size(), 0);
CHECK_GT(config.iv().size(), 0);
CHECK_GT(config.subsamples().size(), 0);
CHECK(msg->WritePod(config.key_id().size()));
CHECK(msg->WriteBuffer(config.key_id().data(), config.key_id().size()));
CHECK(msg->WritePod(config.iv().size()));
CHECK(msg->WriteBuffer(config.iv().data(), config.iv().size()));
CHECK(msg->WritePod(config.subsamples().size()));
for (size_t k = 0; k < config.subsamples().size(); k++) {
CHECK(msg->WritePod(config.subsamples()[k].clear_bytes));
CHECK(msg->WritePod(config.subsamples()[k].cypher_bytes));
}
}
// static
scoped_ptr< ::media::DecryptConfig> DecryptConfigMarshaller::Read(
MediaMessage* msg) {
size_t key_id_size = 0;
CHECK(msg->ReadPod(&key_id_size));
CHECK_GT(key_id_size, 0);
CHECK_LT(key_id_size, kMaxKeyIdSize);
scoped_ptr<char[]> key_id(new char[key_id_size]);
CHECK(msg->ReadBuffer(key_id.get(), key_id_size));
size_t iv_size = 0;
CHECK(msg->ReadPod(&iv_size));
CHECK_GT(iv_size, 0);
CHECK_LT(iv_size, kMaxIvSize);
scoped_ptr<char[]> iv(new char[iv_size]);
CHECK(msg->ReadBuffer(iv.get(), iv_size));
size_t subsample_count = 0;
CHECK(msg->ReadPod(&subsample_count));
CHECK_GT(subsample_count, 0);
CHECK_LT(subsample_count, kMaxSubsampleCount);
std::vector< ::media::SubsampleEntry> subsamples(subsample_count);
for (size_t k = 0; k < subsample_count; k++) {
subsamples[k].clear_bytes = 0;
subsamples[k].cypher_bytes = 0;
CHECK(msg->ReadPod(&subsamples[k].clear_bytes));
CHECK(msg->ReadPod(&subsamples[k].cypher_bytes));
}
return scoped_ptr< ::media::DecryptConfig>(
new ::media::DecryptConfig(
std::string(key_id.get(), key_id_size),
std::string(iv.get(), iv_size),
subsamples));
}
} // namespace media
} // namespace chromecast
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECRYPT_CONFIG_MARSHALLER_H_
#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECRYPT_CONFIG_MARSHALLER_H_
#include "base/memory/scoped_ptr.h"
namespace media {
class DecryptConfig;
}
namespace chromecast {
namespace media {
class MediaMessage;
class DecryptConfigMarshaller {
public:
// Writes the serialized structure of |config| into |msg|.
static void Write(
const ::media::DecryptConfig& config, MediaMessage* msg);
// Returns a DecryptConfig from its serialized structure.
static scoped_ptr< ::media::DecryptConfig> Read(MediaMessage* msg);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_DECRYPT_CONFIG_MARSHALLER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "chromecast/media/cma/ipc/media_message.h"
#include "media/base/video_decoder_config.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
namespace chromecast {
namespace media {
namespace {
const size_t kMaxExtraDataSize = 16 * 1024;
class SizeMarshaller {
public:
static void Write(const gfx::Size& size, MediaMessage* msg) {
CHECK(msg->WritePod(size.width()));
CHECK(msg->WritePod(size.height()));
}
static gfx::Size Read(MediaMessage* msg) {
int w, h;
CHECK(msg->ReadPod(&w));
CHECK(msg->ReadPod(&h));
return gfx::Size(w, h);
}
};
class RectMarshaller {
public:
static void Write(const gfx::Rect& rect, MediaMessage* msg) {
CHECK(msg->WritePod(rect.x()));
CHECK(msg->WritePod(rect.y()));
CHECK(msg->WritePod(rect.width()));
CHECK(msg->WritePod(rect.height()));
}
static gfx::Rect Read(MediaMessage* msg) {
int x, y, w, h;
CHECK(msg->ReadPod(&x));
CHECK(msg->ReadPod(&y));
CHECK(msg->ReadPod(&w));
CHECK(msg->ReadPod(&h));
return gfx::Rect(x, y, w, h);
}
};
} // namespace
// static
void VideoDecoderConfigMarshaller::Write(
const ::media::VideoDecoderConfig& config, MediaMessage* msg) {
CHECK(msg->WritePod(config.codec()));
CHECK(msg->WritePod(config.profile()));
CHECK(msg->WritePod(config.format()));
SizeMarshaller::Write(config.coded_size(), msg);
RectMarshaller::Write(config.visible_rect(), msg);
SizeMarshaller::Write(config.natural_size(), msg);
CHECK(msg->WritePod(config.is_encrypted()));
CHECK(msg->WritePod(config.extra_data_size()));
if (config.extra_data_size() > 0)
CHECK(msg->WriteBuffer(config.extra_data(), config.extra_data_size()));
}
// static
::media::VideoDecoderConfig VideoDecoderConfigMarshaller::Read(
MediaMessage* msg) {
::media::VideoCodec codec;
::media::VideoCodecProfile profile;
::media::VideoFrame::Format format;
gfx::Size coded_size;
gfx::Rect visible_rect;
gfx::Size natural_size;
bool is_encrypted;
size_t extra_data_size;
scoped_ptr<uint8[]> extra_data;
CHECK(msg->ReadPod(&codec));
CHECK(msg->ReadPod(&profile));
CHECK(msg->ReadPod(&format));
coded_size = SizeMarshaller::Read(msg);
visible_rect = RectMarshaller::Read(msg);
natural_size = SizeMarshaller::Read(msg);
CHECK(msg->ReadPod(&is_encrypted));
CHECK(msg->ReadPod(&extra_data_size));
CHECK_GE(codec, ::media::kUnknownVideoCodec);
CHECK_LE(codec, ::media::kVideoCodecMax);
CHECK_GE(profile, ::media::VIDEO_CODEC_PROFILE_UNKNOWN);
CHECK_LE(profile, ::media::VIDEO_CODEC_PROFILE_MAX);
CHECK_GE(format, ::media::VideoFrame::UNKNOWN);
CHECK_LE(format, ::media::VideoFrame::FORMAT_MAX);
CHECK_LT(extra_data_size, kMaxExtraDataSize);
if (extra_data_size > 0) {
extra_data.reset(new uint8[extra_data_size]);
CHECK(msg->ReadBuffer(extra_data.get(), extra_data_size));
}
return ::media::VideoDecoderConfig(
codec, profile, format,
coded_size, visible_rect, natural_size,
extra_data.get(), extra_data_size,
is_encrypted);
}
} // namespace media
} // namespace chromecast
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_CMA_IPC_STREAMER_VIDEO_DECODER_CONFIG_MARSHALLER_H_
#define CHROMECAST_MEDIA_CMA_IPC_STREAMER_VIDEO_DECODER_CONFIG_MARSHALLER_H_
#include "media/base/video_decoder_config.h"
namespace chromecast {
namespace media {
class MediaMessage;
class VideoDecoderConfigMarshaller {
public:
// Writes the serialized structure of |config| into |msg|.
static void Write(
const ::media::VideoDecoderConfig& config, MediaMessage* msg);
// Returns a VideoDecoderConfig from its serialized structure.
static ::media::VideoDecoderConfig Read(MediaMessage* msg);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_IPC_STREAMER_VIDEO_DECODER_CONFIG_MARSHALLER_H_
......@@ -49,6 +49,29 @@
'cma/ipc/media_message_fifo.h',
],
},
{
'target_name': 'cma_ipc_streamer',
'type': '<(component)',
'dependencies': [
'../../base/base.gyp:base',
'../../media/media.gyp:media',
'cma_base',
],
'sources': [
'cma/ipc_streamer/audio_decoder_config_marshaller.cc',
'cma/ipc_streamer/audio_decoder_config_marshaller.h',
'cma/ipc_streamer/av_streamer_proxy.cc',
'cma/ipc_streamer/av_streamer_proxy.h',
'cma/ipc_streamer/coded_frame_provider_host.cc',
'cma/ipc_streamer/coded_frame_provider_host.h',
'cma/ipc_streamer/decoder_buffer_base_marshaller.cc',
'cma/ipc_streamer/decoder_buffer_base_marshaller.h',
'cma/ipc_streamer/decrypt_config_marshaller.cc',
'cma/ipc_streamer/decrypt_config_marshaller.h',
'cma/ipc_streamer/video_decoder_config_marshaller.cc',
'cma/ipc_streamer/video_decoder_config_marshaller.h',
],
},
{
'target_name': 'cma_filters',
'type': '<(component)',
......@@ -69,6 +92,7 @@
'cma_base',
'cma_filters',
'cma_ipc',
'cma_ipc_streamer',
],
},
{
......@@ -97,6 +121,7 @@
'cma/filters/demuxer_stream_adapter_unittest.cc',
'cma/ipc/media_message_fifo_unittest.cc',
'cma/ipc/media_message_unittest.cc',
'cma/ipc_streamer/av_streamer_unittest.cc',
],
},
],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment