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

Side by Side Diff: chromecast/media/audio/cast_audio_output_stream.cc

Issue 1372393007: [Chromecast] Upgrade to new CMA backend API (Closed) Base URL: https://chromium.googlesource.com/chromium/src@master
Patch Set: Remove gyp target for backend adapter Created 5 years, 2 months 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
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chromecast/media/audio/cast_audio_output_stream.h" 5 #include "chromecast/media/audio/cast_audio_output_stream.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
8 #include "base/synchronization/waitable_event.h" 9 #include "base/synchronization/waitable_event.h"
9 #include "base/threading/thread_checker.h" 10 #include "base/threading/thread_checker.h"
10 #include "chromecast/base/metrics/cast_metrics_helper.h" 11 #include "chromecast/base/metrics/cast_metrics_helper.h"
11 #include "chromecast/base/task_runner_impl.h" 12 #include "chromecast/base/task_runner_impl.h"
12 #include "chromecast/media/audio/cast_audio_manager.h" 13 #include "chromecast/media/audio/cast_audio_manager.h"
13 #include "chromecast/media/base/media_message_loop.h" 14 #include "chromecast/media/base/media_message_loop.h"
14 #include "chromecast/media/cma/base/cast_decoder_buffer_impl.h" 15 #include "chromecast/media/cma/base/cast_decoder_buffer_impl.h"
15 #include "chromecast/media/cma/base/decoder_buffer_adapter.h" 16 #include "chromecast/media/cma/base/decoder_buffer_adapter.h"
16 #include "chromecast/media/cma/pipeline/frame_status_cb_impl.h"
17 #include "chromecast/public/media/audio_pipeline_device.h"
18 #include "chromecast/public/media/decoder_config.h" 17 #include "chromecast/public/media/decoder_config.h"
19 #include "chromecast/public/media/decrypt_context.h" 18 #include "chromecast/public/media/decrypt_context.h"
20 #include "chromecast/public/media/media_clock_device.h"
21 #include "chromecast/public/media/media_pipeline_backend.h" 19 #include "chromecast/public/media/media_pipeline_backend.h"
22 #include "chromecast/public/media/media_pipeline_device_params.h" 20 #include "chromecast/public/media/media_pipeline_device_params.h"
23 #include "media/base/bind_to_current_loop.h" 21 #include "media/base/bind_to_current_loop.h"
24 #include "media/base/decoder_buffer.h" 22 #include "media/base/decoder_buffer.h"
25 23
26 namespace chromecast { 24 namespace chromecast {
27 namespace media { 25 namespace media {
26 namespace {
28 27
29 namespace { 28 MediaPipelineBackend::AudioDecoder* InitializeBackend(
30 bool InitClockDevice(MediaClockDevice* clock_device) { 29 const ::media::AudioParameters& audio_params,
31 DCHECK(clock_device); 30 MediaPipelineBackend* backend,
32 DCHECK_EQ(clock_device->GetState(), MediaClockDevice::kStateUninitialized); 31 MediaPipelineBackend::Delegate* delegate) {
32 DCHECK(backend);
33 DCHECK(delegate);
33 34
34 if (!clock_device->SetState(media::MediaClockDevice::kStateIdle)) 35 MediaPipelineBackend::AudioDecoder* decoder = backend->CreateAudioDecoder();
35 return false; 36 if (!decoder)
36 37 return nullptr;
37 if (!clock_device->ResetTimeline(0))
38 return false;
39
40 if (!clock_device->SetRate(1.0))
41 return false;
42
43 return true;
44 }
45
46 bool InitAudioDevice(const ::media::AudioParameters& audio_params,
47 AudioPipelineDevice* audio_device) {
48 DCHECK(audio_device);
49 DCHECK_EQ(audio_device->GetState(), AudioPipelineDevice::kStateUninitialized);
50 38
51 AudioConfig audio_config; 39 AudioConfig audio_config;
52 audio_config.codec = kCodecPCM; 40 audio_config.codec = kCodecPCM;
53 audio_config.sample_format = kSampleFormatS16; 41 audio_config.sample_format = kSampleFormatS16;
54 audio_config.bytes_per_channel = audio_params.bits_per_sample() / 8; 42 audio_config.bytes_per_channel = audio_params.bits_per_sample() / 8;
55 audio_config.channel_number = audio_params.channels(); 43 audio_config.channel_number = audio_params.channels();
56 audio_config.samples_per_second = audio_params.sample_rate(); 44 audio_config.samples_per_second = audio_params.sample_rate();
57 audio_config.extra_data = nullptr; 45 audio_config.extra_data = nullptr;
58 audio_config.extra_data_size = 0; 46 audio_config.extra_data_size = 0;
59 audio_config.is_encrypted = false; 47 audio_config.is_encrypted = false;
60 if (!audio_device->SetConfig(audio_config))
61 return false;
62 48
63 if (!audio_device->SetState(AudioPipelineDevice::kStateIdle)) 49 if (!decoder->SetConfig(audio_config))
64 return false; 50 return nullptr;
65 51
66 return true; 52 if (!backend->Initialize(delegate))
53 return nullptr;
54
55 return decoder;
67 } 56 }
57
68 } // namespace 58 } // namespace
69 59
70 // Backend represents a MediaPipelineBackend adapter that runs on cast 60 // Backend represents a MediaPipelineBackend adapter that runs on cast
71 // media thread (media::MediaMessageLoop::GetTaskRunner). 61 // media thread (media::MediaMessageLoop::GetTaskRunner).
72 // It can be created and destroyed on any thread, but all other member functions 62 // It can be created and destroyed on any thread, but all other member functions
73 // must be called on a single thread. 63 // must be called on a single thread.
74 class CastAudioOutputStream::Backend { 64 class CastAudioOutputStream::Backend : public MediaPipelineBackend::Delegate {
75 public: 65 public:
76 typedef base::Callback<void(bool)> PushFrameCompletionCallback; 66 typedef base::Callback<void(bool)> PushBufferCompletionCallback;
77 67
78 Backend(const ::media::AudioParameters& audio_params) 68 Backend(const ::media::AudioParameters& audio_params)
79 : audio_params_(audio_params) { 69 : audio_params_(audio_params),
70 decoder_(nullptr),
71 first_start_(true),
72 error_(false) {
80 thread_checker_.DetachFromThread(); 73 thread_checker_.DetachFromThread();
81 } 74 }
82 ~Backend() {} 75 ~Backend() override {}
83 76
84 void Open(CastAudioManager* audio_manager, 77 void Open(CastAudioManager* audio_manager,
85 bool* success, 78 bool* success,
86 base::WaitableEvent* completion_event) { 79 base::WaitableEvent* completion_event) {
87 DCHECK(thread_checker_.CalledOnValidThread()); 80 DCHECK(thread_checker_.CalledOnValidThread());
88 DCHECK(backend_ == nullptr); 81 DCHECK(backend_ == nullptr);
82 DCHECK(audio_manager);
83 DCHECK(success);
84 DCHECK(completion_event);
89 85
90 backend_task_runner_.reset(new TaskRunnerImpl()); 86 backend_task_runner_.reset(new TaskRunnerImpl());
91 MediaPipelineDeviceParams device_params( 87 MediaPipelineDeviceParams device_params(
92 MediaPipelineDeviceParams::kModeIgnorePts, backend_task_runner_.get()); 88 MediaPipelineDeviceParams::kModeIgnorePts, backend_task_runner_.get());
93 89 backend_ = audio_manager->CreateMediaPipelineBackend(device_params);
94 scoped_ptr<MediaPipelineBackend> pipeline_backend = 90 if (backend_)
95 audio_manager->CreateMediaPipelineBackend(device_params); 91 decoder_ = InitializeBackend(audio_params_, backend_.get(), this);
96 if (pipeline_backend && InitClockDevice(pipeline_backend->GetClock()) && 92 *success = decoder_ != nullptr;
97 InitAudioDevice(audio_params_, pipeline_backend->GetAudio())) {
98 backend_ = pipeline_backend.Pass();
99 }
100 *success = backend_ != nullptr;
101 completion_event->Signal(); 93 completion_event->Signal();
102 } 94 }
103 95
104 void Close() { 96 void Close() {
105 DCHECK(thread_checker_.CalledOnValidThread()); 97 DCHECK(thread_checker_.CalledOnValidThread());
106 98
107 if (backend_) { 99 if (backend_)
108 backend_->GetClock()->SetState(MediaClockDevice::kStateIdle); 100 backend_->Stop();
109 backend_->GetAudio()->SetState(AudioPipelineDevice::kStateIdle);
110 }
111 backend_.reset(); 101 backend_.reset();
112 backend_task_runner_.reset(); 102 backend_task_runner_.reset();
113 } 103 }
114 104
115 void Start() { 105 void Start() {
116 DCHECK(thread_checker_.CalledOnValidThread()); 106 DCHECK(thread_checker_.CalledOnValidThread());
107 DCHECK(backend_);
117 108
118 MediaClockDevice* clock_device = backend_->GetClock(); 109 if (first_start_)
119 clock_device->SetState(MediaClockDevice::kStateRunning); 110 backend_->Start(0);
120 clock_device->SetRate(1.0f); 111 else
121 112 backend_->Resume();
alokp 2015/10/14 23:16:52 Is it legal to call Start on a backend in error st
kmackay 2015/10/15 00:35:53 I don't see how the backend could be in an error s
122 AudioPipelineDevice* audio_device = backend_->GetAudio();
123 audio_device->SetState(AudioPipelineDevice::kStateRunning);
124 } 113 }
125 114
126 void Stop() { 115 void Stop() {
127 DCHECK(thread_checker_.CalledOnValidThread()); 116 DCHECK(thread_checker_.CalledOnValidThread());
117 DCHECK(backend_);
128 118
129 MediaClockDevice* clock_device = backend_->GetClock(); 119 backend_->Pause();
130 clock_device->SetRate(0.0f); 120 first_start_ = false;
alokp 2015/10/14 23:16:52 move this to Start?
kmackay 2015/10/15 00:35:53 Done.
131 } 121 }
132 122
133 void PushFrame(scoped_refptr<media::DecoderBufferBase> decoder_buffer, 123 void PushBuffer(scoped_refptr<media::DecoderBufferBase> decoder_buffer,
134 const PushFrameCompletionCallback& completion_cb) { 124 const PushBufferCompletionCallback& completion_cb) {
135 DCHECK(thread_checker_.CalledOnValidThread()); 125 DCHECK(thread_checker_.CalledOnValidThread());
126 DCHECK(decoder_);
127 DCHECK(completion_cb_.is_null());
alokp 2015/10/14 23:16:52 Shouldn't this be !is_null?
kmackay 2015/10/15 00:35:53 No, this is checking the member variable to make s
128 if (error_) {
129 completion_cb.Run(false);
130 return;
131 }
136 132
137 AudioPipelineDevice* audio_device = backend_->GetAudio(); 133 if (backend_buffer_)
138 MediaComponentDevice::FrameStatus status = 134 backend_buffer_->set_buffer(decoder_buffer);
139 audio_device->PushFrame(nullptr, // decrypt_context 135 else
140 new CastDecoderBufferImpl(decoder_buffer), 136 backend_buffer_.reset(new CastDecoderBufferImpl(decoder_buffer));
halliwell 2015/10/14 15:02:37 This would be slightly cleaner if we added a no-ar
kmackay 2015/10/14 21:27:26 Done.
141 new media::FrameStatusCBImpl(base::Bind(
142 &Backend::OnPushFrameStatus,
143 base::Unretained(this), completion_cb)));
144 137
145 if (status != MediaComponentDevice::kFramePending) 138 MediaPipelineBackend::BufferStatus status =
146 OnPushFrameStatus(completion_cb, status); 139 decoder_->PushBuffer(nullptr, backend_buffer_.get());
alokp 2015/10/14 23:16:52 either document what nullptr is or use a variable
kmackay 2015/10/15 00:35:53 Done.
140 completion_cb_ = completion_cb;
141 if (status != MediaPipelineBackend::kBufferPending)
142 OnPushBufferComplete(decoder_, status);
147 } 143 }
148 144
149 void SetVolume(double volume) { 145 void SetVolume(double volume) {
150 DCHECK(thread_checker_.CalledOnValidThread()); 146 DCHECK(thread_checker_.CalledOnValidThread());
147 DCHECK(decoder_);
148 decoder_->SetVolume(volume);
149 }
151 150
152 AudioPipelineDevice* audio_device = backend_->GetAudio(); 151 // MediaPipelineBackend::Delegate implementation
153 audio_device->SetStreamVolumeMultiplier(volume); 152 void OnVideoResolutionChanged(MediaPipelineBackend::VideoDecoder* decoder,
153 const Size& size) override {}
154
155 void OnPushBufferComplete(
156 MediaPipelineBackend::Decoder* decoder,
157 MediaPipelineBackend::BufferStatus status) override {
158 DCHECK(thread_checker_.CalledOnValidThread());
159 DCHECK_NE(status, MediaPipelineBackend::kBufferPending);
160
161 base::ResetAndReturn(&completion_cb_)
162 .Run(status == MediaPipelineBackend::kBufferSuccess);
163 }
164
165 void OnEndOfStream(MediaPipelineBackend::Decoder* decoder) override {}
166
167 void OnDecoderError(MediaPipelineBackend::Decoder* decoder) override {
168 error_ = true;
169 if (!completion_cb_.is_null())
170 OnPushBufferComplete(decoder_, MediaPipelineBackend::kBufferFailed);
154 } 171 }
155 172
156 private: 173 private:
157 void OnPushFrameStatus(const PushFrameCompletionCallback& completion_cb,
158 MediaComponentDevice::FrameStatus status) {
159 DCHECK(thread_checker_.CalledOnValidThread());
160 DCHECK_NE(status, MediaComponentDevice::kFramePending);
161
162 completion_cb.Run(status == MediaComponentDevice::kFrameSuccess);
163 }
164
165 const ::media::AudioParameters audio_params_; 174 const ::media::AudioParameters audio_params_;
166 scoped_ptr<MediaPipelineBackend> backend_; 175 scoped_ptr<MediaPipelineBackend> backend_;
167 scoped_ptr<TaskRunnerImpl> backend_task_runner_; 176 scoped_ptr<TaskRunnerImpl> backend_task_runner_;
177 MediaPipelineBackend::AudioDecoder* decoder_;
178 PushBufferCompletionCallback completion_cb_;
179 bool first_start_;
180 bool error_;
181 scoped_ptr<CastDecoderBufferImpl> backend_buffer_;
168 base::ThreadChecker thread_checker_; 182 base::ThreadChecker thread_checker_;
169 DISALLOW_COPY_AND_ASSIGN(Backend); 183 DISALLOW_COPY_AND_ASSIGN(Backend);
170 }; 184 };
171 185
172 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner). 186 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner).
173 CastAudioOutputStream::CastAudioOutputStream( 187 CastAudioOutputStream::CastAudioOutputStream(
174 const ::media::AudioParameters& audio_params, 188 const ::media::AudioParameters& audio_params,
175 CastAudioManager* audio_manager) 189 CastAudioManager* audio_manager)
176 : audio_params_(audio_params), 190 : audio_params_(audio_params),
177 audio_manager_(audio_manager), 191 audio_manager_(audio_manager),
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 DCHECK(audio_task_runner_->BelongsToCurrentThread()); 255 DCHECK(audio_task_runner_->BelongsToCurrentThread());
242 DCHECK(source_callback); 256 DCHECK(source_callback);
243 257
244 source_callback_ = source_callback; 258 source_callback_ = source_callback;
245 backend_task_runner_->PostTask( 259 backend_task_runner_->PostTask(
246 FROM_HERE, base::Bind(&Backend::Start, base::Unretained(backend_.get()))); 260 FROM_HERE, base::Bind(&Backend::Start, base::Unretained(backend_.get())));
247 261
248 next_push_time_ = base::TimeTicks::Now(); 262 next_push_time_ = base::TimeTicks::Now();
249 if (!push_in_progress_) { 263 if (!push_in_progress_) {
250 audio_task_runner_->PostTask(FROM_HERE, 264 audio_task_runner_->PostTask(FROM_HERE,
251 base::Bind(&CastAudioOutputStream::PushFrame, 265 base::Bind(&CastAudioOutputStream::PushBuffer,
252 weak_factory_.GetWeakPtr())); 266 weak_factory_.GetWeakPtr()));
253 push_in_progress_ = true; 267 push_in_progress_ = true;
254 } 268 }
255 269
256 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio(); 270 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio();
257 } 271 }
258 272
259 void CastAudioOutputStream::Stop() { 273 void CastAudioOutputStream::Stop() {
260 DCHECK(audio_task_runner_->BelongsToCurrentThread()); 274 DCHECK(audio_task_runner_->BelongsToCurrentThread());
261 275
(...skipping 19 matching lines...) Expand all
281 295
282 void CastAudioOutputStream::OnClosed() { 296 void CastAudioOutputStream::OnClosed() {
283 DCHECK(audio_task_runner_->BelongsToCurrentThread()); 297 DCHECK(audio_task_runner_->BelongsToCurrentThread());
284 298
285 VLOG(1) << __FUNCTION__ << " : " << this; 299 VLOG(1) << __FUNCTION__ << " : " << this;
286 // Signal to the manager that we're closed and can be removed. 300 // Signal to the manager that we're closed and can be removed.
287 // This should be the last call in the function as it deletes "this". 301 // This should be the last call in the function as it deletes "this".
288 audio_manager_->ReleaseOutputStream(this); 302 audio_manager_->ReleaseOutputStream(this);
289 } 303 }
290 304
291 void CastAudioOutputStream::PushFrame() { 305 void CastAudioOutputStream::PushBuffer() {
292 DCHECK(audio_task_runner_->BelongsToCurrentThread()); 306 DCHECK(audio_task_runner_->BelongsToCurrentThread());
293 DCHECK(push_in_progress_); 307 DCHECK(push_in_progress_);
294 308
295 if (!source_callback_) { 309 if (!source_callback_) {
296 push_in_progress_ = false; 310 push_in_progress_ = false;
297 return; 311 return;
298 } 312 }
299 313
300 uint32_t bytes_delay = 0; 314 uint32_t bytes_delay = 0;
301 int frame_count = source_callback_->OnMoreData(audio_bus_.get(), bytes_delay); 315 int frame_count = source_callback_->OnMoreData(audio_bus_.get(), bytes_delay);
302 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << bytes_delay; 316 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << bytes_delay;
303 317
304 DCHECK_EQ(frame_count, audio_bus_->frames()); 318 DCHECK_EQ(frame_count, audio_bus_->frames());
305 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()), 319 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()),
306 frame_count * audio_params_.GetBytesPerFrame()); 320 frame_count * audio_params_.GetBytesPerFrame());
307 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, 321 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8,
308 decoder_buffer_->writable_data()); 322 decoder_buffer_->writable_data());
309 323
310 auto completion_cb = ::media::BindToCurrentLoop(base::Bind( 324 auto completion_cb = ::media::BindToCurrentLoop(
311 &CastAudioOutputStream::OnPushFrameComplete, weak_factory_.GetWeakPtr())); 325 base::Bind(&CastAudioOutputStream::OnPushBufferComplete,
312 backend_task_runner_->PostTask( 326 weak_factory_.GetWeakPtr()));
313 FROM_HERE, 327 backend_task_runner_->PostTask(FROM_HERE,
314 base::Bind(&Backend::PushFrame, base::Unretained(backend_.get()), 328 base::Bind(&Backend::PushBuffer,
315 decoder_buffer_, completion_cb)); 329 base::Unretained(backend_.get()),
330 decoder_buffer_,
331 completion_cb));
316 } 332 }
317 333
318 void CastAudioOutputStream::OnPushFrameComplete(bool success) { 334 void CastAudioOutputStream::OnPushBufferComplete(bool success) {
319 DCHECK(audio_task_runner_->BelongsToCurrentThread()); 335 DCHECK(audio_task_runner_->BelongsToCurrentThread());
320 DCHECK(push_in_progress_); 336 DCHECK(push_in_progress_);
321 337
322 push_in_progress_ = false; 338 push_in_progress_ = false;
323 339
324 if (!source_callback_) { 340 if (!source_callback_) {
325 return; 341 return;
326 } 342 }
327 if (!success) { 343 if (!success) {
328 source_callback_->OnError(this); 344 source_callback_->OnError(this);
329 return; 345 return;
330 } 346 }
331 347
332 // Schedule next push frame. 348 // Schedule next push buffer.
333 // Need to account for time spent in pulling and pushing frame as well 349 // Need to account for time spent in pulling and pushing buffer as well
334 // as the imprecision of PostDelayedTask(). 350 // as the imprecision of PostDelayedTask().
335 const base::TimeTicks now = base::TimeTicks::Now(); 351 const base::TimeTicks now = base::TimeTicks::Now();
336 base::TimeDelta delay = next_push_time_ + buffer_duration_ - now; 352 base::TimeDelta delay = next_push_time_ + buffer_duration_ - now;
337 delay = std::max(delay, base::TimeDelta()); 353 delay = std::max(delay, base::TimeDelta());
338 next_push_time_ = now + delay; 354 next_push_time_ = now + delay;
339 355
340 audio_task_runner_->PostDelayedTask( 356 audio_task_runner_->PostDelayedTask(
341 FROM_HERE, 357 FROM_HERE,
342 base::Bind(&CastAudioOutputStream::PushFrame, weak_factory_.GetWeakPtr()), 358 base::Bind(&CastAudioOutputStream::PushBuffer,
359 weak_factory_.GetWeakPtr()),
343 delay); 360 delay);
344 push_in_progress_ = true; 361 push_in_progress_ = true;
345 } 362 }
346 363
347 } // namespace media 364 } // namespace media
348 } // namespace chromecast 365 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698