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

Side by Side Diff: ppapi/examples/audio_encode/audio_encode.cc

Issue 1343273005: ppapi: add AudioEncode example (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@nacl-audio-encoder
Patch Set: 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 | « ppapi/examples/audio_encode/BUILD.gn ('k') | ppapi/examples/audio_encode/audio_encode.html » ('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 <stdlib.h>
6 #include <string.h>
7
8 #include <algorithm>
9 #include <limits>
10 #include <vector>
11
12 #include "ppapi/cpp/audio_buffer.h"
13 #include "ppapi/cpp/audio_encoder.h"
14 #include "ppapi/cpp/instance.h"
15 #include "ppapi/cpp/logging.h"
16 #include "ppapi/cpp/media_stream_audio_track.h"
17 #include "ppapi/cpp/module.h"
18 #include "ppapi/cpp/var_array_buffer.h"
19 #include "ppapi/cpp/var_dictionary.h"
20 #include "ppapi/utility/completion_callback_factory.h"
21
22 // When compiling natively on Windows, PostMessage can be #define-d to
23 // something else.
24 #ifdef PostMessage
25 #undef PostMessage
26 #endif
27
28 // This example demonstrates receiving audio samples from an audio
29 // track and encoding them.
30
31 namespace {
32
33 class AudioEncoderInstance : public pp::Instance {
34 public:
35 explicit AudioEncoderInstance(PP_Instance instance);
36 virtual ~AudioEncoderInstance();
37
38 private:
39 struct FrameDescription {
bbudge 2015/11/24 19:20:58 I'm not sure I see the utility of this struct. You
llandwerlin-old 2015/11/25 18:11:46 Done.
40 FrameDescription()
41 : channels(0), sample_rate(0), sample_size(0), samples_per_frame(0) {}
42 FrameDescription(uint32_t channels,
43 uint32_t sample_rate,
44 uint32_t sample_size,
45 uint32_t samples_per_frame)
46 : channels(channels),
47 sample_rate(sample_rate),
48 sample_size(sample_size),
49 samples_per_frame(samples_per_frame) {}
50 ~FrameDescription() {}
51
52 uint32_t channels;
53 uint32_t sample_rate;
54 uint32_t sample_size;
55 uint32_t samples_per_frame;
56 };
57
58 //
bbudge 2015/11/24 19:20:58 delete
llandwerlin-old 2015/11/25 18:11:47 Done.
59 virtual void HandleMessage(const pp::Var& var_message);
60
61 void AddAudioProfile(PP_AudioProfile profile, const std::string& profile_str);
62 void InitializeVideoProfiles();
bbudge 2015/11/24 19:20:58 InitializeAudioProfiles
llandwerlin-old 2015/11/25 18:11:46 Done.
63 PP_AudioProfile ProfileFromString(const std::string& string);
64 std::string ProfileToString(PP_AudioProfile profile);
65
66 void ProbeEncoder();
bbudge 2015/11/24 19:20:58 Maybe a better name is GetProfiles or GetSupported
llandwerlin-old 2015/11/25 18:11:46 Done.
67 void OnEncoderProbed(int32_t result,
68 const std::vector<PP_AudioProfileDescription> profiles);
69
70 void StartAudioTrack(const pp::Resource& resource_track);
71 void StopAudioTrack();
72 void OnGetFirstBuffer(int32_t result, pp::AudioBuffer buffer);
73 void OnGetBuffer(int32_t result, pp::AudioBuffer buffer);
74
75 void InitializeEncoder();
76 void OnEncoderInitialized(int32_t result);
77 void OnAudioTrackReconfigured(int32_t result);
78 void GetEncoderBuffer(const pp::AudioBuffer& track_buffer);
79 void OnGetEncoderBuffer(int32_t result,
80 pp::AudioBuffer buffer,
81 pp::AudioBuffer track_buffer);
82 void OnEncodeDone(int32_t result);
83 void OnBitstreamBuffer(int32_t result, const PP_AudioBitstreamBuffer& buffer);
bbudge 2015/11/24 19:20:58 OnGetBitstreamBuffer for consistency?
llandwerlin-old 2015/11/25 18:11:47 Done.
84
85 pp::VarDictionary NewCommand(const std::string& type);
86
87 void Log(const std::string& message);
88 void LogError(int32_t error, const std::string& message);
89
90 void PostAudioFormat();
91 void PostDataMessage(const void* data, uint32_t size);
92
93 pp::CompletionCallbackFactory<AudioEncoderInstance> callback_factory_;
94
95 bool no_encoding_;
bbudge 2015/11/24 19:20:59 A comment explaining what this is for?
llandwerlin-old 2015/11/25 18:11:46 Done.
96 bool is_encoding_;
97 PP_AudioProfile encoding_profile_;
98
99 FrameDescription frame_description_;
100
101 pp::MediaStreamAudioTrack audio_track_;
102 pp::AudioEncoder audio_encoder_;
103
104 typedef std::map<std::string, PP_AudioProfile> AudioProfileFromStringMap;
105 AudioProfileFromStringMap profile_from_string_;
106
107 typedef std::map<PP_AudioProfile, std::string> AudioProfileToStringMap;
108 AudioProfileToStringMap profile_to_string_;
109 };
110
111 AudioEncoderInstance::AudioEncoderInstance(PP_Instance instance)
112 : pp::Instance(instance),
113 callback_factory_(this),
114 no_encoding_(true),
115 is_encoding_(false),
116 encoding_profile_(PP_AUDIOPROFILE_OPUS) {
117 InitializeVideoProfiles();
118 ProbeEncoder();
119 }
120
121 AudioEncoderInstance::~AudioEncoderInstance() {}
122
123 void AudioEncoderInstance::HandleMessage(const pp::Var& var_message) {
124 if (!var_message.is_dictionary()) {
125 LogError(PP_ERROR_FAILED,
126 "Cannot handle incoming message, not a dictionary");
127 return;
128 }
129
130 pp::VarDictionary dict_message(var_message);
131 std::string command = dict_message.Get("command").AsString();
132
133 if (command == "start") {
134 pp::Var var_track = dict_message.Get("track");
135 if (!var_track.is_resource()) {
136 LogError(PP_ERROR_FAILED, "Given track is not a resource");
137 return;
138 }
139 std::string profile = dict_message.Get("profile").AsString();
140 if (profile == "wav")
141 no_encoding_ = true;
142 else {
143 no_encoding_ = false;
144 encoding_profile_ = ProfileFromString(profile);
145 }
146 StartAudioTrack(var_track.AsResource());
147 } else if (command == "stop") {
148 StopAudioTrack();
149 } else {
150 LogError(PP_ERROR_FAILED, "Invalid command");
151 }
152 }
153
154 void AudioEncoderInstance::AddAudioProfile(PP_AudioProfile profile,
155 const std::string& profile_str) {
156 profile_to_string_.insert(std::make_pair(profile, profile_str));
157 profile_from_string_.insert(std::make_pair(profile_str, profile));
158 }
159
160 void AudioEncoderInstance::InitializeVideoProfiles() {
161 AddAudioProfile(PP_AUDIOPROFILE_OPUS, "opus");
162 }
163
164 PP_AudioProfile AudioEncoderInstance::ProfileFromString(
165 const std::string& string) {
166 AudioProfileFromStringMap::iterator it = profile_from_string_.find(string);
167 if (it == profile_from_string_.end())
168 return PP_AUDIOPROFILE_OPUS;
169 return it->second;
170 }
171
172 std::string AudioEncoderInstance::ProfileToString(PP_AudioProfile profile) {
173 AudioProfileToStringMap::iterator it = profile_to_string_.find(profile);
174 if (it == profile_to_string_.end())
175 return "unknown";
176 return it->second;
177 }
178
179 void AudioEncoderInstance::ProbeEncoder() {
180 audio_encoder_ = pp::AudioEncoder(this);
181 audio_encoder_.GetSupportedProfiles(callback_factory_.NewCallbackWithOutput(
182 &AudioEncoderInstance::OnEncoderProbed));
183 }
184
185 void AudioEncoderInstance::OnEncoderProbed(
186 int32_t result,
187 const std::vector<PP_AudioProfileDescription> profiles) {
188 pp::VarDictionary dictionary = NewCommand("supportedProfiles");
189 pp::VarArray js_profiles;
190 dictionary.Set(pp::Var("profiles"), js_profiles);
191
192 if (result < 0) {
193 LogError(result, "Cannot get supported profiles");
194 PostMessage(dictionary);
195 }
196
197 int32_t idx = 0;
198 for (std::vector<PP_AudioProfileDescription>::const_iterator it =
199 profiles.begin();
200 it != profiles.end(); ++it) {
201 pp::VarDictionary js_profile;
202 js_profile.Set(pp::Var("name"), pp::Var(ProfileToString(it->profile)));
203 js_profile.Set(pp::Var("sample_size"),
204 pp::Var(static_cast<int32_t>(it->sample_size)));
205 js_profile.Set(pp::Var("sample_rate"),
206 pp::Var(static_cast<int32_t>(it->sample_rate)));
207 js_profiles.Set(idx++, js_profile);
208 }
209 PostMessage(dictionary);
210 }
211
212 void AudioEncoderInstance::StartAudioTrack(const pp::Resource& resource_track) {
213 audio_track_ = pp::MediaStreamAudioTrack(resource_track);
214 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
215 &AudioEncoderInstance::OnGetFirstBuffer));
216 }
217
218 void AudioEncoderInstance::StopAudioTrack() {
219 Log("StopAudioTrack!");
220 audio_encoder_.Close();
221 audio_encoder_ = pp::AudioEncoder();
222 audio_track_.Close();
223 audio_track_ = pp::MediaStreamAudioTrack();
224 }
225
226 void AudioEncoderInstance::OnGetFirstBuffer(int32_t result,
bbudge 2015/11/24 19:20:59 This is very similar to OnGetBuffer. Could you use
llandwerlin-old 2015/11/25 18:11:46 Done.
227 pp::AudioBuffer buffer) {
228 if (result == PP_ERROR_ABORTED)
229 return;
230 if (result != PP_OK) {
231 LogError(result, "Cannot get audio track buffer");
232 return;
233 }
234
235 frame_description_ =
236 FrameDescription(buffer.GetNumberOfChannels(), buffer.GetSampleRate(),
237 buffer.GetSampleSize(), buffer.GetNumberOfSamples());
238
239 if (no_encoding_) {
240 PostAudioFormat();
241 PostDataMessage(buffer.GetDataBuffer(), buffer.GetDataBufferSize());
242 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
243 &AudioEncoderInstance::OnGetBuffer));
244 } else {
245 InitializeEncoder();
246 }
247
248 audio_track_.RecycleBuffer(buffer);
249 }
250
251 void AudioEncoderInstance::OnGetBuffer(int32_t result, pp::AudioBuffer buffer) {
252 if (result == PP_ERROR_ABORTED)
253 return;
bbudge 2015/12/04 00:59:12 see comment below.
llandwerlin-old 2015/12/08 12:29:22 This is to avoid to report an error that isn't one
254 if (result != PP_OK) {
255 LogError(result, "Cannot get audio track buffer");
256 return;
257 }
258
259 if (no_encoding_) {
260 PostDataMessage(buffer.GetDataBuffer(), buffer.GetDataBufferSize());
261 audio_track_.RecycleBuffer(buffer);
262 } else
263 GetEncoderBuffer(buffer);
264
265 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
266 &AudioEncoderInstance::OnGetBuffer));
267 }
268
269 void AudioEncoderInstance::InitializeEncoder() {
270 audio_encoder_ = pp::AudioEncoder(this);
271 audio_encoder_.Initialize(
272 frame_description_.channels,
273 static_cast<PP_AudioBuffer_SampleRate>(frame_description_.sample_rate),
274 static_cast<PP_AudioBuffer_SampleSize>(frame_description_.sample_size),
275 encoding_profile_, 100000, PP_HARDWAREACCELERATION_WITHFALLBACK,
276 callback_factory_.NewCallback(
277 &AudioEncoderInstance::OnEncoderInitialized));
278 }
279
280 void AudioEncoderInstance::OnEncoderInitialized(int32_t result) {
281 if (result == PP_ERROR_ABORTED)
282 return;
283 if (result != PP_OK) {
284 LogError(result, "Cannot initialize encoder");
285 return;
286 }
287
288 int32_t attribs[] = {// Duration in milliseconds.
289 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION,
290 1000 / (frame_description_.sample_rate /
291 audio_encoder_.GetNumberOfSamples()),
292 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE};
293 audio_track_.Configure(attribs,
294 callback_factory_.NewCallback(
295 &AudioEncoderInstance::OnAudioTrackReconfigured));
bbudge 2015/11/24 19:20:58 OnAudioTrackConfigured seems like a better name he
llandwerlin-old 2015/11/25 18:11:46 Done.
296
297 frame_description_.samples_per_frame = audio_encoder_.GetNumberOfSamples();
298 PostAudioFormat();
299 }
300
301 void AudioEncoderInstance::OnAudioTrackReconfigured(int32_t result) {
302 if (result == PP_ERROR_ABORTED)
303 return;
bbudge 2015/11/24 19:20:59 Is it necessary to handle this differently than ot
llandwerlin-old 2015/11/25 18:11:46 I'm not sure what you think is different from the
bbudge 2015/12/04 00:59:12 This is from the MediaStreamAudioTrack. Does it no
llandwerlin-old 2015/12/08 12:29:22 I get it now. I want to avoid to display an error
304 if (result != PP_OK) {
305 LogError(result, "Cannot reconfigure audio track buffer duration");
306 return;
307 }
308
309 is_encoding_ = true;
310 audio_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
311 &AudioEncoderInstance::OnBitstreamBuffer));
312 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
313 &AudioEncoderInstance::OnGetBuffer));
bbudge 2015/11/24 19:20:58 nit: It seems clearer and more consistent with enc
llandwerlin-old 2015/11/25 18:11:46 Done.
314 }
315
316 void AudioEncoderInstance::GetEncoderBuffer(
317 const pp::AudioBuffer& track_buffer) {
318 audio_encoder_.GetBuffer(callback_factory_.NewCallbackWithOutput(
319 &AudioEncoderInstance::OnGetEncoderBuffer, track_buffer));
320 }
321
322 void AudioEncoderInstance::OnGetEncoderBuffer(int32_t result,
323 pp::AudioBuffer buffer,
324 pp::AudioBuffer track_buffer) {
325 if (result == PP_ERROR_ABORTED) {
326 audio_track_.RecycleBuffer(track_buffer);
327 return;
328 }
329 if (result != PP_OK) {
330 audio_track_.RecycleBuffer(track_buffer);
331 LogError(result, "Cannot get encoder buffer");
332 return;
333 }
334
335 if (buffer.GetDataBufferSize() != track_buffer.GetDataBufferSize()) {
bbudge 2015/11/24 19:20:59 This seems like a good defensive check, but isn't
llandwerlin-old 2015/11/25 18:11:46 Done.
336 audio_track_.RecycleBuffer(track_buffer);
337 LogError(PP_ERROR_FAILED, "Invalid buffer size");
338 return;
339 }
340
341 memcpy(buffer.GetDataBuffer(), track_buffer.GetDataBuffer(),
342 buffer.GetDataBufferSize());
343 audio_track_.RecycleBuffer(track_buffer);
344
345 audio_encoder_.Encode(buffer, callback_factory_.NewCallback(
346 &AudioEncoderInstance::OnEncodeDone));
bbudge 2015/11/24 19:20:58 I think you could eliminate a lot of code duplicat
llandwerlin-old 2015/11/25 18:11:46 Done.
347 }
348
349 void AudioEncoderInstance::OnEncodeDone(int32_t result) {}
350
351 void AudioEncoderInstance::OnBitstreamBuffer(
352 int32_t result,
353 const PP_AudioBitstreamBuffer& buffer) {
354 if (result == PP_ERROR_ABORTED)
355 return;
356 if (result != PP_OK) {
357 LogError(result, "Cannot get bitstream buffer");
358 return;
359 }
360
361 audio_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
362 &AudioEncoderInstance::OnBitstreamBuffer));
363
364 PostDataMessage(buffer.buffer, buffer.size);
365 audio_encoder_.RecycleBitstreamBuffer(buffer);
366 }
367
368 pp::VarDictionary AudioEncoderInstance::NewCommand(const std::string& type) {
369 pp::VarDictionary dictionary;
370 dictionary.Set(pp::Var("command"), pp::Var(type));
371 return dictionary;
372 }
373
374 void AudioEncoderInstance::Log(const std::string& message) {
375 pp::VarDictionary dictionary = NewCommand("log");
376 dictionary.Set(pp::Var("message"), pp::Var(message));
377 PostMessage(dictionary);
378 }
379
380 void AudioEncoderInstance::LogError(int32_t error, const std::string& message) {
381 pp::VarDictionary dictionary = NewCommand("error");
382 std::string msg("Error: ");
383 msg.append(message);
384 msg.append(" : ");
385 msg.append(pp::Var(error).DebugString());
386 dictionary.Set(pp::Var("message"), pp::Var(msg));
387 PostMessage(dictionary);
388 }
389
390 void AudioEncoderInstance::PostAudioFormat() {
391 pp::VarDictionary dictionary = NewCommand("format");
392 dictionary.Set(pp::Var("channels"),
393 pp::Var(static_cast<int32_t>(frame_description_.channels)));
394 dictionary.Set(pp::Var("sample_rate"),
395 pp::Var(static_cast<int32_t>(frame_description_.sample_rate)));
396 dictionary.Set(pp::Var("sample_size"),
397 pp::Var(static_cast<int32_t>(frame_description_.sample_size)));
398 dictionary.Set(
399 pp::Var("sample_per_frame"),
400 pp::Var(static_cast<int32_t>(frame_description_.samples_per_frame)));
401 PostMessage(dictionary);
402 }
403
404 void AudioEncoderInstance::PostDataMessage(const void* data, uint32_t size) {
405 pp::VarDictionary dictionary = NewCommand("data");
406 uint8_t* buffer;
407
408 pp::VarArrayBuffer array_buffer = pp::VarArrayBuffer(size);
409 buffer = static_cast<uint8_t*>(array_buffer.Map());
410 memcpy(buffer, data, size);
411 array_buffer.Unmap();
412
413 dictionary.Set(pp::Var("buffer"), array_buffer);
414
415 PostMessage(dictionary);
416 }
417
418 class AudioEncoderModule : public pp::Module {
419 public:
420 virtual pp::Instance* CreateInstance(PP_Instance instance) {
421 return new AudioEncoderInstance(instance);
422 }
423 };
424
425 } // namespace
426
427 namespace pp {
428
429 // Factory function for your specialization of the Module object.
430 Module* CreateModule() {
431 return new AudioEncoderModule();
432 }
433
434 } // namespace pp
OLDNEW
« no previous file with comments | « ppapi/examples/audio_encode/BUILD.gn ('k') | ppapi/examples/audio_encode/audio_encode.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698