1 // Copyright 2014 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 "content/renderer/pepper/pepper_media_stream_audio_track_host.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/macros.h"
13 #include "base/numerics/safe_math.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "media/base/audio_bus.h"
17 #include "ppapi/c/pp_errors.h"
18 #include "ppapi/c/ppb_audio_buffer.h"
19 #include "ppapi/host/dispatch_host_message.h"
20 #include "ppapi/host/host_message_context.h"
21 #include "ppapi/host/ppapi_host.h"
22 #include "ppapi/proxy/ppapi_messages.h"
23 #include "ppapi/shared_impl/media_stream_audio_track_shared.h"
24 #include "ppapi/shared_impl/media_stream_buffer.h"
26 using media::AudioParameters
;
27 using ppapi::host::HostMessageContext
;
28 using ppapi::MediaStreamAudioTrackShared
;
32 // Audio buffer durations in milliseconds.
33 const uint32_t kMinDuration
= 10;
34 const uint32_t kDefaultDuration
= 10;
36 const int32_t kDefaultNumberOfBuffers
= 4;
37 const int32_t kMaxNumberOfBuffers
= 1000; // 10 sec
39 // Returns true if the |sample_rate| is supported in
40 // |PP_AudioBuffer_SampleRate|, otherwise false.
41 PP_AudioBuffer_SampleRate
GetPPSampleRate(int sample_rate
) {
42 switch (sample_rate
) {
44 return PP_AUDIOBUFFER_SAMPLERATE_8000
;
46 return PP_AUDIOBUFFER_SAMPLERATE_16000
;
48 return PP_AUDIOBUFFER_SAMPLERATE_22050
;
50 return PP_AUDIOBUFFER_SAMPLERATE_32000
;
52 return PP_AUDIOBUFFER_SAMPLERATE_44100
;
54 return PP_AUDIOBUFFER_SAMPLERATE_48000
;
56 return PP_AUDIOBUFFER_SAMPLERATE_96000
;
58 return PP_AUDIOBUFFER_SAMPLERATE_192000
;
60 return PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN
;
68 PepperMediaStreamAudioTrackHost::AudioSink::AudioSink(
69 PepperMediaStreamAudioTrackHost
* host
)
71 active_buffer_index_(-1),
72 active_buffers_generation_(0),
73 active_buffer_frame_offset_(0),
74 buffers_generation_(0),
75 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
76 number_of_buffers_(kDefaultNumberOfBuffers
),
79 user_buffer_duration_(kDefaultDuration
),
83 PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() {
84 DCHECK_EQ(main_task_runner_
, base::ThreadTaskRunnerHandle::Get());
87 void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index
) {
88 DCHECK_EQ(main_task_runner_
, base::ThreadTaskRunnerHandle::Get());
90 DCHECK_LT(index
, host_
->buffer_manager()->number_of_buffers());
91 base::AutoLock
lock(lock_
);
92 buffers_
.push_back(index
);
95 int32_t PepperMediaStreamAudioTrackHost::AudioSink::Configure(
96 int32_t number_of_buffers
, int32_t duration
,
97 const ppapi::host::ReplyMessageContext
& context
) {
98 DCHECK_EQ(main_task_runner_
, base::ThreadTaskRunnerHandle::Get());
100 if (pending_configure_reply_
.is_valid()) {
101 return PP_ERROR_INPROGRESS
;
103 pending_configure_reply_
= context
;
105 bool changed
= false;
106 if (number_of_buffers
!= number_of_buffers_
)
108 if (duration
!= 0 && duration
!= user_buffer_duration_
) {
109 user_buffer_duration_
= duration
;
112 number_of_buffers_
= number_of_buffers
;
115 // Initialize later in OnSetFormat if bytes_per_second_ is not known yet.
116 if (bytes_per_second_
> 0 && bytes_per_frame_
> 0)
119 SendConfigureReply(PP_OK
);
121 return PP_OK_COMPLETIONPENDING
;
124 void PepperMediaStreamAudioTrackHost::AudioSink::SendConfigureReply(
126 if (pending_configure_reply_
.is_valid()) {
127 pending_configure_reply_
.params
.set_result(result
);
128 host_
->host()->SendReply(
129 pending_configure_reply_
,
130 PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply());
131 pending_configure_reply_
= ppapi::host::ReplyMessageContext();
135 void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread(
136 int bytes_per_second
, int bytes_per_frame
) {
137 bytes_per_second_
= bytes_per_second
;
138 bytes_per_frame_
= bytes_per_frame
;
142 void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() {
143 DCHECK_EQ(main_task_runner_
, base::ThreadTaskRunnerHandle::Get());
145 base::AutoLock
lock(lock_
);
146 // Clear |buffers_|, so the audio thread will drop all incoming audio data.
148 buffers_generation_
++;
150 int32_t frame_rate
= bytes_per_second_
/ bytes_per_frame_
;
151 base::CheckedNumeric
<int32_t> frames_per_buffer
= user_buffer_duration_
;
152 frames_per_buffer
*= frame_rate
;
153 frames_per_buffer
/= base::Time::kMillisecondsPerSecond
;
154 base::CheckedNumeric
<int32_t> buffer_audio_size
=
155 frames_per_buffer
* bytes_per_frame_
;
156 // The size is slightly bigger than necessary, because 8 extra bytes are
157 // added into the struct. Also see |MediaStreamBuffer|. Also, the size of the
158 // buffer may be larger than requested, since the size of each buffer will be
160 base::CheckedNumeric
<int32_t> buffer_size
= buffer_audio_size
;
161 buffer_size
+= sizeof(ppapi::MediaStreamBuffer::Audio
);
162 DCHECK_GT(buffer_size
.ValueOrDie(), 0);
164 // We don't need to hold |lock_| during |host->InitBuffers()| call, because
165 // we just cleared |buffers_| , so the audio thread will drop all incoming
166 // audio data, and not use buffers in |host_|.
167 bool result
= host_
->InitBuffers(number_of_buffers_
,
168 buffer_size
.ValueOrDie(),
171 SendConfigureReply(PP_ERROR_NOMEMORY
);
175 // Fill the |buffers_|, so the audio thread can continue receiving audio data.
176 base::AutoLock
lock(lock_
);
177 output_buffer_size_
= buffer_audio_size
.ValueOrDie();
178 for (int32_t i
= 0; i
< number_of_buffers_
; ++i
) {
179 int32_t index
= host_
->buffer_manager()->DequeueBuffer();
181 buffers_
.push_back(index
);
184 SendConfigureReply(PP_OK
);
187 void PepperMediaStreamAudioTrackHost::AudioSink::
188 SendEnqueueBufferMessageOnMainThread(int32_t index
,
189 int32_t buffers_generation
) {
190 DCHECK_EQ(main_task_runner_
, base::ThreadTaskRunnerHandle::Get());
191 // If |InitBuffers()| is called after this task being posted from the audio
192 // thread, the buffer should become invalid already. We should ignore it.
193 // And because only the main thread modifies the |buffers_generation_|,
194 // so we don't need to lock |lock_| here (main thread).
195 if (buffers_generation
== buffers_generation_
)
196 host_
->SendEnqueueBufferMessageToPlugin(index
);
199 void PepperMediaStreamAudioTrackHost::AudioSink::OnData(
200 const media::AudioBus
& audio_bus
,
201 base::TimeTicks estimated_capture_time
) {
202 DCHECK(audio_thread_checker_
.CalledOnValidThread());
203 DCHECK(audio_params_
.IsValid());
204 DCHECK_EQ(audio_bus
.channels(), audio_params_
.channels());
205 // Here, |audio_params_.frames_per_buffer()| refers to the incomming audio
206 // buffer. However, this doesn't necessarily equal
207 // |buffer->number_of_samples|, which is configured by the user when they set
209 DCHECK_EQ(audio_bus
.frames(), audio_params_
.frames_per_buffer());
210 DCHECK(!estimated_capture_time
.is_null());
212 if (first_frame_capture_time_
.is_null())
213 first_frame_capture_time_
= estimated_capture_time
;
215 const int bytes_per_frame
= audio_params_
.GetBytesPerFrame();
217 base::AutoLock
lock(lock_
);
218 for (int frame_offset
= 0; frame_offset
< audio_bus
.frames(); ) {
219 if (active_buffers_generation_
!= buffers_generation_
) {
220 // Buffers have changed, so drop the active buffer.
221 active_buffer_index_
= -1;
223 if (active_buffer_index_
== -1 && !buffers_
.empty()) {
224 active_buffers_generation_
= buffers_generation_
;
225 active_buffer_frame_offset_
= 0;
226 active_buffer_index_
= buffers_
.front();
227 buffers_
.pop_front();
229 if (active_buffer_index_
== -1) {
230 // Eek! We're dropping frames. Bad, bad, bad!
234 // TODO(penghuang): Support re-sampling and channel mixing by using
235 // media::AudioConverter.
236 ppapi::MediaStreamBuffer::Audio
* buffer
=
237 &(host_
->buffer_manager()->GetBufferPointer(active_buffer_index_
)
239 if (active_buffer_frame_offset_
== 0) {
240 // The active buffer is new, so initialise the header and metadata fields.
241 buffer
->header
.size
= host_
->buffer_manager()->buffer_size();
242 buffer
->header
.type
= ppapi::MediaStreamBuffer::TYPE_AUDIO
;
243 const base::TimeTicks time_at_offset
= estimated_capture_time
+
244 frame_offset
* base::TimeDelta::FromSeconds(1) /
245 audio_params_
.sample_rate();
247 (time_at_offset
- first_frame_capture_time_
).InSecondsF();
248 buffer
->sample_rate
=
249 static_cast<PP_AudioBuffer_SampleRate
>(audio_params_
.sample_rate());
250 buffer
->data_size
= output_buffer_size_
;
251 buffer
->number_of_channels
= audio_params_
.channels();
252 buffer
->number_of_samples
= buffer
->data_size
* audio_params_
.channels() /
256 const int frames_per_buffer
=
257 buffer
->number_of_samples
/ audio_params_
.channels();
258 const int frames_to_copy
= std::min(
259 frames_per_buffer
- active_buffer_frame_offset_
,
260 audio_bus
.frames() - frame_offset
);
261 audio_bus
.ToInterleavedPartial(
264 audio_params_
.bits_per_sample() / 8,
265 buffer
->data
+ active_buffer_frame_offset_
* bytes_per_frame
);
266 active_buffer_frame_offset_
+= frames_to_copy
;
267 frame_offset
+= frames_to_copy
;
269 DCHECK_LE(active_buffer_frame_offset_
, frames_per_buffer
);
270 if (active_buffer_frame_offset_
== frames_per_buffer
) {
271 main_task_runner_
->PostTask(
273 base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread
,
274 weak_factory_
.GetWeakPtr(), active_buffer_index_
,
275 buffers_generation_
));
276 active_buffer_index_
= -1;
281 void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat(
282 const AudioParameters
& params
) {
283 DCHECK(params
.IsValid());
284 // TODO(amistry): How do you handle the case where the user configures a
285 // duration that's shorter than the received buffer duration? One option is to
286 // double buffer, where the size of the intermediate ring buffer is at least
287 // max(user requested duration, received buffer duration). There are other
288 // ways of dealing with it, but which one is "correct"?
289 DCHECK_LE(params
.GetBufferDuration().InMilliseconds(), kMinDuration
);
290 DCHECK_EQ(params
.bits_per_sample(), 16);
291 DCHECK_NE(GetPPSampleRate(params
.sample_rate()),
292 PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN
);
294 // TODO(penghuang): support setting format more than once.
295 if (audio_params_
.IsValid()) {
296 DCHECK_EQ(params
.sample_rate(), audio_params_
.sample_rate());
297 DCHECK_EQ(params
.bits_per_sample(), audio_params_
.bits_per_sample());
298 DCHECK_EQ(params
.channels(), audio_params_
.channels());
300 audio_thread_checker_
.DetachFromThread();
301 audio_params_
= params
;
303 main_task_runner_
->PostTask(
305 base::Bind(&AudioSink::SetFormatOnMainThread
,
306 weak_factory_
.GetWeakPtr(), params
.GetBytesPerSecond(),
307 params
.GetBytesPerFrame()));
311 PepperMediaStreamAudioTrackHost::PepperMediaStreamAudioTrackHost(
312 RendererPpapiHost
* host
,
313 PP_Instance instance
,
314 PP_Resource resource
,
315 const blink::WebMediaStreamTrack
& track
)
316 : PepperMediaStreamTrackHostBase(host
, instance
, resource
),
320 DCHECK(!track_
.isNull());
323 PepperMediaStreamAudioTrackHost::~PepperMediaStreamAudioTrackHost() {
327 int32_t PepperMediaStreamAudioTrackHost::OnResourceMessageReceived(
328 const IPC::Message
& msg
,
329 HostMessageContext
* context
) {
330 PPAPI_BEGIN_MESSAGE_MAP(PepperMediaStreamAudioTrackHost
, msg
)
331 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
332 PpapiHostMsg_MediaStreamAudioTrack_Configure
, OnHostMsgConfigure
)
333 PPAPI_END_MESSAGE_MAP()
334 return PepperMediaStreamTrackHostBase::OnResourceMessageReceived(msg
,
338 int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure(
339 HostMessageContext
* context
,
340 const MediaStreamAudioTrackShared::Attributes
& attributes
) {
341 if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes
))
342 return PP_ERROR_BADARGUMENT
;
344 int32_t buffers
= attributes
.buffers
345 ? std::min(kMaxNumberOfBuffers
, attributes
.buffers
)
346 : kDefaultNumberOfBuffers
;
347 return audio_sink_
.Configure(buffers
, attributes
.duration
,
348 context
->MakeReplyMessageContext());
351 void PepperMediaStreamAudioTrackHost::OnClose() {
353 MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_
, track_
);
356 audio_sink_
.SendConfigureReply(PP_ERROR_ABORTED
);
359 void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() {
360 int32_t index
= buffer_manager()->DequeueBuffer();
362 audio_sink_
.EnqueueBuffer(index
);
365 void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() {
367 media::AudioParameters format
=
368 MediaStreamAudioSink::GetFormatFromAudioTrack(track_
);
369 // Although this should only be called on the audio capture thread, that
370 // can't happen until the sink is added to the audio track below.
371 if (format
.IsValid())
372 audio_sink_
.OnSetFormat(format
);
374 MediaStreamAudioSink::AddToAudioTrack(&audio_sink_
, track_
);
379 } // namespace content