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

Side by Side Diff: content/renderer/pepper/pepper_audio_encoder_host.cc

Issue 1348563003: ppapi: implement PPB_AudioEncoder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rework destruction sequence Created 5 years, 1 month 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
« no previous file with comments | « content/renderer/pepper/pepper_audio_encoder_host.h ('k') | ppapi/ppapi_sources.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "base/bind.h"
6 #include "base/memory/shared_memory.h"
7 #include "content/public/renderer/renderer_ppapi_host.h"
8 #include "content/renderer/pepper/host_globals.h"
9 #include "content/renderer/pepper/pepper_audio_encoder_host.h"
10 #include "content/renderer/render_thread_impl.h"
11 #include "media/base/bind_to_current_loop.h"
12 #include "ppapi/c/pp_codecs.h"
13 #include "ppapi/c/pp_errors.h"
14 #include "ppapi/host/dispatch_host_message.h"
15 #include "ppapi/host/ppapi_host.h"
16 #include "ppapi/proxy/ppapi_messages.h"
17 #include "ppapi/shared_impl/media_stream_buffer.h"
18 #include "third_party/opus/src/include/opus.h"
19
20 using ppapi::proxy::SerializedHandle;
21
22 namespace content {
23
24 namespace {
25
26 // Buffer up to 150ms (15 x 10ms per frame).
27 const uint32_t kDefaultNumberOfAudioBuffers = 15;
28
29 // Class used to pass audio data between threads.
30 class AudioData {
31 public:
32 AudioData(const AudioData& other) : data(other.data), size(other.size) {}
33 AudioData(uint8_t* data, size_t size) : data(data), size(size) {}
34 ~AudioData() {}
35
36 uint8_t* data;
37 size_t size;
38 };
39
40 bool PP_HardwareAccelerationCompatible(bool accelerated,
41 PP_HardwareAcceleration requested) {
42 switch (requested) {
43 case PP_HARDWAREACCELERATION_ONLY:
44 return accelerated;
45 case PP_HARDWAREACCELERATION_NONE:
46 return !accelerated;
47 case PP_HARDWAREACCELERATION_WITHFALLBACK:
48 return true;
49 // No default case, to catch unhandled PP_HardwareAcceleration values.
50 }
51 return false;
52 }
53
54 AudioData GetAudioDataFromBufferManager(
55 const scoped_ptr<ppapi::MediaStreamBufferManager>& buffer_manager,
56 int32_t* buffer_id) {
57 DCHECK(buffer_manager->HasAvailableBuffer());
58 int32_t id = buffer_manager->DequeueBuffer();
59 ppapi::MediaStreamBuffer* buffer = buffer_manager->GetBufferPointer(id);
60 *buffer_id = id;
61 return AudioData(static_cast<uint8_t*>(buffer->audio.data),
62 buffer->audio.data_size);
63 }
64
65 AudioData GetBitstreamDataFromBufferManager(
66 const scoped_ptr<ppapi::MediaStreamBufferManager>& buffer_manager,
67 int32_t* buffer_id) {
68 DCHECK(buffer_manager->HasAvailableBuffer());
69 int32_t id = buffer_manager->DequeueBuffer();
70 uint8_t* buffer =
71 reinterpret_cast<uint8_t*>(buffer_manager->GetBufferPointer(id));
72 *buffer_id = id;
73 return AudioData(buffer, buffer_manager->buffer_size());
74 }
75
76 void StopAudioEncoder(
77 scoped_ptr<PepperAudioEncoderHost::AudioEncoderImpl> encoder,
78 scoped_ptr<ppapi::MediaStreamBufferManager> audio_buffer_manager,
79 scoped_ptr<ppapi::MediaStreamBufferManager> bitstream_buffer_manager) {}
80
81 } // namespace
82
83 // This class should be constructed and initialized on the main renderer
84 // thread, used and destructed on the media thread.
85 class PepperAudioEncoderHost::AudioEncoderImpl {
86 public:
87 // Callback used to signal encoded data. If |size| is negative, an error
88 // occured.
89 using BitstreamBufferReadyCB = base::Callback<void(int32_t size)>;
90
91 AudioEncoderImpl();
92 ~AudioEncoderImpl();
93
94 // Used on the renderer thread.
95 std::vector<PP_AudioProfileDescription> GetSupportedProfiles();
96 bool Initialize(const ppapi::proxy::PPB_AudioEncodeParameters& parameters);
97 int32_t GetNumberOfSamplesPerFrame();
98
99 // Used on the media thread.
100 void Encode(const AudioData& input,
101 const AudioData& output,
102 BitstreamBufferReadyCB callback);
103 void RequestBitrateChange(uint32_t bitrate);
104
105 private:
106 // Initialization parameters.
107 ppapi::proxy::PPB_AudioEncodeParameters parameters_;
108
109 scoped_ptr<uint8[]> encoder_memory_;
110 OpusEncoder* opus_encoder_;
111
112 DISALLOW_COPY_AND_ASSIGN(AudioEncoderImpl);
113 };
114
115 PepperAudioEncoderHost::AudioEncoderImpl::AudioEncoderImpl() {}
bbudge 2015/11/12 20:31:31 Could you initialize opus_encoder_(NULL) and param
llandwerlin-old 2015/11/13 12:46:23 Done.
116
117 PepperAudioEncoderHost::AudioEncoderImpl::~AudioEncoderImpl() {}
118
119 std::vector<PP_AudioProfileDescription>
120 PepperAudioEncoderHost::AudioEncoderImpl::GetSupportedProfiles() {
121 std::vector<PP_AudioProfileDescription> profiles;
122 static const uint32_t sampling_rates[] = {8000, 12000, 16000, 24000, 48000};
123
124 for (uint32_t i = 0; i < arraysize(sampling_rates); ++i) {
125 PP_AudioProfileDescription profile;
126 profile.profile = PP_AUDIOPROFILE_OPUS;
127 profile.max_channels = 2;
128 profile.sample_size = PP_AUDIOBUFFER_SAMPLESIZE_16_BITS;
129 profile.sample_rate = sampling_rates[i];
130 profile.hardware_accelerated = PP_FALSE;
131 profiles.push_back(profile);
132 }
133 return profiles;
134 }
135
136 bool PepperAudioEncoderHost::AudioEncoderImpl::Initialize(
137 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
bbudge 2015/11/12 20:31:31 Could the body be reworked here so that fields are
llandwerlin-old 2015/11/13 12:46:23 Ok, I think the DCHECK should be on encoder_memory
138 if (parameters.output_profile != PP_AUDIOPROFILE_OPUS)
139 return false;
140
141 DCHECK(!encoder_memory_);
142
143 int32_t encoder_size = opus_encoder_get_size(parameters.channels);
144 if (encoder_size < 1)
145 return false;
146
147 encoder_memory_.reset(new uint8[encoder_size]);
148 opus_encoder_ = reinterpret_cast<OpusEncoder*>(encoder_memory_.get());
149
150 if (opus_encoder_init(opus_encoder_, parameters.input_sample_rate,
151 parameters.channels, OPUS_APPLICATION_AUDIO) != OPUS_OK)
152 return false;
153
154 if (opus_encoder_ctl(opus_encoder_,
155 OPUS_SET_BITRATE(parameters.initial_bitrate <= 0
156 ? OPUS_AUTO
157 : parameters.initial_bitrate)) !=
158 OPUS_OK)
159 return false;
160
161 parameters_ = parameters;
162
163 return true;
164 }
165
166 int32_t PepperAudioEncoderHost::AudioEncoderImpl::GetNumberOfSamplesPerFrame() {
bbudge 2015/11/12 20:31:31 DCHECK() that Initialize succeeded for sanity here
llandwerlin-old 2015/11/13 12:46:23 Done.
167 // Opus supports 2.5, 5, 10, 20, 40 or 60ms audio frames. We take
168 // 10ms by default.
169 return parameters_.input_sample_rate / 100;
170 }
171
172 void PepperAudioEncoderHost::AudioEncoderImpl::Encode(
173 const AudioData& input,
174 const AudioData& output,
175 BitstreamBufferReadyCB callback) {
176 int32_t result = opus_encode(
177 opus_encoder_, reinterpret_cast<opus_int16*>(input.data),
178 (input.size / parameters_.channels) / parameters_.input_sample_size,
179 output.data, output.size);
180 callback.Run(result);
181 }
182
183 void PepperAudioEncoderHost::AudioEncoderImpl::RequestBitrateChange(
184 uint32_t bitrate) {
185 opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate));
186 }
187
188 PepperAudioEncoderHost::PepperAudioEncoderHost(RendererPpapiHost* host,
189 PP_Instance instance,
190 PP_Resource resource)
191 : ResourceHost(host->GetPpapiHost(), instance, resource),
192 renderer_ppapi_host_(host),
193 initialized_(false),
194 encoder_last_error_(PP_ERROR_FAILED),
195 audio_buffer_manager_(new ppapi::MediaStreamBufferManager(this)),
196 bitstream_buffer_manager_(new ppapi::MediaStreamBufferManager(this)),
bbudge 2015/11/12 20:31:31 It's probably better to defer constructing these u
llandwerlin-old 2015/11/13 12:46:23 Done.
197 media_task_runner_(RenderThreadImpl::current()
198 ->GetMediaThreadTaskRunner()),
199 weak_ptr_factory_(this) {}
200
201 PepperAudioEncoderHost::~PepperAudioEncoderHost() {
202 Close();
203 }
204
205 int32_t PepperAudioEncoderHost::OnResourceMessageReceived(
206 const IPC::Message& msg,
207 ppapi::host::HostMessageContext* context) {
208 PPAPI_BEGIN_MESSAGE_MAP(PepperAudioEncoderHost, msg)
209 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
210 PpapiHostMsg_AudioEncoder_GetSupportedProfiles,
211 OnHostMsgGetSupportedProfiles)
212 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioEncoder_Initialize,
213 OnHostMsgInitialize)
214 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioEncoder_Encode,
215 OnHostMsgEncode)
216 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
217 PpapiHostMsg_AudioEncoder_RecycleBitstreamBuffer,
218 OnHostMsgRecycleBitstreamBuffer)
219 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
220 PpapiHostMsg_AudioEncoder_RequestBitrateChange,
221 OnHostMsgRequestBitrateChange)
222 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_AudioEncoder_Close,
223 OnHostMsgClose)
224 PPAPI_END_MESSAGE_MAP()
225 return PP_ERROR_FAILED;
226 }
227
228 int32_t PepperAudioEncoderHost::OnHostMsgGetSupportedProfiles(
229 ppapi::host::HostMessageContext* context) {
230 std::vector<PP_AudioProfileDescription> profiles;
231 GetSupportedProfiles(&profiles);
232
233 host()->SendReply(
234 context->MakeReplyMessageContext(),
235 PpapiPluginMsg_AudioEncoder_GetSupportedProfilesReply(profiles));
236
237 return PP_OK_COMPLETIONPENDING;
238 }
239
240 int32_t PepperAudioEncoderHost::OnHostMsgInitialize(
241 ppapi::host::HostMessageContext* context,
242 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
243 if (initialized_)
244 return PP_ERROR_FAILED;
245
246 if (!IsInitializationValid(parameters))
247 return PP_ERROR_NOTSUPPORTED;
248
249 encode_parameters_ = parameters;
bbudge 2015/11/12 20:31:31 nit: s/encode_parameters_/parameters_ for consiste
llandwerlin-old 2015/11/13 12:46:23 Done.
250
251 int32_t error = PP_ERROR_FAILED;
bbudge 2015/11/12 20:31:31 I think the initialization code is clearer if you
llandwerlin-old 2015/11/13 12:46:23 Thanks, done.
252 if (parameters.acceleration == PP_HARDWAREACCELERATION_NONE ||
253 parameters.acceleration == PP_HARDWAREACCELERATION_WITHFALLBACK) {
254 encoder_.reset(new AudioEncoderImpl);
255 if (encoder_->Initialize(parameters)) {
256 if (AllocateBuffers()) {
257 initialized_ = true;
258 encoder_last_error_ = PP_OK;
259
260 ppapi::host::ReplyMessageContext reply_context =
261 context->MakeReplyMessageContext();
262 reply_context.params.AppendHandle(SerializedHandle(
263 renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(
264 audio_buffer_manager_->shm()->handle()),
265 audio_buffer_manager_->shm()->mapped_size()));
266 reply_context.params.AppendHandle(SerializedHandle(
267 renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(
268 bitstream_buffer_manager_->shm()->handle()),
269 bitstream_buffer_manager_->shm()->mapped_size()));
270
271 host()->SendReply(reply_context,
272 PpapiPluginMsg_AudioEncoder_InitializeReply(
273 encoder_->GetNumberOfSamplesPerFrame(),
274 audio_buffer_manager_->number_of_buffers(),
275 audio_buffer_manager_->buffer_size(),
276 bitstream_buffer_manager_->number_of_buffers(),
277 bitstream_buffer_manager_->buffer_size()));
278
279 return PP_OK_COMPLETIONPENDING;
280 }
281 error = PP_ERROR_NOMEMORY;
282 } else
283 error = PP_ERROR_FAILED;
284 }
285
286 encoder_ = nullptr;
287
288 return error;
289 }
290
291 int32_t PepperAudioEncoderHost::OnHostMsgEncode(
292 ppapi::host::HostMessageContext* context,
293 int32_t buffer_id) {
294 if (encoder_last_error_)
295 return encoder_last_error_;
296
297 if (buffer_id < 0 || buffer_id >= audio_buffer_manager_->number_of_buffers())
298 return PP_ERROR_BADARGUMENT;
299
300 audio_buffer_manager_->EnqueueBuffer(buffer_id);
301
302 DoEncode();
303
304 return PP_OK_COMPLETIONPENDING;
305 }
306
307 int32_t PepperAudioEncoderHost::OnHostMsgRecycleBitstreamBuffer(
308 ppapi::host::HostMessageContext* context,
309 int32_t buffer_id) {
310 if (encoder_last_error_)
311 return encoder_last_error_;
312
313 if (buffer_id < 0 ||
314 buffer_id >= bitstream_buffer_manager_->number_of_buffers())
315 return PP_ERROR_BADARGUMENT;
316
317 bitstream_buffer_manager_->EnqueueBuffer(buffer_id);
318
319 DoEncode();
320
321 return PP_OK;
322 }
323
324 int32_t PepperAudioEncoderHost::OnHostMsgRequestBitrateChange(
325 ppapi::host::HostMessageContext* context,
326 uint32_t bitrate) {
327 if (encoder_last_error_)
328 return encoder_last_error_;
329
330 media_task_runner_->PostTask(
331 FROM_HERE, base::Bind(&AudioEncoderImpl::RequestBitrateChange,
332 base::Unretained(encoder_.get()), bitrate));
333
334 return PP_OK;
335 }
336
337 int32_t PepperAudioEncoderHost::OnHostMsgClose(
338 ppapi::host::HostMessageContext* context) {
339 encoder_last_error_ = PP_ERROR_FAILED;
340 Close();
341
342 return PP_OK;
343 }
344
345 void PepperAudioEncoderHost::GetSupportedProfiles(
346 std::vector<PP_AudioProfileDescription>* profiles) {
347 DCHECK(RenderThreadImpl::current());
348
349 AudioEncoderImpl software_encoder;
350 *profiles = software_encoder.GetSupportedProfiles();
bbudge 2015/11/12 20:31:31 Looks like this should be a static method.
llandwerlin-old 2015/11/13 12:46:23 Done.
351 }
352
353 bool PepperAudioEncoderHost::IsInitializationValid(
354 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
355 DCHECK(RenderThreadImpl::current());
356
357 std::vector<PP_AudioProfileDescription> profiles;
358 GetSupportedProfiles(&profiles);
359
360 for (const PP_AudioProfileDescription& profile : profiles) {
361 if (parameters.output_profile == profile.profile &&
362 parameters.input_sample_size == profile.sample_size &&
363 parameters.input_sample_rate == profile.sample_rate &&
364 parameters.channels <= profile.max_channels &&
365 PP_HardwareAccelerationCompatible(
366 profile.hardware_accelerated == PP_TRUE ? true : false,
367 parameters.acceleration))
368 return true;
369 }
370
371 return false;
372 }
373
374 bool PepperAudioEncoderHost::AllocateBuffers() {
375 DCHECK(RenderThreadImpl::current());
376 DCHECK(encoder_);
377
378 base::CheckedNumeric<size_t> buffer_size =
379 encoder_->GetNumberOfSamplesPerFrame();
380 buffer_size *= encode_parameters_.channels;
381 buffer_size *= encode_parameters_.input_sample_size;
382
383 return AllocateAudioBuffers(buffer_size) &&
384 AllocateBitstreamBuffers(buffer_size);
bbudge 2015/11/12 20:31:31 I think it would be better to inline these functio
llandwerlin-old 2015/11/13 12:46:23 Done.
385 }
386
387 bool PepperAudioEncoderHost::AllocateAudioBuffers(
388 const base::CheckedNumeric<size_t>& buffer_size) {
389 base::CheckedNumeric<size_t> total_buffer_size = buffer_size;
390 total_buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
391 if (!total_buffer_size.IsValid())
392 return false;
393
394 base::CheckedNumeric<size_t> total_memory_size =
395 total_buffer_size.ValueOrDie();
396 total_memory_size *= kDefaultNumberOfAudioBuffers;
397 if (!total_memory_size.IsValid())
398 return false;
399
400 if (!audio_buffer_manager_->SetBuffers(
401 kDefaultNumberOfAudioBuffers, total_buffer_size.ValueOrDie(),
402 RenderThreadImpl::current()
403 ->HostAllocateSharedMemoryBuffer(total_memory_size.ValueOrDie())
bbudge 2015/11/12 20:31:31 This could return a null pointer, and SetBuffers d
llandwerlin-old 2015/11/13 12:46:23 Done.
404 .Pass(),
405 false))
406 return false;
407
408 for (int32_t i = 0; i < audio_buffer_manager_->number_of_buffers(); ++i) {
409 ppapi::MediaStreamBuffer::Audio* buffer =
410 &(audio_buffer_manager_->GetBufferPointer(i)->audio);
411 buffer->header.size = total_buffer_size.ValueOrDie();
412 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
413 buffer->sample_rate = static_cast<PP_AudioBuffer_SampleRate>(
414 encode_parameters_.input_sample_rate);
415 buffer->number_of_channels = encode_parameters_.channels;
416 buffer->number_of_samples = encoder_->GetNumberOfSamplesPerFrame();
417 buffer->data_size = buffer_size.ValueOrDie();
418 }
419
420 return true;
421 }
422
423 bool PepperAudioEncoderHost::AllocateBitstreamBuffers(
424 const base::CheckedNumeric<size_t>& buffer_size) {
425 // Individual buffers are twice the size of the raw data.
426 base::CheckedNumeric<size_t> bitstream_buffer_size = buffer_size;
427 bitstream_buffer_size *= 2;
428 if (!bitstream_buffer_size.IsValid())
429 return false;
430
431 base::CheckedNumeric<size_t> total_size = bitstream_buffer_size;
432 total_size *= kDefaultNumberOfAudioBuffers;
433 if (!total_size.IsValid())
434 return false;
435
436 if (!bitstream_buffer_manager_->SetBuffers(
437 kDefaultNumberOfAudioBuffers, bitstream_buffer_size.ValueOrDie(),
438 RenderThreadImpl::current()
439 ->HostAllocateSharedMemoryBuffer(total_size.ValueOrDie())
440 .Pass(),
441 true))
442 return false;
443
444 return true;
445 }
446
447 void PepperAudioEncoderHost::DoEncode() {
448 if (!audio_buffer_manager_->HasAvailableBuffer() ||
449 !bitstream_buffer_manager_->HasAvailableBuffer())
450 return;
451
452 int32_t audio_buffer_id, bitstream_buffer_id;
453 AudioData input(
454 GetAudioDataFromBufferManager(audio_buffer_manager_, &audio_buffer_id));
455 AudioData output(GetBitstreamDataFromBufferManager(bitstream_buffer_manager_,
456 &bitstream_buffer_id));
bbudge 2015/11/12 20:31:31 I think this would be clearer if you just inline t
llandwerlin-old 2015/11/13 12:46:23 Done.
457
458 media_task_runner_->PostTask(
459 FROM_HERE, base::Bind(&AudioEncoderImpl::Encode,
460 base::Unretained(encoder_.get()), input, output,
461 media::BindToCurrentLoop(base::Bind(
462 &PepperAudioEncoderHost::BitstreamBufferReady,
463 weak_ptr_factory_.GetWeakPtr(), audio_buffer_id,
464 bitstream_buffer_id))));
465 }
466
467 void PepperAudioEncoderHost::BitstreamBufferReady(int32_t audio_buffer_id,
468 int32_t bitstream_buffer_id,
469 int32_t size) {
470 DCHECK(RenderThreadImpl::current());
471
472 if (encoder_last_error_)
473 return;
474
475 host()->SendUnsolicitedReply(
476 pp_resource(), PpapiPluginMsg_AudioEncoder_EncodeReply(audio_buffer_id));
477
478 if (size < 0) {
479 NotifyPepperError(PP_ERROR_FAILED);
480 return;
481 }
482
483 host()->SendUnsolicitedReply(
484 pp_resource(), PpapiPluginMsg_AudioEncoder_BitstreamBufferReady(
485 bitstream_buffer_id, static_cast<uint32_t>(size)));
486 }
487
488 void PepperAudioEncoderHost::NotifyPepperError(int32_t error) {
489 DCHECK(RenderThreadImpl::current());
490
491 encoder_last_error_ = error;
492 Close();
493 host()->SendUnsolicitedReply(
494 pp_resource(),
495 PpapiPluginMsg_AudioEncoder_NotifyError(encoder_last_error_));
496 }
497
498 void PepperAudioEncoderHost::Close() {
499 DCHECK(RenderThreadImpl::current());
500
501 // Destroy the encoder and the audio/bitstream buffers on the media thread
502 // to avoid freeing memory the encoder might still be working on.
503 media_task_runner_->PostTask(
504 FROM_HERE, base::Bind(&StopAudioEncoder, base::Passed(encoder_.Pass()),
505 base::Passed(audio_buffer_manager_.Pass()),
506 base::Passed(bitstream_buffer_manager_.Pass())));
507 }
508
509 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/pepper/pepper_audio_encoder_host.h ('k') | ppapi/ppapi_sources.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698