Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chromecast / media / audio / cast_audio_output_stream.cc
blob8a394668ad3f16f45db3502cf162870cb51e1be9
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
3 // found in the LICENSE file.
5 #include "chromecast/media/audio/cast_audio_output_stream.h"
7 #include "base/bind.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "chromecast/media/audio/cast_audio_manager.h"
10 #include "chromecast/media/cma/base/cast_decoder_buffer_impl.h"
11 #include "chromecast/media/cma/base/decoder_buffer_adapter.h"
12 #include "chromecast/media/cma/pipeline/frame_status_cb_impl.h"
13 #include "chromecast/public/media/audio_pipeline_device.h"
14 #include "chromecast/public/media/decoder_config.h"
15 #include "chromecast/public/media/decrypt_context.h"
16 #include "chromecast/public/media/media_clock_device.h"
17 #include "chromecast/public/media/media_pipeline_backend.h"
18 #include "media/audio/fake_audio_worker.h"
19 #include "media/base/decoder_buffer.h"
21 namespace chromecast {
22 namespace media {
24 namespace {
25 bool InitClockDevice(MediaClockDevice* clock_device) {
26 DCHECK(clock_device);
27 DCHECK_EQ(clock_device->GetState(), MediaClockDevice::kStateUninitialized);
29 if (!clock_device->SetState(media::MediaClockDevice::kStateIdle))
30 return false;
32 if (!clock_device->ResetTimeline(0))
33 return false;
35 if (!clock_device->SetRate(1.0))
36 return false;
38 return true;
41 bool InitAudioDevice(const ::media::AudioParameters& audio_params,
42 AudioPipelineDevice* audio_device) {
43 DCHECK(audio_device);
44 DCHECK_EQ(audio_device->GetState(), AudioPipelineDevice::kStateUninitialized);
46 AudioConfig audio_config;
47 audio_config.codec = kCodecPCM;
48 audio_config.sample_format = kSampleFormatS16;
49 audio_config.bytes_per_channel = audio_params.bits_per_sample() / 8;
50 audio_config.channel_number = audio_params.channels();
51 audio_config.samples_per_second = audio_params.sample_rate();
52 audio_config.extra_data = nullptr;
53 audio_config.extra_data_size = 0;
54 audio_config.is_encrypted = false;
55 if (!audio_device->SetConfig(audio_config))
56 return false;
58 if (!audio_device->SetState(AudioPipelineDevice::kStateIdle))
59 return false;
61 return true;
63 } // namespace
65 CastAudioOutputStream::CastAudioOutputStream(
66 const ::media::AudioParameters& audio_params,
67 CastAudioManager* audio_manager)
68 : audio_params_(audio_params),
69 audio_manager_(audio_manager),
70 volume_(1.0),
71 audio_device_busy_(false),
72 weak_factory_(this) {
73 VLOG(1) << "CastAudioOutputStream " << this << " created with "
74 << audio_params_.AsHumanReadableString();
77 CastAudioOutputStream::~CastAudioOutputStream() {}
79 bool CastAudioOutputStream::Open() {
80 ::media::AudioParameters::Format format = audio_params_.format();
81 DCHECK((format == ::media::AudioParameters::AUDIO_PCM_LINEAR) ||
82 (format == ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY));
84 ::media::ChannelLayout channel_layout = audio_params_.channel_layout();
85 if ((channel_layout != ::media::CHANNEL_LAYOUT_MONO) &&
86 (channel_layout != ::media::CHANNEL_LAYOUT_STEREO)) {
87 LOG(WARNING) << "Unsupported channel layout: " << channel_layout;
88 return false;
90 DCHECK_GE(audio_params_.channels(), 1);
91 DCHECK_LE(audio_params_.channels(), 2);
93 media_pipeline_backend_ = audio_manager_->CreateMediaPipelineBackend();
94 if (!media_pipeline_backend_) {
95 LOG(WARNING) << "Failed to create media pipeline backend.";
96 return false;
99 if (!InitClockDevice(media_pipeline_backend_->GetClock())) {
100 LOG(WARNING) << "Failed to initialize clock device.";
101 return false;
104 if (!InitAudioDevice(audio_params_, media_pipeline_backend_->GetAudio())) {
105 LOG(WARNING) << "Failed to initialize audio device.";
106 return false;
109 audio_bus_ = ::media::AudioBus::Create(audio_params_);
110 audio_worker_.reset(new ::media::FakeAudioWorker(
111 base::ThreadTaskRunnerHandle::Get(), audio_params_));
113 VLOG(1) << __FUNCTION__ << " : " << this;
114 return true;
117 void CastAudioOutputStream::Close() {
118 VLOG(1) << __FUNCTION__ << " : " << this;
120 if (media_pipeline_backend_) {
121 media_pipeline_backend_->GetClock()->SetState(MediaClockDevice::kStateIdle);
122 media_pipeline_backend_->GetAudio()->SetState(
123 AudioPipelineDevice::kStateIdle);
126 audio_worker_.reset();
127 audio_bus_.reset();
128 media_pipeline_backend_.reset();
130 // Signal to the manager that we're closed and can be removed.
131 // This should be the last call in the function as it deletes "this".
132 audio_manager_->ReleaseOutputStream(this);
135 void CastAudioOutputStream::Start(AudioSourceCallback* source_callback) {
136 MediaClockDevice* clock_device = media_pipeline_backend_->GetClock();
137 DCHECK(clock_device);
138 if (!clock_device->SetState(MediaClockDevice::kStateRunning)) {
139 LOG(WARNING) << "Failed to run clock device.";
140 return;
142 clock_device->SetRate(1.0f);
144 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
145 DCHECK(audio_device);
146 if (!audio_device->SetState(AudioPipelineDevice::kStateRunning)) {
147 LOG(WARNING) << "Failed to run audio device.";
148 return;
151 VLOG(1) << __FUNCTION__ << " : " << this;
152 audio_worker_->Start(base::Bind(&CastAudioOutputStream::PushFrame,
153 weak_factory_.GetWeakPtr(), source_callback));
156 void CastAudioOutputStream::Stop() {
157 VLOG(1) << __FUNCTION__ << " : " << this;
159 MediaClockDevice* clock_device = media_pipeline_backend_->GetClock();
160 DCHECK(clock_device);
161 clock_device->SetState(MediaClockDevice::kStateIdle);
162 clock_device->SetRate(0.0f);
164 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
165 DCHECK(audio_device);
166 audio_device->SetState(AudioPipelineDevice::kStatePaused);
167 audio_worker_->Stop();
168 audio_device_busy_ = false;
171 void CastAudioOutputStream::SetVolume(double volume) {
172 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
173 DCHECK(audio_device);
174 audio_device->SetStreamVolumeMultiplier(volume);
175 volume_ = volume;
178 void CastAudioOutputStream::GetVolume(double* volume) {
179 *volume = volume_;
182 void CastAudioOutputStream::PushFrame(AudioSourceCallback* source_callback) {
183 DCHECK(source_callback);
184 if (audio_device_busy_) {
185 // Skip pulling data if audio device is still busy.
186 LOG(WARNING) << __FUNCTION__ << " : " << this
187 << " skipped because audio device is busy.";
188 return;
191 int frame_count = source_callback->OnMoreData(audio_bus_.get(), 0);
192 DCHECK_EQ(frame_count, audio_bus_->frames());
193 int buffer_size = frame_count * audio_params_.GetBytesPerFrame();
194 scoped_refptr<::media::DecoderBuffer> decoder_buffer(
195 new ::media::DecoderBuffer(buffer_size));
196 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8,
197 decoder_buffer->writable_data());
199 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
200 DCHECK(audio_device);
201 MediaComponentDevice::FrameStatus status = audio_device->PushFrame(
202 nullptr, // decrypt_context
203 new CastDecoderBufferImpl(new DecoderBufferAdapter(decoder_buffer)),
204 new media::FrameStatusCBImpl(
205 base::Bind(&CastAudioOutputStream::OnPushFrameStatus,
206 weak_factory_.GetWeakPtr(), source_callback)));
208 if (status == MediaComponentDevice::kFrameFailed) {
209 // Note: We cannot call OnPushFrameError directly because it will lead to
210 // a recursive lock, which is not supported by base::Lock.
211 // This callback is called with a lock held inside FakeAudioWorker.
212 // Calling FakeAudioWorker::Stop will try to acquire the lock again.
213 base::ThreadTaskRunnerHandle::Get()->PostTask(
214 FROM_HERE, base::Bind(&CastAudioOutputStream::OnPushFrameError,
215 weak_factory_.GetWeakPtr(), source_callback));
216 } else if (status == MediaComponentDevice::kFramePending) {
217 audio_device_busy_ = true;
221 void CastAudioOutputStream::OnPushFrameStatus(
222 AudioSourceCallback* source_callback,
223 MediaComponentDevice::FrameStatus status) {
224 DCHECK(audio_device_busy_);
225 audio_device_busy_ = false;
227 DCHECK_NE(status, MediaComponentDevice::kFramePending);
228 if (status == MediaComponentDevice::kFrameFailed)
229 OnPushFrameError(source_callback);
232 void CastAudioOutputStream::OnPushFrameError(
233 AudioSourceCallback* source_callback) {
234 LOG(WARNING) << __FUNCTION__ << " : " << this;
235 // Inform audio source about the error and stop pulling data.
236 audio_worker_->Stop();
237 source_callback->OnError(this);
240 } // namespace media
241 } // namespace chromecast