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

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

Issue 1343273005: ppapi: add AudioEncode example (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@nacl-audio-encoder
Patch Set: last nits 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/audio_encode.cc ('k') | ppapi/ppapi_tests.gypi » ('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 <!DOCTYPE html>
2 <html>
3 <!--
4 Copyright 2015 The Chromium Authors. All rights reserved.
5 Use of this source code is governed by a BSD-style license that can be
6 found in the LICENSE file.
7 -->
8 <head>
9 <title>Audio Encoder Example</title>
10 <script type="text/javascript">
11 var plugin;
12 var track;
13 var writer;
14 var profiles;
15
16 function writeString(array, offset, string) {
17 var out = new Uint8Array(array);
18 for (var i = 0; i < string.length; i++)
19 out.set([string.charCodeAt(i)], i + offset);
20 return string.length;
21 }
22 function writeValue(array, bytes, offset, value) {
23 var out = new Uint8Array(array);
24 for (var i = 0; i < bytes; i++)
25 out.set([((value >>> (i * 8)) & 0xff)], offset + i);
26 return bytes;
27 }
28 // Writes way data.
29 var WavWriter = function() {
30 this.chunkSizeOffset = 0;
31 this.arrayBuffer = new ArrayBuffer();
32 };
33 WavWriter.prototype = {
34 writeHeader: function(format) {
35 this.arrayBuffer =
36 new ArrayBuffer(4 + 4 + 4 + 4 + 4 + 2 + 2 + 4 + 4 + 2 + 2 + 4 + 4);
37 var i = 0;
38 // File header
39 i += writeString(this.arrayBuffer, i, 'RIFF');
40 i += 4; // Gap for final size.
41 i += writeString(this.arrayBuffer, i, 'WAVE');
42 // Chunk ID.
43 i += writeString(this.arrayBuffer, i, 'fmt ');
44 // Chunk length.
45 i += writeValue(this.arrayBuffer, 4, i, 16);
46 // Codec (uncompressed LPCM).
47 i += writeValue(this.arrayBuffer, 2, i, 1);
48 // Number of channels.
49 i += writeValue(this.arrayBuffer, 2, i, format.channels);
50 // Sample rate.
51 i += writeValue(this.arrayBuffer, 4, i, format.sample_rate);
52 // Average bytes per seconds (sample rate * bytes per sample)
53 i += writeValue(this.arrayBuffer, 4, i,
54 format.sample_rate * format.sample_size);
55 // Bytes per sample.
56 i += writeValue(this.arrayBuffer, 2, i,
57 format.sample_size * format.channels);
58 // Bits per sample.
59 i += writeValue(this.arrayBuffer, 2, i, format.sample_size * 8);
60
61 // Data chunk
62 i += writeString(this.arrayBuffer, i, 'data');
63 this.chunkSizeOffset = i; // Location of the chunk's size
64 },
65 writeData: function(data) {
66 var tmp = new Uint8Array(this.arrayBuffer.byteLength + data.byteLength);
67 tmp.set(new Uint8Array(this.arrayBuffer), 0);
68 tmp.set(new Uint8Array(data), this.arrayBuffer.byteLength);
69 this.arrayBuffer = tmp.buffer;
70 },
71 end: function() {
72 var out = new Uint32Array(this.arrayBuffer);
73 out.set([this.arrayBuffer.byteLength - 8], 1);
74 out.set([this.arrayBuffer.byteLength - this.chunkSizeOffset],
75 this.chunkSizeOffset / 4);
76 },
77 getSize: function() {
78 return this.arrayBuffer.byteLength;
79 },
80 getData: function() {
81 return this.arrayBuffer;
82 },
83 getExtension: function() {
84 return 'wav';
85 },
86 };
87
88 // Writes ogg data.
89 var OggWriter = function(profile) {
90 this.writeHeader = this._writeOpusHeader;
91 this.arrayBuffer = new ArrayBuffer();
92 this.pageSequence = 0;
93 this.bitstreamNumber = 0;
94 this.position = 0;
95 this.dataWritten = false;
96 };
97 OggWriter.prototype = {
98 _Start: 0x2,
99 _Continue: 0x1,
100 _Stop: 0x4,
101 _append: function(data) {
102 var tmp = new Uint8Array(this.arrayBuffer.byteLength + data.byteLength);
103 tmp.set(new Uint8Array(this.arrayBuffer), 0);
104 tmp.set(new Uint8Array(data), this.arrayBuffer.byteLength);
105 this.arrayBuffer = tmp.buffer;
106 },
107 _makeCRCTable: function() {
108 var crcTable = [];
109 for (var n = 0; n < 256; n++) {
110 var r = n << 24;
111 for (var i = 0; i < 8; i++) {
112 if (r & 0x80000000)
113 r = (r << 1) ^ 0x04c11db7;
114 else
115 r <<= 1;
116 }
117 crcTable[n] = r & 0xffffffff;
118 }
119 return crcTable;
120 },
121 _crc32: function(data, start, end) {
122 var crc = 0;
123 var u8data = new Uint8Array(data)
124 var crcTable = this._crcTable || (this._crcTable = this._makeCRCTable()) ;
125 for (var i = start; i < end; i++)
126 crc = (crc << 8) ^ crcTable[((crc >> 24) & 0xff) ^ u8data[i]];
127 return crc;
128 },
129 _writePage: function(flag, size, position) {
130 var pages = 1 + Math.floor(size / 255);
131 var buffer = new ArrayBuffer(27 + pages), i = 0;
132 // capture_pattern.
133 i += writeString(buffer, i, 'OggS');
134 // stream_structure_version.
135 i += writeValue(buffer, 1, i, 0);
136 // header_type_flag.
137 i += writeValue(buffer, 1, i, flag);
138 // granule_position.
139 // TODO(llandwerlin): Not writing more than 32bits for now,
140 // because Javascript doesn't have 64bits integers, this limits
141 // the duration to ~24 hours at 48kHz sampling rate.
142 i += writeValue(buffer, 4, i, position != undefined ? position : 0);
143 i += writeValue(buffer, 4, i, 0);
144 // bitstream_serial_number.
145 i += writeValue(buffer, 4, i, this.bitstreamNumber);
146 // page_sequence_number.
147 i += writeValue(buffer, 4, i, this.pageSequence++);
148 // CRC_checksum.
149 i += writeValue(buffer, 4, i, 0);
150 // number_page_segments.
151 i += writeValue(buffer, 1, i, pages);
152 // segment sizes.
153 for (var j = 0; j < (pages - 1); j++)
154 i += writeValue(buffer, 1, i, 255);
155 i += writeValue(buffer, 1, i, size % 255);
156
157 this._append(buffer);
158 },
159 _writePageChecksum: function(pageOffset) {
160 var crc = this._crc32(this.arrayBuffer, pageOffset,
161 this.arrayBuffer.byteLength);
162 writeValue(this.arrayBuffer, 4, pageOffset + 22, crc);
163 },
164 _writeOpusHeader: function(format) {
165 this.format = format;
166 var start = this.getSize();
167 var buffer = new ArrayBuffer(8 + 1 + 1 + 2 + 4 + 2 + 1), i = 0;
168 // Opus header.
169 i += writeString(buffer, i, 'OpusHead');
170 // version.
171 i += writeValue(buffer, 1, i, 1);
172 // channel count.
173 i += writeValue(buffer, 1, i, format.channels);
174 // pre-skip.
175 i += writeValue(buffer, 2, i, 0);
176 // input sample rate.
177 i += writeValue(buffer, 4, i, format.sample_rate);
178 // output gain.
179 i += writeValue(buffer, 2, i, 0);
180 // channel mapping family.
181 i += writeValue(buffer, 1, i, 0);
182
183 this._writePage(this._Start, buffer.byteLength);
184 this._append(buffer);
185 this._writePageChecksum(start);
186 this._writeCommentHeader('OpusTags');
187 },
188 _writeCommentHeader: function(name) {
189 var start = this.getSize();
190 var buffer = new ArrayBuffer(8 + 4 + 8 + 4 + 4 + 13), i = 0;
191 // Opus comment header.
192 i += writeString(buffer, i, name);
193 // Vendor string.
194 i += this._writeLengthString(buffer, i, 'Chromium');
195 // User comment list length
196 i += writeValue(buffer, 4, i, 1);
197 // User comment 0 length.
198 i += this._writeLengthString(buffer, i, 'TITLE=example');
199
200 this._writePage(this._Continue, buffer.byteLength);
201 this._append(buffer);
202 this._writePageChecksum(start);
203 },
204 _writeLengthString: function(buffer, offset, str) {
205 return (writeValue(buffer, offset, 4, str.length) +
206 writeString(buffer, offset, str));
207 },
208 writeData: function(data) {
209 this.position += this.format.sample_per_frame / this.format.channels;
210 var start = this.getSize();
211 this._writePage(0, data.byteLength, this.position);
212 this._append(data);
213 this._writePageChecksum(start);
214 this.dataWritten = true;
215 },
216 end: function() {
217 this._writePage(this._Stop, 0);
218 },
219 getSize: function() {
220 return this.arrayBuffer.byteLength;
221 },
222 getData: function() {
223 return this.arrayBuffer;
224 },
225 getExtension: function() {
226 return 'ogg';
227 },
228 };
229
230 function $(id) {
231 return document.getElementById(id);
232 }
233
234 function success(stream) {
235 track = stream.getAudioTracks()[0];
236 var list = $('profileList');
237 var profile = profiles[list.selectedIndex];
238 plugin.postMessage({
239 command: 'start',
240 profile: profile.name,
241 sample_size: profile.sample_size,
242 sample_rate: profile.sample_rate,
243 track: track,
244 });
245 }
246
247 function failure(e) {
248 console.log("Error: ", e);
249 }
250
251 function cleanupDownload() {
252 var download = $('download');
253 if (!download)
254 return;
255 download.parentNode.removeChild(download);
256 }
257
258 function setDownload(data, filename) {
259 var mimeType = 'application/octet-stream';
260 var blob = new Blob([data], { type: mimeType });
261 var a = document.createElement('a');
262 a.id = "download";
263 a.download = filename;
264 a.href = window.URL.createObjectURL(blob);
265 a.textContent = 'Download';
266 a.dataset.downloadurl = [mimeType, a.download, a.href].join(':');
267 $('download-box').appendChild(a);
268 }
269
270 function startRecord() {
271 var list = $('profileList');
272 var profile = profiles[list.selectedIndex];
273 if (profile.name == 'wav')
274 writer = new WavWriter();
275 else
276 writer = new OggWriter(profile);
277 cleanupDownload();
278
279 var constraints = [];
280 if (profile.name == 'opus') {
281 // Chromium outputs 32kHz sampling rate by default. This isn't a
282 // supported sampling rate for the Opus codec. So we force the
283 // output to 48kHz. If Chromium implements the GetUserMedia
284 // audio constraints at some point, we could potentially get rid
285 // of this.
286 constraints.push({ googAudioProcessing48kHzSupport: true });
287 }
288
289 navigator.webkitGetUserMedia({ audio: { optional: constraints },
290 video: false},
291 success, failure);
292 }
293
294 function stopRecord() {
295 plugin.postMessage({
296 command: "stop"
297 });
298 track.stop();
299 writer.end();
300 setDownload(writer.getData(), 'Capture.' + writer.getExtension());
301 }
302
303 function saveBlob(blob) {
304 var blobUrl = URL.createObjectURL(blob);
305 window.location = blobUrl;
306 }
307
308 function handleMessage(msg) {
309 if (msg.data.command == 'log') {
310 console.log(msg.data.message);
311 } else if (msg.data.command == 'error') {
312 console.error(msg.data.message);
313 } else if (msg.data.command == 'data') {
314 writer.writeData(msg.data.buffer);
315 $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
316 } else if (msg.data.command == 'format') {
317 writer.writeHeader(msg.data);
318 $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
319 } else if (msg.data.command == 'supportedProfiles') {
320 profiles = [];
321 var profileList = $('profileList');
322 while (profileList.lastChild)
323 profileList.remove(profileList.lastChild);
324
325 var item = document.createElement('option');
326 item.label = 'wav';
327 profiles.push({ name: 'wav',
328 sample_rate: 0,
329 sample_size: 0,
330 sample_per_frame:
331 msg.data.profiles[0].sample_per_frame });
332 profileList.appendChild(item);
333 for (var i = 0; i < msg.data.profiles.length; i++) {
334 var item = document.createElement('option');
335 item.label = msg.data.profiles[i].name + ' - ' +
336 msg.data.profiles[i].sample_rate + 'Hz';
337 profiles.push(msg.data.profiles[i]);
338 profileList.appendChild(item);
339 }
340 }
341 }
342
343 function resetData() {
344 writer = new WavWriter();
345 $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
346 }
347
348 function initialize() {
349 plugin = $('plugin');
350 plugin.addEventListener('message', handleMessage, false);
351
352 $('start').addEventListener('click', function (e) {
353 resetData();
354 startRecord();
355 });
356 $('stop').addEventListener('click', function (e) {
357 stopRecord();
358 });
359 }
360
361 document.addEventListener('DOMContentLoaded', initialize, false);
362 </script>
363 </head>
364
365 <body>
366 <h1>Pepper Audio Encoder API Example</h1><br>
367 This example demonstrates receiving frames from an audio MediaStreamTrack and
368 encoding them using AudioEncode.<br>
369
370 <select id="profileList"></select>
371 <input type="button" id="start" value="Start Recording"/>
372 <input type="button" id="stop" value="Stop Recording"/>
373 <div id="download-box"></div>
374 <div id="length"></div>
375 <br>
376 <embed id="plugin" type="application/x-ppapi-example-audio-encode"/>
377 </body>
378 </html>
OLDNEW
« no previous file with comments | « ppapi/examples/audio_encode/audio_encode.cc ('k') | ppapi/ppapi_tests.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698