Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(716)

Side by Side Diff: chromecast/media/cma/pipeline/av_pipeline_impl.cc

Issue 741863002: Chromecast: adds a media pipeline feeding data to CMA device backends. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@cma-cdm
Patch Set: address nits, merge Pause/Flush Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/pipeline/av_pipeline_impl.h"
6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "chromecast/media/base/decrypt_context.h"
12 #include "chromecast/media/cdm/browser_cdm_cast.h"
13 #include "chromecast/media/cma/backend/media_clock_device.h"
14 #include "chromecast/media/cma/backend/media_component_device.h"
15 #include "chromecast/media/cma/base/buffering_frame_provider.h"
16 #include "chromecast/media/cma/base/buffering_state.h"
17 #include "chromecast/media/cma/base/cma_logging.h"
18 #include "chromecast/media/cma/base/coded_frame_provider.h"
19 #include "chromecast/media/cma/base/decoder_buffer_base.h"
20 #include "chromecast/media/cma/pipeline/decrypt_util.h"
21 #include "media/base/audio_decoder_config.h"
22 #include "media/base/decrypt_config.h"
23
24 namespace chromecast {
25 namespace media {
26
27 namespace {
28
29 const int kNoCallbackId = -1;
30
31 } // namespace
32
33 AvPipelineImpl::AvPipelineImpl(
34 MediaComponentDevice* media_component_device,
35 const UpdateConfigCB& update_config_cb)
36 : update_config_cb_(update_config_cb),
37 media_component_device_(media_component_device),
38 state_(kUninitialized),
39 buffered_time_(::media::kNoTimestamp()),
40 playable_buffered_time_(::media::kNoTimestamp()),
41 enable_feeding_(false),
42 pending_read_(false),
43 pending_push_(false),
44 enable_time_update_(false),
45 pending_time_update_task_(false),
46 media_keys_(NULL),
47 media_keys_callback_id_(kNoCallbackId),
48 weak_factory_(this) {
49 DCHECK(media_component_device);
50 weak_this_ = weak_factory_.GetWeakPtr();
51 thread_checker_.DetachFromThread();
52 }
53
54 AvPipelineImpl::~AvPipelineImpl() {
55 // If there are weak pointers in the wild, they must be invalidated
56 // on the right thread.
57 DCHECK(thread_checker_.CalledOnValidThread());
58 media_component_device_->SetClient(MediaComponentDevice::Client());
59
60 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId)
61 media_keys_->UnregisterPlayer(media_keys_callback_id_);
62 }
63
64 void AvPipelineImpl::TransitionToState(State state) {
65 DCHECK(thread_checker_.CalledOnValidThread());
66 state_ = state;
67 }
68
69 void AvPipelineImpl::SetCodedFrameProvider(
70 scoped_ptr<CodedFrameProvider> frame_provider,
71 size_t max_buffer_size,
72 size_t max_frame_size) {
73 DCHECK_EQ(state_, kUninitialized);
74 DCHECK(frame_provider);
75
76 // Wrap the incoming frame provider to add some buffering capabilities.
77 frame_provider_.reset(
78 new BufferingFrameProvider(
79 frame_provider.Pass(),
80 max_buffer_size,
81 max_frame_size,
82 base::Bind(&AvPipelineImpl::OnFrameBuffered, weak_this_)));
83 }
84
85 void AvPipelineImpl::SetClient(const AvPipelineClient& client) {
86 DCHECK(thread_checker_.CalledOnValidThread());
87 DCHECK_EQ(state_, kUninitialized);
88 client_ = client;
89 }
90
91 bool AvPipelineImpl::Initialize() {
92 DCHECK(thread_checker_.CalledOnValidThread());
93 DCHECK_EQ(state_, kUninitialized);
94
95 MediaComponentDevice::Client client;
96 client.eos_cb = base::Bind(&AvPipelineImpl::OnEos, weak_this_);
97 media_component_device_->SetClient(client);
98 if (!media_component_device_->SetState(MediaComponentDevice::kStateIdle))
99 return false;
100
101 return true;
102 }
103
104 bool AvPipelineImpl::StartPlayingFrom(
105 base::TimeDelta time,
106 const scoped_refptr<BufferingState>& buffering_state) {
107 DCHECK(thread_checker_.CalledOnValidThread());
108 DCHECK_EQ(state_, kFlushed);
109
110 // Media time where rendering should start
111 // and switch to a state where the audio device accepts incoming buffers.
112 if (!media_component_device_->SetStartPts(time) ||
113 !media_component_device_->SetState(MediaComponentDevice::kStatePaused)) {
114 return false;
115 }
116
117 // Buffering related initialization.
118 DCHECK(frame_provider_);
119 buffering_state_ = buffering_state;
120 if (buffering_state_.get())
121 buffering_state_->SetMediaTime(time);
122
123 if (!media_component_device_->SetState(MediaComponentDevice::kStateRunning))
124 return false;
125
126 // Start feeding the pipeline.
127 enable_feeding_ = true;
128 base::MessageLoopProxy::current()->PostTask(
129 FROM_HERE,
130 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
131
132 return true;
133 }
134
135 void AvPipelineImpl::Flush(const base::Closure& done_cb) {
136 DCHECK(thread_checker_.CalledOnValidThread());
137 DCHECK_EQ(state_, kFlushing);
138 DCHECK_EQ(
139 media_component_device_->GetState(), MediaComponentDevice::kStateRunning);
140 // Note: returning to idle state aborts any pending frame push.
141 media_component_device_->SetState(MediaComponentDevice::kStateIdle);
142 pending_push_ = false;
143
144 // Break the feeding loop.
145 enable_feeding_ = false;
146
147 // Remove any pending buffer.
148 pending_buffer_ = scoped_refptr<DecoderBufferBase>();
149
150 // Finally, remove any frames left in the frame provider.
151 pending_read_ = false;
152 buffered_time_ = ::media::kNoTimestamp();
153 playable_buffered_time_ = ::media::kNoTimestamp();
154 non_playable_frames_.clear();
155 frame_provider_->Flush(done_cb);
156 }
157
158 void AvPipelineImpl::Stop() {
159 DCHECK(thread_checker_.CalledOnValidThread());
160
161 // Stop can be called from any state.
162 if (state_ == kUninitialized || state_ == kStopped)
163 return;
164
165 // Stop feeding the pipeline.
166 enable_feeding_ = false;
167
168 // Release hardware resources on Stop.
169 if (media_component_device_->GetState() ==
170 MediaComponentDevice::kStatePaused ||
171 media_component_device_->GetState() ==
172 MediaComponentDevice::kStateRunning) {
173 media_component_device_->SetState(MediaComponentDevice::kStateIdle);
174 }
175 if (media_component_device_->GetState() == MediaComponentDevice::kStateIdle) {
176 media_component_device_->SetState(
177 MediaComponentDevice::kStateUninitialized);
178 }
179 }
180
181 void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) {
182 DCHECK(thread_checker_.CalledOnValidThread());
183 DCHECK(media_keys);
184
185 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId)
186 media_keys_->UnregisterPlayer(media_keys_callback_id_);
187
188 media_keys_ = media_keys;
189 media_keys_callback_id_ = media_keys_->RegisterPlayer(
190 base::Bind(&AvPipelineImpl::OnCdmStateChanged, weak_this_),
191 base::Bind(&AvPipelineImpl::OnCdmDestroyed, weak_this_));
192 }
193
194 void AvPipelineImpl::OnEos() {
195 DCHECK(thread_checker_.CalledOnValidThread());
196 CMALOG(kLogControl) << __FUNCTION__;
197 if (state_ != kPlaying)
198 return;
199
200 if (!client_.eos_cb.is_null())
201 client_.eos_cb.Run();
202 }
203
204 void AvPipelineImpl::FetchBufferIfNeeded() {
205 DCHECK(thread_checker_.CalledOnValidThread());
206 if (!enable_feeding_)
207 return;
208
209 if (pending_read_ || pending_buffer_.get())
210 return;
211
212 pending_read_ = true;
213 frame_provider_->Read(
214 base::Bind(&AvPipelineImpl::OnNewFrame, weak_this_));
215 }
216
217 void AvPipelineImpl::OnNewFrame(
218 const scoped_refptr<DecoderBufferBase>& buffer,
219 const ::media::AudioDecoderConfig& audio_config,
220 const ::media::VideoDecoderConfig& video_config) {
221 DCHECK(thread_checker_.CalledOnValidThread());
222 pending_read_ = false;
223
224 if (audio_config.IsValidConfig() || video_config.IsValidConfig())
225 update_config_cb_.Run(audio_config, video_config);
226
227 pending_buffer_ = buffer;
228 ProcessPendingBuffer();
229
230 base::MessageLoopProxy::current()->PostTask(
231 FROM_HERE,
232 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
233 }
234
235 void AvPipelineImpl::ProcessPendingBuffer() {
236 if (!enable_feeding_)
237 return;
238
239 // Initiate a read if there isn't already one.
240 if (!pending_buffer_.get() && !pending_read_) {
241 base::MessageLoopProxy::current()->PostTask(
242 FROM_HERE,
243 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
244 return;
245 }
246
247 if (!pending_buffer_.get() || pending_push_)
248 return;
249
250 // Break the feeding loop when the end of stream is reached.
251 if (pending_buffer_->end_of_stream()) {
252 CMALOG(kLogControl) << __FUNCTION__ << ": EOS reached, stopped feeding";
253 enable_feeding_ = false;
254 }
255
256 scoped_refptr<DecryptContext> decrypt_context;
257 if (!pending_buffer_->end_of_stream() &&
258 pending_buffer_->decrypt_config()) {
259 // Verify that CDM has the key ID.
260 // Should not send the frame if the key ID is not available yet.
261 std::string key_id(pending_buffer_->decrypt_config()->key_id());
262 if (!media_keys_) {
263 CMALOG(kLogControl) << "No CDM for frame: pts="
264 << pending_buffer_->timestamp().InMilliseconds();
265 return;
266 }
267 decrypt_context = media_keys_->GetDecryptContext(key_id);
268 if (!decrypt_context.get()) {
269 CMALOG(kLogControl) << "frame(pts="
270 << pending_buffer_->timestamp().InMilliseconds()
271 << "): waiting for key id "
272 << base::HexEncode(&key_id[0], key_id.size());
273 return;
274 }
275
276 // If we do have the clear key, decrypt the pending buffer
277 // and reset the decryption context (not needed anymore).
278 crypto::SymmetricKey* key = decrypt_context->GetKey();
279 if (key != NULL) {
280 pending_buffer_ = DecryptDecoderBuffer(pending_buffer_, key);
281 decrypt_context = scoped_refptr<DecryptContext>();
282 }
283 }
284
285 MediaComponentDevice::FrameStatus status = media_component_device_->PushFrame(
286 decrypt_context,
287 pending_buffer_,
288 base::Bind(&AvPipelineImpl::OnFramePushed, weak_this_));
289 pending_buffer_ = scoped_refptr<DecoderBufferBase>();
290
291 pending_push_ = (status == MediaComponentDevice::kFramePending);
292 if (!pending_push_)
293 OnFramePushed(status);
294 }
295
296 void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) {
297 DCHECK(thread_checker_.CalledOnValidThread());
298 pending_push_ = false;
299 if (status == MediaComponentDevice::kFrameFailed) {
300 LOG(WARNING) << "AvPipelineImpl: PushFrame failed";
301 enable_feeding_ = false;
302 state_ = kError;
303 return;
304 }
305 base::MessageLoopProxy::current()->PostTask(
306 FROM_HERE,
307 base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_));
308 }
309
310 void AvPipelineImpl::OnCdmStateChanged() {
311 DCHECK(thread_checker_.CalledOnValidThread());
312
313 // Update the buffering state if needed.
314 if (buffering_state_.get())
315 UpdatePlayableFrames();
316
317 // Process the pending buffer in case the CDM now has the frame key id.
318 ProcessPendingBuffer();
319 }
320
321 void AvPipelineImpl::OnCdmDestroyed() {
322 DCHECK(thread_checker_.CalledOnValidThread());
323 media_keys_ = NULL;
324 }
325
326 void AvPipelineImpl::OnFrameBuffered(
327 const scoped_refptr<DecoderBufferBase>& buffer,
328 bool is_at_max_capacity) {
329 DCHECK(thread_checker_.CalledOnValidThread());
330
331 if (!buffering_state_.get())
332 return;
333
334 if (!buffer->end_of_stream() &&
335 (buffered_time_ == ::media::kNoTimestamp() ||
336 buffered_time_ < buffer->timestamp())) {
337 buffered_time_ = buffer->timestamp();
338 }
339
340 if (is_at_max_capacity)
341 buffering_state_->NotifyMaxCapacity(buffered_time_);
342
343 // No need to update the list of playable frames,
344 // if we are already blocking on a frame.
345 bool update_playable_frames = non_playable_frames_.empty();
346 non_playable_frames_.push_back(buffer);
347 if (update_playable_frames)
348 UpdatePlayableFrames();
349 }
350
351 void AvPipelineImpl::UpdatePlayableFrames() {
352 while (!non_playable_frames_.empty()) {
353 const scoped_refptr<DecoderBufferBase>& non_playable_frame =
354 non_playable_frames_.front();
355
356 if (non_playable_frame->end_of_stream()) {
357 buffering_state_->NotifyEos();
358 } else {
359 const ::media::DecryptConfig* decrypt_config =
360 non_playable_frame->decrypt_config();
361 if (decrypt_config &&
362 !(media_keys_ &&
363 media_keys_->GetDecryptContext(decrypt_config->key_id()).get())) {
364 // The frame is still not playable. All the following are thus not
365 // playable.
366 break;
367 }
368
369 if (playable_buffered_time_ == ::media::kNoTimestamp() ||
370 playable_buffered_time_ < non_playable_frame->timestamp()) {
371 playable_buffered_time_ = non_playable_frame->timestamp();
372 buffering_state_->SetBufferedTime(playable_buffered_time_);
373 }
374 }
375
376 // The frame is playable: remove it from the list of non playable frames.
377 non_playable_frames_.pop_front();
378 }
379 }
380
381 } // namespace media
382 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/media/cma/pipeline/av_pipeline_impl.h ('k') | chromecast/media/cma/pipeline/decrypt_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698