Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / pepper / pepper_media_stream_audio_track_host.cc
blobc8f9e7d51509be2690f9c76caf8558cb2e8a1bd5
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"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/macros.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/numerics/safe_math.h"
15 #include "media/base/audio_bus.h"
16 #include "ppapi/c/pp_errors.h"
17 #include "ppapi/c/ppb_audio_buffer.h"
18 #include "ppapi/host/dispatch_host_message.h"
19 #include "ppapi/host/host_message_context.h"
20 #include "ppapi/host/ppapi_host.h"
21 #include "ppapi/proxy/ppapi_messages.h"
22 #include "ppapi/shared_impl/media_stream_audio_track_shared.h"
23 #include "ppapi/shared_impl/media_stream_buffer.h"
25 using media::AudioParameters;
26 using ppapi::host::HostMessageContext;
27 using ppapi::MediaStreamAudioTrackShared;
29 namespace {
31 // Audio buffer durations in milliseconds.
32 const uint32_t kMinDuration = 10;
33 const uint32_t kDefaultDuration = 10;
35 const int32_t kDefaultNumberOfBuffers = 4;
36 const int32_t kMaxNumberOfBuffers = 1000; // 10 sec
38 // Returns true if the |sample_rate| is supported in
39 // |PP_AudioBuffer_SampleRate|, otherwise false.
40 PP_AudioBuffer_SampleRate GetPPSampleRate(int sample_rate) {
41 switch (sample_rate) {
42 case 8000:
43 return PP_AUDIOBUFFER_SAMPLERATE_8000;
44 case 16000:
45 return PP_AUDIOBUFFER_SAMPLERATE_16000;
46 case 22050:
47 return PP_AUDIOBUFFER_SAMPLERATE_22050;
48 case 32000:
49 return PP_AUDIOBUFFER_SAMPLERATE_32000;
50 case 44100:
51 return PP_AUDIOBUFFER_SAMPLERATE_44100;
52 case 48000:
53 return PP_AUDIOBUFFER_SAMPLERATE_48000;
54 case 96000:
55 return PP_AUDIOBUFFER_SAMPLERATE_96000;
56 case 192000:
57 return PP_AUDIOBUFFER_SAMPLERATE_192000;
58 default:
59 return PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN;
63 } // namespace
65 namespace content {
67 PepperMediaStreamAudioTrackHost::AudioSink::AudioSink(
68 PepperMediaStreamAudioTrackHost* host)
69 : host_(host),
70 active_buffer_index_(-1),
71 active_buffers_generation_(0),
72 active_buffer_frame_offset_(0),
73 buffers_generation_(0),
74 main_message_loop_proxy_(base::MessageLoopProxy::current()),
75 number_of_buffers_(kDefaultNumberOfBuffers),
76 bytes_per_second_(0),
77 bytes_per_frame_(0),
78 user_buffer_duration_(kDefaultDuration),
79 weak_factory_(this) {}
81 PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() {
82 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
85 void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) {
86 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
87 DCHECK_GE(index, 0);
88 DCHECK_LT(index, host_->buffer_manager()->number_of_buffers());
89 base::AutoLock lock(lock_);
90 buffers_.push_back(index);
93 int32_t PepperMediaStreamAudioTrackHost::AudioSink::Configure(
94 int32_t number_of_buffers, int32_t duration,
95 const ppapi::host::ReplyMessageContext& context) {
96 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
98 if (pending_configure_reply_.is_valid()) {
99 return PP_ERROR_INPROGRESS;
101 pending_configure_reply_ = context;
103 bool changed = false;
104 if (number_of_buffers != number_of_buffers_)
105 changed = true;
106 if (duration != 0 && duration != user_buffer_duration_) {
107 user_buffer_duration_ = duration;
108 changed = true;
110 number_of_buffers_ = number_of_buffers;
112 if (changed) {
113 // Initialize later in OnSetFormat if bytes_per_second_ is not known yet.
114 if (bytes_per_second_ > 0 && bytes_per_frame_ > 0)
115 InitBuffers();
116 } else {
117 SendConfigureReply(PP_OK);
119 return PP_OK_COMPLETIONPENDING;
122 void PepperMediaStreamAudioTrackHost::AudioSink::SendConfigureReply(
123 int32_t result) {
124 if (pending_configure_reply_.is_valid()) {
125 pending_configure_reply_.params.set_result(result);
126 host_->host()->SendReply(
127 pending_configure_reply_,
128 PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply());
129 pending_configure_reply_ = ppapi::host::ReplyMessageContext();
133 void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread(
134 int bytes_per_second, int bytes_per_frame) {
135 bytes_per_second_ = bytes_per_second;
136 bytes_per_frame_ = bytes_per_frame;
137 InitBuffers();
140 void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() {
141 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
143 base::AutoLock lock(lock_);
144 // Clear |buffers_|, so the audio thread will drop all incoming audio data.
145 buffers_.clear();
146 buffers_generation_++;
148 int32_t frame_rate = bytes_per_second_ / bytes_per_frame_;
149 base::CheckedNumeric<int32_t> frames_per_buffer = user_buffer_duration_;
150 frames_per_buffer *= frame_rate;
151 frames_per_buffer /= base::Time::kMillisecondsPerSecond;
152 base::CheckedNumeric<int32_t> buffer_audio_size =
153 frames_per_buffer * bytes_per_frame_;
154 // The size is slightly bigger than necessary, because 8 extra bytes are
155 // added into the struct. Also see |MediaStreamBuffer|. Also, the size of the
156 // buffer may be larger than requested, since the size of each buffer will be
157 // 4-byte aligned.
158 base::CheckedNumeric<int32_t> buffer_size = buffer_audio_size;
159 buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
160 DCHECK_GT(buffer_size.ValueOrDie(), 0);
162 // We don't need to hold |lock_| during |host->InitBuffers()| call, because
163 // we just cleared |buffers_| , so the audio thread will drop all incoming
164 // audio data, and not use buffers in |host_|.
165 bool result = host_->InitBuffers(number_of_buffers_,
166 buffer_size.ValueOrDie(),
167 kRead);
168 if (!result) {
169 SendConfigureReply(PP_ERROR_NOMEMORY);
170 return;
173 // Fill the |buffers_|, so the audio thread can continue receiving audio data.
174 base::AutoLock lock(lock_);
175 output_buffer_size_ = buffer_audio_size.ValueOrDie();
176 for (int32_t i = 0; i < number_of_buffers_; ++i) {
177 int32_t index = host_->buffer_manager()->DequeueBuffer();
178 DCHECK_GE(index, 0);
179 buffers_.push_back(index);
182 SendConfigureReply(PP_OK);
185 void PepperMediaStreamAudioTrackHost::AudioSink::
186 SendEnqueueBufferMessageOnMainThread(int32_t index,
187 int32_t buffers_generation) {
188 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
189 // If |InitBuffers()| is called after this task being posted from the audio
190 // thread, the buffer should become invalid already. We should ignore it.
191 // And because only the main thread modifies the |buffers_generation_|,
192 // so we don't need to lock |lock_| here (main thread).
193 if (buffers_generation == buffers_generation_)
194 host_->SendEnqueueBufferMessageToPlugin(index);
197 void PepperMediaStreamAudioTrackHost::AudioSink::OnData(
198 const media::AudioBus& audio_bus,
199 base::TimeTicks estimated_capture_time) {
200 DCHECK(audio_thread_checker_.CalledOnValidThread());
201 DCHECK(audio_params_.IsValid());
202 DCHECK_EQ(audio_bus.channels(), audio_params_.channels());
203 // Here, |audio_params_.frames_per_buffer()| refers to the incomming audio
204 // buffer. However, this doesn't necessarily equal
205 // |buffer->number_of_samples|, which is configured by the user when they set
206 // buffer duration.
207 DCHECK_EQ(audio_bus.frames(), audio_params_.frames_per_buffer());
208 DCHECK(!estimated_capture_time.is_null());
210 if (first_frame_capture_time_.is_null())
211 first_frame_capture_time_ = estimated_capture_time;
213 const int bytes_per_frame = audio_params_.GetBytesPerFrame();
215 base::AutoLock lock(lock_);
216 for (int frame_offset = 0; frame_offset < audio_bus.frames(); ) {
217 if (active_buffers_generation_ != buffers_generation_) {
218 // Buffers have changed, so drop the active buffer.
219 active_buffer_index_ = -1;
221 if (active_buffer_index_ == -1 && !buffers_.empty()) {
222 active_buffers_generation_ = buffers_generation_;
223 active_buffer_frame_offset_ = 0;
224 active_buffer_index_ = buffers_.front();
225 buffers_.pop_front();
227 if (active_buffer_index_ == -1) {
228 // Eek! We're dropping frames. Bad, bad, bad!
229 break;
232 // TODO(penghuang): Support re-sampling and channel mixing by using
233 // media::AudioConverter.
234 ppapi::MediaStreamBuffer::Audio* buffer =
235 &(host_->buffer_manager()->GetBufferPointer(active_buffer_index_)
236 ->audio);
237 if (active_buffer_frame_offset_ == 0) {
238 // The active buffer is new, so initialise the header and metadata fields.
239 buffer->header.size = host_->buffer_manager()->buffer_size();
240 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
241 const base::TimeTicks time_at_offset = estimated_capture_time +
242 frame_offset * base::TimeDelta::FromSeconds(1) /
243 audio_params_.sample_rate();
244 buffer->timestamp =
245 (time_at_offset - first_frame_capture_time_).InMillisecondsF();
246 buffer->sample_rate =
247 static_cast<PP_AudioBuffer_SampleRate>(audio_params_.sample_rate());
248 buffer->data_size = output_buffer_size_;
249 buffer->number_of_channels = audio_params_.channels();
250 buffer->number_of_samples = buffer->data_size * audio_params_.channels() /
251 bytes_per_frame;
254 const int frames_per_buffer =
255 buffer->number_of_samples / audio_params_.channels();
256 const int frames_to_copy = std::min(
257 frames_per_buffer - active_buffer_frame_offset_,
258 audio_bus.frames() - frame_offset);
259 audio_bus.ToInterleavedPartial(
260 frame_offset,
261 frames_to_copy,
262 audio_params_.bits_per_sample() / 8,
263 buffer->data + active_buffer_frame_offset_ * bytes_per_frame);
264 active_buffer_frame_offset_ += frames_to_copy;
265 frame_offset += frames_to_copy;
267 DCHECK_LE(active_buffer_frame_offset_, frames_per_buffer);
268 if (active_buffer_frame_offset_ == frames_per_buffer) {
269 main_message_loop_proxy_->PostTask(
270 FROM_HERE,
271 base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread,
272 weak_factory_.GetWeakPtr(),
273 active_buffer_index_,
274 buffers_generation_));
275 active_buffer_index_ = -1;
280 void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat(
281 const AudioParameters& params) {
282 DCHECK(params.IsValid());
283 // TODO(amistry): How do you handle the case where the user configures a
284 // duration that's shorter than the received buffer duration? One option is to
285 // double buffer, where the size of the intermediate ring buffer is at least
286 // max(user requested duration, received buffer duration). There are other
287 // ways of dealing with it, but which one is "correct"?
288 DCHECK_LE(params.GetBufferDuration().InMilliseconds(), kMinDuration);
289 DCHECK_EQ(params.bits_per_sample(), 16);
290 DCHECK_NE(GetPPSampleRate(params.sample_rate()),
291 PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN);
293 // TODO(penghuang): support setting format more than once.
294 if (audio_params_.IsValid()) {
295 DCHECK_EQ(params.sample_rate(), audio_params_.sample_rate());
296 DCHECK_EQ(params.bits_per_sample(), audio_params_.bits_per_sample());
297 DCHECK_EQ(params.channels(), audio_params_.channels());
298 } else {
299 audio_thread_checker_.DetachFromThread();
300 audio_params_ = params;
302 main_message_loop_proxy_->PostTask(
303 FROM_HERE,
304 base::Bind(&AudioSink::SetFormatOnMainThread,
305 weak_factory_.GetWeakPtr(),
306 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),
317 track_(track),
318 connected_(false),
319 audio_sink_(this) {
320 DCHECK(!track_.isNull());
323 PepperMediaStreamAudioTrackHost::~PepperMediaStreamAudioTrackHost() {
324 OnClose();
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,
335 context);
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() {
352 if (connected_) {
353 MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_);
354 connected_ = false;
356 audio_sink_.SendConfigureReply(PP_ERROR_ABORTED);
359 void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() {
360 int32_t index = buffer_manager()->DequeueBuffer();
361 DCHECK_GE(index, 0);
362 audio_sink_.EnqueueBuffer(index);
365 void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() {
366 if (!connected_) {
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_);
375 connected_ = true;
379 } // namespace content