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, |
slan
2015/10/06 19:33:59
nit: DCHECK all raw input
kmackay
2015/10/06 21:44:55
Done.
| |
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); |
87 | 80 |
88 scoped_ptr<MediaPipelineBackend> pipeline_backend = | 81 backend_ = audio_manager->CreateMediaPipelineBackend(); |
slan
2015/10/06 19:34:00
Can't an AudioOuput stream be created and opened a
kmackay
2015/10/06 21:44:55
A whole new backend is created for each stream.
slan
2015/10/06 22:15:05
Duh, thank you.
| |
89 audio_manager->CreateMediaPipelineBackend(); | 82 if (backend_) |
90 if (pipeline_backend && InitClockDevice(pipeline_backend->GetClock()) && | 83 decoder_ = InitializeBackend(audio_params_, backend_.get(), this); |
91 InitAudioDevice(audio_params_, pipeline_backend->GetAudio())) { | 84 *success = decoder_ != nullptr; |
92 backend_ = pipeline_backend.Pass(); | |
93 } | |
94 *success = backend_ != nullptr; | |
95 completion_event->Signal(); | 85 completion_event->Signal(); |
96 } | 86 } |
97 | 87 |
98 void Close() { | 88 void Close() { |
99 DCHECK(thread_checker_.CalledOnValidThread()); | 89 DCHECK(thread_checker_.CalledOnValidThread()); |
100 | 90 |
101 if (backend_) { | 91 if (backend_) |
102 backend_->GetClock()->SetState(MediaClockDevice::kStateIdle); | 92 backend_->Stop(); |
103 backend_->GetAudio()->SetState(AudioPipelineDevice::kStateIdle); | |
104 } | |
105 backend_.reset(); | 93 backend_.reset(); |
106 } | 94 } |
107 | 95 |
108 void Start() { | 96 void Start() { |
109 DCHECK(thread_checker_.CalledOnValidThread()); | 97 DCHECK(thread_checker_.CalledOnValidThread()); |
110 | 98 |
111 MediaClockDevice* clock_device = backend_->GetClock(); | 99 if (first_start_) |
112 clock_device->SetState(MediaClockDevice::kStateRunning); | 100 backend_->Start(0); |
slan
2015/10/06 19:34:00
DCHECK backend_ before use to be defensive. Here a
kmackay
2015/10/06 21:44:55
Done.
| |
113 clock_device->SetRate(1.0f); | 101 else |
114 | 102 backend_->Resume(); |
115 AudioPipelineDevice* audio_device = backend_->GetAudio(); | |
116 audio_device->SetState(AudioPipelineDevice::kStateRunning); | |
117 } | 103 } |
118 | 104 |
119 void Stop() { | 105 void Stop() { |
120 DCHECK(thread_checker_.CalledOnValidThread()); | 106 DCHECK(thread_checker_.CalledOnValidThread()); |
121 | 107 |
122 MediaClockDevice* clock_device = backend_->GetClock(); | 108 backend_->Pause(); |
slan
2015/10/06 19:34:00
DCHECK backend_ before use
kmackay
2015/10/06 21:44:55
Done.
| |
123 clock_device->SetRate(0.0f); | 109 first_start_ = false; |
124 } | 110 } |
125 | 111 |
126 void PushFrame(scoped_refptr<media::DecoderBufferBase> decoder_buffer, | 112 void PushBuffer(scoped_refptr<media::DecoderBufferBase> decoder_buffer, |
127 const PushFrameCompletionCallback& completion_cb) { | 113 const PushBufferCompletionCallback& completion_cb) { |
128 DCHECK(thread_checker_.CalledOnValidThread()); | 114 DCHECK(thread_checker_.CalledOnValidThread()); |
115 DCHECK(decoder_); | |
116 DCHECK(completion_cb_.is_null()); | |
117 if (error_) { | |
118 completion_cb.Run(false); | |
119 return; | |
120 } | |
129 | 121 |
130 AudioPipelineDevice* audio_device = backend_->GetAudio(); | 122 if (backend_buffer_) |
131 MediaComponentDevice::FrameStatus status = | 123 backend_buffer_->set_buffer(decoder_buffer); |
132 audio_device->PushFrame(nullptr, // decrypt_context | 124 else |
133 new CastDecoderBufferImpl(decoder_buffer), | 125 backend_buffer_.reset(new CastDecoderBufferImpl(decoder_buffer)); |
134 new media::FrameStatusCBImpl(base::Bind( | |
135 &Backend::OnPushFrameStatus, | |
136 base::Unretained(this), completion_cb))); | |
137 | 126 |
138 if (status != MediaComponentDevice::kFramePending) | 127 MediaPipelineBackend::BufferStatus status = |
139 OnPushFrameStatus(completion_cb, status); | 128 decoder_->PushBuffer(nullptr, backend_buffer_.get()); |
129 completion_cb_ = completion_cb; | |
130 if (status != MediaPipelineBackend::kBufferPending) | |
131 OnPushBufferComplete(decoder_, status); | |
140 } | 132 } |
141 | 133 |
142 void SetVolume(double volume) { | 134 void SetVolume(double volume) { |
143 DCHECK(thread_checker_.CalledOnValidThread()); | 135 DCHECK(thread_checker_.CalledOnValidThread()); |
136 decoder_->SetVolume(volume); | |
slan
2015/10/06 19:34:00
DCHECK(decoder_) before use
kmackay
2015/10/06 21:44:55
Done.
| |
137 } | |
144 | 138 |
145 AudioPipelineDevice* audio_device = backend_->GetAudio(); | 139 // MediaPipelineBackend::Delegate implementation |
146 audio_device->SetStreamVolumeMultiplier(volume); | 140 void OnVideoResolutionChanged(MediaPipelineBackend::Decoder* decoder, |
141 const Size& size) override {} | |
142 | |
143 void OnPushBufferComplete( | |
144 MediaPipelineBackend::Decoder* decoder, | |
145 MediaPipelineBackend::BufferStatus status) override { | |
146 DCHECK(thread_checker_.CalledOnValidThread()); | |
147 DCHECK_NE(status, MediaPipelineBackend::kBufferPending); | |
148 | |
149 base::ResetAndReturn(&completion_cb_) | |
150 .Run(status == MediaPipelineBackend::kBufferSuccess); | |
151 } | |
152 | |
153 void OnEndOfStream(MediaPipelineBackend::Decoder* decoder) override {} | |
154 | |
155 void OnDecoderError(MediaPipelineBackend::Decoder* decoder) override { | |
156 error_ = true; | |
157 if (!completion_cb_.is_null()) | |
158 OnPushBufferComplete(decoder_, MediaPipelineBackend::kBufferFailed); | |
147 } | 159 } |
148 | 160 |
149 private: | 161 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_; | 162 const ::media::AudioParameters audio_params_; |
159 scoped_ptr<MediaPipelineBackend> backend_; | 163 scoped_ptr<MediaPipelineBackend> backend_; |
164 MediaPipelineBackend::AudioDecoder* decoder_; | |
165 PushBufferCompletionCallback completion_cb_; | |
166 bool first_start_; | |
167 bool error_; | |
168 scoped_ptr<CastDecoderBufferImpl> backend_buffer_; | |
160 base::ThreadChecker thread_checker_; | 169 base::ThreadChecker thread_checker_; |
161 DISALLOW_COPY_AND_ASSIGN(Backend); | 170 DISALLOW_COPY_AND_ASSIGN(Backend); |
162 }; | 171 }; |
163 | 172 |
164 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner). | 173 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner). |
165 CastAudioOutputStream::CastAudioOutputStream( | 174 CastAudioOutputStream::CastAudioOutputStream( |
166 const ::media::AudioParameters& audio_params, | 175 const ::media::AudioParameters& audio_params, |
167 CastAudioManager* audio_manager) | 176 CastAudioManager* audio_manager) |
168 : audio_params_(audio_params), | 177 : audio_params_(audio_params), |
169 audio_manager_(audio_manager), | 178 audio_manager_(audio_manager), |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
233 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 242 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
234 DCHECK(source_callback); | 243 DCHECK(source_callback); |
235 | 244 |
236 source_callback_ = source_callback; | 245 source_callback_ = source_callback; |
237 backend_task_runner_->PostTask( | 246 backend_task_runner_->PostTask( |
238 FROM_HERE, base::Bind(&Backend::Start, base::Unretained(backend_.get()))); | 247 FROM_HERE, base::Bind(&Backend::Start, base::Unretained(backend_.get()))); |
239 | 248 |
240 next_push_time_ = base::TimeTicks::Now(); | 249 next_push_time_ = base::TimeTicks::Now(); |
241 if (!backend_busy_) { | 250 if (!backend_busy_) { |
242 audio_task_runner_->PostTask(FROM_HERE, | 251 audio_task_runner_->PostTask(FROM_HERE, |
243 base::Bind(&CastAudioOutputStream::PushFrame, | 252 base::Bind(&CastAudioOutputStream::PushBuffer, |
244 weak_factory_.GetWeakPtr())); | 253 weak_factory_.GetWeakPtr())); |
245 } | 254 } |
246 | 255 |
247 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio(); | 256 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio(); |
248 } | 257 } |
249 | 258 |
250 void CastAudioOutputStream::Stop() { | 259 void CastAudioOutputStream::Stop() { |
251 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 260 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
252 | 261 |
253 source_callback_ = nullptr; | 262 source_callback_ = nullptr; |
(...skipping 18 matching lines...) Expand all Loading... | |
272 | 281 |
273 void CastAudioOutputStream::OnClosed() { | 282 void CastAudioOutputStream::OnClosed() { |
274 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 283 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
275 | 284 |
276 VLOG(1) << __FUNCTION__ << " : " << this; | 285 VLOG(1) << __FUNCTION__ << " : " << this; |
277 // Signal to the manager that we're closed and can be removed. | 286 // 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". | 287 // This should be the last call in the function as it deletes "this". |
279 audio_manager_->ReleaseOutputStream(this); | 288 audio_manager_->ReleaseOutputStream(this); |
280 } | 289 } |
281 | 290 |
282 void CastAudioOutputStream::PushFrame() { | 291 void CastAudioOutputStream::PushBuffer() { |
283 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 292 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
284 DCHECK(!backend_busy_); | 293 DCHECK(!backend_busy_); |
285 | 294 |
286 if (!source_callback_) | 295 if (!source_callback_) |
287 return; | 296 return; |
288 | 297 |
289 uint32_t bytes_delay = 0; | 298 uint32_t bytes_delay = 0; |
290 int frame_count = source_callback_->OnMoreData(audio_bus_.get(), bytes_delay); | 299 int frame_count = source_callback_->OnMoreData(audio_bus_.get(), bytes_delay); |
291 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << bytes_delay; | 300 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << bytes_delay; |
292 | 301 |
293 DCHECK_EQ(frame_count, audio_bus_->frames()); | 302 DCHECK_EQ(frame_count, audio_bus_->frames()); |
294 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()), | 303 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()), |
295 frame_count * audio_params_.GetBytesPerFrame()); | 304 frame_count * audio_params_.GetBytesPerFrame()); |
296 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, | 305 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, |
297 decoder_buffer_->writable_data()); | 306 decoder_buffer_->writable_data()); |
298 | 307 |
299 auto completion_cb = ::media::BindToCurrentLoop(base::Bind( | 308 auto completion_cb = ::media::BindToCurrentLoop( |
300 &CastAudioOutputStream::OnPushFrameComplete, weak_factory_.GetWeakPtr())); | 309 base::Bind(&CastAudioOutputStream::OnPushBufferComplete, |
301 backend_task_runner_->PostTask( | 310 weak_factory_.GetWeakPtr())); |
302 FROM_HERE, | 311 backend_task_runner_->PostTask(FROM_HERE, |
303 base::Bind(&Backend::PushFrame, base::Unretained(backend_.get()), | 312 base::Bind(&Backend::PushBuffer, |
304 decoder_buffer_, completion_cb)); | 313 base::Unretained(backend_.get()), |
314 decoder_buffer_, | |
315 completion_cb)); | |
305 backend_busy_ = true; | 316 backend_busy_ = true; |
306 } | 317 } |
307 | 318 |
308 void CastAudioOutputStream::OnPushFrameComplete(bool success) { | 319 void CastAudioOutputStream::OnPushBufferComplete(bool success) { |
309 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 320 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
310 | 321 |
311 backend_busy_ = false; | 322 backend_busy_ = false; |
312 if (!source_callback_) | 323 if (!source_callback_) |
313 return; | 324 return; |
314 | 325 |
315 if (!success) { | 326 if (!success) { |
316 source_callback_->OnError(this); | 327 source_callback_->OnError(this); |
317 return; | 328 return; |
318 } | 329 } |
319 | 330 |
320 // Schedule next push frame. | 331 // Schedule next push buffer. |
321 // Need to account for time spent in pulling and pushing frame as well | 332 // Need to account for time spent in pulling and pushing buffer as well |
322 // as the imprecision of PostDelayedTask(). | 333 // as the imprecision of PostDelayedTask(). |
323 const base::TimeTicks now = base::TimeTicks::Now(); | 334 const base::TimeTicks now = base::TimeTicks::Now(); |
324 base::TimeDelta delay = next_push_time_ + buffer_duration_ - now; | 335 base::TimeDelta delay = next_push_time_ + buffer_duration_ - now; |
325 delay = std::max(delay, base::TimeDelta()); | 336 delay = std::max(delay, base::TimeDelta()); |
326 next_push_time_ = now + delay; | 337 next_push_time_ = now + delay; |
327 | 338 |
328 audio_task_runner_->PostDelayedTask( | 339 audio_task_runner_->PostDelayedTask( |
329 FROM_HERE, | 340 FROM_HERE, |
330 base::Bind(&CastAudioOutputStream::PushFrame, weak_factory_.GetWeakPtr()), | 341 base::Bind(&CastAudioOutputStream::PushBuffer, |
342 weak_factory_.GetWeakPtr()), | |
331 delay); | 343 delay); |
332 } | 344 } |
333 | 345 |
334 } // namespace media | 346 } // namespace media |
335 } // namespace chromecast | 347 } // namespace chromecast |
OLD | NEW |