Chromium Code Reviews| 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, | |
|
slan
2017/05/30 16:26:01
nit: DCHECKs on success and waitable_event
alokp
2017/05/30 23:19:33
Is it worth DCHECK'ing every pointer, especially t
slan
2017/05/30 23:44:39
SGTM
| |
| 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__; | |
|
slan
2017/05/30 16:26:01
rm
alokp
2017/05/30 23:19:33
Actually I want to log this event.
slan
2017/05/30 23:44:39
Acknowledged.
| |
| 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 |