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) { | |
80 thread_checker_.DetachFromThread(); | 73 thread_checker_.DetachFromThread(); |
81 } | 74 } |
82 ~Backend() {} | 75 ~Backend() override {} |
83 | 76 |
84 void Open(CastAudioManager* audio_manager, | 77 void Open(CastAudioManager* audio_manager, |
85 bool* success, | 78 bool* success, |
86 base::WaitableEvent* completion_event) { | 79 base::WaitableEvent* completion_event) { |
87 DCHECK(thread_checker_.CalledOnValidThread()); | 80 DCHECK(thread_checker_.CalledOnValidThread()); |
88 DCHECK(backend_ == nullptr); | 81 DCHECK(backend_ == nullptr); |
82 DCHECK(audio_manager); | |
83 DCHECK(success); | |
84 DCHECK(completion_event); | |
89 | 85 |
90 backend_task_runner_.reset(new TaskRunnerImpl()); | 86 backend_task_runner_.reset(new TaskRunnerImpl()); |
91 MediaPipelineDeviceParams device_params( | 87 MediaPipelineDeviceParams device_params( |
92 MediaPipelineDeviceParams::kModeIgnorePts, backend_task_runner_.get()); | 88 MediaPipelineDeviceParams::kModeIgnorePts, backend_task_runner_.get()); |
93 | 89 backend_ = audio_manager->CreateMediaPipelineBackend(device_params); |
94 scoped_ptr<MediaPipelineBackend> pipeline_backend = | 90 if (backend_) |
95 audio_manager->CreateMediaPipelineBackend(device_params); | 91 decoder_ = InitializeBackend(audio_params_, backend_.get(), this); |
96 if (pipeline_backend && InitClockDevice(pipeline_backend->GetClock()) && | 92 *success = decoder_ != nullptr; |
97 InitAudioDevice(audio_params_, pipeline_backend->GetAudio())) { | |
98 backend_ = pipeline_backend.Pass(); | |
99 } | |
100 *success = backend_ != nullptr; | |
101 completion_event->Signal(); | 93 completion_event->Signal(); |
102 } | 94 } |
103 | 95 |
104 void Close() { | 96 void Close() { |
105 DCHECK(thread_checker_.CalledOnValidThread()); | 97 DCHECK(thread_checker_.CalledOnValidThread()); |
106 | 98 |
107 if (backend_) { | 99 if (backend_) |
108 backend_->GetClock()->SetState(MediaClockDevice::kStateIdle); | 100 backend_->Stop(); |
109 backend_->GetAudio()->SetState(AudioPipelineDevice::kStateIdle); | |
110 } | |
111 backend_.reset(); | 101 backend_.reset(); |
112 backend_task_runner_.reset(); | 102 backend_task_runner_.reset(); |
113 } | 103 } |
114 | 104 |
115 void Start() { | 105 void Start() { |
116 DCHECK(thread_checker_.CalledOnValidThread()); | 106 DCHECK(thread_checker_.CalledOnValidThread()); |
107 DCHECK(backend_); | |
117 | 108 |
118 MediaClockDevice* clock_device = backend_->GetClock(); | 109 if (first_start_) |
119 clock_device->SetState(MediaClockDevice::kStateRunning); | 110 backend_->Start(0); |
120 clock_device->SetRate(1.0f); | 111 else |
121 | 112 backend_->Resume(); |
alokp
2015/10/14 23:16:52
Is it legal to call Start on a backend in error st
kmackay
2015/10/15 00:35:53
I don't see how the backend could be in an error s
| |
122 AudioPipelineDevice* audio_device = backend_->GetAudio(); | |
123 audio_device->SetState(AudioPipelineDevice::kStateRunning); | |
124 } | 113 } |
125 | 114 |
126 void Stop() { | 115 void Stop() { |
127 DCHECK(thread_checker_.CalledOnValidThread()); | 116 DCHECK(thread_checker_.CalledOnValidThread()); |
117 DCHECK(backend_); | |
128 | 118 |
129 MediaClockDevice* clock_device = backend_->GetClock(); | 119 backend_->Pause(); |
130 clock_device->SetRate(0.0f); | 120 first_start_ = false; |
alokp
2015/10/14 23:16:52
move this to Start?
kmackay
2015/10/15 00:35:53
Done.
| |
131 } | 121 } |
132 | 122 |
133 void PushFrame(scoped_refptr<media::DecoderBufferBase> decoder_buffer, | 123 void PushBuffer(scoped_refptr<media::DecoderBufferBase> decoder_buffer, |
134 const PushFrameCompletionCallback& completion_cb) { | 124 const PushBufferCompletionCallback& completion_cb) { |
135 DCHECK(thread_checker_.CalledOnValidThread()); | 125 DCHECK(thread_checker_.CalledOnValidThread()); |
126 DCHECK(decoder_); | |
127 DCHECK(completion_cb_.is_null()); | |
alokp
2015/10/14 23:16:52
Shouldn't this be !is_null?
kmackay
2015/10/15 00:35:53
No, this is checking the member variable to make s
| |
128 if (error_) { | |
129 completion_cb.Run(false); | |
130 return; | |
131 } | |
136 | 132 |
137 AudioPipelineDevice* audio_device = backend_->GetAudio(); | 133 if (backend_buffer_) |
138 MediaComponentDevice::FrameStatus status = | 134 backend_buffer_->set_buffer(decoder_buffer); |
139 audio_device->PushFrame(nullptr, // decrypt_context | 135 else |
140 new CastDecoderBufferImpl(decoder_buffer), | 136 backend_buffer_.reset(new CastDecoderBufferImpl(decoder_buffer)); |
halliwell
2015/10/14 15:02:37
This would be slightly cleaner if we added a no-ar
kmackay
2015/10/14 21:27:26
Done.
| |
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, backend_buffer_.get()); |
alokp
2015/10/14 23:16:52
either document what nullptr is or use a variable
kmackay
2015/10/15 00:35:53
Done.
| |
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 scoped_ptr<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 |