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

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: Add bitstream buffer type to MediaStreamBuffer 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 if (parameters.acceleration == PP_HARDWAREACCELERATION_ONLY)
bbudge 2015/11/17 20:23:59 This should be caught in IsInitializationValid.
221 return PP_ERROR_FAILED;
222
223 scoped_ptr<AudioEncoderImpl> encoder(new AudioEncoderImpl);
224 if (!encoder->Initialize(parameters))
225 return PP_ERROR_FAILED;
226 if (!AllocateBuffers(parameters, encoder->GetNumberOfSamplesPerFrame()))
227 return PP_ERROR_NOMEMORY;
228
229 initialized_ = true;
230 encoder_last_error_ = PP_OK;
231 encoder_.swap(encoder);
232
233 ppapi::host::ReplyMessageContext reply_context =
234 context->MakeReplyMessageContext();
235 reply_context.params.AppendHandle(
236 SerializedHandle(renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(
237 audio_buffer_manager_->shm()->handle()),
238 audio_buffer_manager_->shm()->mapped_size()));
239 reply_context.params.AppendHandle(
240 SerializedHandle(renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(
241 bitstream_buffer_manager_->shm()->handle()),
242 bitstream_buffer_manager_->shm()->mapped_size()));
243 host()->SendReply(reply_context,
244 PpapiPluginMsg_AudioEncoder_InitializeReply(
245 encoder_->GetNumberOfSamplesPerFrame(),
246 audio_buffer_manager_->number_of_buffers(),
247 audio_buffer_manager_->buffer_size(),
248 bitstream_buffer_manager_->number_of_buffers(),
249 bitstream_buffer_manager_->buffer_size()));
250
251 return PP_OK_COMPLETIONPENDING;
252 }
253
254 int32_t PepperAudioEncoderHost::OnHostMsgEncode(
255 ppapi::host::HostMessageContext* context,
256 int32_t buffer_id) {
257 if (encoder_last_error_)
258 return encoder_last_error_;
259
260 if (buffer_id < 0 || buffer_id >= audio_buffer_manager_->number_of_buffers())
261 return PP_ERROR_BADARGUMENT;
bbudge 2015/11/17 20:23:59 The resource checks for a bad buffer resource. Thi
llandwerlin-old 2015/11/18 11:31:46 Done.
262
263 audio_buffer_manager_->EnqueueBuffer(buffer_id);
264
265 DoEncode();
266
267 return PP_OK_COMPLETIONPENDING;
268 }
269
270 int32_t PepperAudioEncoderHost::OnHostMsgRecycleBitstreamBuffer(
271 ppapi::host::HostMessageContext* context,
272 int32_t buffer_id) {
273 if (encoder_last_error_)
274 return encoder_last_error_;
275
276 if (buffer_id < 0 ||
277 buffer_id >= bitstream_buffer_manager_->number_of_buffers())
278 return PP_ERROR_BADARGUMENT;
279
280 bitstream_buffer_manager_->EnqueueBuffer(buffer_id);
281
282 DoEncode();
283
284 return PP_OK;
285 }
286
287 int32_t PepperAudioEncoderHost::OnHostMsgRequestBitrateChange(
288 ppapi::host::HostMessageContext* context,
289 uint32_t bitrate) {
290 if (encoder_last_error_)
291 return encoder_last_error_;
292
293 media_task_runner_->PostTask(
294 FROM_HERE, base::Bind(&AudioEncoderImpl::RequestBitrateChange,
295 base::Unretained(encoder_.get()), bitrate));
296
297 return PP_OK;
298 }
299
300 int32_t PepperAudioEncoderHost::OnHostMsgClose(
301 ppapi::host::HostMessageContext* context) {
302 encoder_last_error_ = PP_ERROR_FAILED;
303 Close();
304
305 return PP_OK;
306 }
307
308 void PepperAudioEncoderHost::GetSupportedProfiles(
309 std::vector<PP_AudioProfileDescription>* profiles) {
310 DCHECK(RenderThreadImpl::current());
311
312 *profiles = AudioEncoderImpl::GetSupportedProfiles();
313 }
314
315 bool PepperAudioEncoderHost::IsInitializationValid(
316 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
317 DCHECK(RenderThreadImpl::current());
318
319 std::vector<PP_AudioProfileDescription> profiles;
320 GetSupportedProfiles(&profiles);
321
322 for (const PP_AudioProfileDescription& profile : profiles) {
323 if (parameters.output_profile == profile.profile &&
324 parameters.input_sample_size == profile.sample_size &&
325 parameters.input_sample_rate == profile.sample_rate &&
326 parameters.channels <= profile.max_channels &&
327 PP_HardwareAccelerationCompatible(
328 profile.hardware_accelerated == PP_TRUE, parameters.acceleration))
329 return true;
330 }
331
332 return false;
333 }
334
335 bool PepperAudioEncoderHost::AllocateBuffers(
336 const ppapi::proxy::PPB_AudioEncodeParameters& parameters,
337 int32_t samples_per_frame) {
338 DCHECK(RenderThreadImpl::current());
339
340 // Audio buffers size computation.
341 base::CheckedNumeric<size_t> audio_buffer_size = samples_per_frame;
342 audio_buffer_size *= parameters.channels;
343 audio_buffer_size *= parameters.input_sample_size;
344
345 base::CheckedNumeric<size_t> total_audio_buffer_size = audio_buffer_size;
346 total_audio_buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
347 base::CheckedNumeric<size_t> total_audio_memory_size =
348 total_audio_buffer_size;
349 total_audio_memory_size *= kDefaultNumberOfAudioBuffers;
350
351 // Bitstream buffers size computation (individual bitstream buffers are
352 // twice the size of the raw data, to handle the worst case where
353 // compression doesn't work).
354 base::CheckedNumeric<size_t> bitstream_buffer_size = audio_buffer_size;
355 bitstream_buffer_size *= 2;
356 bitstream_buffer_size += sizeof(ppapi::MediaStreamBuffer::Bitstream);
357 base::CheckedNumeric<size_t> total_bitstream_memory_size =
358 bitstream_buffer_size;
359 total_bitstream_memory_size *= kDefaultNumberOfAudioBuffers;
360
361 if (!total_audio_memory_size.IsValid() ||
362 !total_bitstream_memory_size.IsValid())
363 return false;
364
365 scoped_ptr<base::SharedMemory> audio_memory(
366 RenderThreadImpl::current()->HostAllocateSharedMemoryBuffer(
367 total_audio_memory_size.ValueOrDie()));
368 if (!audio_memory)
369 return false;
370 scoped_ptr<ppapi::MediaStreamBufferManager> audio_buffer_manager(
371 new ppapi::MediaStreamBufferManager(this));
372 if (!audio_buffer_manager->SetBuffers(kDefaultNumberOfAudioBuffers,
373 total_audio_buffer_size.ValueOrDie(),
374 audio_memory.Pass(), false))
375 return false;
376
377 for (int32_t i = 0; i < audio_buffer_manager->number_of_buffers(); ++i) {
378 ppapi::MediaStreamBuffer::Audio* buffer =
379 &(audio_buffer_manager->GetBufferPointer(i)->audio);
380 buffer->header.size = total_audio_buffer_size.ValueOrDie();
381 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
382 buffer->sample_rate =
383 static_cast<PP_AudioBuffer_SampleRate>(parameters.input_sample_rate);
384 buffer->number_of_channels = parameters.channels;
385 buffer->number_of_samples = samples_per_frame;
386 buffer->data_size = audio_buffer_size.ValueOrDie();
387 }
388
389 scoped_ptr<base::SharedMemory> bitstream_memory(
390 RenderThreadImpl::current()->HostAllocateSharedMemoryBuffer(
391 total_bitstream_memory_size.ValueOrDie()));
392 if (!bitstream_memory)
393 return false;
394 scoped_ptr<ppapi::MediaStreamBufferManager> bitstream_buffer_manager(
395 new ppapi::MediaStreamBufferManager(this));
396 if (!bitstream_buffer_manager->SetBuffers(kDefaultNumberOfAudioBuffers,
397 bitstream_buffer_size.ValueOrDie(),
398 bitstream_memory.Pass(), true))
399 return false;
400
401 for (int32_t i = 0; i < bitstream_buffer_manager->number_of_buffers(); ++i) {
402 ppapi::MediaStreamBuffer::Bitstream* buffer =
403 &(bitstream_buffer_manager->GetBufferPointer(i)->bitstream);
404 buffer->header.size = bitstream_buffer_size.ValueOrDie();
405 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_BITSTREAM;
406 }
407
408 audio_buffer_manager_.swap(audio_buffer_manager);
409 bitstream_buffer_manager_.swap(bitstream_buffer_manager);
410
411 return true;
412 }
413
414 void PepperAudioEncoderHost::DoEncode() {
415 DCHECK(RenderThreadImpl::current());
416 DCHECK(!encoder_last_error_);
417
418 if (!audio_buffer_manager_->HasAvailableBuffer() ||
419 !bitstream_buffer_manager_->HasAvailableBuffer())
420 return;
421
422 int32_t audio_buffer_id = audio_buffer_manager_->DequeueBuffer();
423 int32_t bitstream_buffer_id = bitstream_buffer_manager_->DequeueBuffer();
424
425 ppapi::MediaStreamBuffer* audio_buffer =
426 audio_buffer_manager_->GetBufferPointer(audio_buffer_id);
427 ppapi::MediaStreamBuffer* bitstream_buffer =
428 bitstream_buffer_manager_->GetBufferPointer(bitstream_buffer_id);
429
430 media_task_runner_->PostTask(
431 FROM_HERE,
432 base::Bind(&AudioEncoderImpl::Encode, base::Unretained(encoder_.get()),
433 static_cast<uint8_t*>(audio_buffer->audio.data),
434 audio_buffer->audio.data_size,
bbudge 2015/11/17 20:23:59 A malicious plugin could modify the header we init
llandwerlin-old 2015/11/18 11:31:46 Thanks, I just realized the size isn't right eithe
435 static_cast<uint8_t*>(bitstream_buffer->bitstream.data),
436 bitstream_buffer->header.size,
bbudge 2015/11/17 20:23:59 Ditto.
llandwerlin-old 2015/11/18 11:31:46 Done.
437 media::BindToCurrentLoop(
438 base::Bind(&PepperAudioEncoderHost::BitstreamBufferReady,
439 weak_ptr_factory_.GetWeakPtr(), audio_buffer_id,
440 bitstream_buffer_id))));
441 }
442
443 void PepperAudioEncoderHost::BitstreamBufferReady(int32_t audio_buffer_id,
444 int32_t bitstream_buffer_id,
445 int32_t size) {
446 DCHECK(RenderThreadImpl::current());
447
448 if (encoder_last_error_)
449 return;
450
451 host()->SendUnsolicitedReply(
452 pp_resource(), PpapiPluginMsg_AudioEncoder_EncodeReply(audio_buffer_id));
453
454 if (size < 0) {
455 NotifyPepperError(PP_ERROR_FAILED);
456 return;
457 }
458
459 ppapi::MediaStreamBuffer::Bitstream* buffer =
460 &(bitstream_buffer_manager_->GetBufferPointer(bitstream_buffer_id)
461 ->bitstream);
462 buffer->data_size = static_cast<uint32_t>(size);
463
464 host()->SendUnsolicitedReply(
465 pp_resource(),
466 PpapiPluginMsg_AudioEncoder_BitstreamBufferReady(bitstream_buffer_id));
467 }
468
469 void PepperAudioEncoderHost::NotifyPepperError(int32_t error) {
470 DCHECK(RenderThreadImpl::current());
471
472 encoder_last_error_ = error;
473 Close();
474 host()->SendUnsolicitedReply(
475 pp_resource(),
476 PpapiPluginMsg_AudioEncoder_NotifyError(encoder_last_error_));
477 }
478
479 void PepperAudioEncoderHost::Close() {
480 DCHECK(RenderThreadImpl::current());
481
482 // Destroy the encoder and the audio/bitstream buffers on the media thread
483 // to avoid freeing memory the encoder might still be working on.
484 media_task_runner_->PostTask(
485 FROM_HERE, base::Bind(&StopAudioEncoder, base::Passed(encoder_.Pass()),
486 base::Passed(audio_buffer_manager_.Pass()),
487 base::Passed(bitstream_buffer_manager_.Pass())));
488 }
489
490 // static
491 void PepperAudioEncoderHost::StopAudioEncoder(
492 scoped_ptr<AudioEncoderImpl> encoder,
493 scoped_ptr<ppapi::MediaStreamBufferManager> audio_buffer_manager,
494 scoped_ptr<ppapi::MediaStreamBufferManager> bitstream_buffer_manager) {}
495
496 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698