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

Unified 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: More Windows fixes in proxy code Created 5 years, 6 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 side-by-side diff with in-line comments
Download patch
Index: ppapi/examples/audio_encode/audio_encode.html
diff --git a/ppapi/examples/audio_encode/audio_encode.html b/ppapi/examples/audio_encode/audio_encode.html
new file mode 100644
index 0000000000000000000000000000000000000000..b21053e0d1175a7e52d01cb3e3d77d9e3fcd548a
--- /dev/null
+++ b/ppapi/examples/audio_encode/audio_encode.html
@@ -0,0 +1,430 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright 2015 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Audio Encoder Example</title>
+ <script type="text/javascript">
+ var plugin;
+ var track;
+ var writer;
+ var profiles;
+
+ function writeString(array, offset, string) {
+ var out = new Uint8Array(array);
+ for (var i = 0; i < string.length; i++)
+ out.set([string.charCodeAt(i)], i + offset);
+ return string.length;
+ }
+ function writeValue(array, bytes, offset, value) {
+ var out = new Uint8Array(array);
+ for (var i = 0; i < bytes; i++)
+ out.set([((value >>> (i * 8)) & 0xff)], offset + i);
+ return bytes;
+ }
+ // Writes way data.
+ var WavWriter = function() {
+ this.chunkSizeOffset = 0;
+ this.arrayBuffer = new ArrayBuffer();
+ };
+ WavWriter.prototype = {
+ writeHeader: function(format) {
+ this.arrayBuffer =
+ new ArrayBuffer(4 + 4 + 4 + 4 + 4 + 2 + 2 + 4 + 4 + 2 + 2 + 4 + 4);
+ var i = 0;
+ // File header
+ i += writeString(this.arrayBuffer, i, 'RIFF');
+ i += 4; // Gap for final size.
+ i += writeString(this.arrayBuffer, i, 'WAVE');
+ // Chunk ID.
+ i += writeString(this.arrayBuffer, i, 'fmt ');
+ // Chunk length.
+ i += writeValue(this.arrayBuffer, 4, i, 16);
+ // Codec (uncompressed LPCM).
+ i += writeValue(this.arrayBuffer, 2, i, 1);
+ // Number of channels.
+ i += writeValue(this.arrayBuffer, 2, i, format.channels);
+ // Sample rate.
+ i += writeValue(this.arrayBuffer, 4, i, format.sample_rate);
+ // Average bytes per seconds (sample rate * bytes per sample)
+ i += writeValue(this.arrayBuffer, 4, i,
+ format.sample_rate * format.sample_size);
+ // Bytes per sample.
+ i += writeValue(this.arrayBuffer, 2, i,
+ format.sample_size * format.channels);
+ // Bits per sample.
+ i += writeValue(this.arrayBuffer, 2, i, format.sample_size * 8);
+
+ // Data chunk
+ i += writeString(this.arrayBuffer, i, 'data');
+ this.chunkSizeOffset = i; // Location of the chunk's size
+ },
+ writeData: function(data) {
+ var tmp = new Uint8Array(this.arrayBuffer.byteLength + data.byteLength);
+ tmp.set(new Uint8Array(this.arrayBuffer), 0);
+ tmp.set(new Uint8Array(data), this.arrayBuffer.byteLength);
+ this.arrayBuffer = tmp.buffer;
+ },
+ end: function() {
+ var out = new Uint32Array(this.arrayBuffer);
+ out.set([this.arrayBuffer.byteLength - 8], 1);
+ out.set([this.arrayBuffer.byteLength - this.chunkSizeOffset],
+ this.chunkSizeOffset / 4);
+ },
+ getSize: function() {
+ return this.arrayBuffer.byteLength;
+ },
+ getData: function() {
+ return this.arrayBuffer;
+ },
+ getExtension: function() {
+ return 'wav';
+ },
+ };
+
+ // Writes ogg data.
+ var OggWriter = function(profile) {
+ if (profile.name == 'opus')
+ this.writeHeader = this._writeOpusHeader;
+ else
+ this.writeHeader = this._writeSpeexHeader;
+ this.arrayBuffer = new ArrayBuffer();
+ this.pageSequence = 0;
+ this.bitstreamNumber = 0;
+ this.position = 0;
+ this.dataWritten = false;
+ };
+ OggWriter.prototype = {
+ _Start: 0x2,
+ _Continue: 0x1,
+ _Stop: 0x4,
+ _append: function(data) {
+ var tmp = new Uint8Array(this.arrayBuffer.byteLength + data.byteLength);
+ tmp.set(new Uint8Array(this.arrayBuffer), 0);
+ tmp.set(new Uint8Array(data), this.arrayBuffer.byteLength);
+ this.arrayBuffer = tmp.buffer;
+ },
+ _makeCRCTable: function() {
+ var crcTable = [];
+ for (var n = 0; n < 256; n++) {
+ var r = n << 24;
+ for (var i = 0; i < 8; i++) {
+ if (r & 0x80000000)
+ r = (r << 1) ^ 0x04c11db7;
+ else
+ r <<= 1;
+ }
+ crcTable[n] = r & 0xffffffff;
+ }
+ return crcTable;
+ },
+ _crc32: function(data, start, end) {
+ var crc = 0;
+ var u8data = new Uint8Array(data)
+ var crcTable = this._crcTable || (this._crcTable = this._makeCRCTable());
+ for (var i = start; i < end; i++)
+ crc = (crc << 8) ^ crcTable[((crc >> 24) & 0xff) ^ u8data[i]];
+ return crc;
+ },
+ _writePage: function(flag, size, position) {
+ var pages = 1 + Math.floor(size / 255);
+ var buffer = new ArrayBuffer(27 + pages), i = 0;
+ // capture_pattern.
+ i += writeString(buffer, i, 'OggS');
+ // stream_structure_version.
+ i += writeValue(buffer, 1, i, 0);
+ // header_type_flag.
+ i += writeValue(buffer, 1, i, flag);
+ // granule_position.
+ // TODO(llandwerlin): Not writing more than 32bits for now,
+ // because Javascript doesn't have 64bits integers, this limits
+ // the duration to ~24 hours at 48kHz sampling rate.
+ i += writeValue(buffer, 4, i, position != undefined ? position : 0);
+ i += writeValue(buffer, 4, i, 0);
+ // bitstream_serial_number.
+ i += writeValue(buffer, 4, i, this.bitstreamNumber);
+ // page_sequence_number.
+ i += writeValue(buffer, 4, i, this.pageSequence++);
+ // CRC_checksum.
+ i += writeValue(buffer, 4, i, 0);
+ // number_page_segments.
+ i += writeValue(buffer, 1, i, pages);
+ // segment sizes.
+ for (var j = 0; j < (pages - 1); j++)
+ i += writeValue(buffer, 1, i, 255);
+ i += writeValue(buffer, 1, i, size % 255);
+
+ this._append(buffer);
+ },
+ _writePageChecksum: function(pageOffset) {
+ var crc = this._crc32(this.arrayBuffer, pageOffset,
+ this.arrayBuffer.byteLength);
+ writeValue(this.arrayBuffer, 4, pageOffset + 22, crc);
+ },
+ _writeOpusHeader: function(format) {
+ this.format = format;
+ var start = this.getSize();
+ var buffer = new ArrayBuffer(8 + 1 + 1 + 2 + 4 + 2 + 1), i = 0;
+ // Opus header.
+ i += writeString(buffer, i, 'OpusHead');
+ // version.
+ i += writeValue(buffer, 1, i, 1);
+ // channel count.
+ i += writeValue(buffer, 1, i, format.channels);
+ // pre-skip.
+ i += writeValue(buffer, 2, i, 0);
+ // input sample rate.
+ i += writeValue(buffer, 4, i, format.sample_rate);
+ // output gain.
+ i += writeValue(buffer, 2, i, 0);
+ // channel mapping family.
+ i += writeValue(buffer, 1, i, 0);
+
+ this._writePage(this._Start, buffer.byteLength);
+ this._append(buffer);
+ this._writePageChecksum(start);
+ this._writeOpusCommentHeader('OpusTags');
+ },
+ _writeSpeexHeader: function(format) {
+ this.format = format;
+ var start = this.getSize();
+ var buffer = new ArrayBuffer(80), i = 0;
+
+ var sampleRateToMode = function(rate) {
+ switch (rate) {
+ case 8000:
+ return 0;
+ case 16000:
+ return 1;
+ case 32000:
+ return 2;
+ }
+ return 0;
+ };
+
+ // speex_string.
+ i += writeString(buffer, i, 'Speex ');
+ // speex_version.
+ i += writeString(buffer, i, ' ');
+ // speex_version_id.
+ i += writeValue(buffer, 4, i, 0);
+ // header_size.
+ i += writeValue(buffer, 4, i, buffer.byteLength);
+ // rate.
+ i += writeValue(buffer, 4, i, format.sample_rate);
+ // mode.
+ i += writeValue(buffer, 4, i, sampleRateToMode(format.sample_rate));
+ // mode_bitstream_version.
+ i += writeValue(buffer, 4, i, 0);
+ // nb_channels.
+ i += writeValue(buffer, 4, i, format.channels);
+ // bitrate.
+ i += writeValue(buffer, 4, i, 1);
+ // frame_size.
+ i += writeValue(buffer, 4, i, format.sample_per_frame);
+ // vbr.
+ i += writeValue(buffer, 4, i, 1);
+ // frames_per_packet.
+ i += writeValue(buffer, 4, i, 1);
+ // extra_headers.
+ i += writeValue(buffer, 4, i, 0);
+
+ this._writePage(this._Start, buffer.byteLength);
+ this._append(buffer);
+ this._writePageChecksum(start);
+ this._writeCommentHeader('Speex ');
+ },
+ _writeCommentHeader: function(name) {
+ var start = this.getSize();
+ var buffer = new ArrayBuffer(8 + 4 + 8 + 4 + 4 + 13), i = 0;
+ // Opus/Speex comment header.
+ i += writeString(buffer, i, name);
+ // Vendor string.
+ i += this._writeLengthString(buffer, i, 'Chromium');
+ // User comment list length
+ i += writeValue(buffer, 4, i, 1);
+ // User comment 0 length.
+ i += this._writeLengthString(buffer, i, 'TITLE=example');
+
+ this._writePage(this._Continue, buffer.byteLength);
+ this._append(buffer);
+ this._writePageChecksum(start);
+ },
+ _writeLengthString: function(buffer, offset, str) {
+ return (writeValue(buffer, offset, 4, str.length) +
+ writeString(buffer, offset, str));
+ },
+ writeData: function(data) {
+ this.position += this.format.sample_per_frame / this.format.channels;
+ var start = this.getSize();
+ this._writePage(0, data.byteLength, this.position);
+ this._append(data);
+ this._writePageChecksum(start);
+ this.dataWritten = true;
+ },
+ end: function() {
+ this._writePage(this._Stop, 0);
+ },
+ getSize: function() {
+ return this.arrayBuffer.byteLength;
+ },
+ getData: function() {
+ return this.arrayBuffer;
+ },
+ getExtension: function() {
+ return 'ogg';
+ },
+ };
+
+ function $(id) {
+ return document.getElementById(id);
+ }
+
+ function success(stream) {
+ track = stream.getAudioTracks()[0];
+ var list = $('profileList');
+ var profile = profiles[list.selectedIndex];
+ plugin.postMessage({
+ command: 'start',
+ profile: profile.name,
+ sample_size: profile.sample_size,
+ sample_rate: profile.sample_rate,
+ track: track,
+ });
+ }
+
+ function failure(e) {
+ console.log("Error: ", e);
+ }
+
+ function cleanupDownload() {
+ var download = $('download');
+ if (!download)
+ return;
+ download.parentNode.removeChild(download);
+ }
+
+ function setDownload(data, filename) {
+ var mimeType = 'application/octet-stream';
+ var blob = new Blob([data], { type: mimeType });
+ var a = document.createElement('a');
+ a.id = "download";
+ a.download = filename;
+ a.href = window.URL.createObjectURL(blob);
+ a.textContent = 'Download';
+ a.dataset.downloadurl = [mimeType, a.download, a.href].join(':');
+ $('download-box').appendChild(a);
+ }
+
+ function startRecord() {
+ var list = $('profileList');
+ var profile = profiles[list.selectedIndex];
+ if (profile.name == 'wav')
+ writer = new WavWriter();
+ else
+ writer = new OggWriter(profile);
+ cleanupDownload();
+
+ var constraints = [];
+ if (profile.name == 'opus') {
+ // Chromium outputs 32kHz sampling rate by default. This isn't a
+ // supported sampling rate for the Opus codec. So we force the
+ // output to 48kHz. If Chromium implements the GetUserMedia
+ // audio constraints at some point, we could potentially get rid
+ // of this.
+ constraints.push({ googAudioProcessing48kHzSupport: true });
+ }
+
+ navigator.webkitGetUserMedia({ audio: { optional: constraints },
+ video: false},
+ success, failure);
+ }
+
+ function stopRecord() {
+ plugin.postMessage({
+ command: "stop"
+ });
+ track.stop();
+ writer.end();
+ setDownload(writer.getData(), 'Capture.' + writer.getExtension());
+ }
+
+ function saveBlob(blob) {
+ var blobUrl = URL.createObjectURL(blob);
+ window.location = blobUrl;
+ }
+
+ function handleMessage(msg) {
+ if (msg.data.command == 'log') {
+ console.log(msg.data.message);
+ } else if (msg.data.command == 'error') {
+ console.error(msg.data.message);
+ } else if (msg.data.command == 'data') {
+ writer.writeData(msg.data.buffer);
+ $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
+ } else if (msg.data.command == 'format') {
+ writer.writeHeader(msg.data);
+ $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
+ } else if (msg.data.command == 'supportedProfiles') {
+ profiles = [];
+ var profileList = $('profileList');
+ for (var node in profileList.childNodes)
+ profileList.remove(node);
+
+ var item = document.createElement('option');
+ item.label = 'wav';
+ profiles.push({ name: 'wav',
+ sample_rate: 0,
+ sample_size: 0,
+ sample_per_frame:
+ msg.data.profiles[0].sample_per_frame });
+ profileList.appendChild(item);
+ for (var i = 0; i < msg.data.profiles.length; i++) {
+ var item = document.createElement('option');
+ item.label = msg.data.profiles[i].name + ' - ' +
+ msg.data.profiles[i].sample_rate + 'Hz';
+ profiles.push(msg.data.profiles[i]);
+ profileList.appendChild(item);
+ }
+ }
+ }
+
+ function resetData() {
+ writer = new WavWriter();
+ $('length').textContent = ' Size: ' + writer.getSize() + ' bytes';
+ }
+
+ function initialize() {
+ plugin = $('plugin');
+ plugin.addEventListener('message', handleMessage, false);
+
+ $('start').addEventListener('click', function (e) {
+ resetData();
+ startRecord();
+ });
+ $('stop').addEventListener('click', function (e) {
+ stopRecord();
+ });
+ }
+
+ document.addEventListener('DOMContentLoaded', initialize, false);
+ </script>
+</head>
+
+<body>
+ <h1>Pepper Audio Encoder API Example</h1><br>
+ This example demonstrates receiving frames from an audio MediaStreamTrack and
+ encoding them using AudioEncode.<br>
+
+ <select id="profileList"></select>
+ <input type="button" id="start" value="Start Recording"/>
+ <input type="button" id="stop" value="Stop Recording"/>
+ <div id="download-box"></div>
+ <div id="length"></div>
+ <br>
+ <embed id="plugin" type="application/x-ppapi-example-audio-encode"/>
+</body>
+</html>
« 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