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/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), |
| 73 backend_buffer_(nullptr) { |
80 thread_checker_.DetachFromThread(); | 74 thread_checker_.DetachFromThread(); |
81 } | 75 } |
82 ~Backend() {} | 76 ~Backend() override {} |
83 | 77 |
84 void Open(CastAudioManager* audio_manager, | 78 void Open(CastAudioManager* audio_manager, |
85 bool* success, | 79 bool* success, |
86 base::WaitableEvent* completion_event) { | 80 base::WaitableEvent* completion_event) { |
87 DCHECK(thread_checker_.CalledOnValidThread()); | 81 DCHECK(thread_checker_.CalledOnValidThread()); |
88 DCHECK(backend_ == nullptr); | 82 DCHECK(backend_ == nullptr); |
| 83 DCHECK(audio_manager); |
| 84 DCHECK(success); |
| 85 DCHECK(completion_event); |
89 | 86 |
90 backend_task_runner_.reset(new TaskRunnerImpl()); | 87 backend_task_runner_.reset(new TaskRunnerImpl()); |
91 MediaPipelineDeviceParams device_params( | 88 MediaPipelineDeviceParams device_params( |
92 MediaPipelineDeviceParams::kModeIgnorePts, backend_task_runner_.get()); | 89 MediaPipelineDeviceParams::kModeIgnorePts, backend_task_runner_.get()); |
93 | 90 backend_ = audio_manager->CreateMediaPipelineBackend(device_params); |
94 scoped_ptr<MediaPipelineBackend> pipeline_backend = | 91 if (backend_) |
95 audio_manager->CreateMediaPipelineBackend(device_params); | 92 decoder_ = InitializeBackend(audio_params_, backend_.get(), this); |
96 if (pipeline_backend && InitClockDevice(pipeline_backend->GetClock()) && | 93 *success = decoder_ != nullptr; |
97 InitAudioDevice(audio_params_, pipeline_backend->GetAudio())) { | |
98 backend_ = pipeline_backend.Pass(); | |
99 } | |
100 *success = backend_ != nullptr; | |
101 completion_event->Signal(); | 94 completion_event->Signal(); |
102 } | 95 } |
103 | 96 |
104 void Close() { | 97 void Close() { |
105 DCHECK(thread_checker_.CalledOnValidThread()); | 98 DCHECK(thread_checker_.CalledOnValidThread()); |
106 | 99 |
107 if (backend_) { | 100 if (backend_) |
108 backend_->GetClock()->SetState(MediaClockDevice::kStateIdle); | 101 backend_->Stop(); |
109 backend_->GetAudio()->SetState(AudioPipelineDevice::kStateIdle); | |
110 } | |
111 backend_.reset(); | 102 backend_.reset(); |
112 backend_task_runner_.reset(); | 103 backend_task_runner_.reset(); |
113 } | 104 } |
114 | 105 |
115 void Start() { | 106 void Start() { |
116 DCHECK(thread_checker_.CalledOnValidThread()); | 107 DCHECK(thread_checker_.CalledOnValidThread()); |
| 108 DCHECK(backend_); |
117 | 109 |
118 MediaClockDevice* clock_device = backend_->GetClock(); | 110 if (first_start_) { |
119 clock_device->SetState(MediaClockDevice::kStateRunning); | 111 first_start_ = false; |
120 clock_device->SetRate(1.0f); | 112 backend_->Start(0); |
121 | 113 } else { |
122 AudioPipelineDevice* audio_device = backend_->GetAudio(); | 114 backend_->Resume(); |
123 audio_device->SetState(AudioPipelineDevice::kStateRunning); | 115 } |
124 } | 116 } |
125 | 117 |
126 void Stop() { | 118 void Stop() { |
127 DCHECK(thread_checker_.CalledOnValidThread()); | 119 DCHECK(thread_checker_.CalledOnValidThread()); |
| 120 DCHECK(backend_); |
128 | 121 |
129 MediaClockDevice* clock_device = backend_->GetClock(); | 122 backend_->Pause(); |
130 clock_device->SetRate(0.0f); | |
131 } | 123 } |
132 | 124 |
133 void PushFrame(scoped_refptr<media::DecoderBufferBase> decoder_buffer, | 125 void PushBuffer(scoped_refptr<media::DecoderBufferBase> decoder_buffer, |
134 const PushFrameCompletionCallback& completion_cb) { | 126 const PushBufferCompletionCallback& completion_cb) { |
135 DCHECK(thread_checker_.CalledOnValidThread()); | 127 DCHECK(thread_checker_.CalledOnValidThread()); |
| 128 DCHECK(decoder_); |
| 129 DCHECK(!completion_cb.is_null()); |
| 130 DCHECK(completion_cb_.is_null()); |
| 131 if (error_) { |
| 132 completion_cb.Run(false); |
| 133 return; |
| 134 } |
136 | 135 |
137 AudioPipelineDevice* audio_device = backend_->GetAudio(); | 136 backend_buffer_.set_buffer(decoder_buffer); |
138 MediaComponentDevice::FrameStatus status = | |
139 audio_device->PushFrame(nullptr, // decrypt_context | |
140 new CastDecoderBufferImpl(decoder_buffer), | |
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 /* decrypt_context */, &backend_buffer_); |
| 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 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 Loading... |
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 Loading... |
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 |
OLD | NEW |