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 "ppapi/proxy/audio_encoder_resource.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/memory/unsafe_shared_memory_region.h"
11 #include "ppapi/c/pp_array_output.h"
12 #include "ppapi/c/pp_codecs.h"
13 #include "ppapi/proxy/audio_buffer_resource.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/shared_impl/array_writer.h"
16 #include "ppapi/shared_impl/media_stream_buffer.h"
17 #include "ppapi/thunk/enter.h"
18
19 namespace ppapi {
20 namespace proxy {
21
AudioEncoderResource(Connection connection,PP_Instance instance)22 AudioEncoderResource::AudioEncoderResource(Connection connection,
23 PP_Instance instance)
24 : PluginResource(connection, instance),
25 encoder_last_error_(PP_ERROR_FAILED),
26 initialized_(false),
27 audio_buffer_manager_(this),
28 bitstream_buffer_manager_(this) {
29 SendCreate(RENDERER, PpapiHostMsg_AudioEncoder_Create());
30 }
31
~AudioEncoderResource()32 AudioEncoderResource::~AudioEncoderResource() {
33 }
34
AsPPB_AudioEncoder_API()35 thunk::PPB_AudioEncoder_API* AudioEncoderResource::AsPPB_AudioEncoder_API() {
36 return this;
37 }
38
GetSupportedProfiles(const PP_ArrayOutput & output,const scoped_refptr<TrackedCallback> & callback)39 int32_t AudioEncoderResource::GetSupportedProfiles(
40 const PP_ArrayOutput& output,
41 const scoped_refptr<TrackedCallback>& callback) {
42 if (TrackedCallback::IsPending(get_supported_profiles_callback_))
43 return PP_ERROR_INPROGRESS;
44
45 get_supported_profiles_callback_ = callback;
46 Call<PpapiPluginMsg_AudioEncoder_GetSupportedProfilesReply>(
47 RENDERER, PpapiHostMsg_AudioEncoder_GetSupportedProfiles(),
48 base::Bind(&AudioEncoderResource::OnPluginMsgGetSupportedProfilesReply,
49 this, output));
50 return PP_OK_COMPLETIONPENDING;
51 }
52
Initialize(uint32_t channels,PP_AudioBuffer_SampleRate input_sample_rate,PP_AudioBuffer_SampleSize input_sample_size,PP_AudioProfile output_profile,uint32_t initial_bitrate,PP_HardwareAcceleration acceleration,const scoped_refptr<TrackedCallback> & callback)53 int32_t AudioEncoderResource::Initialize(
54 uint32_t channels,
55 PP_AudioBuffer_SampleRate input_sample_rate,
56 PP_AudioBuffer_SampleSize input_sample_size,
57 PP_AudioProfile output_profile,
58 uint32_t initial_bitrate,
59 PP_HardwareAcceleration acceleration,
60 const scoped_refptr<TrackedCallback>& callback) {
61 if (initialized_)
62 return PP_ERROR_FAILED;
63 if (TrackedCallback::IsPending(initialize_callback_))
64 return PP_ERROR_INPROGRESS;
65
66 initialize_callback_ = callback;
67
68 PPB_AudioEncodeParameters parameters;
69 parameters.channels = channels;
70 parameters.input_sample_rate = input_sample_rate;
71 parameters.input_sample_size = input_sample_size;
72 parameters.output_profile = output_profile;
73 parameters.initial_bitrate = initial_bitrate;
74 parameters.acceleration = acceleration;
75
76 Call<PpapiPluginMsg_AudioEncoder_InitializeReply>(
77 RENDERER, PpapiHostMsg_AudioEncoder_Initialize(parameters),
78 base::Bind(&AudioEncoderResource::OnPluginMsgInitializeReply, this));
79 return PP_OK_COMPLETIONPENDING;
80 }
81
GetNumberOfSamples()82 int32_t AudioEncoderResource::GetNumberOfSamples() {
83 if (encoder_last_error_)
84 return encoder_last_error_;
85 return number_of_samples_;
86 }
87
GetBuffer(PP_Resource * audio_buffer,const scoped_refptr<TrackedCallback> & callback)88 int32_t AudioEncoderResource::GetBuffer(
89 PP_Resource* audio_buffer,
90 const scoped_refptr<TrackedCallback>& callback) {
91 if (encoder_last_error_)
92 return encoder_last_error_;
93 if (TrackedCallback::IsPending(get_buffer_callback_))
94 return PP_ERROR_INPROGRESS;
95
96 get_buffer_data_ = audio_buffer;
97 get_buffer_callback_ = callback;
98
99 TryGetAudioBuffer();
100
101 return PP_OK_COMPLETIONPENDING;
102 }
103
Encode(PP_Resource audio_buffer,const scoped_refptr<TrackedCallback> & callback)104 int32_t AudioEncoderResource::Encode(
105 PP_Resource audio_buffer,
106 const scoped_refptr<TrackedCallback>& callback) {
107 if (encoder_last_error_)
108 return encoder_last_error_;
109
110 AudioBufferMap::iterator it = audio_buffers_.find(audio_buffer);
111 if (it == audio_buffers_.end())
112 // TODO(llandwerlin): accept MediaStreamAudioTrack's audio buffers.
113 return PP_ERROR_BADRESOURCE;
114
115 scoped_refptr<AudioBufferResource> buffer_resource = it->second;
116
117 encode_callbacks_.insert(
118 std::make_pair(buffer_resource->GetBufferIndex(), callback));
119
120 Post(RENDERER,
121 PpapiHostMsg_AudioEncoder_Encode(buffer_resource->GetBufferIndex()));
122
123 // Invalidate the buffer to prevent a CHECK failure when the
124 // AudioBufferResource is destructed.
125 buffer_resource->Invalidate();
126 audio_buffers_.erase(it);
127
128 return PP_OK_COMPLETIONPENDING;
129 }
130
GetBitstreamBuffer(PP_AudioBitstreamBuffer * bitstream_buffer,const scoped_refptr<TrackedCallback> & callback)131 int32_t AudioEncoderResource::GetBitstreamBuffer(
132 PP_AudioBitstreamBuffer* bitstream_buffer,
133 const scoped_refptr<TrackedCallback>& callback) {
134 if (encoder_last_error_)
135 return encoder_last_error_;
136 if (TrackedCallback::IsPending(get_bitstream_buffer_callback_))
137 return PP_ERROR_INPROGRESS;
138
139 get_bitstream_buffer_callback_ = callback;
140 get_bitstream_buffer_data_ = bitstream_buffer;
141
142 TryWriteBitstreamBuffer();
143
144 return PP_OK_COMPLETIONPENDING;
145 }
146
RecycleBitstreamBuffer(const PP_AudioBitstreamBuffer * bitstream_buffer)147 void AudioEncoderResource::RecycleBitstreamBuffer(
148 const PP_AudioBitstreamBuffer* bitstream_buffer) {
149 if (encoder_last_error_)
150 return;
151
152 BufferMap::const_iterator it =
153 bitstream_buffer_map_.find(bitstream_buffer->buffer);
154 if (it != bitstream_buffer_map_.end())
155 Post(RENDERER,
156 PpapiHostMsg_AudioEncoder_RecycleBitstreamBuffer(it->second));
157 }
158
RequestBitrateChange(uint32_t bitrate)159 void AudioEncoderResource::RequestBitrateChange(uint32_t bitrate) {
160 if (encoder_last_error_)
161 return;
162 Post(RENDERER, PpapiHostMsg_AudioEncoder_RequestBitrateChange(bitrate));
163 }
164
Close()165 void AudioEncoderResource::Close() {
166 if (encoder_last_error_)
167 return;
168 Post(RENDERER, PpapiHostMsg_AudioEncoder_Close());
169 if (!encoder_last_error_ || !initialized_)
170 NotifyError(PP_ERROR_ABORTED);
171 ReleaseBuffers();
172 }
173
OnReplyReceived(const ResourceMessageReplyParams & params,const IPC::Message & msg)174 void AudioEncoderResource::OnReplyReceived(
175 const ResourceMessageReplyParams& params,
176 const IPC::Message& msg) {
177 PPAPI_BEGIN_MESSAGE_MAP(AudioEncoderResource, msg)
178 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
179 PpapiPluginMsg_AudioEncoder_BitstreamBufferReady,
180 OnPluginMsgBitstreamBufferReady)
181 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_AudioEncoder_EncodeReply,
182 OnPluginMsgEncodeReply)
183 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_AudioEncoder_NotifyError,
184 OnPluginMsgNotifyError)
185 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
186 PluginResource::OnReplyReceived(params, msg))
187 PPAPI_END_MESSAGE_MAP()
188 }
189
OnPluginMsgGetSupportedProfilesReply(const PP_ArrayOutput & output,const ResourceMessageReplyParams & params,const std::vector<PP_AudioProfileDescription> & profiles)190 void AudioEncoderResource::OnPluginMsgGetSupportedProfilesReply(
191 const PP_ArrayOutput& output,
192 const ResourceMessageReplyParams& params,
193 const std::vector<PP_AudioProfileDescription>& profiles) {
194 ArrayWriter writer(output);
195 if (params.result() != PP_OK || !writer.is_valid() ||
196 !writer.StoreVector(profiles)) {
197 SafeRunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED);
198 return;
199 }
200
201 SafeRunCallback(&get_supported_profiles_callback_,
202 base::checked_cast<int32_t>(profiles.size()));
203 }
204
OnPluginMsgInitializeReply(const ResourceMessageReplyParams & params,int32_t number_of_samples,int32_t audio_buffer_count,int32_t audio_buffer_size,int32_t bitstream_buffer_count,int32_t bitstream_buffer_size)205 void AudioEncoderResource::OnPluginMsgInitializeReply(
206 const ResourceMessageReplyParams& params,
207 int32_t number_of_samples,
208 int32_t audio_buffer_count,
209 int32_t audio_buffer_size,
210 int32_t bitstream_buffer_count,
211 int32_t bitstream_buffer_size) {
212 DCHECK(!initialized_);
213
214 int32_t error = params.result();
215 if (error) {
216 SafeRunCallback(&initialize_callback_, error);
217 return;
218 }
219
220 // Get audio buffers shared memory buffer.
221 base::UnsafeSharedMemoryRegion region;
222 if (!params.TakeUnsafeSharedMemoryRegionAtIndex(0, ®ion) ||
223 !audio_buffer_manager_.SetBuffers(audio_buffer_count, audio_buffer_size,
224 std::move(region), true)) {
225 SafeRunCallback(&initialize_callback_, PP_ERROR_NOMEMORY);
226 return;
227 }
228
229 // Get bitstream buffers shared memory buffer.
230 if (!params.TakeUnsafeSharedMemoryRegionAtIndex(1, ®ion) ||
231 !bitstream_buffer_manager_.SetBuffers(bitstream_buffer_count,
232 bitstream_buffer_size,
233 std::move(region), false)) {
234 SafeRunCallback(&initialize_callback_, PP_ERROR_NOMEMORY);
235 return;
236 }
237
238 for (int32_t i = 0; i < bitstream_buffer_manager_.number_of_buffers(); i++)
239 bitstream_buffer_map_.insert(std::make_pair(
240 bitstream_buffer_manager_.GetBufferPointer(i)->bitstream.data, i));
241
242 encoder_last_error_ = PP_OK;
243 number_of_samples_ = number_of_samples;
244 initialized_ = true;
245
246 SafeRunCallback(&initialize_callback_, PP_OK);
247 }
248
OnPluginMsgEncodeReply(const ResourceMessageReplyParams & params,int32_t buffer_id)249 void AudioEncoderResource::OnPluginMsgEncodeReply(
250 const ResourceMessageReplyParams& params,
251 int32_t buffer_id) {
252 // We need to ensure there are still callbacks to be called before
253 // processing this message. We might receive an EncodeReply message after
254 // having sent a Close message to the renderer. In this case, we don't
255 // have any callback left to call.
256 if (encode_callbacks_.empty())
257 return;
258
259 EncodeMap::iterator it = encode_callbacks_.find(buffer_id);
260 DCHECK(encode_callbacks_.end() != it);
261
262 scoped_refptr<TrackedCallback> callback = it->second;
263 encode_callbacks_.erase(it);
264 SafeRunCallback(&callback, encoder_last_error_);
265
266 audio_buffer_manager_.EnqueueBuffer(buffer_id);
267 // If the plugin is waiting for an audio buffer, we can give the one
268 // that just became available again.
269 if (TrackedCallback::IsPending(get_buffer_callback_))
270 TryGetAudioBuffer();
271 }
272
OnPluginMsgBitstreamBufferReady(const ResourceMessageReplyParams & params,int32_t buffer_id)273 void AudioEncoderResource::OnPluginMsgBitstreamBufferReady(
274 const ResourceMessageReplyParams& params,
275 int32_t buffer_id) {
276 bitstream_buffer_manager_.EnqueueBuffer(buffer_id);
277
278 if (TrackedCallback::IsPending(get_bitstream_buffer_callback_))
279 TryWriteBitstreamBuffer();
280 }
281
OnPluginMsgNotifyError(const ResourceMessageReplyParams & params,int32_t error)282 void AudioEncoderResource::OnPluginMsgNotifyError(
283 const ResourceMessageReplyParams& params,
284 int32_t error) {
285 NotifyError(error);
286 }
287
NotifyError(int32_t error)288 void AudioEncoderResource::NotifyError(int32_t error) {
289 DCHECK(error);
290
291 encoder_last_error_ = error;
292 SafeRunCallback(&get_supported_profiles_callback_, error);
293 SafeRunCallback(&initialize_callback_, error);
294 SafeRunCallback(&get_buffer_callback_, error);
295 get_buffer_data_ = nullptr;
296 SafeRunCallback(&get_bitstream_buffer_callback_, error);
297 get_bitstream_buffer_data_ = nullptr;
298 for (EncodeMap::iterator it = encode_callbacks_.begin();
299 it != encode_callbacks_.end(); ++it)
300 SafeRunCallback(&it->second, error);
301 encode_callbacks_.clear();
302 }
303
TryGetAudioBuffer()304 void AudioEncoderResource::TryGetAudioBuffer() {
305 DCHECK(TrackedCallback::IsPending(get_buffer_callback_));
306
307 if (!audio_buffer_manager_.HasAvailableBuffer())
308 return;
309
310 int32_t buffer_id = audio_buffer_manager_.DequeueBuffer();
311 scoped_refptr<AudioBufferResource> resource = new AudioBufferResource(
312 pp_instance(), buffer_id,
313 audio_buffer_manager_.GetBufferPointer(buffer_id));
314 audio_buffers_.insert(
315 AudioBufferMap::value_type(resource->pp_resource(), resource));
316
317 // Take a reference for the plugin.
318 *get_buffer_data_ = resource->GetReference();
319 get_buffer_data_ = nullptr;
320 SafeRunCallback(&get_buffer_callback_, PP_OK);
321 }
322
TryWriteBitstreamBuffer()323 void AudioEncoderResource::TryWriteBitstreamBuffer() {
324 DCHECK(TrackedCallback::IsPending(get_bitstream_buffer_callback_));
325
326 if (!bitstream_buffer_manager_.HasAvailableBuffer())
327 return;
328
329 int32_t buffer_id = bitstream_buffer_manager_.DequeueBuffer();
330 MediaStreamBuffer* buffer =
331 bitstream_buffer_manager_.GetBufferPointer(buffer_id);
332
333 get_bitstream_buffer_data_->buffer = buffer->bitstream.data;
334 get_bitstream_buffer_data_->size = buffer->bitstream.data_size;
335 get_bitstream_buffer_data_ = nullptr;
336 SafeRunCallback(&get_bitstream_buffer_callback_, PP_OK);
337 }
338
ReleaseBuffers()339 void AudioEncoderResource::ReleaseBuffers() {
340 for (AudioBufferMap::iterator it = audio_buffers_.begin();
341 it != audio_buffers_.end(); ++it)
342 it->second->Invalidate();
343 audio_buffers_.clear();
344 }
345
346 } // namespace proxy
347 } // namespace ppapi
348