WebAudio AAC streaming playback from WebSocket - websocket

I need to playback aac (adts) streaming from WebSocket, I searched the internet and found aac.js can do it but sounds like delay almost 2~5 seconds.
And I found some people use WebAudio stuff to do it. I try it with below code but decoder return error after some packets (not always but repeat error):
Uncaught (in promise) DOMException: Unable to decode audio data
My sample javascript:
var context = new AudioContext();
var nextStartTime = 0;
var audioWs = new WebSocket("ws://" + hostip + ":8084");
audioWs.binaryType = 'arraybuffer';
audioWs.onopen = function(evt) {
console.log("onOpen");
bexit = 0;
};
audioWs.onclose = function(evt) {
console.log("close");
};
audioWs.onmessage = function(evt) {
var blob = evt.data;
if ( blob.slice == undefined) {
console.log("undefined message");
return;
}
decodeAudio(blob);
};
audioWs.onerror = function(evt) {
console.log("error");
};
var decodeAudio = function(blob) {
context.decodeAudioData(
blob,
function(buffer) {
if (!buffer) {
console.log('error decoding file data: ' + url);
return;
}
console.log("playback buffer");
playBuffer(buffer);
},
function(error) {
console.error("decodeAudioData error", error);
});
};
var playBuffer = function(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
if(nextStartTime == 0){
nextStartTime = context.currentTime + buffer.duration/2;
}
source.connect(context.destination);
source.start(nextStartTime);
nextStartTime += buffer.duration;
};
I not sure if it's caused by data received from websocket incomming too fast then cause decoder error! How should I fix it?
Or maybe it's not good way to do this task with WebAudio API?

WebAudio's decodeAudioData can't decode partial blobs. It needs the whole file, in general, because in many encoded formats, the necessary information (sample rates, channels, sample size, etc.) is only at the beginning.

Related

Vonage browser to browser audio call return some error

I am using Laravel, and trying add browser to browser audio calling. I am using Vonage (Tookbox) API for this, but I am getting some error.
here is my code:
async function audioCall() {
var publisher;
var targetElement = 'publisher';
var pubOptions = {publishAudio:true, publishVideo:false};
publisher = OT.initPublisher(targetElement, pubOptions, function(error) {
if (error) {
alert("The client cannot publish.");
} else {
console.log('Publisher initialized.');
}
});
// Setting an audio source to a new MediaStreamTrack
const stream = await OT.getUserMedia({
videoSource: null
});
const [audioSource] = stream.getAudioTracks();
publisher.setAudioSource(audioSource).then(() => console.log('Audio source updated'));
// Cycling through microphone inputs
let audioInputs;
let currentIndex = 0;
OT.getDevices((err, devices) => {
audioInputs = devices.filter((device) => device.kind === 'audioInput');
// Find the right starting index for cycleMicrophone
audioInputs.forEach((device, idx) => {
if (device.label === publisher.getAudioSource().label) {
currentIndex = idx;
}
});
});
const cycleMicrophone = () => {
currentIndex += 1;
let deviceId = audioInputs[currentIndex % audioInputs.length].deviceId;
publisher.setAudioSource(deviceId);
};
}
This code return an error on console:
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
I believe the issue is that you have
device.kind === 'audioInput'
and I'm pretty sure device.kind comes out like 'audioinput' (all lowercase).
examples:
https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/kind
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices#examples
That would make audioInputs empty (try to console.log it to verify) and gives you the error because there is no device.
Try:
device.kind.toLowerCase() === 'audioinput'
Hope it works out.

base64 from API call to Adobe PDF Embed API

I'm trying to embed a PDF on a web page. The native PDF viewer in Chrome has a size limit of around 2mb, so I'm trying to use Adobe's API (https://www.adobe.io/apis/documentcloud/dcsdk/pdf-embed.html)
Because of the way that a CMS that I'm using stored files, I have a Base64 string to work with.
Using code from https://gist.github.com/kiritodeveloper/06186343e41e21a94411b3987e84c5de, I was able to pass my base64 string to Adobe's API and embed the PDF about 60% of the time in Chrome, but now it doesn't work in Firefox.
I'm using ajax to get to my CMS's API to retrieve the data, but I think that the adobe listener is running before the data is returned; when it does work, it's just chance. How can I get the order right so that this will work? Can I use an asynchronous call to get this data?
const clientID = "ADOBE API KEY HERE";
var queryPath = "$/PDFBase64"
jQuery( document).ready(function() {
var pdfData = '';
getPDF();
function base64ToArrayBuffer(base64) {
var bin = window.atob(base64);
var len = bin.length;
var uInt8Array = new Uint8Array(len);
for (var i = 0; i < len; i++) {
uInt8Array[i] = bin.charCodeAt(i);
}
return uInt8Array.buffer;
}
document.addEventListener("adobe_dc_view_sdk.ready", function () {
var adobeDCView = new AdobeDC.View({
clientId: clientID,
divId: "adobe-dc-view"
});
adobeDCView.previewFile(
{
// convert the Base64 encoded PDF and convert it to an ArrayBuffer
// and pass it as a Promise
content: { promise: Promise.resolve(base64ToArrayBuffer(pdfData)) },
// The filename name to display in the View SDK UI
// and also used as the filename of the PDF when downloaded
metaData: { fileName: "Download.pdf" }
}, {})
});
function getPDF() {
jQuery.ajax("/api/Query?queryname=" + queryPath ,
{
type : "get",
"async":false,
contentType: "application/json",
headers: {"RequestVerificationToken":
document.getElementById("__RequestVerificationToken").value},
success: function(data){
iqa = data.Items.$values;
if(iqa.length>0){
for (var i = 0; i < iqa.length; i++) {
filt = iqa[i].Properties.$values;
pdfData = filt.filter(x => x.Name == "PDF")[0].Value;
};
}
return pdfData;
}, error : function(xhr, textStatus, errorThrown ) {
if (textStatus == 'timeout') {
this.tryCount++;
if (this.tryCount <= this.retryLimit) {
//try again
$.ajax(this);
return;
}
return;
}
if (xhr.status == 500) {
console.log(errorThrown);
} else {
//handle error
}
}
});
};
});

Unable to create valid screenshot using browser.takescreenshot()

This is the code that I have in the afterEach block.
afterEach(function() {
jasmine.getEnv().addReporter(new function () {
this.specDone = function (result) {
console.log("result is :"+result);
if (result.failedExpectations.length > 0) {
browser.takeScreenshot().then(function(png) {
console.log("png is :"+png);
console.dir(png);
var stream = fs.createWriteStream(specName.description+".png");
var stream2 = fs.createWriteStream(specName.description+"2.png");
stream.write(new Buffer(png,'utf8', function (err)
{
if (err) throw err
console.log('File saved.')
}));
stream2.write(new Buffer(png,'base64'));
stream.end();
stream2.end();
});
}
};
});
But none of the photo viewers is able to open the files. Any insights as to what is going wrong is highly appreciated.
System is windows 10 and browser Firefox 46.*
The console.log of the png returned by the browser.takeScreenshot() is as below:
png is :{"name":"screenshot","sessionId":"c18f3e08-9da0-4c71-9a10-391bab714e7a","status":0,"value":"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
I am not sure why takeShotshot() method returns the entire JSON object instead of just the base64 value for the screenshot. In your case, you can check whether the object returned by takeScreenShot methods contains any key with name value. If value is is present, then use it to get base64 value of the screenshot.
browser.takeScreenshot().then(function(png) {
var base64Value = png.hasOwnProperty("value") ? png.value : png;
var stream = fs.createWriteStream(specName.description+".png");
var stream2 = fs.createWriteStream(specName.description+"2.png");
stream.write(new Buffer(png,'utf8', function (err)
{
if (err) throw err
console.log('File saved.')
}));
stream2.write(new Buffer(base64Value,'base64'));
stream.end();
stream2.end();
})

Fetching Images through Cloud Code

I've been scratching my head over this for a while. What am I doing wrong? Your help is much appreciated :)
I've tried many different image codes, but I think it's a promise issue I'm seeing. With the code below I only see the "Start of loop" log message.
If I move the results push outside the promise structure to underneath then I see the Stage log messages, albeit after all the Start of loops have printed (hence why I put the push in the then function).
Parse.Cloud.job("fetchjson", function(request, status) {
var url = 'some json url';
Parse.Cloud.httpRequest({url: url}).then(function(httpResponse){
//var Image = require("parse-image");
var Seeds = Parse.Object.extend("Seeds");
var jsonobj = JSON.parse(httpResponse.text);
var results = [];
// do NOT iterate arrays with `for... in loops`
for(var i = 0; i < jsonobj.seeds.length; i++){
var seed = new Seed();
var a = new Seed(jsonobj.seeds[i]);
console.log("Start of loop");
Parse.Cloud.httpRequest({url: a.get("image") }).then(function(response) {
console.log("Stage 1");
//var file = new Parse.File('thumb.jpg', { base64: response.buffer.toString('base64', 0, response.buffer.length) });
//return file.save();
return "hi"
}).then(function(thumb) {
console.log("Stage 2");
//a.set("imageFile", thumb);
//a.set("viewsInt", parseInt(a.get("views")));
}, function(error) {
console.log("Error occurred :(");
}).then(function(){
results.push(seed.save(a)); // add to aggregate
});
}
// .when waits for all promises
Parse.Promise.when(results).then(function(data){
status.success("All saved");
});
}, function(error) {
console.error('Request failed with response code ' + httpResponse.status);
status.error("Failed");
});
});

embed webgl in powerpoint or render animated gif from webgl

anyone got an idea how to embed a webgl animation into powerpoint. any tools that can be used on server side to capture an animated gif?
I did not make it work to embed webgl html directly in a powerpoint.
You can create images of webgl by calling toDataURL() as in
var canvas = document.createElement("canvas");
var gl = canvas.getContext("experimental-webgl");
function render() {
gl.clearColor(Math.random(), Math.random(), Math.random(), 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// takes a 'screenshot' of the canvas.
var image = canvas.toDataURL();
requestAnimationFrame(render);
}
render();
To make an animation you could send each of those screenshots to a server
...
var image = canvas.toDataURL();
var req = new XMLHTTPRequest();
req.open("POST", "http://localhost:8080", true);
var data = {
cmd: 'screenshot',
dataURL: image,
};
req.setRequestHeader("Content-type", "application/json");
req.send(JSON.stringify(data));
Here's a node.js server that will save the screenshots as .png files. You could then load them into some program to turn them into a gif.
var port = 8080
var screenshotCount = 0;
var http = require('http'),
url = require('url'),
fs = require('fs'),
util = require('util'),
path = require('path'),
querystring = require('querystring');
function postHandler(request, callback) {
var query_ = { };
var content_ = '';
request.addListener('data', function(chunk) {
content_ += chunk;
});
request.addListener('end', function() {
query_ = JSON.parse(content_);
callback(query_);
});
}
function sendJSONResponse(res, object) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.write(JSON.stringify(object), 'utf8');
res.end();
}
function startsWith(str, start) {
return (str.length >= start.length &&
str.substr(0, start.length) == start);
}
function saveScreenshotFromDataURL(dataURL) {
var EXPECTED_HEADER = "data:image/png;base64,";
if (startsWith(dataURL, EXPECTED_HEADER)) {
var filename = "screenshot-" + (screenshotCount++) + ".png";
fs.writeFile(
filename,
dataURL.substr(
EXPECTED_HEADER.length,
dataURL.length - EXPECTED_HEADER.length),
'base64');
util.print("Saved Screenshot: " + filename + "\n");
}
}
server = http.createServer(function(req, res) {
// your normal server code
if (req.method == "POST") {
postHandler(req, function(query) {
switch (query.cmd) {
case 'screenshot':
saveScreenshotFromDataURL(query.dataURL);
sendJSONResponse(res, { ok: true });
break;
default:
util.print("err: unknown post: " + query + "\n");
break;
}
});
}
}),
server.listen(port);
Note that server only saves screenshots, it doesn't serve files (for brevity). So you'll need to either add that functionality or serve the files from another server.

Resources