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 <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
| 12 #include "base/synchronization/waitable_event.h" |
| 13 #include "base/threading/thread_task_runner_handle.h" |
12 #include "chromecast/base/metrics/cast_metrics_helper.h" | 14 #include "chromecast/base/metrics/cast_metrics_helper.h" |
13 #include "chromecast/base/task_runner_impl.h" | 15 #include "chromecast/base/task_runner_impl.h" |
14 #include "chromecast/media/audio/cast_audio_manager.h" | 16 #include "chromecast/media/audio/cast_audio_manager.h" |
| 17 #include "chromecast/media/cma/backend/media_pipeline_backend_factory.h" |
15 #include "chromecast/media/cma/base/decoder_buffer_adapter.h" | 18 #include "chromecast/media/cma/base/decoder_buffer_adapter.h" |
16 #include "chromecast/public/media/decoder_config.h" | 19 #include "chromecast/public/media/decoder_config.h" |
17 #include "chromecast/public/media/media_pipeline_backend.h" | 20 #include "chromecast/public/media/media_pipeline_backend.h" |
18 #include "chromecast/public/media/media_pipeline_device_params.h" | 21 #include "chromecast/public/media/media_pipeline_device_params.h" |
19 #include "chromecast/public/volume_control.h" | 22 #include "chromecast/public/volume_control.h" |
20 #include "media/audio/audio_device_description.h" | 23 #include "media/audio/audio_device_description.h" |
| 24 #include "media/base/audio_timestamp_helper.h" |
21 #include "media/base/decoder_buffer.h" | 25 #include "media/base/decoder_buffer.h" |
22 | 26 |
23 namespace { | 27 namespace { |
24 const int kMaxQueuedDataMs = 1000; | 28 const int kMaxQueuedDataMs = 1000; |
| 29 |
| 30 void SignalWaitableEvent(bool* success, |
| 31 base::WaitableEvent* waitable_event, |
| 32 bool result) { |
| 33 *success = result; |
| 34 waitable_event->Signal(); |
| 35 } |
25 } // namespace | 36 } // namespace |
26 | 37 |
27 namespace chromecast { | 38 namespace chromecast { |
28 namespace media { | 39 namespace media { |
29 | 40 |
30 // Backend represents a MediaPipelineBackend adapter. | 41 // Backend represents a MediaPipelineBackend adapter. |
31 // It can be created and destroyed on any thread, | 42 // It can be created and destroyed on any thread, |
32 // but all other member functions must be called on a single thread. | 43 // but all other member functions must be called on a single thread. |
33 class CastAudioOutputStream::Backend | 44 class CastAudioOutputStream::Backend |
34 : public MediaPipelineBackend::Decoder::Delegate { | 45 : public MediaPipelineBackend::Decoder::Delegate { |
35 public: | 46 public: |
36 using PushBufferCompletionCallback = base::Callback<void(bool)>; | 47 using OpenCompletionCallback = base::OnceCallback<void(bool)>; |
37 | 48 |
38 Backend() : decoder_(nullptr), first_start_(true), error_(false) { | 49 Backend(const ::media::AudioParameters& audio_params) |
39 thread_checker_.DetachFromThread(); | 50 : audio_params_(audio_params), |
| 51 timestamp_helper_(audio_params_.sample_rate()), |
| 52 buffer_duration_(audio_params.GetBufferDuration()), |
| 53 first_start_(true), |
| 54 push_in_progress_(false), |
| 55 decoder_(nullptr), |
| 56 source_callback_(nullptr), |
| 57 weak_factory_(this) { |
| 58 DETACH_FROM_THREAD(thread_checker_); |
40 } | 59 } |
41 ~Backend() override {} | 60 ~Backend() override { |
| 61 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 62 if (backend_ && !first_start_) // Only stop the backend if it was started. |
| 63 backend_->Stop(); |
| 64 } |
42 | 65 |
43 bool Open(const ::media::AudioParameters& audio_params, | 66 void Open(MediaPipelineBackendFactory* backend_factory, |
44 CastAudioManager* audio_manager) { | 67 OpenCompletionCallback completion_cb) { |
45 DCHECK(thread_checker_.CalledOnValidThread()); | 68 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
46 DCHECK(audio_manager); | 69 DCHECK(backend_factory); |
47 DCHECK(backend_ == nullptr); | 70 DCHECK(backend_ == nullptr); |
48 | 71 |
49 backend_task_runner_.reset(new TaskRunnerImpl()); | 72 backend_task_runner_.reset(new TaskRunnerImpl()); |
50 MediaPipelineDeviceParams device_params( | 73 MediaPipelineDeviceParams device_params( |
51 MediaPipelineDeviceParams::kModeIgnorePts, | 74 MediaPipelineDeviceParams::kModeIgnorePts, |
52 MediaPipelineDeviceParams::kAudioStreamSoundEffects, | 75 MediaPipelineDeviceParams::kAudioStreamSoundEffects, |
53 backend_task_runner_.get(), AudioContentType::kMedia, | 76 backend_task_runner_.get(), AudioContentType::kMedia, |
54 ::media::AudioDeviceDescription::kDefaultDeviceId); | 77 ::media::AudioDeviceDescription::kDefaultDeviceId); |
55 backend_ = audio_manager->CreateMediaPipelineBackend(device_params); | 78 backend_ = backend_factory->CreateBackend(device_params); |
56 if (!backend_) | 79 if (!backend_) { |
57 return false; | 80 std::move(completion_cb).Run(false); |
| 81 return; |
| 82 } |
58 | 83 |
59 decoder_ = backend_->CreateAudioDecoder(); | 84 decoder_ = backend_->CreateAudioDecoder(); |
60 if (!decoder_) | 85 if (!decoder_) { |
61 return false; | 86 std::move(completion_cb).Run(false); |
| 87 return; |
| 88 } |
62 decoder_->SetDelegate(this); | 89 decoder_->SetDelegate(this); |
63 | 90 |
64 AudioConfig audio_config; | 91 AudioConfig audio_config; |
65 audio_config.codec = kCodecPCM; | 92 audio_config.codec = kCodecPCM; |
66 audio_config.sample_format = kSampleFormatS16; | 93 audio_config.sample_format = kSampleFormatS16; |
67 audio_config.bytes_per_channel = audio_params.bits_per_sample() / 8; | 94 audio_config.bytes_per_channel = audio_params_.bits_per_sample() / 8; |
68 audio_config.channel_number = audio_params.channels(); | 95 audio_config.channel_number = audio_params_.channels(); |
69 audio_config.samples_per_second = audio_params.sample_rate(); | 96 audio_config.samples_per_second = audio_params_.sample_rate(); |
70 if (!decoder_->SetConfig(audio_config)) | 97 if (!decoder_->SetConfig(audio_config)) { |
71 return false; | 98 std::move(completion_cb).Run(false); |
| 99 return; |
| 100 } |
72 | 101 |
73 return backend_->Initialize(); | 102 if (!backend_->Initialize()) { |
| 103 std::move(completion_cb).Run(false); |
| 104 return; |
| 105 } |
| 106 |
| 107 audio_bus_ = ::media::AudioBus::Create(audio_params_); |
| 108 decoder_buffer_ = new DecoderBufferAdapter( |
| 109 new ::media::DecoderBuffer(audio_params_.GetBytesPerBuffer())); |
| 110 timestamp_helper_.SetBaseTimestamp(base::TimeDelta()); |
| 111 std::move(completion_cb).Run(true); |
74 } | 112 } |
75 | 113 |
76 void Close() { | 114 void Start(AudioSourceCallback* source_callback) { |
77 DCHECK(thread_checker_.CalledOnValidThread()); | 115 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
78 | |
79 if (backend_ && !first_start_) // Only stop the backend if it was started. | |
80 backend_->Stop(); | |
81 backend_.reset(); | |
82 backend_task_runner_.reset(); | |
83 } | |
84 | |
85 void Start() { | |
86 DCHECK(thread_checker_.CalledOnValidThread()); | |
87 DCHECK(backend_); | 116 DCHECK(backend_); |
88 | 117 |
89 if (first_start_) { | 118 if (first_start_) { |
90 first_start_ = false; | 119 first_start_ = false; |
91 backend_->Start(0); | 120 backend_->Start(0); |
92 } else { | 121 } else { |
93 backend_->Resume(); | 122 backend_->Resume(); |
94 } | 123 } |
| 124 |
| 125 source_callback_ = source_callback; |
| 126 next_push_time_ = base::TimeTicks::Now(); |
| 127 if (!push_in_progress_) { |
| 128 push_in_progress_ = true; |
| 129 PushBuffer(); |
| 130 } |
95 } | 131 } |
96 | 132 |
97 void Stop() { | 133 void Stop(base::OnceClosure completion_cb) { |
98 DCHECK(thread_checker_.CalledOnValidThread()); | 134 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
99 DCHECK(backend_); | 135 DCHECK(backend_); |
100 | 136 |
101 backend_->Pause(); | 137 backend_->Pause(); |
| 138 source_callback_ = nullptr; |
| 139 std::move(completion_cb).Run(); |
102 } | 140 } |
103 | 141 |
104 void PushBuffer(scoped_refptr<media::DecoderBufferBase> decoder_buffer, | 142 void SetVolume(double volume) { |
105 const PushBufferCompletionCallback& completion_cb) { | 143 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
106 DCHECK(thread_checker_.CalledOnValidThread()); | |
107 DCHECK(decoder_); | 144 DCHECK(decoder_); |
108 DCHECK(!completion_cb.is_null()); | 145 decoder_->SetVolume(volume); |
109 DCHECK(completion_cb_.is_null()); | 146 } |
110 if (error_) { | 147 |
111 completion_cb.Run(false); | 148 private: |
| 149 void PushBuffer() { |
| 150 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 151 DCHECK(push_in_progress_); |
| 152 |
| 153 if (!source_callback_) { |
| 154 push_in_progress_ = false; |
112 return; | 155 return; |
113 } | 156 } |
114 | 157 |
115 backend_buffer_ = decoder_buffer; | 158 MediaPipelineBackend::AudioDecoder::RenderingDelay rendering_delay = |
116 completion_cb_ = completion_cb; | 159 decoder_->GetRenderingDelay(); |
117 BufferStatus status = decoder_->PushBuffer(backend_buffer_.get()); | 160 base::TimeDelta delay = |
| 161 base::TimeDelta::FromMicroseconds(rendering_delay.delay_microseconds); |
| 162 base::TimeTicks delay_timestamp = |
| 163 base::TimeTicks() + base::TimeDelta::FromMicroseconds( |
| 164 rendering_delay.timestamp_microseconds); |
| 165 int frame_count = source_callback_->OnMoreData(delay, delay_timestamp, 0, |
| 166 audio_bus_.get()); |
| 167 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << delay; |
| 168 |
| 169 DCHECK_EQ(frame_count, audio_bus_->frames()); |
| 170 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()), |
| 171 frame_count * audio_params_.GetBytesPerFrame()); |
| 172 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, |
| 173 decoder_buffer_->writable_data()); |
| 174 decoder_buffer_->set_timestamp(timestamp_helper_.GetTimestamp()); |
| 175 timestamp_helper_.AddFrames(frame_count); |
| 176 |
| 177 BufferStatus status = decoder_->PushBuffer(decoder_buffer_.get()); |
118 if (status != MediaPipelineBackend::kBufferPending) | 178 if (status != MediaPipelineBackend::kBufferPending) |
119 OnPushBufferComplete(status); | 179 OnPushBufferComplete(status); |
120 } | 180 } |
121 | 181 |
122 void SetVolume(double volume) { | |
123 DCHECK(thread_checker_.CalledOnValidThread()); | |
124 DCHECK(decoder_); | |
125 decoder_->SetVolume(volume); | |
126 } | |
127 | |
128 MediaPipelineBackend::AudioDecoder::RenderingDelay GetRenderingDelay() { | |
129 DCHECK(thread_checker_.CalledOnValidThread()); | |
130 DCHECK(decoder_); | |
131 return decoder_->GetRenderingDelay(); | |
132 } | |
133 | |
134 private: | |
135 // MediaPipelineBackend::Decoder::Delegate implementation | 182 // MediaPipelineBackend::Decoder::Delegate implementation |
136 void OnPushBufferComplete(BufferStatus status) override { | 183 void OnPushBufferComplete(BufferStatus status) override { |
137 DCHECK(thread_checker_.CalledOnValidThread()); | 184 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
138 DCHECK_NE(status, MediaPipelineBackend::kBufferPending); | 185 DCHECK_NE(status, MediaPipelineBackend::kBufferPending); |
139 | 186 |
140 // |completion_cb_| may be null if OnDecoderError was called. | 187 DCHECK(push_in_progress_); |
141 if (completion_cb_.is_null()) | 188 push_in_progress_ = false; |
| 189 |
| 190 if (!source_callback_) |
142 return; | 191 return; |
143 | 192 |
144 base::ResetAndReturn(&completion_cb_) | 193 if (status != MediaPipelineBackend::kBufferSuccess) { |
145 .Run(status == MediaPipelineBackend::kBufferSuccess); | 194 source_callback_->OnError(); |
| 195 return; |
| 196 } |
| 197 |
| 198 // Schedule next push buffer. We don't want to allow more than |
| 199 // kMaxQueuedDataMs of queued audio. |
| 200 const base::TimeTicks now = base::TimeTicks::Now(); |
| 201 next_push_time_ = std::max(now, next_push_time_ + buffer_duration_); |
| 202 |
| 203 base::TimeDelta delay = (next_push_time_ - now) - |
| 204 base::TimeDelta::FromMilliseconds(kMaxQueuedDataMs); |
| 205 delay = std::max(delay, base::TimeDelta()); |
| 206 |
| 207 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| 208 FROM_HERE, base::Bind(&Backend::PushBuffer, weak_factory_.GetWeakPtr()), |
| 209 delay); |
| 210 push_in_progress_ = true; |
146 } | 211 } |
147 | 212 |
148 void OnEndOfStream() override {} | 213 void OnEndOfStream() override {} |
149 | 214 |
150 void OnDecoderError() override { | 215 void OnDecoderError() override { |
151 DCHECK(thread_checker_.CalledOnValidThread()); | 216 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
152 error_ = true; | 217 OnPushBufferComplete(MediaPipelineBackend::kBufferFailed); |
153 if (!completion_cb_.is_null()) | |
154 OnPushBufferComplete(MediaPipelineBackend::kBufferFailed); | |
155 } | 218 } |
156 | 219 |
157 void OnKeyStatusChanged(const std::string& key_id, | 220 void OnKeyStatusChanged(const std::string& key_id, |
158 CastKeyStatus key_status, | 221 CastKeyStatus key_status, |
159 uint32_t system_code) override {} | 222 uint32_t system_code) override {} |
160 | 223 |
161 void OnVideoResolutionChanged(const Size& size) override {} | 224 void OnVideoResolutionChanged(const Size& size) override {} |
162 | 225 |
| 226 const ::media::AudioParameters audio_params_; |
| 227 std::unique_ptr<::media::AudioBus> audio_bus_; |
| 228 scoped_refptr<media::DecoderBufferBase> decoder_buffer_; |
| 229 ::media::AudioTimestampHelper timestamp_helper_; |
| 230 const base::TimeDelta buffer_duration_; |
| 231 bool first_start_; |
| 232 bool push_in_progress_; |
| 233 base::TimeTicks next_push_time_; |
| 234 std::unique_ptr<TaskRunnerImpl> backend_task_runner_; |
163 std::unique_ptr<MediaPipelineBackend> backend_; | 235 std::unique_ptr<MediaPipelineBackend> backend_; |
164 std::unique_ptr<TaskRunnerImpl> backend_task_runner_; | |
165 MediaPipelineBackend::AudioDecoder* decoder_; | 236 MediaPipelineBackend::AudioDecoder* decoder_; |
166 PushBufferCompletionCallback completion_cb_; | 237 AudioSourceCallback* source_callback_; |
167 bool first_start_; | |
168 bool error_; | |
169 scoped_refptr<DecoderBufferBase> backend_buffer_; | |
170 base::ThreadChecker thread_checker_; | |
171 | 238 |
| 239 THREAD_CHECKER(thread_checker_); |
| 240 base::WeakPtrFactory<Backend> weak_factory_; |
172 DISALLOW_COPY_AND_ASSIGN(Backend); | 241 DISALLOW_COPY_AND_ASSIGN(Backend); |
173 }; | 242 }; |
174 | 243 |
175 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner). | 244 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner). |
176 CastAudioOutputStream::CastAudioOutputStream( | 245 CastAudioOutputStream::CastAudioOutputStream( |
177 const ::media::AudioParameters& audio_params, | 246 const ::media::AudioParameters& audio_params, |
178 CastAudioManager* audio_manager) | 247 CastAudioManager* audio_manager) |
179 : audio_params_(audio_params), | 248 : audio_params_(audio_params), audio_manager_(audio_manager), volume_(1.0) { |
180 audio_manager_(audio_manager), | |
181 volume_(1.0), | |
182 source_callback_(nullptr), | |
183 timestamp_helper_(audio_params_.sample_rate()), | |
184 backend_(new Backend()), | |
185 buffer_duration_(audio_params.GetBufferDuration()), | |
186 push_in_progress_(false), | |
187 weak_factory_(this) { | |
188 VLOG(1) << "CastAudioOutputStream " << this << " created with " | 249 VLOG(1) << "CastAudioOutputStream " << this << " created with " |
189 << audio_params_.AsHumanReadableString(); | 250 << audio_params_.AsHumanReadableString(); |
190 } | 251 } |
191 | 252 |
192 CastAudioOutputStream::~CastAudioOutputStream() { | 253 CastAudioOutputStream::~CastAudioOutputStream() { |
| 254 DCHECK(!backend_); |
193 } | 255 } |
194 | 256 |
195 bool CastAudioOutputStream::Open() { | 257 bool CastAudioOutputStream::Open() { |
| 258 VLOG(1) << this << ": " << __func__; |
196 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 259 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
197 | 260 |
198 ::media::AudioParameters::Format format = audio_params_.format(); | 261 ::media::AudioParameters::Format format = audio_params_.format(); |
199 DCHECK((format == ::media::AudioParameters::AUDIO_PCM_LINEAR) || | 262 DCHECK((format == ::media::AudioParameters::AUDIO_PCM_LINEAR) || |
200 (format == ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY)); | 263 (format == ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY)); |
201 | 264 |
202 ::media::ChannelLayout channel_layout = audio_params_.channel_layout(); | 265 ::media::ChannelLayout channel_layout = audio_params_.channel_layout(); |
203 if ((channel_layout != ::media::CHANNEL_LAYOUT_MONO) && | 266 if ((channel_layout != ::media::CHANNEL_LAYOUT_MONO) && |
204 (channel_layout != ::media::CHANNEL_LAYOUT_STEREO)) { | 267 (channel_layout != ::media::CHANNEL_LAYOUT_STEREO)) { |
205 LOG(WARNING) << "Unsupported channel layout: " << channel_layout; | 268 LOG(WARNING) << "Unsupported channel layout: " << channel_layout; |
206 return false; | 269 return false; |
207 } | 270 } |
208 DCHECK_GE(audio_params_.channels(), 1); | 271 DCHECK_GE(audio_params_.channels(), 1); |
209 DCHECK_LE(audio_params_.channels(), 2); | 272 DCHECK_LE(audio_params_.channels(), 2); |
210 | 273 |
211 if (!backend_->Open(audio_params_, audio_manager_)) { | 274 bool success = false; |
212 LOG(WARNING) << "Failed to create media pipeline backend."; | 275 DCHECK(!backend_); |
213 return false; | 276 backend_ = base::MakeUnique<Backend>(audio_params_); |
| 277 { |
| 278 base::WaitableEvent completion_event( |
| 279 base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| 280 base::WaitableEvent::InitialState::NOT_SIGNALED); |
| 281 audio_manager_->backend_task_runner()->PostTask( |
| 282 FROM_HERE, |
| 283 base::BindOnce( |
| 284 &Backend::Open, base::Unretained(backend_.get()), |
| 285 audio_manager_->backend_factory(), |
| 286 base::BindOnce(&SignalWaitableEvent, &success, &completion_event))); |
| 287 completion_event.Wait(); |
214 } | 288 } |
215 | 289 |
216 audio_bus_ = ::media::AudioBus::Create(audio_params_); | 290 if (!success) |
217 decoder_buffer_ = new DecoderBufferAdapter( | 291 LOG(WARNING) << "Failed to open audio output stream."; |
218 new ::media::DecoderBuffer(audio_params_.GetBytesPerBuffer())); | 292 return success; |
219 timestamp_helper_.SetBaseTimestamp(base::TimeDelta()); | |
220 | |
221 VLOG(1) << __FUNCTION__ << " : " << this; | |
222 return true; | |
223 } | 293 } |
224 | 294 |
225 void CastAudioOutputStream::Close() { | 295 void CastAudioOutputStream::Close() { |
| 296 VLOG(1) << this << ": " << __func__; |
226 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 297 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
227 VLOG(1) << __FUNCTION__ << " : " << this; | |
228 | 298 |
229 backend_->Close(); | 299 DCHECK(backend_); |
| 300 audio_manager_->backend_task_runner()->DeleteSoon(FROM_HERE, |
| 301 backend_.release()); |
| 302 |
230 // Signal to the manager that we're closed and can be removed. | 303 // Signal to the manager that we're closed and can be removed. |
231 // This should be the last call in the function as it deletes "this". | 304 // This should be the last call in the function as it deletes "this". |
232 audio_manager_->ReleaseOutputStream(this); | 305 audio_manager_->ReleaseOutputStream(this); |
233 } | 306 } |
234 | 307 |
235 void CastAudioOutputStream::Start(AudioSourceCallback* source_callback) { | 308 void CastAudioOutputStream::Start(AudioSourceCallback* source_callback) { |
| 309 VLOG(2) << this << ": " << __func__; |
236 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 310 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
237 DCHECK(source_callback); | 311 DCHECK(source_callback); |
| 312 DCHECK(backend_); |
238 | 313 |
239 source_callback_ = source_callback; | 314 audio_manager_->backend_task_runner()->PostTask( |
240 backend_->Start(); | 315 FROM_HERE, |
241 | 316 base::BindOnce(&Backend::Start, base::Unretained(backend_.get()), |
242 next_push_time_ = base::TimeTicks::Now(); | 317 source_callback)); |
243 if (!push_in_progress_) { | |
244 audio_manager_->GetTaskRunner()->PostTask( | |
245 FROM_HERE, base::Bind(&CastAudioOutputStream::PushBuffer, | |
246 weak_factory_.GetWeakPtr())); | |
247 push_in_progress_ = true; | |
248 } | |
249 | 318 |
250 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio(); | 319 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio(); |
251 } | 320 } |
252 | 321 |
253 void CastAudioOutputStream::Stop() { | 322 void CastAudioOutputStream::Stop() { |
| 323 VLOG(2) << this << ": " << __func__; |
254 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 324 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 325 DCHECK(backend_); |
255 | 326 |
256 source_callback_ = nullptr; | 327 base::WaitableEvent completion_event( |
257 backend_->Stop(); | 328 base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| 329 base::WaitableEvent::InitialState::NOT_SIGNALED); |
| 330 audio_manager_->backend_task_runner()->PostTask( |
| 331 FROM_HERE, |
| 332 base::BindOnce(&Backend::Stop, base::Unretained(backend_.get()), |
| 333 base::BindOnce(&base::WaitableEvent::Signal, |
| 334 base::Unretained(&completion_event)))); |
| 335 completion_event.Wait(); |
258 } | 336 } |
259 | 337 |
260 void CastAudioOutputStream::SetVolume(double volume) { | 338 void CastAudioOutputStream::SetVolume(double volume) { |
| 339 VLOG(2) << this << ": " << __func__ << "(" << volume << ")"; |
261 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 340 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
262 | 341 |
263 volume_ = volume; | 342 volume_ = volume; |
264 backend_->SetVolume(volume); | 343 if (backend_) { |
| 344 audio_manager_->backend_task_runner()->PostTask( |
| 345 FROM_HERE, base::BindOnce(&Backend::SetVolume, |
| 346 base::Unretained(backend_.get()), volume_)); |
| 347 } |
265 } | 348 } |
266 | 349 |
267 void CastAudioOutputStream::GetVolume(double* volume) { | 350 void CastAudioOutputStream::GetVolume(double* volume) { |
268 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 351 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
269 | 352 |
270 *volume = volume_; | 353 *volume = volume_; |
271 } | 354 } |
272 | 355 |
273 void CastAudioOutputStream::PushBuffer() { | |
274 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | |
275 DCHECK(push_in_progress_); | |
276 | |
277 if (!source_callback_) { | |
278 push_in_progress_ = false; | |
279 return; | |
280 } | |
281 | |
282 MediaPipelineBackend::AudioDecoder::RenderingDelay rendering_delay = | |
283 backend_->GetRenderingDelay(); | |
284 base::TimeDelta delay = | |
285 base::TimeDelta::FromMicroseconds(rendering_delay.delay_microseconds); | |
286 base::TimeTicks delay_timestamp = | |
287 base::TimeTicks() + | |
288 base::TimeDelta::FromMicroseconds(rendering_delay.timestamp_microseconds); | |
289 int frame_count = | |
290 source_callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus_.get()); | |
291 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << delay; | |
292 | |
293 DCHECK_EQ(frame_count, audio_bus_->frames()); | |
294 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()), | |
295 frame_count * audio_params_.GetBytesPerFrame()); | |
296 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, | |
297 decoder_buffer_->writable_data()); | |
298 decoder_buffer_->set_timestamp(timestamp_helper_.GetTimestamp()); | |
299 timestamp_helper_.AddFrames(frame_count); | |
300 | |
301 auto completion_cb = base::Bind(&CastAudioOutputStream::OnPushBufferComplete, | |
302 weak_factory_.GetWeakPtr()); | |
303 backend_->PushBuffer(decoder_buffer_, completion_cb); | |
304 } | |
305 | |
306 void CastAudioOutputStream::OnPushBufferComplete(bool success) { | |
307 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | |
308 DCHECK(push_in_progress_); | |
309 | |
310 push_in_progress_ = false; | |
311 | |
312 if (!source_callback_) | |
313 return; | |
314 if (!success) { | |
315 source_callback_->OnError(); | |
316 return; | |
317 } | |
318 | |
319 // Schedule next push buffer. We don't want to allow more than | |
320 // kMaxQueuedDataMs of queued audio. | |
321 const base::TimeTicks now = base::TimeTicks::Now(); | |
322 next_push_time_ = std::max(now, next_push_time_ + buffer_duration_); | |
323 | |
324 base::TimeDelta delay = (next_push_time_ - now) - | |
325 base::TimeDelta::FromMilliseconds(kMaxQueuedDataMs); | |
326 delay = std::max(delay, base::TimeDelta()); | |
327 | |
328 audio_manager_->GetTaskRunner()->PostDelayedTask( | |
329 FROM_HERE, base::Bind(&CastAudioOutputStream::PushBuffer, | |
330 weak_factory_.GetWeakPtr()), | |
331 delay); | |
332 push_in_progress_ = true; | |
333 } | |
334 | |
335 } // namespace media | 356 } // namespace media |
336 } // namespace chromecast | 357 } // namespace chromecast |
OLD | NEW |