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"
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
{
25 bool InitClockDevice(MediaClockDevice
* clock_device
) {
27 DCHECK_EQ(clock_device
->GetState(), MediaClockDevice::kStateUninitialized
);
29 if (!clock_device
->SetState(media::MediaClockDevice::kStateIdle
))
32 if (!clock_device
->ResetTimeline(0))
35 if (!clock_device
->SetRate(1.0))
41 bool InitAudioDevice(const ::media::AudioParameters
& audio_params
,
42 AudioPipelineDevice
* 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
))
58 if (!audio_device
->SetState(AudioPipelineDevice::kStateIdle
))
65 CastAudioOutputStream::CastAudioOutputStream(
66 const ::media::AudioParameters
& audio_params
,
67 CastAudioManager
* audio_manager
)
68 : audio_params_(audio_params
),
69 audio_manager_(audio_manager
),
71 audio_device_busy_(false),
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
;
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.";
99 if (!InitClockDevice(media_pipeline_backend_
->GetClock())) {
100 LOG(WARNING
) << "Failed to initialize clock device.";
104 if (!InitAudioDevice(audio_params_
, media_pipeline_backend_
->GetAudio())) {
105 LOG(WARNING
) << "Failed to initialize audio device.";
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;
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();
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.";
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.";
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
);
178 void CastAudioOutputStream::GetVolume(double* 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.";
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);
241 } // namespace chromecast