1 // Copyright (c) 2012 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 "media/audio/pulse/pulse_input.h"
7 #include <pulse/pulseaudio.h>
9 #include "base/logging.h"
10 #include "media/audio/pulse/audio_manager_pulse.h"
11 #include "media/audio/pulse/pulse_util.h"
12 #include "media/base/seekable_buffer.h"
16 using pulse::AutoPulseLock
;
17 using pulse::WaitForOperationCompletion
;
19 PulseAudioInputStream::PulseAudioInputStream(AudioManagerPulse
* audio_manager
,
20 const std::string
& device_name
,
21 const AudioParameters
& params
,
22 pa_threaded_mainloop
* mainloop
,
24 : audio_manager_(audio_manager
),
26 device_name_(device_name
),
30 stream_started_(false),
31 pa_mainloop_(mainloop
),
34 context_state_changed_(false) {
39 PulseAudioInputStream::~PulseAudioInputStream() {
40 // All internal structures should already have been freed in Close(),
41 // which calls AudioManagerPulse::Release which deletes this object.
45 bool PulseAudioInputStream::Open() {
46 DCHECK(thread_checker_
.CalledOnValidThread());
47 AutoPulseLock
auto_lock(pa_mainloop_
);
48 if (!pulse::CreateInputStream(pa_mainloop_
, pa_context_
, &handle_
, params_
,
49 device_name_
, &StreamNotifyCallback
, this)) {
55 buffer_
.reset(new media::SeekableBuffer(0, 2 * params_
.GetBytesPerBuffer()));
56 audio_data_buffer_
.reset(new uint8
[params_
.GetBytesPerBuffer()]);
60 void PulseAudioInputStream::Start(AudioInputCallback
* callback
) {
61 DCHECK(thread_checker_
.CalledOnValidThread());
65 // AGC needs to be started out of the lock.
68 AutoPulseLock
auto_lock(pa_mainloop_
);
73 // Clean up the old buffer.
74 pa_stream_drop(handle_
);
77 // Start the streaming.
79 pa_stream_set_read_callback(handle_
, &ReadCallback
, this);
80 pa_stream_readable_size(handle_
);
81 stream_started_
= true;
83 pa_operation
* operation
= pa_stream_cork(handle_
, 0, NULL
, NULL
);
84 WaitForOperationCompletion(pa_mainloop_
, operation
);
87 void PulseAudioInputStream::Stop() {
88 DCHECK(thread_checker_
.CalledOnValidThread());
89 AutoPulseLock
auto_lock(pa_mainloop_
);
95 // Set the flag to false to stop filling new data to soundcard.
96 stream_started_
= false;
98 pa_operation
* operation
= pa_stream_flush(handle_
,
99 &pulse::StreamSuccessCallback
,
101 WaitForOperationCompletion(pa_mainloop_
, operation
);
104 pa_stream_set_read_callback(handle_
, NULL
, NULL
);
105 operation
= pa_stream_cork(handle_
, 1, &pulse::StreamSuccessCallback
,
107 WaitForOperationCompletion(pa_mainloop_
, operation
);
110 void PulseAudioInputStream::Close() {
111 DCHECK(thread_checker_
.CalledOnValidThread());
113 AutoPulseLock
auto_lock(pa_mainloop_
);
115 // Disable all the callbacks before disconnecting.
116 pa_stream_set_state_callback(handle_
, NULL
, NULL
);
117 pa_stream_flush(handle_
, NULL
, NULL
);
119 if (pa_stream_get_state(handle_
) != PA_STREAM_UNCONNECTED
)
120 pa_stream_disconnect(handle_
);
122 // Release PulseAudio structures.
123 pa_stream_unref(handle_
);
129 callback_
->OnClose(this);
131 // Signal to the manager that we're closed and can be removed.
132 // This should be the last call in the function as it deletes "this".
133 audio_manager_
->ReleaseInputStream(this);
136 double PulseAudioInputStream::GetMaxVolume() {
137 return static_cast<double>(PA_VOLUME_NORM
);
140 void PulseAudioInputStream::SetVolume(double volume
) {
141 AutoPulseLock
auto_lock(pa_mainloop_
);
145 size_t index
= pa_stream_get_device_index(handle_
);
146 pa_operation
* operation
= NULL
;
148 // Get the number of channels for the source only when the |channels_| is 0.
149 // We are assuming the stream source is not changed on the fly here.
150 operation
= pa_context_get_source_info_by_index(
151 pa_context_
, index
, &VolumeCallback
, this);
152 WaitForOperationCompletion(pa_mainloop_
, operation
);
154 DLOG(WARNING
) << "Failed to get the number of channels for the source";
159 pa_cvolume pa_volume
;
160 pa_cvolume_set(&pa_volume
, channels_
, volume
);
161 operation
= pa_context_set_source_volume_by_index(
162 pa_context_
, index
, &pa_volume
, NULL
, NULL
);
164 // Don't need to wait for this task to complete.
165 pa_operation_unref(operation
);
168 double PulseAudioInputStream::GetVolume() {
169 if (pa_threaded_mainloop_in_thread(pa_mainloop_
)) {
170 // When being called by the pulse thread, GetVolume() is asynchronous and
171 // called under AutoPulseLock.
175 size_t index
= pa_stream_get_device_index(handle_
);
176 pa_operation
* operation
= pa_context_get_source_info_by_index(
177 pa_context_
, index
, &VolumeCallback
, this);
178 // Do not wait for the operation since we can't block the pulse thread.
179 pa_operation_unref(operation
);
181 // Return zero and the callback will asynchronously update the |volume_|.
184 // Called by other thread, put an AutoPulseLock and wait for the operation.
185 AutoPulseLock
auto_lock(pa_mainloop_
);
189 size_t index
= pa_stream_get_device_index(handle_
);
190 pa_operation
* operation
= pa_context_get_source_info_by_index(
191 pa_context_
, index
, &VolumeCallback
, this);
192 WaitForOperationCompletion(pa_mainloop_
, operation
);
198 // static, used by pa_stream_set_read_callback.
199 void PulseAudioInputStream::ReadCallback(pa_stream
* handle
,
202 PulseAudioInputStream
* stream
=
203 reinterpret_cast<PulseAudioInputStream
*>(user_data
);
208 // static, used by pa_context_get_source_info_by_index.
209 void PulseAudioInputStream::VolumeCallback(pa_context
* context
,
210 const pa_source_info
* info
,
211 int error
, void* user_data
) {
212 PulseAudioInputStream
* stream
=
213 reinterpret_cast<PulseAudioInputStream
*>(user_data
);
216 pa_threaded_mainloop_signal(stream
->pa_mainloop_
, 0);
220 if (stream
->channels_
!= info
->channel_map
.channels
)
221 stream
->channels_
= info
->channel_map
.channels
;
223 pa_volume_t volume
= PA_VOLUME_MUTED
; // Minimum possible value.
224 // Use the max volume of any channel as the volume.
225 for (int i
= 0; i
< stream
->channels_
; ++i
) {
226 if (volume
< info
->volume
.values
[i
])
227 volume
= info
->volume
.values
[i
];
230 // It is safe to access |volume_| here since VolumeCallback() is running
232 stream
->volume_
= static_cast<double>(volume
);
235 // static, used by pa_stream_set_state_callback.
236 void PulseAudioInputStream::StreamNotifyCallback(pa_stream
* s
,
238 PulseAudioInputStream
* stream
=
239 reinterpret_cast<PulseAudioInputStream
*>(user_data
);
240 if (s
&& stream
->callback_
&&
241 pa_stream_get_state(s
) == PA_STREAM_FAILED
) {
242 stream
->callback_
->OnError(stream
);
245 pa_threaded_mainloop_signal(stream
->pa_mainloop_
, 0);
248 void PulseAudioInputStream::ReadData() {
249 uint32 hardware_delay
= pulse::GetHardwareLatencyInBytes(
250 handle_
, params_
.sample_rate(), params_
.GetBytesPerFrame());
252 // Update the AGC volume level once every second. Note that,
253 // |volume| is also updated each time SetVolume() is called
254 // through IPC by the render-side AGC.
255 // We disregard the |normalized_volume| from GetAgcVolume()
256 // and use the value calculated by |volume_|.
257 double normalized_volume
= 0.0;
258 GetAgcVolume(&normalized_volume
);
259 normalized_volume
= volume_
/ GetMaxVolume();
263 const void* data
= NULL
;
264 pa_stream_peek(handle_
, &data
, &length
);
265 if (!data
|| length
== 0)
268 buffer_
->Append(reinterpret_cast<const uint8
*>(data
), length
);
270 // Checks if we still have data.
271 pa_stream_drop(handle_
);
272 } while (pa_stream_readable_size(handle_
) > 0);
274 int packet_size
= params_
.GetBytesPerBuffer();
275 while (buffer_
->forward_bytes() >= packet_size
) {
276 buffer_
->Read(audio_data_buffer_
.get(), packet_size
);
277 callback_
->OnData(this, audio_data_buffer_
.get(), packet_size
,
278 hardware_delay
, normalized_volume
);
280 if (buffer_
->forward_bytes() < packet_size
)
283 // TODO(xians): Remove once PPAPI is using circular buffers.
284 DVLOG(1) << "OnData is being called consecutively, sleep 5ms to "
285 << "wait until render consumes the data";
286 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
289 pa_threaded_mainloop_signal(pa_mainloop_
, 0);