Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Side by Side Diff: chromecast/media/audio/audio_output_stream.cc

Issue 1308153005: [Chromecast] Plumbs raw audio through CMA backend. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: adjusted logging Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chromecast/media/audio/audio_output_stream.h"
6
7 #include "base/bind.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "chromecast/media/audio/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"
20
21 namespace chromecast {
22 namespace media {
23
24 namespace {
25 bool InitClockDevice(MediaClockDevice* clock_device) {
26 DCHECK(clock_device);
27 DCHECK_EQ(clock_device->GetState(), MediaClockDevice::kStateUninitialized);
28
29 if (!clock_device->SetState(media::MediaClockDevice::kStateIdle))
30 return false;
31
32 if (!clock_device->ResetTimeline(0))
33 return false;
34
35 if (!clock_device->SetRate(1.0))
36 return false;
37
38 return true;
39 }
40
41 bool InitAudioDevice(const ::media::AudioParameters& audio_params,
42 AudioPipelineDevice* audio_device) {
43 DCHECK(audio_device);
44 DCHECK_EQ(audio_device->GetState(), AudioPipelineDevice::kStateUninitialized);
45
46 AudioConfig audio_config;
47 audio_config.codec = kCodecPCM;
48 audio_config.sample_format = kSampleFormatPlanarS16;
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;
57
58 if (!audio_device->SetState(AudioPipelineDevice::kStateIdle))
59 return false;
60
61 return true;
62 }
63 } // namespace
64
65 AudioOutputStream::AudioOutputStream(
66 const ::media::AudioParameters& audio_params,
67 AudioManager* 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 LOG(INFO) << __FUNCTION__ << " : " << this;
74 LOG(INFO) << "Audio Parameters: " << audio_params_.AsHumanReadableString();
halliwell 2015/09/01 21:27:04 nit: can we put these back on the same log line?
75 }
76
77 AudioOutputStream::~AudioOutputStream() {}
78
79 bool AudioOutputStream::Open() {
80 ::media::AudioParameters::Format format = audio_params_.format();
81 if ((format != ::media::AudioParameters::AUDIO_PCM_LINEAR) &&
82 (format != ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY)) {
83 LOG(WARNING) << "Unsupported audio format: " << format;
84 return false;
85 }
86
87 ::media::ChannelLayout channel_layout = audio_params_.channel_layout();
88 if ((channel_layout != ::media::CHANNEL_LAYOUT_MONO) &&
89 (channel_layout != ::media::CHANNEL_LAYOUT_STEREO)) {
90 LOG(WARNING) << "Unsupported channel layout: " << channel_layout;
91 return false;
92 }
93 DCHECK_GE(audio_params_.channels(), 1);
94 DCHECK_LE(audio_params_.channels(), 2);
95
96 media_pipeline_backend_ = audio_manager_->CreateMediaPipelineBackend();
97 if (!media_pipeline_backend_) {
98 LOG(WARNING) << "Failed to create media pipeline backend.";
99 return false;
100 }
101
102 if (!InitClockDevice(media_pipeline_backend_->GetClock())) {
103 LOG(WARNING) << "Failed to initialize clock device.";
104 return false;
105 }
106
107 if (!InitAudioDevice(audio_params_, media_pipeline_backend_->GetAudio())) {
108 LOG(WARNING) << "Failed to initialize audio device.";
109 return false;
110 }
111
112 audio_bus_ = ::media::AudioBus::Create(audio_params_);
113 audio_worker_.reset(new ::media::FakeAudioWorker(
114 base::ThreadTaskRunnerHandle::Get(), audio_params_));
115
116 LOG(INFO) << __FUNCTION__ << " : " << this;
117 return true;
118 }
119
120 void AudioOutputStream::Close() {
121 LOG(INFO) << __FUNCTION__ << " : " << this;
122
123 if (media_pipeline_backend_) {
124 media_pipeline_backend_->GetClock()->SetState(MediaClockDevice::kStateIdle);
125 media_pipeline_backend_->GetAudio()->SetState(
126 AudioPipelineDevice::kStateIdle);
127 }
128
129 audio_worker_.reset();
130 audio_bus_.reset();
131 media_pipeline_backend_.reset();
132
133 // Signal to the manager that we're closed and can be removed.
134 // This should be the last call in the function as it deletes "this".
135 audio_manager_->ReleaseOutputStream(this);
136 }
137
138 void AudioOutputStream::Start(AudioSourceCallback* source_callback) {
139 MediaClockDevice* clock_device = media_pipeline_backend_->GetClock();
140 DCHECK(clock_device);
141 if (!clock_device->SetState(MediaClockDevice::kStateRunning)) {
142 LOG(WARNING) << "Failed to run clock device.";
143 return;
144 }
145 clock_device->SetRate(1.0f);
146
147 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
148 DCHECK(audio_device);
149 if (!audio_device->SetState(AudioPipelineDevice::kStateRunning)) {
150 LOG(WARNING) << "Failed to run audio device.";
151 return;
152 }
153
154 LOG(INFO) << __FUNCTION__ << " : " << this;
155 audio_worker_->Start(base::Bind(&AudioOutputStream::PushFrame,
156 weak_factory_.GetWeakPtr(), source_callback));
157 }
158
159 void AudioOutputStream::Stop() {
160 LOG(INFO) << __FUNCTION__ << " : " << this;
161
162 MediaClockDevice* clock_device = media_pipeline_backend_->GetClock();
163 DCHECK(clock_device);
164 clock_device->SetState(MediaClockDevice::kStateIdle);
165 clock_device->SetRate(0.0f);
166
167 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
168 DCHECK(audio_device);
169 audio_device->SetState(AudioPipelineDevice::kStatePaused);
170 audio_worker_->Stop();
171 audio_device_busy_ = false;
172 }
173
174 void AudioOutputStream::SetVolume(double volume) {
175 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
176 DCHECK(audio_device);
177 audio_device->SetStreamVolumeMultiplier(volume);
178 volume_ = volume;
179 }
180
181 void AudioOutputStream::GetVolume(double* volume) {
182 *volume = volume_;
183 }
184
185 void AudioOutputStream::PushFrame(AudioSourceCallback* source_callback) {
186 DCHECK(source_callback);
187 if (audio_device_busy_) {
188 // Skip pulling data if audio device is still busy.
189 LOG(WARNING) << __FUNCTION__ << " : " << this
190 << " skipped because audio device is busy.";
halliwell 2015/09/01 21:27:03 hmmm ... isn't this log going to happen all the ti
alokp 2015/09/01 23:26:12 No it should not fire all the time. AudioWorker ca
191 return;
192 }
193
194 int frame_count = source_callback->OnMoreData(audio_bus_.get(), 0);
195 DCHECK_EQ(frame_count, audio_bus_->frames());
196 int buffer_size = frame_count * audio_params_.GetBytesPerFrame();
197 scoped_refptr<::media::DecoderBuffer> decoder_buffer(
198 new ::media::DecoderBuffer(buffer_size));
199 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8,
200 decoder_buffer->writable_data());
201
202 AudioPipelineDevice* audio_device = media_pipeline_backend_->GetAudio();
203 DCHECK(audio_device);
204 MediaComponentDevice::FrameStatus status = audio_device->PushFrame(
205 nullptr, // decrypt_context
206 new CastDecoderBufferImpl(new DecoderBufferAdapter(decoder_buffer)),
207 new media::FrameStatusCBImpl(
208 base::Bind(&AudioOutputStream::OnPushFrameStatus,
209 weak_factory_.GetWeakPtr(), source_callback)));
210
211 if (status == MediaComponentDevice::kFrameFailed) {
212 // Note: We cannot call OnPushFrameError directly because it will lead to
213 // a recursive lock, which is not supported by base::Lock.
214 // This callback is called with a lock held inside FakeAudioWorker.
215 // Calling FakeAudioWorker::Stop will try to acquire the lock again.
216 base::ThreadTaskRunnerHandle::Get()->PostTask(
217 FROM_HERE, base::Bind(&AudioOutputStream::OnPushFrameError,
218 weak_factory_.GetWeakPtr(), source_callback));
219 } else if (status == MediaComponentDevice::kFramePending) {
220 audio_device_busy_ = true;
221 }
222 }
223
224 void AudioOutputStream::OnPushFrameStatus(
225 AudioSourceCallback* source_callback,
226 MediaComponentDevice::FrameStatus status) {
227 DCHECK(audio_device_busy_);
228 audio_device_busy_ = false;
229
230 DCHECK_NE(status, MediaComponentDevice::kFramePending);
231 if (status == MediaComponentDevice::kFrameFailed)
232 OnPushFrameError(source_callback);
233 }
234
235 void AudioOutputStream::OnPushFrameError(AudioSourceCallback* source_callback) {
236 LOG(WARNING) << __FUNCTION__ << " : " << this;
237 // Inform audio source about the error and stop pulling data.
238 audio_worker_->Stop();
239 source_callback->OnError(this);
240 }
241
242 } // namespace media
243 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698