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

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: Drop bitstream buffer manager from proxy 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
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 bool PP_HardwareAccelerationCompatible(bool accelerated,
30 PP_HardwareAcceleration requested) {
31 switch (requested) {
32 case PP_HARDWAREACCELERATION_ONLY:
33 return accelerated;
34 case PP_HARDWAREACCELERATION_NONE:
35 return !accelerated;
36 case PP_HARDWAREACCELERATION_WITHFALLBACK:
37 return true;
38 // No default case, to catch unhandled PP_HardwareAcceleration values.
39 }
40 return false;
41 }
42
43 } // namespace
44
45 // This class should be constructed and initialized on the main renderer
46 // thread, used and destructed on the media thread.
47 class PepperAudioEncoderHost::AudioEncoderImpl {
48 public:
49 // Callback used to signal encoded data. If |size| is negative, an error
50 // occurred.
51 using BitstreamBufferReadyCB = base::Callback<void(int32_t size)>;
52
53 AudioEncoderImpl();
54 ~AudioEncoderImpl();
55
56 // Used on the renderer thread.
57 static std::vector<PP_AudioProfileDescription> GetSupportedProfiles();
58 bool Initialize(const ppapi::proxy::PPB_AudioEncodeParameters& parameters);
59 int32_t GetNumberOfSamplesPerFrame();
60
61 // Used on the media thread.
62 void Encode(uint8_t* input_data,
63 size_t input_size,
64 uint8_t* output_data,
65 size_t output_size,
66 BitstreamBufferReadyCB callback);
67 void RequestBitrateChange(uint32_t bitrate);
68
69 private:
70 scoped_ptr<uint8[]> encoder_memory_;
71 OpusEncoder* opus_encoder_;
72
73 // Initialization parameters, only valid if |encoder_memory_| is not
74 // nullptr.
75 ppapi::proxy::PPB_AudioEncodeParameters parameters_;
76
77 DISALLOW_COPY_AND_ASSIGN(AudioEncoderImpl);
78 };
79
80 PepperAudioEncoderHost::AudioEncoderImpl::AudioEncoderImpl()
81 : opus_encoder_(nullptr) {}
82
83 PepperAudioEncoderHost::AudioEncoderImpl::~AudioEncoderImpl() {}
84
85 // static
86 std::vector<PP_AudioProfileDescription>
87 PepperAudioEncoderHost::AudioEncoderImpl::GetSupportedProfiles() {
88 std::vector<PP_AudioProfileDescription> profiles;
89 static const uint32_t sampling_rates[] = {8000, 12000, 16000, 24000, 48000};
90
91 for (uint32_t i = 0; i < arraysize(sampling_rates); ++i) {
92 PP_AudioProfileDescription profile;
93 profile.profile = PP_AUDIOPROFILE_OPUS;
94 profile.max_channels = 2;
95 profile.sample_size = PP_AUDIOBUFFER_SAMPLESIZE_16_BITS;
96 profile.sample_rate = sampling_rates[i];
97 profile.hardware_accelerated = PP_FALSE;
98 profiles.push_back(profile);
99 }
100 return profiles;
101 }
102
103 bool PepperAudioEncoderHost::AudioEncoderImpl::Initialize(
104 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
105 if (parameters.output_profile != PP_AUDIOPROFILE_OPUS)
106 return false;
107
108 DCHECK(!encoder_memory_);
109
110 int32_t encoder_size = opus_encoder_get_size(parameters.channels);
111 if (encoder_size < 1)
112 return false;
113
114 scoped_ptr<uint8[]> encoder_memory(new uint8[encoder_size]);
115 opus_encoder_ = reinterpret_cast<OpusEncoder*>(encoder_memory.get());
116
117 if (opus_encoder_init(opus_encoder_, parameters.input_sample_rate,
118 parameters.channels, OPUS_APPLICATION_AUDIO) != OPUS_OK)
119 return false;
120
121 if (opus_encoder_ctl(opus_encoder_,
122 OPUS_SET_BITRATE(parameters.initial_bitrate <= 0
123 ? OPUS_AUTO
124 : parameters.initial_bitrate)) !=
125 OPUS_OK)
126 return false;
127
128 encoder_memory_.swap(encoder_memory);
129 parameters_ = parameters;
130
131 return true;
132 }
133
134 int32_t PepperAudioEncoderHost::AudioEncoderImpl::GetNumberOfSamplesPerFrame() {
135 DCHECK(encoder_memory_);
136 // Opus supports 2.5, 5, 10, 20, 40 or 60ms audio frames. We take
137 // 10ms by default.
138 return parameters_.input_sample_rate / 100;
139 }
140
141 void PepperAudioEncoderHost::AudioEncoderImpl::Encode(
142 uint8_t* input_data,
143 size_t input_size,
144 uint8_t* output_data,
145 size_t output_size,
146 BitstreamBufferReadyCB callback) {
147 DCHECK(encoder_memory_);
148 int32_t result = opus_encode(
149 opus_encoder_, reinterpret_cast<opus_int16*>(input_data),
150 (input_size / parameters_.channels) / parameters_.input_sample_size,
151 output_data, output_size);
152 callback.Run(result);
153 }
154
155 void PepperAudioEncoderHost::AudioEncoderImpl::RequestBitrateChange(
156 uint32_t bitrate) {
157 DCHECK(encoder_memory_);
158 opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate));
159 }
160
161 PepperAudioEncoderHost::PepperAudioEncoderHost(RendererPpapiHost* host,
162 PP_Instance instance,
163 PP_Resource resource)
164 : ResourceHost(host->GetPpapiHost(), instance, resource),
165 renderer_ppapi_host_(host),
166 initialized_(false),
167 encoder_last_error_(PP_ERROR_FAILED),
168 media_task_runner_(RenderThreadImpl::current()
169 ->GetMediaThreadTaskRunner()),
170 weak_ptr_factory_(this) {}
171
172 PepperAudioEncoderHost::~PepperAudioEncoderHost() {
173 Close();
174 }
175
176 int32_t PepperAudioEncoderHost::OnResourceMessageReceived(
177 const IPC::Message& msg,
178 ppapi::host::HostMessageContext* context) {
179 PPAPI_BEGIN_MESSAGE_MAP(PepperAudioEncoderHost, msg)
180 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
181 PpapiHostMsg_AudioEncoder_GetSupportedProfiles,
182 OnHostMsgGetSupportedProfiles)
183 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioEncoder_Initialize,
184 OnHostMsgInitialize)
185 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioEncoder_Encode,
186 OnHostMsgEncode)
187 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
188 PpapiHostMsg_AudioEncoder_RecycleBitstreamBuffer,
189 OnHostMsgRecycleBitstreamBuffer)
190 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
191 PpapiHostMsg_AudioEncoder_RequestBitrateChange,
192 OnHostMsgRequestBitrateChange)
193 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_AudioEncoder_Close,
194 OnHostMsgClose)
195 PPAPI_END_MESSAGE_MAP()
196 return PP_ERROR_FAILED;
197 }
198
199 int32_t PepperAudioEncoderHost::OnHostMsgGetSupportedProfiles(
200 ppapi::host::HostMessageContext* context) {
201 std::vector<PP_AudioProfileDescription> profiles;
202 GetSupportedProfiles(&profiles);
203
204 host()->SendReply(
205 context->MakeReplyMessageContext(),
206 PpapiPluginMsg_AudioEncoder_GetSupportedProfilesReply(profiles));
207
208 return PP_OK_COMPLETIONPENDING;
209 }
210
211 int32_t PepperAudioEncoderHost::OnHostMsgInitialize(
212 ppapi::host::HostMessageContext* context,
213 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
214 if (initialized_)
215 return PP_ERROR_FAILED;
216
217 if (!IsInitializationValid(parameters))
218 return PP_ERROR_NOTSUPPORTED;
219
220 parameters_ = parameters;
bbudge 2015/11/17 00:53:40 I don't think this field is actually used in the h
llandwerlin-old 2015/11/17 15:22:30 Done.
221
222 if (parameters.acceleration == PP_HARDWAREACCELERATION_ONLY)
223 return PP_ERROR_FAILED;
224
225 scoped_ptr<AudioEncoderImpl> encoder(new AudioEncoderImpl);
226 if (!encoder->Initialize(parameters))
227 return PP_ERROR_FAILED;
228 if (!AllocateBuffers(parameters, encoder->GetNumberOfSamplesPerFrame()))
229 return PP_ERROR_NOMEMORY;
230
231 initialized_ = true;
232 encoder_last_error_ = PP_OK;
233 encoder_.swap(encoder);
234
235 ppapi::host::ReplyMessageContext reply_context =
236 context->MakeReplyMessageContext();
237 reply_context.params.AppendHandle(
238 SerializedHandle(renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(
239 audio_buffer_manager_->shm()->handle()),
240 audio_buffer_manager_->shm()->mapped_size()));
241 reply_context.params.AppendHandle(
242 SerializedHandle(renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(
243 bitstream_buffer_manager_->shm()->handle()),
244 bitstream_buffer_manager_->shm()->mapped_size()));
245 host()->SendReply(reply_context,
246 PpapiPluginMsg_AudioEncoder_InitializeReply(
247 encoder_->GetNumberOfSamplesPerFrame(),
248 audio_buffer_manager_->number_of_buffers(),
249 audio_buffer_manager_->buffer_size(),
250 bitstream_buffer_manager_->number_of_buffers(),
251 bitstream_buffer_manager_->buffer_size()));
252
253 return PP_OK_COMPLETIONPENDING;
254 }
255
256 int32_t PepperAudioEncoderHost::OnHostMsgEncode(
257 ppapi::host::HostMessageContext* context,
258 int32_t buffer_id) {
259 if (encoder_last_error_)
260 return encoder_last_error_;
261
262 if (buffer_id < 0 || buffer_id >= audio_buffer_manager_->number_of_buffers())
263 return PP_ERROR_BADARGUMENT;
264
265 audio_buffer_manager_->EnqueueBuffer(buffer_id);
266
267 DoEncode();
268
269 return PP_OK_COMPLETIONPENDING;
270 }
271
272 int32_t PepperAudioEncoderHost::OnHostMsgRecycleBitstreamBuffer(
273 ppapi::host::HostMessageContext* context,
274 int32_t buffer_id) {
275 if (encoder_last_error_)
276 return encoder_last_error_;
277
278 if (buffer_id < 0 ||
279 buffer_id >= bitstream_buffer_manager_->number_of_buffers())
280 return PP_ERROR_BADARGUMENT;
281
282 bitstream_buffer_manager_->EnqueueBuffer(buffer_id);
283
284 DoEncode();
285
286 return PP_OK;
287 }
288
289 int32_t PepperAudioEncoderHost::OnHostMsgRequestBitrateChange(
290 ppapi::host::HostMessageContext* context,
291 uint32_t bitrate) {
292 if (encoder_last_error_)
293 return encoder_last_error_;
294
295 media_task_runner_->PostTask(
296 FROM_HERE, base::Bind(&AudioEncoderImpl::RequestBitrateChange,
297 base::Unretained(encoder_.get()), bitrate));
298
299 return PP_OK;
300 }
301
302 int32_t PepperAudioEncoderHost::OnHostMsgClose(
303 ppapi::host::HostMessageContext* context) {
304 encoder_last_error_ = PP_ERROR_FAILED;
305 Close();
306
307 return PP_OK;
308 }
309
310 void PepperAudioEncoderHost::GetSupportedProfiles(
311 std::vector<PP_AudioProfileDescription>* profiles) {
312 DCHECK(RenderThreadImpl::current());
313
314 *profiles = AudioEncoderImpl::GetSupportedProfiles();
315 }
316
317 bool PepperAudioEncoderHost::IsInitializationValid(
318 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
319 DCHECK(RenderThreadImpl::current());
320
321 std::vector<PP_AudioProfileDescription> profiles;
322 GetSupportedProfiles(&profiles);
323
324 for (const PP_AudioProfileDescription& profile : profiles) {
325 if (parameters.output_profile == profile.profile &&
326 parameters.input_sample_size == profile.sample_size &&
327 parameters.input_sample_rate == profile.sample_rate &&
328 parameters.channels <= profile.max_channels &&
329 PP_HardwareAccelerationCompatible(
330 profile.hardware_accelerated == PP_TRUE ? true : false,
bbudge 2015/11/17 00:53:39 nit: this is equivalent to just profile.hardware_a
llandwerlin-old 2015/11/17 15:22:30 Done.
331 parameters.acceleration))
332 return true;
333 }
334
335 return false;
336 }
337
338 bool PepperAudioEncoderHost::AllocateBuffers(
339 const ppapi::proxy::PPB_AudioEncodeParameters& parameters,
340 int32_t samples_per_frame) {
bbudge 2015/11/17 00:53:39 Suggestion for the body of this method: Do all siz
llandwerlin-old 2015/11/17 15:22:30 Done.
341 DCHECK(RenderThreadImpl::current());
342
343 // Audio buffers size computation & allocation.
344 base::CheckedNumeric<size_t> audio_buffer_size = samples_per_frame;
345 audio_buffer_size *= parameters.channels;
346 audio_buffer_size *= parameters.input_sample_size;
347
348 base::CheckedNumeric<size_t> total_audio_buffer_size = audio_buffer_size;
349 total_audio_buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
350 if (!total_audio_buffer_size.IsValid())
351 return false;
352
353 base::CheckedNumeric<size_t> total_audio_memory_size =
354 total_audio_buffer_size.ValueOrDie();
bbudge 2015/11/17 00:53:40 No need for ValueOrDie here.
llandwerlin-old 2015/11/17 15:22:30 Done.
355 total_audio_memory_size *= kDefaultNumberOfAudioBuffers;
356 if (!total_audio_memory_size.IsValid())
357 return false;
358
359 scoped_ptr<base::SharedMemory> audio_memory(
360 RenderThreadImpl::current()->HostAllocateSharedMemoryBuffer(
361 total_audio_memory_size.ValueOrDie()));
362 if (!audio_memory)
363 return false;
364
365 scoped_ptr<ppapi::MediaStreamBufferManager> audio_buffer_manager(
366 new ppapi::MediaStreamBufferManager(this));
367 if (!audio_buffer_manager->SetBuffers(kDefaultNumberOfAudioBuffers,
368 total_audio_buffer_size.ValueOrDie(),
369 audio_memory.Pass(), false))
370 return false;
371
372 for (int32_t i = 0; i < audio_buffer_manager->number_of_buffers(); ++i) {
373 ppapi::MediaStreamBuffer::Audio* buffer =
374 &(audio_buffer_manager->GetBufferPointer(i)->audio);
375 buffer->header.size = total_audio_buffer_size.ValueOrDie();
376 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
377 buffer->sample_rate =
378 static_cast<PP_AudioBuffer_SampleRate>(parameters.input_sample_rate);
379 buffer->number_of_channels = parameters.channels;
380 buffer->number_of_samples = samples_per_frame;
381 buffer->data_size = audio_buffer_size.ValueOrDie();
382 }
383
384 // Bitstream buffers size computation & allocation (individual bitstream
385 // buffers are twice the size of the raw data, to handle the worse case
bbudge 2015/11/17 00:53:39 nit: worst
llandwerlin-old 2015/11/17 15:22:30 Done.
386 // where compression doesn't work).
387 base::CheckedNumeric<size_t> bitstream_buffer_size = audio_buffer_size;
388 bitstream_buffer_size *= 2;
389 if (!bitstream_buffer_size.IsValid())
390 return false;
391
392 base::CheckedNumeric<size_t> total_bitstream_memory_size =
393 bitstream_buffer_size;
394 total_bitstream_memory_size *= kDefaultNumberOfAudioBuffers;
395 if (!total_bitstream_memory_size.IsValid())
396 return false;
397
398 scoped_ptr<base::SharedMemory> bitstream_memory(
399 RenderThreadImpl::current()->HostAllocateSharedMemoryBuffer(
400 total_bitstream_memory_size.ValueOrDie()));
401 if (!bitstream_memory)
402 return false;
403
404 scoped_ptr<ppapi::MediaStreamBufferManager> bitstream_buffer_manager(
405 new ppapi::MediaStreamBufferManager(this));
406 if (!bitstream_buffer_manager->SetBuffers(kDefaultNumberOfAudioBuffers,
407 bitstream_buffer_size.ValueOrDie(),
408 bitstream_memory.Pass(), true))
409 return false;
410
411 audio_buffer_manager_.swap(audio_buffer_manager);
412 bitstream_buffer_manager_.swap(bitstream_buffer_manager);
413
414 return true;
415 }
416
417 void PepperAudioEncoderHost::DoEncode() {
418 DCHECK(RenderThreadImpl::current());
419 DCHECK(encoder_);
bbudge 2015/11/17 00:53:40 It seems strange that this is the only place you D
llandwerlin-old 2015/11/17 15:22:30 Done.
420
421 if (!audio_buffer_manager_->HasAvailableBuffer() ||
422 !bitstream_buffer_manager_->HasAvailableBuffer())
423 return;
424
425 int32_t audio_buffer_id = audio_buffer_manager_->DequeueBuffer();
426 int32_t bitstream_buffer_id = bitstream_buffer_manager_->DequeueBuffer();
427
428 ppapi::MediaStreamBuffer* audio_buffer =
429 audio_buffer_manager_->GetBufferPointer(audio_buffer_id);
430 uint8_t* bitstream_data = reinterpret_cast<uint8_t*>(
431 bitstream_buffer_manager_->GetBufferPointer(bitstream_buffer_id));
432
433 media_task_runner_->PostTask(
434 FROM_HERE,
435 base::Bind(&AudioEncoderImpl::Encode, base::Unretained(encoder_.get()),
436 static_cast<uint8_t*>(audio_buffer->audio.data),
437 audio_buffer->audio.data_size, bitstream_data,
438 bitstream_buffer_manager_->buffer_size(),
439 media::BindToCurrentLoop(
440 base::Bind(&PepperAudioEncoderHost::BitstreamBufferReady,
441 weak_ptr_factory_.GetWeakPtr(), audio_buffer_id,
442 bitstream_buffer_id))));
443 }
444
445 void PepperAudioEncoderHost::BitstreamBufferReady(int32_t audio_buffer_id,
446 int32_t bitstream_buffer_id,
447 int32_t size) {
448 DCHECK(RenderThreadImpl::current());
449
450 if (encoder_last_error_)
451 return;
452
453 host()->SendUnsolicitedReply(
454 pp_resource(), PpapiPluginMsg_AudioEncoder_EncodeReply(audio_buffer_id));
455
456 if (size < 0) {
457 NotifyPepperError(PP_ERROR_FAILED);
458 return;
459 }
460
461 host()->SendUnsolicitedReply(
462 pp_resource(), PpapiPluginMsg_AudioEncoder_BitstreamBufferReady(
463 bitstream_buffer_id, static_cast<uint32_t>(size)));
bbudge 2015/11/17 00:53:40 size is int32_t everywhere else.
llandwerlin-old 2015/11/17 15:22:30 Done.
464 }
465
466 void PepperAudioEncoderHost::NotifyPepperError(int32_t error) {
467 DCHECK(RenderThreadImpl::current());
468
469 encoder_last_error_ = error;
470 Close();
471 host()->SendUnsolicitedReply(
472 pp_resource(),
473 PpapiPluginMsg_AudioEncoder_NotifyError(encoder_last_error_));
474 }
475
476 void PepperAudioEncoderHost::Close() {
477 DCHECK(RenderThreadImpl::current());
478
479 // Destroy the encoder and the audio/bitstream buffers on the media thread
480 // to avoid freeing memory the encoder might still be working on.
481 media_task_runner_->PostTask(
482 FROM_HERE, base::Bind(&StopAudioEncoder, base::Passed(encoder_.Pass()),
483 base::Passed(audio_buffer_manager_.Pass()),
484 base::Passed(bitstream_buffer_manager_.Pass())));
485 }
486
487 // static
488 void PepperAudioEncoderHost::StopAudioEncoder(
489 scoped_ptr<AudioEncoderImpl> encoder,
490 scoped_ptr<ppapi::MediaStreamBufferManager> audio_buffer_manager,
491 scoped_ptr<ppapi::MediaStreamBufferManager> bitstream_buffer_manager) {}
bbudge 2015/11/17 00:53:40 Make free function.
llandwerlin-old 2015/11/17 15:22:30 Are you asking to put the function in an anonymous
bbudge 2015/11/17 20:23:59 I see now. It's fine like this.
492
493 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698