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

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

Issue 1151973003: ppapi: implement PPB_AudioEncoder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months 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 <!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() {
90 this.arrayBuffer = new ArrayBuffer();
91 this.pageSequence = 0;
92 this.bitstreamNumber = 0;
93 this.position = 0;
94 this.dataWritten = false;
95 };
96 OggWriter.prototype = {
97 _Start: 0x2,
98 _Continue: 0x1,
99 _Stop: 0x4,
100 _append: function(data) {
101 var tmp = new Uint8Array(this.arrayBuffer.byteLength + data.byteLength);
102 tmp.set(new Uint8Array(this.arrayBuffer), 0);
103 tmp.set(new Uint8Array(data), this.arrayBuffer.byteLength);
104 this.arrayBuffer = tmp.buffer;
105 },
106 _makeCRCTable: function() {
107 var crcTable = [];
108 for (var n = 0; n < 256; n++) {
109 var r = n << 24;
110 for (var i = 0; i < 8; i++) {
111 if (r & 0x80000000)
112 r = (r << 1) ^ 0x04c11db7;
113 else
114 r <<= 1;
115 }
116 crcTable[n] = r & 0xffffffff;
117 }
118 return crcTable;
119 },
120 _crc32: function(data, start, end) {
121 var crc = 0;
122 var u8data = new Uint8Array(data)
123 var crcTable = this._crcTable || (this._crcTable = this._makeCRCTable()) ;
124 for (var i = start; i < end; i++)
125 crc = (crc << 8) ^ crcTable[((crc >> 24) & 0xff) ^ u8data[i]];
126 return crc;
127 },
128 _writePage: function(flag, size, position) {
129 var pages = 1 + Math.floor(size / 255);
130 var buffer = new ArrayBuffer(27 + pages), i = 0;
131 // capture_pattern.
132 i += writeString(buffer, i, 'OggS');
133 // stream_structure_version.
134 i += writeValue(buffer, 1, i, 0);
135 // header_type_flag.
136 i += writeValue(buffer, 1, i, flag);
137 // granule_position.
138 // TODO(llandwerlin): Not writing more than 32bits for now,
139 // because Javascript doesn't have 64bits integers, this limits
140 // the duration to ~24 hours at 48kHz sampling rate.
141 i += writeValue(buffer, 4, i, position != undefined ? position : 0);
142 i += writeValue(buffer, 4, i, 0);
143 // bitstream_serial_number.
144 i += writeValue(buffer, 4, i, this.bitstreamNumber);
145 // page_sequence_number.
146 i += writeValue(buffer, 4, i, this.pageSequence++);
147 // CRC_checksum.
148 i += writeValue(buffer, 4, i, 0);
149 // number_page_segments.
150 i += writeValue(buffer, 1, i, pages);
151 // segment sizes.
152 for (var j = 0; j < (pages - 1); j++)
153 i += writeValue(buffer, 1, i, 255);
154 i += writeValue(buffer, 1, i, size % 255);
155
156 this._append(buffer);
157 },
158 _writePageChecksum: function(pageOffset) {
159 var crc = this._crc32(this.arrayBuffer, pageOffset,
160 this.arrayBuffer.byteLength);
161 writeValue(this.arrayBuffer, 4, pageOffset + 22, crc);
162 },
163 writeHeader: function(format) {
164 this.format = format;
165 var start = this.getSize();
166 var buffer = new ArrayBuffer(8 + 1 + 1 + 2 + 4 + 2 + 1), i = 0;
167 // Opus header.
168 i += writeString(buffer, i, 'OpusHead');
169 // version.
170 i += writeValue(buffer, 1, i, 1);
171 // channel count.
172 i += writeValue(buffer, 1, i, format.channels);
173 // pre-skip.
174 i += writeValue(buffer, 2, i, 0);
175 // input sample rate.
176 i += writeValue(buffer, 4, i, format.sample_rate);
177 // output gain.
178 i += writeValue(buffer, 2, i, 0);
179 // channel mapping family.
180 i += writeValue(buffer, 1, i, 0);
181
182 this._writePage(this._Start, buffer.byteLength);
183 this._append(buffer);
184 this._writePageChecksum(start);
185 this._writeCommentHeader();
186 },
187 _writeLengthString: function(buffer, offset, str) {
188 return (writeValue(buffer, offset, 4, str.length) +
189 writeString(buffer, offset, str));
190 },
191 _writeCommentHeader: function() {
192 var start = this.getSize();
193 var buffer = new ArrayBuffer(8 + 4 + 8 + 4 + 4 + 13), i = 0;
194 // Opus comment header.
195 i += writeString(buffer, i, 'OpusTags');
196 // Vendor string.
197 i += this._writeLengthString(buffer, i, 'Chromium');
198 // User comment list length
199 i += writeValue(buffer, 4, i, 1);
200 // User comment 0 length.
201 i += this._writeLengthString(buffer, i, 'TITLE=example');
202
203 this._writePage(this._Continue, buffer.byteLength);
204 this._append(buffer);
205 this._writePageChecksum(start);
206 },
207 writeData: function(data) {
208 this.position += this.format.sample_per_frame / this.format.channels;
209 var start = this.getSize();
210 this._writePage(0, data.byteLength, this.position);
211 this._append(data);
212 this._writePageChecksum(start);
213 this.dataWritten = true;
214 },
215 end: function() {
216 this._writePage(this._Stop, 0);
217 },
218 getSize: function() {
219 return this.arrayBuffer.byteLength;
220 },
221 getData: function() {
222 return this.arrayBuffer;
223 },
224 getExtension: function() {
225 return 'ogg';
226 },
227 };
228
229 function $(id) {
230 return document.getElementById(id);
231 }
232
233 function success(stream) {
234 track = stream.getAudioTracks()[0];
235 var list = $('profileList');
236 var profile = profiles[list.selectedIndex];
237 plugin.postMessage({
238 command: 'start',
239 profile: profile.name,
240 sample_size: profile.sample_size,
241 sample_rate: profile.sample_rate,
242 track: track,
243 });
244 }
245
246 function failure(e) {
247 console.log("Error: ", e);
248 }
249
250 function cleanupDownload() {
251 var download = $('download');
252 if (!download)
253 return;
254 download.parentNode.removeChild(download);
255 }
256
257 function setDownload(data, filename) {
258 var mimeType = 'application/octet-stream';
259 var blob = new Blob([data], { type: mimeType });
260 var a = document.createElement('a');
261 a.id = "download";
262 a.download = filename;
263 a.href = window.URL.createObjectURL(blob);
264 a.textContent = 'Download';
265 a.dataset.downloadurl = [mimeType, a.download, a.href].join(':');
266 $('download-box').appendChild(a);
267 }
268
269 function startRecord() {
270 var list = $('profileList');
271 var profile = profiles[list.selectedIndex];
272 if (profile.name == 'wav')
273 writer = new WavWriter();
274 else
275 writer = new OggWriter(profile);
276 cleanupDownload();
277 // Chromium outputs 32kHz sampling rate by default. This isn't a
278 // supported sampling rate for the Opus codec. So we force the
279 // output to 48kHz. If Chromium implements the GetUserMedia
280 // audio constraints at some point, we could potentially get rid
281 // of this.
282 navigator.webkitGetUserMedia({audio: {
283 optional: [ { googAudioProcessing48kHzSupport: true } ] },
284 video: false},
285 success, failure);
286 }
287
288 function stopRecord() {
289 plugin.postMessage({
290 command: "stop"
291 });
292 track.stop();
293 writer.end();
294 setDownload(writer.getData(), 'Capture.' + writer.getExtension());
295 }
296
297 function saveBlob(blob) {
298 var blobUrl = URL.createObjectURL(blob);
299 window.location = blobUrl;
300 }
301
302 function handleMessage(msg) {
303 if (msg.data.command == 'log') {
304 console.log(msg.data.message);
305 } else if (msg.data.command == 'error') {
306 console.error(msg.data.message);
307 } else if (msg.data.command == 'data') {
308 writer.writeData(msg.data.buffer);
309 $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
310 } else if (msg.data.command == 'format') {
311 writer.writeHeader(msg.data);
312 $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
313 } else if (msg.data.command == 'supportedProfiles') {
314 profiles = [];
315 var profileList = $('profileList');
316 for (var node in profileList.childNodes)
317 profileList.remove(node);
318
319 var item = document.createElement('option');
320 item.label = 'wav';
321 profiles.push({ name: 'wav',
322 sample_rate: 0,
323 sample_size: 0,
324 sample_per_frame:
325 msg.data.profiles[0].sample_per_frame });
326 profileList.appendChild(item);
327 for (var i = 0; i < msg.data.profiles.length; i++) {
328 var item = document.createElement('option');
329 item.label = msg.data.profiles[i].name + ' - ' +
330 msg.data.profiles[i].sample_rate + 'Hz';
331 profiles.push(msg.data.profiles[i]);
332 profileList.appendChild(item);
333 }
334 }
335 }
336
337 function resetData() {
338 writer = new WavWriter();
339 $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
340 }
341
342 function initialize() {
343 plugin = $('plugin');
344 plugin.addEventListener('message', handleMessage, false);
345
346 $('start').addEventListener('click', function (e) {
347 resetData();
348 startRecord();
349 });
350 $('stop').addEventListener('click', function (e) {
351 stopRecord();
352 });
353 }
354
355 document.addEventListener('DOMContentLoaded', initialize, false);
356 </script>
357 </head>
358
359 <body>
360 <h1>Pepper Audio Encoder API Example</h1><br>
361 This example demonstrates receiving frames from an audio MediaStreamTrack and
362 encoding them using AudioEncode.<br>
363
364 <select id="profileList"></select>
365 <input type="button" id="start" value="Start Recording"/>
366 <input type="button" id="stop" value="Stop Recording"/>
367 <div id="download-box"></div>
368 <div id="length"></div>
369 <br>
370 <embed id="plugin" type="application/x-ppapi-example-audio-encode"/>
371 </body>
372 </html>
OLDNEW
« no previous file with comments | « ppapi/examples/audio_encode/audio_encode.cc ('k') | ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698