Combining implementation of autobahn websockets, gstreamers and html5 mediaSource API - websocket

I am running a websocket server using autobahn|python. on the server side, I also have a gstreamer pipeline running which I am using to capture webm frames using "appsink".
The gstreamer pipeline that is implemented is:
gst-launch-1.0 v4l2src ! video/x-raw,width=640,height=480 ! videoconvert ! vp8enc ! webmmux ! appsink name="sink"
Everytime, I receive a buffer in the appsink, I send it over a websocket as a binary "message" using sendMessage.
def on_new_buffer(appsink):
global once
gstsample = appsink.emit('pull-sample')
gstbuffer = gstsample.get_buffer()
frame_data = gstbuffer.extract_dup(0,gstbuffer.get_size())
for c in global_clients:
c.sendMessage(frame_data,True)
print("Directly sent: {0} bytes".format(len(frame_data)))
return False
on the client side, I have a complicated flow of received frame_data blob.
There is a FileReader, MediaSource and source buffer. whenever a frame_data is received, it is read as buffer using filereader. if the filereader is busy reading the previous frame_data, it will append it to "buffer_pool". once the frame_data is read as buffer, it is appended to "sourceBuffer". if the "sourceBuffer" is still updating the previous chunk, it will be appended to "sourceBufferpool".
<script>
var video = document.getElementById('v');
var playButton = document.getElementById('playbutton');
var mediaSource;
var sourceBuffer;
var buffer_pool = [];
var sourceBufferpool = [];
function setupVideo() {
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
if (!!!window.MediaSource) {
alert('MediaSource API is not available');
}
mediaSource = new MediaSource();
video.src = window.URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', function (e) {
try {
sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
} catch(e) {
console.log('Exception calling addSourceBuffer for video', e);
return;
}
//sourceBuffer.addEventListener('updatestart', function(e) { console.log('updatestart: ' + e.target + mediaSource.readyState); });
//sourceBuffer.addEventListener('updateend', function(e) { console.log('updateend: ' + e.target + mediaSource.readyState); });
sourceBuffer.addEventListener('error', function(e) { console.log('error: ' + e.target + mediaSource.readyState); });
sourceBuffer.addEventListener('abort', function(e) { console.log('abort: ' + e.target + mediaSource.readyState); });
sourceBuffer.addEventListener('update', function() {
if (sourceBufferpool.length > 0 && !sourceBuffer.updating) {
try {
sourceBuffer.appendBuffer(sourceBufferpool.shift());
console.log('update: pooled buffer appended ' + sourceBufferpool.length + mediaSource.readyState);
}catch(e){
console.log('Exception calling appendBuffer for video ', e);
return;
}
}
},false)
if (video.paused) {
video.play()
}
startWSStreaming();
},false)
mediaSource.addEventListener('sourceended', function(e) { console.log('sourceended: ' + mediaSource.readyState); });
mediaSource.addEventListener('sourceclose', function(e) { console.log('sourceclose: ' + mediaSource.readyState); });
mediaSource.addEventListener('error', function(e) { console.log('error: ' + mediaSource.readyState); });
}
function startWSStreaming() {
var reader = new FileReader();
reader.onload = function (evt) {
if (sourceBuffer.updating || sourceBufferpool.length > 0){
sourceBufferpool.push(new Uint8Array(evt.target.result));
console.log('update: pooled buffer appended ' + sourceBufferpool.length + mediaSource.readyState);
}else{
sourceBuffer.appendBuffer(new Uint8Array(evt.target.result));
console.log('update: direct buffer appended ' + sourceBufferpool.length + mediaSource.readyState);
}
}
reader.onloadend = function (evt) {
if (buffer_pool.length > 0) {
var chunk = new Blob([buffer_pool.shift()], {type: 'video/webm'});
evt.target.readAsArrayBuffer(chunk);
console.log('Processed buffer pool: current size ' + buffer_pool.length);
}
}
ws = new WebSocket("ws://localhost:9000/");
ws.onopen = function () {
document.getElementById("MSG1").innerHTML = 'Websocket opened <br>';
}
ws.onmessage = function(e) {
myBuffer = e.data;
if (reader.readyState == 1 || buffer_pool.length > 0) {
buffer_pool.push(myBuffer);
console.log('Received buffer pooled: current size ' + buffer_pool.length);
}else{
var chunk = new Blob([myBuffer], {type: 'video/webm'});
reader.readAsArrayBuffer(chunk);
console.log('First buffer processed');
}
}
}
</script>
now, the end result is, I see only one frame on a browser window and then the video freezes. After checking chrome://media-internals/, I get the following clue:
Timestamp Property Value
00:00:00 00 pipeline_state kCreated
00:00:00 00 EVENT PIPELINE_CREATED
00:00:00 00 EVENT WEBMEDIAPLAYER_CREATED
00:00:00 00 url blob:http%3A//localhost%3A8080/09060a78-9759-4fcd-97a2-997121ba6122
00:00:00 00 pipeline_state kInitDemuxer
00:00:01 668 duration unknown
00:00:01 669 pipeline_state kInitVideoRenderer
00:00:01 685 pipeline_state kPlaying
00:00:03 820 EVENT PLAY
00:00:04 191 error Got a block with a timecode before the previous block.
00:00:04 191 pipeline_error pipeline: decode error
00:00:04 191 pipeline_state kStopping
00:00:04 192 pipeline_state kStopped
00:00:28 483 EVENT WEBMEDIAPLAYER_DESTROYED
Phew, quite a long description!!! I hope you have made it till this line.
now, the real questions:
why is the video freezing after diplaying just one frame?
is it because of websocket "sendMessage" method, as I am treating webm chunks as distinct messages whereas, this should need treatment as "sendMessageFrameData"?
will I need some sequencing on the arrived frame_data so that they are received in the order they were sent?
or my entire approach is incorrect?
please help!

Related

WebAudio AAC streaming playback from 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.

Decode audio file chunks using decodeAudioData() in Firefox

I converted the audio file to aac-adts and send it to client as chunks.
then append all chunks to a buffer to be played.
the problem in Firefox that it throws "The buffer passed to decodeAudioData contains an unknown content type." error only with the second chunk.
After checking the mimetype of the first chunk "audio/x-hx-aac-adts" and the second chunk "application/octet-stream". it seams to be different.
Firefox decodes the first chunk successfully, but not the other.
any help??
request.open("GET", "/Content/AudioFiles/chunk-2.aac", true);
request.responseType = 'arraybuffer';
request.send();
request.onload = function () {
var undecodedAudio = request.response;
context.decodeAudioData(undecodedAudio, function (data) {
sourceBuffer.buffer = data;
numberOfChannels = sourceBuffer.buffer.numberOfChannels;
var splitter = context.createChannelSplitter(numberOfChannels);
var channels = document.getElementById('channels');
for (var i = 0; i < numberOfChannels; i++) {
var element = '<div class="channelDiv"><input type="checkbox" class="channel" onclick="toggleChannel(' + i + ')" id="channel' + i + '"/> <label for="channel' + i + '">channel ' + i + '</label></div>';
channels.innerHTML += element;
$('.channel').each(function (index, item) {
item.checked = true;
});
gains[i] = context.createGain();
}
sourceBuffer.connect(splitter, 0, 0);
//Connect splitter' outputs to each Gain Nodes
gains.forEach(function (item, index) {
splitter.connect(item, index);
item.connect(context.destination, 0);
});
});
};

Parse background job add multiple record stops after 21 records

I am trying to add multiple records via Parse background job. I am not getting any error it's just stop saving records after 21 records. Here's my code:
var _ = require('underscore');
Parse.Cloud.job('usagovJobsRunner', function (request, status) {
var promises = [];
for (var index = 0; index < 2; index++) {
promises.push(Parse.Cloud.httpRequest({
url: 'http://jobs.github.com/positions.json',
followRedirects: true,
params: {
page: index
}
}));
}
var jobs = [];
Parse.Promise.when(promises).then(function () {
console.log('arguments length: ' + arguments.length);
_.each(arguments, function (page) {
console.log('page lenght: ' + page.data.length);
_.each(page.data, function (job) {
var model = new (Parse.Object.extend('Jobs'));
model.set({
jobId: job.id,
title: job.title,
description: job.description,
location: job.location,
jobPosting: new Date(job.created_at),
type: job.type,
company: job.company,
companyUrl: job.company_url,
companyLogoUrl: job.company_logo,
source: 'Github Jobs',
jobUrl: job.url
});
jobs.push(model)
});
});
console.log('jobs count: ' + jobs.length);
Parse.Cloud.useMasterKey();
// save all the newly created objects
Parse.Object.saveAll(jobs, {
success: function (objs) {
// objects have been saved...
console.log('jobs saved.');
status.success('Github Jobs runner successfully loaded ' + objs.length + ' jobs');
},
error: function (error) {
// an error occurred...
console.log(error);
status.error('Error: ' + error);
}
});
status.success();
}, function (err) {
console.log(err);
status.error(err);
});
});
Here are my logs:
I2015-08-28T10:50:31.327Z]arguments length: 2
I2015-08-28T10:50:31.328Z]page lenght: 50
I2015-08-28T10:50:31.363Z]page lenght: 50
I2015-08-28T10:50:31.404Z]jobs count: 100
I2015-08-28T10:50:31.442Z]v15: Ran job usagovJobsRunner with: Input:
{} Result: undefined
Don't mix promises with traditional callbacks, choose one style and stick with it. Call status.success(); only once when everything is complete.
For calling to an arbitrary page depth you should create a plain JS function which takes a page number and returns a promise which is completed with the page data and page number. Now, you can call that function and when it's complete you can check if another page needs to be loaded and you also know what the page number is. When you get to the end you can call status.success()

titanium:image upload to server issue

Hello friends,
I am developing app in Titanium and also develop a functionality to upload image to server using POST method and I am select a photo from photo gallery and send to the server but I am can't got successfully response from server and also I want to send image name as a parameter like 45645.png and media parameters etc but I can't send image name so please give me idea how can i solve my issue.
Refer upload image to server: http://mobile.tutsplus.com/tutorials/appcelerator/titanium-mobile-build-an-image-uploader/
//photo gallery for select photo
function getPhotGallery ()
{
Titanium.Media.openPhotoGallery({
success:function(event)
{
//var cropRect = event.cropRect;
var image = event.media;
// set image view
Ti.API.debug('Our type was: '+event.mediaType);
if(event.mediaType == Ti.Media.MEDIA_TYPE_PHOTO)
{
uploadPhotoImageView.image = image;
UploadPhotoToServer(uploadPhotoImageView.image);
}
else
{
}
//Titanium.API.info('PHOTO GALLERY SUCCESS cropRect.x ' + cropRect.x + ' cropRect.y ' + cropRect.y + ' cropRect.height ' + cropRect.height + ' cropRect.width ' + cropRect.width);
},
cancel:function()
{
},
error:function(error)
{
},
allowEditing:true,
//popoverView:popoverView,
//arrowDirection:arrowDirection,
mediaTypes:[Ti.Media.MEDIA_TYPE_PHOTO]
});
}
//upload photo to server uisng POST method
function UploadPhotoToServer(media)
{
//var filename = mobileNumber.value + '.png';
var filename = '123456.png';
if (Titanium.Network.online == true)
{
var imgUploadLoader = Titanium.Network.createHTTPClient();
//open the client
imgUploadLoader.open('POST', 'http://projects.spinxweb.net/ContactsTracking/iphone-file-upload.aspx');
// send the data
imgUploadLoader.send(
{
media: media,
"name": filename
});
imgUploadLoader.onerror = function(e)
{
Ti.API.info('IN ERROR ' + e.error);
alert('Sorry, we could not upload your photo! Please try again.');
};
imgUploadLoader.onload = function()
{
Ti.API.info('IN ONLOAD ' + this.status + ' readyState ' + this.readyState);
if(this.responseText != 'false')
{
var url = this.responseText; //set our url variable to the response
Ti.API.log('Upload image url:'+url);
//alert('Upload photo successfully');
//getLoginData();
}
else
{
alert('Whoops, something failed in your upload script.');
}
};
imgUploadLoader.onsendstream = function(e)
{
Ti.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress);
if(Ti.Platform.osname == 'android')
{
}
else
{
}
};
}
else
{
alert('You must have a valid Internet connection in order to upload this photo.');
}
}
Not sure if this is exactly what you're looking for, but I've had success with this code:
eventSuccess : function ( e )
{
var xhr = Ti.Network.createHTTPClient ( );
xhr.open ( "POST", 'yourserver.com/webservice.php' );
xhr.setTimeout ( 20000 );
xhr.send (
{
"CommandType" : "image",
"file" : e.media,
"name" : filename
});
xhr.onload = function ( e )
{
Ti.API.info ("image sent to server");
}
}
This is running when a success event is coming back from the camera. The image is then sent to the server.
You'll then need something on the server side, like this:
move_uploaded_file ($_FILES["file"]["tmp_name"], "incoming/images/" . $_FILES["file"]["name"]);

Firefox extension is freezing Firefox until request is completed

For some reason the function is freezing along with firefox until it fully retrieve the stream from requested site. Is there any mechanism to prevent freezing, so it works as expected?
in XUL
<statusbarpanel id="eee_label" tooltip="eee_tooltip"
onclick="eee.retrieve_rate(event);"/>
Javascript
retrieve_rate: function(e)
{
var ajax = null;
ajax = new XMLHttpRequest();
ajax.open('GET', 'http://site.com', false);
ajax.onload = function()
{
if (ajax.status == 200)
{
var regexp = /blabla/g;
var match = regexp.exec(ajax.responseText);
while (match != null)
{
window.dump('Currency: ' + match[1] + ', Rate: '
+ match[2] + ', Change: ' + match[3] + "\n");
if(match[1] == "USD")
rate_USD = sprintf("%s:%s", match[1], match[2]);
if(match[1] == "EUR")
rate_EUR = sprintf("%s:%s", match[1], match[2]);
if(match[1] == "RUB")
rate_RUB = sprintf("%s/%s", match[1], match[2]);
match = regexp.exec(ajax.responseText);
}
var rate = document.getElementById('eee_label');
rate.label = rate_USD + " " + rate_EUR + " " + rate_RUB;
}
else
{
}
};
ajax.send();
I tried to put window.dump() right after ajax.send() and it dumped in the console also after the request is completed.
You need to make an asynchronous AJAX request by passing true as the last parameter to ajax.open.
Note that once you do that, the send function will return immediately, and any code after it will run before the request finishes.

Resources