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