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

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: 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) {
damienv1 2014/11/20 17:08:26 nit: remove curly brace
gunsch 2014/11/20 19:50:27 Done.
61 media_keys_->UnregisterPlayer(media_keys_callback_id_);
62 }
63 }
64
65 void AvPipelineImpl::TransitionToState(State state) {
66 DCHECK(thread_checker_.CalledOnValidThread());
67 state_ = state;
68 }
69
70 void AvPipelineImpl::SetCodedFrameProvider(
71 scoped_ptr<CodedFrameProvider> frame_provider,
72 size_t max_buffer_size,
73 size_t max_frame_size) {
74 DCHECK_EQ(state_, kUninitialized);
75 DCHECK(frame_provider);
76
77 // Wrap the incoming frame provider to add some buffering capabilities.
78 frame_provider_.reset(
79 new BufferingFrameProvider(
80 frame_provider.Pass(),
81 max_buffer_size,
82 max_frame_size,
83 base::Bind(&AvPipelineImpl::OnFrameBuffered, weak_this_)));
84 }
85
86 void AvPipelineImpl::SetClient(const AvPipelineClient& client) {
87 DCHECK(thread_checker_.CalledOnValidThread());
88 DCHECK_EQ(state_, kUninitialized);
89 client_ = client;
90 }
91
92 bool AvPipelineImpl::Initialize() {
93 DCHECK(thread_checker_.CalledOnValidThread());
94 DCHECK_EQ(state_, kUninitialized);
95
96 MediaComponentDevice::Client client;
97 client.eos_cb = base::Bind(&AvPipelineImpl::OnEos, weak_this_);
98 media_component_device_->SetClient(client);
99 if (!media_component_device_->SetState(MediaComponentDevice::kStateIdle))
100 return false;
101
102 return true;
103 }
104
105 bool AvPipelineImpl::StartPlayingFrom(
106 base::TimeDelta time,
107 const scoped_refptr<BufferingState>& buffering_state) {
108 DCHECK(thread_checker_.CalledOnValidThread());
109 DCHECK_EQ(state_, kFlushed);
110
111 // Media time where rendering should start
112 // and switch to a state where the audio device accepts incoming buffers.
113 if (!media_component_device_->SetStartPts(time) ||
114 !media_component_device_->SetState(MediaComponentDevice::kStatePaused)) {
115 return false;
116 }
117
118 // Buffering related initialization.
119 DCHECK(frame_provider_);
120 buffering_state_ = buffering_state;
121 if (buffering_state_.get())
122 buffering_state_->SetMediaTime(time);
123
124 // Start feeding the pipeline.
125 enable_feeding_ = true;
126 base::MessageLoopProxy::current()->PostTask(
127 FROM_HERE,
128 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
129
130 if (!media_component_device_->SetState(MediaComponentDevice::kStateRunning))
131 return false;
damienv1 2014/11/20 17:08:26 StartPlayingFrom used to be 2 different actions (P
gunsch 2014/11/20 19:50:27 Strictly speaking, this code is in order from when
132
133 return true;
134 }
135
136 bool AvPipelineImpl::Pause() {
137 DCHECK(thread_checker_.CalledOnValidThread());
138 DCHECK_EQ(state_, kPlaying);
139
140 DCHECK_EQ(
141 media_component_device_->GetState(), MediaComponentDevice::kStateRunning);
damienv1 2014/11/20 17:08:26 Can you add a comment for this block of code to me
gunsch 2014/11/20 19:50:27 Done.
142 media_component_device_->SetState(MediaComponentDevice::kStateIdle);
143 pending_push_ = false;
144
145 // Break the feeding loop.
146 enable_feeding_ = false;
147
148 return true;
149 }
150
151 void AvPipelineImpl::Flush(const base::Closure& done_cb) {
damienv1 2014/11/20 17:33:39 Could you merge Pause and Flush (always used toget
gunsch 2014/11/20 19:50:27 Done.
152 DCHECK(thread_checker_.CalledOnValidThread());
153 DCHECK_EQ(state_, kFlushing);
154
155 // Remove any pending buffer.
156 pending_buffer_ = scoped_refptr<DecoderBufferBase>();
157
158 // Finally, remove any frames left in the frame provider.
159 pending_read_ = false;
160 buffered_time_ = ::media::kNoTimestamp();
161 playable_buffered_time_ = ::media::kNoTimestamp();
162 non_playable_frames_.clear();
163 frame_provider_->Flush(done_cb);
164 }
165
166 void AvPipelineImpl::Stop() {
167 DCHECK(thread_checker_.CalledOnValidThread());
168
169 // Stop can be called from any state.
170 if (state_ == kUninitialized || state_ == kStopped)
171 return;
172
173 // Stop feeding the pipeline.
174 enable_feeding_ = false;
175
176 // Release hardware resources on Stop.
177 if (media_component_device_->GetState() ==
178 MediaComponentDevice::kStatePaused ||
179 media_component_device_->GetState() ==
180 MediaComponentDevice::kStateRunning) {
181 media_component_device_->SetState(MediaComponentDevice::kStateIdle);
182 }
183 if (media_component_device_->GetState() == MediaComponentDevice::kStateIdle) {
184 media_component_device_->SetState(
185 MediaComponentDevice::kStateUninitialized);
186 }
187 }
188
189 void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) {
190 DCHECK(thread_checker_.CalledOnValidThread());
191 DCHECK(media_keys);
192
193 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) {
damienv1 2014/11/20 17:08:26 nit: remove curly braces.
gunsch 2014/11/20 19:50:27 Done.
194 media_keys_->UnregisterPlayer(media_keys_callback_id_);
195 }
196
197 media_keys_ = media_keys;
198 media_keys_callback_id_ = media_keys_->RegisterPlayer(
199 base::Bind(&AvPipelineImpl::OnCdmStateChanged, weak_this_),
200 base::Bind(&AvPipelineImpl::OnCdmDestroyed, weak_this_));
201 }
202
203 void AvPipelineImpl::OnEos() {
204 DCHECK(thread_checker_.CalledOnValidThread());
205 CMALOG(kLogControl) << __FUNCTION__;
206 if (state_ != kPlaying)
207 return;
208
209 if (!client_.eos_cb.is_null())
210 client_.eos_cb.Run();
211 }
212
213 void AvPipelineImpl::FetchBufferIfNeeded() {
214 DCHECK(thread_checker_.CalledOnValidThread());
215 if (!enable_feeding_)
216 return;
217
218 if (pending_read_ || pending_buffer_.get())
219 return;
220
221 pending_read_ = true;
222 frame_provider_->Read(
223 base::Bind(&AvPipelineImpl::OnNewFrame, weak_this_));
224 }
225
226 void AvPipelineImpl::OnNewFrame(
227 const scoped_refptr<DecoderBufferBase>& buffer,
228 const ::media::AudioDecoderConfig& audio_config,
229 const ::media::VideoDecoderConfig& video_config) {
230 DCHECK(thread_checker_.CalledOnValidThread());
231 pending_read_ = false;
232
233 if (audio_config.IsValidConfig() || video_config.IsValidConfig()) {
damienv1 2014/11/20 17:08:26 nit: remove curly braces.
gunsch 2014/11/20 19:50:27 Done.
234 update_config_cb_.Run(audio_config, video_config);
235 }
236
237 pending_buffer_ = buffer;
238 ProcessPendingBuffer();
239
240 base::MessageLoopProxy::current()->PostTask(
241 FROM_HERE,
242 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
243 }
244
245 void AvPipelineImpl::ProcessPendingBuffer() {
246 if (!enable_feeding_)
247 return;
248
249 // Initiate a read if there isn't already one.
250 if (!pending_buffer_.get() && !pending_read_) {
251 base::MessageLoopProxy::current()->PostTask(
252 FROM_HERE,
253 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_));
254 return;
255 }
256
257 if (!pending_buffer_.get() || pending_push_)
258 return;
259
260 // Break the feeding loop when the end of stream is reached.
261 if (pending_buffer_->end_of_stream()) {
262 CMALOG(kLogControl) << __FUNCTION__ << ": EOS reached, stopped feeding";
263 enable_feeding_ = false;
264 }
265
266 scoped_refptr<DecryptContext> decrypt_context;
267 if (!pending_buffer_->end_of_stream() &&
268 pending_buffer_->decrypt_config()) {
269 // Verify that CDM has the key ID.
270 // Should not send the frame if the key ID is not available yet.
271 std::string key_id(pending_buffer_->decrypt_config()->key_id());
272 if (!media_keys_) {
273 CMALOG(kLogControl) << "No CDM for frame: pts="
274 << pending_buffer_->timestamp().InMilliseconds();
275 return;
276 }
277 decrypt_context = media_keys_->GetDecryptContext(key_id);
278 if (!decrypt_context.get()) {
279 CMALOG(kLogControl) << "frame(pts="
280 << pending_buffer_->timestamp().InMilliseconds()
281 << "): waiting for key id "
282 << base::HexEncode(&key_id[0], key_id.size());
283 return;
284 }
285
286 // If we do have the clear key, decrypt the pending buffer
287 // and reset the decryption context (not needed anymore).
288 crypto::SymmetricKey* key = decrypt_context->GetKey();
289 if (key != NULL) {
290 pending_buffer_ = DecryptDecoderBuffer(pending_buffer_, key);
291 decrypt_context = scoped_refptr<DecryptContext>();
292 }
293 }
294
295 MediaComponentDevice::FrameStatus status = media_component_device_->PushFrame(
296 decrypt_context,
297 pending_buffer_,
298 base::Bind(&AvPipelineImpl::OnFramePushed, weak_this_));
299 pending_buffer_ = scoped_refptr<DecoderBufferBase>();
300
301 pending_push_ = (status == MediaComponentDevice::kFramePending);
302 if (!pending_push_)
303 OnFramePushed(status);
304 }
305
306 void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) {
307 DCHECK(thread_checker_.CalledOnValidThread());
308 pending_push_ = false;
309 if (status == MediaComponentDevice::kFrameFailed) {
310 LOG(WARNING) << "AvPipelineImpl: PushFrame failed";
311 enable_feeding_ = false;
312 state_ = kError;
313 return;
314 }
315 base::MessageLoopProxy::current()->PostTask(
316 FROM_HERE,
317 base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_));
318 }
319
320 void AvPipelineImpl::OnCdmStateChanged() {
321 DCHECK(thread_checker_.CalledOnValidThread());
322
323 // Update the buffering state if needed.
324 if (buffering_state_.get())
325 UpdatePlayableFrames();
326
327 // Process the pending buffer in case the CDM now has the frame key id.
328 ProcessPendingBuffer();
329 }
330
331 void AvPipelineImpl::OnCdmDestroyed() {
332 DCHECK(thread_checker_.CalledOnValidThread());
333 media_keys_ = NULL;
334 }
335
336 void AvPipelineImpl::OnFrameBuffered(
337 const scoped_refptr<DecoderBufferBase>& buffer,
338 bool is_at_max_capacity) {
339 DCHECK(thread_checker_.CalledOnValidThread());
340
341 if (!buffering_state_.get())
342 return;
343
344 if (!buffer->end_of_stream() &&
345 (buffered_time_ == ::media::kNoTimestamp() ||
346 buffered_time_ < buffer->timestamp())) {
347 buffered_time_ = buffer->timestamp();
348 }
349
350 if (is_at_max_capacity)
351 buffering_state_->NotifyMaxCapacity(buffered_time_);
352
353 // No need to update the list of playable frames,
354 // if we are already blocking on a frame.
355 bool update_playable_frames = non_playable_frames_.empty();
356 non_playable_frames_.push_back(buffer);
357 if (update_playable_frames)
358 UpdatePlayableFrames();
359 }
360
361 void AvPipelineImpl::UpdatePlayableFrames() {
362 while (!non_playable_frames_.empty()) {
363 const scoped_refptr<DecoderBufferBase>& non_playable_frame =
364 non_playable_frames_.front();
365
366 if (non_playable_frame->end_of_stream()) {
367 buffering_state_->NotifyEos();
368 } else {
369 const ::media::DecryptConfig* decrypt_config =
370 non_playable_frame->decrypt_config();
371 if (decrypt_config &&
372 !(media_keys_ &&
373 media_keys_->GetDecryptContext(decrypt_config->key_id()).get())) {
374 // The frame is still not playable. All the following are thus not
375 // playable.
376 break;
377 }
378
379 if (playable_buffered_time_ == ::media::kNoTimestamp() ||
380 playable_buffered_time_ < non_playable_frame->timestamp()) {
381 playable_buffered_time_ = non_playable_frame->timestamp();
382 buffering_state_->SetBufferedTime(playable_buffered_time_);
383 }
384 }
385
386 // The frame is playable: remove it from the list of non playable frames.
387 non_playable_frames_.pop_front();
388 }
389 }
390
391 } // namespace media
392 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698