1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "chromecast/media/cma/base/coded_frame_provider.h"
11#include "chromecast/media/cma/base/decoder_buffer_base.h"
12#include "chromecast/media/cma/ipc/media_memory_chunk.h"
13#include "chromecast/media/cma/ipc/media_message.h"
14#include "chromecast/media/cma/ipc/media_message_fifo.h"
15#include "chromecast/media/cma/ipc/media_message_type.h"
16#include "chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h"
17#include "chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.h"
18#include "chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h"
19
20namespace chromecast {
21namespace media {
22
23AvStreamerProxy::AvStreamerProxy()
24    : is_running_(false),
25      pending_read_(false),
26      pending_av_data_(false),
27      weak_factory_(this),
28      weak_this_(weak_factory_.GetWeakPtr()) {
29  thread_checker_.DetachFromThread();
30}
31
32AvStreamerProxy::~AvStreamerProxy() {
33  DCHECK(thread_checker_.CalledOnValidThread());
34}
35
36void AvStreamerProxy::SetCodedFrameProvider(
37    scoped_ptr<CodedFrameProvider> frame_provider) {
38  DCHECK(thread_checker_.CalledOnValidThread());
39  DCHECK(!frame_provider_);
40  frame_provider_.reset(frame_provider.release());
41}
42
43void AvStreamerProxy::SetMediaMessageFifo(
44    scoped_ptr<MediaMessageFifo> fifo) {
45  DCHECK(thread_checker_.CalledOnValidThread());
46  DCHECK(!fifo_);
47  fifo_.reset(fifo.release());
48}
49
50void AvStreamerProxy::Start() {
51  DCHECK(!is_running_);
52
53  is_running_ = true;
54  RequestBufferIfNeeded();
55}
56
57void AvStreamerProxy::StopAndFlush(const base::Closure& done_cb) {
58  is_running_ = false;
59
60  pending_av_data_ = false;
61  pending_audio_config_ = ::media::AudioDecoderConfig();
62  pending_video_config_ = ::media::VideoDecoderConfig();
63  pending_buffer_ = scoped_refptr<DecoderBufferBase>();
64
65  pending_read_ = false;
66  frame_provider_->Flush(done_cb);
67}
68
69void AvStreamerProxy::OnFifoReadEvent() {
70  DCHECK(thread_checker_.CalledOnValidThread());
71
72  // Some enough space might have been released
73  // to accommodate the pending data.
74  if (pending_av_data_)
75    ProcessPendingData();
76}
77
78void AvStreamerProxy::RequestBufferIfNeeded() {
79  DCHECK(thread_checker_.CalledOnValidThread());
80
81  if (!is_running_ || pending_read_ || pending_av_data_)
82    return;
83
84  // |frame_provider_| is assumed to run on the same message loop.
85  // Add a BindToCurrentLoop if that's not the case in the future.
86  pending_read_ = true;
87  frame_provider_->Read(base::Bind(&AvStreamerProxy::OnNewBuffer, weak_this_));
88}
89
90void AvStreamerProxy::OnNewBuffer(
91    const scoped_refptr<DecoderBufferBase>& buffer,
92    const ::media::AudioDecoderConfig& audio_config,
93    const ::media::VideoDecoderConfig& video_config) {
94  DCHECK(thread_checker_.CalledOnValidThread());
95
96  pending_read_ = false;
97
98  if (buffer->end_of_stream())
99    is_running_ = false;
100
101  DCHECK(!pending_av_data_);
102  pending_av_data_ = true;
103
104  pending_buffer_ = buffer;
105  pending_audio_config_ = audio_config;
106  pending_video_config_ = video_config;
107
108  ProcessPendingData();
109}
110
111void AvStreamerProxy::ProcessPendingData() {
112  if (pending_audio_config_.IsValidConfig()) {
113    if (!SendAudioDecoderConfig(pending_audio_config_))
114      return;
115    pending_audio_config_ = ::media::AudioDecoderConfig();
116  }
117
118  if (pending_video_config_.IsValidConfig()) {
119    if (!SendVideoDecoderConfig(pending_video_config_))
120      return;
121    pending_video_config_ = ::media::VideoDecoderConfig();
122  }
123
124  if (pending_buffer_.get()) {
125    if (!SendBuffer(pending_buffer_))
126      return;
127    pending_buffer_ = scoped_refptr<DecoderBufferBase>();
128  }
129
130  pending_av_data_ = false;
131  base::MessageLoopProxy::current()->PostTask(
132      FROM_HERE,
133      base::Bind(&AvStreamerProxy::RequestBufferIfNeeded, weak_this_));
134}
135
136bool AvStreamerProxy::SendAudioDecoderConfig(
137    const ::media::AudioDecoderConfig& config) {
138  // Create a dummy message to calculate first the message size.
139  scoped_ptr<MediaMessage> dummy_msg(
140      MediaMessage::CreateDummyMessage(AudioConfigMediaMsg));
141  AudioDecoderConfigMarshaller::Write(config, dummy_msg.get());
142
143  // Create the real message and write the actual content.
144  scoped_ptr<MediaMessage> msg(
145      MediaMessage::CreateMessage(
146          AudioConfigMediaMsg,
147          base::Bind(&MediaMessageFifo::ReserveMemory,
148                     base::Unretained(fifo_.get())),
149          dummy_msg->content_size()));
150  if (!msg)
151    return false;
152
153  AudioDecoderConfigMarshaller::Write(config, msg.get());
154  return true;
155}
156
157bool AvStreamerProxy::SendVideoDecoderConfig(
158    const ::media::VideoDecoderConfig& config) {
159  // Create a dummy message to calculate first the message size.
160  scoped_ptr<MediaMessage> dummy_msg(
161      MediaMessage::CreateDummyMessage(VideoConfigMediaMsg));
162  VideoDecoderConfigMarshaller::Write(config, dummy_msg.get());
163
164  // Create the real message and write the actual content.
165  scoped_ptr<MediaMessage> msg(
166      MediaMessage::CreateMessage(
167          VideoConfigMediaMsg,
168          base::Bind(&MediaMessageFifo::ReserveMemory,
169                     base::Unretained(fifo_.get())),
170          dummy_msg->content_size()));
171  if (!msg)
172    return false;
173
174  VideoDecoderConfigMarshaller::Write(config, msg.get());
175  return true;
176}
177
178bool AvStreamerProxy::SendBuffer(
179    const scoped_refptr<DecoderBufferBase>& buffer) {
180  // Create a dummy message to calculate first the message size.
181  scoped_ptr<MediaMessage> dummy_msg(
182      MediaMessage::CreateDummyMessage(FrameMediaMsg));
183  DecoderBufferBaseMarshaller::Write(buffer, dummy_msg.get());
184
185  // Create the real message and write the actual content.
186  scoped_ptr<MediaMessage> msg(
187      MediaMessage::CreateMessage(
188          FrameMediaMsg,
189          base::Bind(&MediaMessageFifo::ReserveMemory,
190                     base::Unretained(fifo_.get())),
191          dummy_msg->content_size()));
192  if (!msg)
193    return false;
194
195  DecoderBufferBaseMarshaller::Write(buffer, msg.get());
196  return true;
197}
198
199}  // namespace media
200}  // namespace chromecast
201