mesa gn build: suppress -Wstring-conversion warnings
[chromium-blink-merge.git] / content / renderer / pepper / pepper_media_stream_audio_track_host.cc
blob72279f652900a42b057ab63455de1d00ef67aef8
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 "ppapi/c/pp_errors.h"
16 #include "ppapi/c/ppb_audio_buffer.h"
17 #include "ppapi/host/dispatch_host_message.h"
18 #include "ppapi/host/host_message_context.h"
19 #include "ppapi/host/ppapi_host.h"
20 #include "ppapi/proxy/ppapi_messages.h"
21 #include "ppapi/shared_impl/media_stream_audio_track_shared.h"
22 #include "ppapi/shared_impl/media_stream_buffer.h"
24 using media::AudioParameters;
25 using ppapi::host::HostMessageContext;
26 using ppapi::MediaStreamAudioTrackShared;
28 namespace {
30 // Audio buffer durations in milliseconds.
31 const uint32_t kMinDuration = 10;
32 const uint32_t kDefaultDuration = 10;
34 const int32_t kDefaultNumberOfBuffers = 4;
35 const int32_t kMaxNumberOfBuffers = 1000; // 10 sec
37 // Returns true if the |sample_rate| is supported in
38 // |PP_AudioBuffer_SampleRate|, otherwise false.
39 PP_AudioBuffer_SampleRate GetPPSampleRate(int sample_rate) {
40 switch (sample_rate) {
41 case 8000:
42 return PP_AUDIOBUFFER_SAMPLERATE_8000;
43 case 16000:
44 return PP_AUDIOBUFFER_SAMPLERATE_16000;
45 case 22050:
46 return PP_AUDIOBUFFER_SAMPLERATE_22050;
47 case 32000:
48 return PP_AUDIOBUFFER_SAMPLERATE_32000;
49 case 44100:
50 return PP_AUDIOBUFFER_SAMPLERATE_44100;
51 case 48000:
52 return PP_AUDIOBUFFER_SAMPLERATE_48000;
53 case 96000:
54 return PP_AUDIOBUFFER_SAMPLERATE_96000;
55 case 192000:
56 return PP_AUDIOBUFFER_SAMPLERATE_192000;
57 default:
58 return PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN;
62 } // namespace
64 namespace content {
66 PepperMediaStreamAudioTrackHost::AudioSink::AudioSink(
67 PepperMediaStreamAudioTrackHost* host)
68 : host_(host),
69 buffer_data_size_(0),
70 active_buffer_index_(-1),
71 active_buffers_generation_(0),
72 active_buffer_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 user_buffer_duration_(kDefaultDuration),
78 weak_factory_(this) {}
80 PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() {
81 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
84 void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) {
85 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
86 DCHECK_GE(index, 0);
87 DCHECK_LT(index, host_->buffer_manager()->number_of_buffers());
88 base::AutoLock lock(lock_);
89 buffers_.push_back(index);
92 int32_t PepperMediaStreamAudioTrackHost::AudioSink::Configure(
93 int32_t number_of_buffers, int32_t duration,
94 const ppapi::host::ReplyMessageContext& context) {
95 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
97 if (pending_configure_reply_.is_valid()) {
98 return PP_ERROR_INPROGRESS;
100 pending_configure_reply_ = context;
102 bool changed = false;
103 if (number_of_buffers != number_of_buffers_)
104 changed = true;
105 if (duration != 0 && duration != user_buffer_duration_) {
106 user_buffer_duration_ = duration;
107 changed = true;
109 number_of_buffers_ = number_of_buffers;
111 if (changed) {
112 // Initialize later in OnSetFormat if bytes_per_second_ is not known yet.
113 if (bytes_per_second_ > 0 && bytes_per_frame_ > 0)
114 InitBuffers();
115 } else {
116 SendConfigureReply(PP_OK);
118 return PP_OK_COMPLETIONPENDING;
121 void PepperMediaStreamAudioTrackHost::AudioSink::SendConfigureReply(
122 int32_t result) {
123 if (pending_configure_reply_.is_valid()) {
124 pending_configure_reply_.params.set_result(result);
125 host_->host()->SendReply(
126 pending_configure_reply_,
127 PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply());
128 pending_configure_reply_ = ppapi::host::ReplyMessageContext();
132 void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread(
133 int bytes_per_second, int bytes_per_frame) {
134 bytes_per_second_ = bytes_per_second;
135 bytes_per_frame_ = bytes_per_frame;
136 InitBuffers();
139 void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() {
140 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
142 base::AutoLock lock(lock_);
143 // Clear |buffers_|, so the audio thread will drop all incoming audio data.
144 buffers_.clear();
145 buffers_generation_++;
147 int32_t frame_rate = bytes_per_second_ / bytes_per_frame_;
148 base::CheckedNumeric<int32_t> frames_per_buffer = user_buffer_duration_;
149 frames_per_buffer *= frame_rate;
150 frames_per_buffer /= base::Time::kMillisecondsPerSecond;
151 base::CheckedNumeric<int32_t> buffer_audio_size =
152 frames_per_buffer * bytes_per_frame_;
153 // The size is slightly bigger than necessary, because 8 extra bytes are
154 // added into the struct. Also see |MediaStreamBuffer|. Also, the size of the
155 // buffer may be larger than requested, since the size of each buffer will be
156 // 4-byte aligned.
157 base::CheckedNumeric<int32_t> buffer_size = buffer_audio_size;
158 buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
159 DCHECK_GT(buffer_size.ValueOrDie(), 0);
161 // We don't need to hold |lock_| during |host->InitBuffers()| call, because
162 // we just cleared |buffers_| , so the audio thread will drop all incoming
163 // audio data, and not use buffers in |host_|.
164 bool result = host_->InitBuffers(number_of_buffers_,
165 buffer_size.ValueOrDie(),
166 kRead);
167 if (!result) {
168 SendConfigureReply(PP_ERROR_NOMEMORY);
169 return;
172 // Fill the |buffers_|, so the audio thread can continue receiving audio data.
173 base::AutoLock lock(lock_);
174 output_buffer_size_ = buffer_audio_size.ValueOrDie();
175 for (int32_t i = 0; i < number_of_buffers_; ++i) {
176 int32_t index = host_->buffer_manager()->DequeueBuffer();
177 DCHECK_GE(index, 0);
178 buffers_.push_back(index);
181 SendConfigureReply(PP_OK);
184 void PepperMediaStreamAudioTrackHost::AudioSink::
185 SendEnqueueBufferMessageOnMainThread(int32_t index,
186 int32_t buffers_generation) {
187 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
188 // If |InitBuffers()| is called after this task being posted from the audio
189 // thread, the buffer should become invalid already. We should ignore it.
190 // And because only the main thread modifies the |buffers_generation_|,
191 // so we don't need to lock |lock_| here (main thread).
192 if (buffers_generation == buffers_generation_)
193 host_->SendEnqueueBufferMessageToPlugin(index);
196 void PepperMediaStreamAudioTrackHost::AudioSink::OnData(const int16* audio_data,
197 int sample_rate,
198 int number_of_channels,
199 int number_of_frames) {
200 DCHECK(audio_thread_checker_.CalledOnValidThread());
201 DCHECK(audio_data);
202 DCHECK_EQ(sample_rate, audio_params_.sample_rate());
203 DCHECK_EQ(number_of_channels, audio_params_.channels());
204 // Here, |number_of_frames| and |audio_params_.frames_per_buffer()| refer to
205 // the incomming audio buffer. However, this doesn't necessarily equal
206 // |buffer->number_of_samples|, which is configured by the user when they set
207 // buffer duration.
208 DCHECK_EQ(number_of_frames, audio_params_.frames_per_buffer());
210 const uint32_t bytes_per_frame = number_of_channels *
211 audio_params_.bits_per_sample() / 8;
213 int frames_remaining = number_of_frames;
214 base::TimeDelta timestamp_offset;
216 base::AutoLock lock(lock_);
217 while (frames_remaining) {
218 if (active_buffers_generation_ != buffers_generation_) {
219 // Buffers have changed, so drop the active buffer.
220 active_buffer_index_ = -1;
222 if (active_buffer_index_ == -1 && !buffers_.empty()) {
223 active_buffers_generation_ = buffers_generation_;
224 active_buffer_offset_ = 0;
225 active_buffer_index_ = buffers_.front();
226 buffers_.pop_front();
228 if (active_buffer_index_ == -1) {
229 // Eek! We're dropping frames. Bad, bad, bad!
230 break;
233 // TODO(penghuang): support re-sampling, etc.
234 ppapi::MediaStreamBuffer::Audio* buffer =
235 &(host_->buffer_manager()->GetBufferPointer(active_buffer_index_)
236 ->audio);
237 if (active_buffer_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 buffer->timestamp = (timestamp_ + timestamp_offset).InMillisecondsF();
242 buffer->sample_rate = static_cast<PP_AudioBuffer_SampleRate>(sample_rate);
243 buffer->data_size = output_buffer_size_;
244 buffer->number_of_channels = number_of_channels;
245 buffer->number_of_samples = buffer->data_size * number_of_channels /
246 bytes_per_frame;
248 uint32_t buffer_bytes_remaining =
249 buffer->data_size - active_buffer_offset_;
250 DCHECK_EQ(buffer_bytes_remaining % bytes_per_frame, 0U);
251 uint32_t incoming_bytes_remaining = frames_remaining * bytes_per_frame;
252 uint32_t bytes_to_copy = std::min(buffer_bytes_remaining,
253 incoming_bytes_remaining);
254 uint32_t frames_to_copy = bytes_to_copy / bytes_per_frame;
255 DCHECK_EQ(bytes_to_copy % bytes_per_frame, 0U);
256 memcpy(buffer->data + active_buffer_offset_,
257 audio_data, bytes_to_copy);
258 active_buffer_offset_ += bytes_to_copy;
259 audio_data += bytes_to_copy / sizeof(*audio_data);
260 frames_remaining -= frames_to_copy;
261 timestamp_offset += base::TimeDelta::FromMilliseconds(
262 frames_to_copy * base::Time::kMillisecondsPerSecond / sample_rate);
264 DCHECK_LE(active_buffer_offset_, buffer->data_size);
265 if (active_buffer_offset_ == buffer->data_size) {
266 main_message_loop_proxy_->PostTask(
267 FROM_HERE,
268 base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread,
269 weak_factory_.GetWeakPtr(),
270 active_buffer_index_,
271 buffers_generation_));
272 active_buffer_index_ = -1;
275 timestamp_ += buffer_duration_;
278 void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat(
279 const AudioParameters& params) {
280 DCHECK(params.IsValid());
281 // TODO(amistry): How do you handle the case where the user configures a
282 // duration that's shorter than the received buffer duration? One option is to
283 // double buffer, where the size of the intermediate ring buffer is at least
284 // max(user requested duration, received buffer duration). There are other
285 // ways of dealing with it, but which one is "correct"?
286 DCHECK_LE(params.GetBufferDuration().InMilliseconds(), kMinDuration);
287 DCHECK_EQ(params.bits_per_sample(), 16);
288 DCHECK_NE(GetPPSampleRate(params.sample_rate()),
289 PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN);
291 audio_params_ = params;
293 // TODO(penghuang): support setting format more than once.
294 buffer_duration_ = params.GetBufferDuration();
295 buffer_data_size_ = params.GetBytesPerBuffer();
297 if (original_audio_params_.IsValid()) {
298 DCHECK_EQ(params.sample_rate(), original_audio_params_.sample_rate());
299 DCHECK_EQ(params.bits_per_sample(),
300 original_audio_params_.bits_per_sample());
301 DCHECK_EQ(params.channels(), original_audio_params_.channels());
302 } else {
303 audio_thread_checker_.DetachFromThread();
304 original_audio_params_ = params;
306 int bytes_per_frame = params.channels() * params.bits_per_sample() / 8;
307 main_message_loop_proxy_->PostTask(
308 FROM_HERE,
309 base::Bind(&AudioSink::SetFormatOnMainThread,
310 weak_factory_.GetWeakPtr(),
311 params.GetBytesPerSecond(),
312 bytes_per_frame));
316 PepperMediaStreamAudioTrackHost::PepperMediaStreamAudioTrackHost(
317 RendererPpapiHost* host,
318 PP_Instance instance,
319 PP_Resource resource,
320 const blink::WebMediaStreamTrack& track)
321 : PepperMediaStreamTrackHostBase(host, instance, resource),
322 track_(track),
323 connected_(false),
324 audio_sink_(this) {
325 DCHECK(!track_.isNull());
328 PepperMediaStreamAudioTrackHost::~PepperMediaStreamAudioTrackHost() {
329 OnClose();
332 int32_t PepperMediaStreamAudioTrackHost::OnResourceMessageReceived(
333 const IPC::Message& msg,
334 HostMessageContext* context) {
335 PPAPI_BEGIN_MESSAGE_MAP(PepperMediaStreamAudioTrackHost, msg)
336 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
337 PpapiHostMsg_MediaStreamAudioTrack_Configure, OnHostMsgConfigure)
338 PPAPI_END_MESSAGE_MAP()
339 return PepperMediaStreamTrackHostBase::OnResourceMessageReceived(msg,
340 context);
343 int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure(
344 HostMessageContext* context,
345 const MediaStreamAudioTrackShared::Attributes& attributes) {
346 if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes))
347 return PP_ERROR_BADARGUMENT;
349 int32_t buffers = attributes.buffers
350 ? std::min(kMaxNumberOfBuffers, attributes.buffers)
351 : kDefaultNumberOfBuffers;
352 return audio_sink_.Configure(buffers, attributes.duration,
353 context->MakeReplyMessageContext());
356 void PepperMediaStreamAudioTrackHost::OnClose() {
357 if (connected_) {
358 MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_);
359 connected_ = false;
361 audio_sink_.SendConfigureReply(PP_ERROR_ABORTED);
364 void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() {
365 int32_t index = buffer_manager()->DequeueBuffer();
366 DCHECK_GE(index, 0);
367 audio_sink_.EnqueueBuffer(index);
370 void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() {
371 if (!connected_) {
372 MediaStreamAudioSink::AddToAudioTrack(&audio_sink_, track_);
373 connected_ = true;
377 } // namespace content