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

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: bbudge's review Created 5 years 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>
bbudge 2015/12/04 00:59:13 Is <limits> used? #include <map> #include <string
llandwerlin-old 2015/12/08 12:29:23 Done.
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 // pp::Instance implementation.
40 virtual void HandleMessage(const pp::Var& var_message);
41
42 void AddAudioProfile(PP_AudioProfile profile, const std::string& profile_str);
43 void InitializeAudioProfiles();
44 PP_AudioProfile ProfileFromString(const std::string& string);
45 std::string ProfileToString(PP_AudioProfile profile);
46
47 void GetSupportedProfiles();
48 void OnGetSupportedProfiles(
49 int32_t result,
50 const std::vector<PP_AudioProfileDescription> profiles);
51
52 void StartAudioTrack(const pp::Resource& resource_track);
53 void StopAudioTrack();
54 void OnGetFirstBuffer(int32_t result, pp::AudioBuffer buffer);
bbudge 2015/12/04 00:59:12 delete.
llandwerlin-old 2015/12/08 12:29:23 Done.
55 void OnGetBuffer(int32_t result, pp::AudioBuffer buffer);
56
57 void InitializeEncoder();
58 void OnEncoderInitialized(int32_t result);
59 void OnAudioTrackConfigured(int32_t result);
60 void GetEncoderBuffer(const pp::AudioBuffer& track_buffer);
61 void OnGetEncoderBuffer(int32_t result,
62 pp::AudioBuffer buffer,
63 pp::AudioBuffer track_buffer);
64 void OnEncodeDone(int32_t result);
65 void OnGetBitstreamBuffer(int32_t result,
66 const PP_AudioBitstreamBuffer& buffer);
67
68 pp::VarDictionary NewCommand(const std::string& type);
69
70 void Log(const std::string& message);
71 void LogError(int32_t error, const std::string& message);
72
73 void PostAudioFormat();
74 void PostDataMessage(const void* data, uint32_t size);
75
76 pp::CompletionCallbackFactory<AudioEncoderInstance> callback_factory_;
77
78 // Set when the plugin is not performing any encoding and just passing the
79 // uncompressed data directly to web page.
80 bool no_encoding_;
81 bool is_encoding_;
bbudge 2015/12/04 00:59:13 This seems to be read-only - you initialize and se
llandwerlin-old 2015/12/08 12:29:22 Removing as there is only one codec supported for
82 PP_AudioProfile encoding_profile_;
83
84 uint32_t channels_;
85 uint32_t sample_rate_;
86 uint32_t sample_size_;
87 uint32_t samples_per_frame_;
88
89 pp::MediaStreamAudioTrack audio_track_;
90 pp::AudioEncoder audio_encoder_;
91
92 typedef std::map<std::string, PP_AudioProfile> AudioProfileFromStringMap;
93 AudioProfileFromStringMap profile_from_string_;
94
95 typedef std::map<PP_AudioProfile, std::string> AudioProfileToStringMap;
96 AudioProfileToStringMap profile_to_string_;
97 };
98
99 AudioEncoderInstance::AudioEncoderInstance(PP_Instance instance)
100 : pp::Instance(instance),
101 callback_factory_(this),
102 no_encoding_(true),
103 is_encoding_(false),
104 encoding_profile_(PP_AUDIOPROFILE_OPUS),
105 channels_(0),
106 sample_rate_(0),
107 sample_size_(0),
108 samples_per_frame_(0) {
109 InitializeAudioProfiles();
110 GetSupportedProfiles();
111 }
112
113 AudioEncoderInstance::~AudioEncoderInstance() {}
114
115 void AudioEncoderInstance::HandleMessage(const pp::Var& var_message) {
116 if (!var_message.is_dictionary()) {
117 LogError(PP_ERROR_FAILED,
118 "Cannot handle incoming message, not a dictionary");
119 return;
120 }
121
122 pp::VarDictionary dict_message(var_message);
123 std::string command = dict_message.Get("command").AsString();
124
125 if (command == "start") {
126 pp::Var var_track = dict_message.Get("track");
127 if (!var_track.is_resource()) {
128 LogError(PP_ERROR_FAILED, "Given track is not a resource");
129 return;
130 }
131 std::string profile = dict_message.Get("profile").AsString();
132 if (profile == "wav")
bbudge 2015/12/04 00:59:13 match bracketing style of 'else' part.
llandwerlin-old 2015/12/08 12:29:22 Done.
133 no_encoding_ = true;
134 else {
135 no_encoding_ = false;
136 encoding_profile_ = ProfileFromString(profile);
137 }
138 StartAudioTrack(var_track.AsResource());
139 } else if (command == "stop") {
140 StopAudioTrack();
141 } else {
142 LogError(PP_ERROR_FAILED, "Invalid command");
143 }
144 }
145
146 void AudioEncoderInstance::AddAudioProfile(PP_AudioProfile profile,
147 const std::string& profile_str) {
148 profile_to_string_.insert(std::make_pair(profile, profile_str));
149 profile_from_string_.insert(std::make_pair(profile_str, profile));
150 }
151
152 void AudioEncoderInstance::InitializeAudioProfiles() {
153 AddAudioProfile(PP_AUDIOPROFILE_OPUS, "opus");
154 }
155
156 PP_AudioProfile AudioEncoderInstance::ProfileFromString(
157 const std::string& string) {
158 AudioProfileFromStringMap::iterator it = profile_from_string_.find(string);
159 if (it == profile_from_string_.end())
160 return PP_AUDIOPROFILE_OPUS;
161 return it->second;
162 }
163
164 std::string AudioEncoderInstance::ProfileToString(PP_AudioProfile profile) {
165 AudioProfileToStringMap::iterator it = profile_to_string_.find(profile);
166 if (it == profile_to_string_.end())
167 return "unknown";
168 return it->second;
169 }
170
171 void AudioEncoderInstance::GetSupportedProfiles() {
172 audio_encoder_ = pp::AudioEncoder(this);
bbudge 2015/12/04 00:59:12 Initialize in constructor.
llandwerlin-old 2015/12/08 12:29:23 Done.
173 audio_encoder_.GetSupportedProfiles(callback_factory_.NewCallbackWithOutput(
174 &AudioEncoderInstance::OnGetSupportedProfiles));
175 }
176
177 void AudioEncoderInstance::OnGetSupportedProfiles(
178 int32_t result,
179 const std::vector<PP_AudioProfileDescription> profiles) {
180 pp::VarDictionary dictionary = NewCommand("supportedProfiles");
181 pp::VarArray js_profiles;
182 dictionary.Set(pp::Var("profiles"), js_profiles);
183
184 if (result < 0) {
185 LogError(result, "Cannot get supported profiles");
186 PostMessage(dictionary);
187 }
188
189 int32_t idx = 0;
190 for (std::vector<PP_AudioProfileDescription>::const_iterator it =
191 profiles.begin();
192 it != profiles.end(); ++it) {
193 pp::VarDictionary js_profile;
194 js_profile.Set(pp::Var("name"), pp::Var(ProfileToString(it->profile)));
195 js_profile.Set(pp::Var("sample_size"),
196 pp::Var(static_cast<int32_t>(it->sample_size)));
197 js_profile.Set(pp::Var("sample_rate"),
198 pp::Var(static_cast<int32_t>(it->sample_rate)));
199 js_profiles.Set(idx++, js_profile);
200 }
201 PostMessage(dictionary);
202 }
203
204 void AudioEncoderInstance::StartAudioTrack(const pp::Resource& resource_track) {
205 channels_ = 0;
206 audio_track_ = pp::MediaStreamAudioTrack(resource_track);
207 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
208 &AudioEncoderInstance::OnGetBuffer));
209 }
210
211 void AudioEncoderInstance::StopAudioTrack() {
212 Log("StopAudioTrack!");
213 audio_encoder_.Close();
214 audio_encoder_ = pp::AudioEncoder();
215 audio_track_.Close();
bbudge 2015/12/04 00:59:12 Would it be safer and clearer to Close the audio t
llandwerlin-old 2015/12/08 12:29:23 Done.
216 audio_track_ = pp::MediaStreamAudioTrack();
217 }
218
219 void AudioEncoderInstance::OnGetFirstBuffer(int32_t result,
bbudge 2015/12/04 00:59:13 This method isn't called.
llandwerlin-old 2015/12/08 12:29:23 Sorry, done.
220 pp::AudioBuffer buffer) {
221 if (result == PP_ERROR_ABORTED)
222 return;
223 if (result != PP_OK) {
224 LogError(result, "Cannot get audio track buffer");
225 return;
226 }
227 }
228
229 void AudioEncoderInstance::OnGetBuffer(int32_t result, pp::AudioBuffer buffer) {
230 if (result == PP_ERROR_ABORTED)
231 return;
bbudge 2015/12/04 00:59:13 This can be handled by next 'if'.
llandwerlin-old 2015/12/08 12:29:23 The point is to avoid a warning because aborted me
232 if (result != PP_OK) {
233 LogError(result, "Cannot get audio track buffer");
234 return;
235 }
236
237 // If this is the first buffer, then we need to initialize the encoder.
238 if (channels_ == 0) {
239 channels_ = buffer.GetNumberOfChannels();
240 sample_rate_ = buffer.GetSampleRate();
241 sample_size_ = buffer.GetSampleSize();
242 samples_per_frame_ = buffer.GetNumberOfSamples();
243
244 if (no_encoding_) {
245 PostAudioFormat();
246 PostDataMessage(buffer.GetDataBuffer(), buffer.GetDataBufferSize());
247 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
248 &AudioEncoderInstance::OnGetBuffer));
249 } else {
250 InitializeEncoder();
bbudge 2015/12/04 00:59:13 Is there data in the first buffer? If so, you migh
llandwerlin-old 2015/12/08 12:29:23 Indeed this drops the first audio buffer (first 10
bbudge 2015/12/08 17:12:30 OK
251 }
252
253 audio_track_.RecycleBuffer(buffer);
254 } else {
255 if (no_encoding_) {
256 PostDataMessage(buffer.GetDataBuffer(), buffer.GetDataBufferSize());
257 audio_track_.RecycleBuffer(buffer);
258 } else
259 GetEncoderBuffer(buffer);
bbudge 2015/12/04 00:59:12 match bracketing style of 'if' part.
llandwerlin-old 2015/12/08 12:29:23 Done.
260
261 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
262 &AudioEncoderInstance::OnGetBuffer));
263 }
264 }
265
266 void AudioEncoderInstance::InitializeEncoder() {
267 audio_encoder_ = pp::AudioEncoder(this);
bbudge 2015/12/04 00:59:12 Initialize in constructor.
llandwerlin-old 2015/12/08 12:29:23 I think this is required because once the encoder
bbudge 2015/12/08 17:12:30 OK, I guess the same logic would apply to the audi
268 audio_encoder_.Initialize(
269 channels_, static_cast<PP_AudioBuffer_SampleRate>(sample_rate_),
270 static_cast<PP_AudioBuffer_SampleSize>(sample_size_), encoding_profile_,
271 100000, PP_HARDWAREACCELERATION_WITHFALLBACK,
272 callback_factory_.NewCallback(
273 &AudioEncoderInstance::OnEncoderInitialized));
274 }
275
276 void AudioEncoderInstance::OnEncoderInitialized(int32_t result) {
277 if (result == PP_ERROR_ABORTED)
278 return;
bbudge 2015/12/04 00:59:13 This seems like an error that should be logged. Ca
llandwerlin-old 2015/12/08 12:29:23 Again, this is related to a user's action. I think
bbudge 2015/12/08 17:12:30 Right, just catch all errors below.
279 if (result != PP_OK) {
280 LogError(result, "Cannot initialize encoder");
281 return;
282 }
283
284 int32_t attribs[] = {
285 // Duration in milliseconds.
286 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION,
287 1000 / (sample_rate_ / audio_encoder_.GetNumberOfSamples()),
288 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE};
289 audio_track_.Configure(attribs,
290 callback_factory_.NewCallback(
291 &AudioEncoderInstance::OnAudioTrackConfigured));
292
293 samples_per_frame_ = audio_encoder_.GetNumberOfSamples();
294 PostAudioFormat();
295 }
296
297 void AudioEncoderInstance::OnAudioTrackConfigured(int32_t result) {
298 if (result == PP_ERROR_ABORTED)
299 return;
300 if (result != PP_OK) {
301 LogError(result, "Cannot configure audio track buffer duration");
302 return;
303 }
304
305 is_encoding_ = true;
306 audio_track_.GetBuffer(callback_factory_.NewCallbackWithOutput(
307 &AudioEncoderInstance::OnGetBuffer));
308 audio_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
309 &AudioEncoderInstance::OnGetBitstreamBuffer));
310 }
311
312 void AudioEncoderInstance::GetEncoderBuffer(
313 const pp::AudioBuffer& track_buffer) {
314 audio_encoder_.GetBuffer(callback_factory_.NewCallbackWithOutput(
315 &AudioEncoderInstance::OnGetEncoderBuffer, track_buffer));
316 }
317
318 void AudioEncoderInstance::OnGetEncoderBuffer(int32_t result,
319 pp::AudioBuffer buffer,
320 pp::AudioBuffer track_buffer) {
321 const char* error(nullptr);
322
323 if (result == PP_ERROR_ABORTED)
324 audio_track_.RecycleBuffer(track_buffer);
bbudge 2015/12/04 00:59:12 As this stands, you might recycle this buffer twic
llandwerlin-old 2015/12/08 12:29:23 Whoops, my mistake.
325 if (result != PP_OK)
326 error = "Cannot get encoder buffer";
327 if (buffer.GetDataBufferSize() != track_buffer.GetDataBufferSize()) {
328 result = PP_ERROR_FAILED;
329 error = "Invalid buffer size";
330 }
331
332 if (result == PP_OK) {
333 memcpy(buffer.GetDataBuffer(), track_buffer.GetDataBuffer(),
334 buffer.GetDataBufferSize());
335 audio_encoder_.Encode(buffer, callback_factory_.NewCallback(
336 &AudioEncoderInstance::OnEncodeDone));
337 } else {
338 LogError(result, error);
339 StopAudioTrack();
340 }
341 audio_track_.RecycleBuffer(track_buffer);
342 }
343
344 void AudioEncoderInstance::OnEncodeDone(int32_t result) {}
345
346 void AudioEncoderInstance::OnGetBitstreamBuffer(
347 int32_t result,
348 const PP_AudioBitstreamBuffer& buffer) {
349 if (result == PP_ERROR_ABORTED)
350 return;
351 if (result != PP_OK) {
352 LogError(result, "Cannot get bitstream buffer");
353 return;
354 }
355
356 audio_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
357 &AudioEncoderInstance::OnGetBitstreamBuffer));
358
359 PostDataMessage(buffer.buffer, buffer.size);
360 audio_encoder_.RecycleBitstreamBuffer(buffer);
361 }
362
363 pp::VarDictionary AudioEncoderInstance::NewCommand(const std::string& type) {
364 pp::VarDictionary dictionary;
365 dictionary.Set(pp::Var("command"), pp::Var(type));
366 return dictionary;
367 }
368
369 void AudioEncoderInstance::Log(const std::string& message) {
370 pp::VarDictionary dictionary = NewCommand("log");
371 dictionary.Set(pp::Var("message"), pp::Var(message));
372 PostMessage(dictionary);
373 }
374
375 void AudioEncoderInstance::LogError(int32_t error, const std::string& message) {
376 pp::VarDictionary dictionary = NewCommand("error");
377 std::string msg("Error: ");
378 msg.append(message);
379 msg.append(" : ");
380 msg.append(pp::Var(error).DebugString());
381 dictionary.Set(pp::Var("message"), pp::Var(msg));
382 PostMessage(dictionary);
383 }
384
385 void AudioEncoderInstance::PostAudioFormat() {
386 pp::VarDictionary dictionary = NewCommand("format");
387 dictionary.Set(pp::Var("channels"), pp::Var(static_cast<int32_t>(channels_)));
388 dictionary.Set(pp::Var("sample_rate"),
389 pp::Var(static_cast<int32_t>(sample_rate_)));
390 dictionary.Set(pp::Var("sample_size"),
391 pp::Var(static_cast<int32_t>(sample_size_)));
392 dictionary.Set(pp::Var("sample_per_frame"),
393 pp::Var(static_cast<int32_t>(samples_per_frame_)));
394 PostMessage(dictionary);
395 }
396
397 void AudioEncoderInstance::PostDataMessage(const void* data, uint32_t size) {
398 pp::VarDictionary dictionary = NewCommand("data");
399 uint8_t* buffer;
400
401 pp::VarArrayBuffer array_buffer = pp::VarArrayBuffer(size);
402 buffer = static_cast<uint8_t*>(array_buffer.Map());
403 memcpy(buffer, data, size);
404 array_buffer.Unmap();
405
406 dictionary.Set(pp::Var("buffer"), array_buffer);
407
408 PostMessage(dictionary);
409 }
410
411 class AudioEncoderModule : public pp::Module {
412 public:
413 virtual pp::Instance* CreateInstance(PP_Instance instance) {
414 return new AudioEncoderInstance(instance);
415 }
416 };
417
418 } // namespace
419
420 namespace pp {
421
422 // Factory function for your specialization of the Module object.
423 Module* CreateModule() {
424 return new AudioEncoderModule();
425 }
426
427 } // 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