Upload HTML5 MediaRecorder's recorded video to AWS S3 in realtime - html

I want to record video and upload it in AWS S3 in realtime.
Things that I have done so far.
As soon as user clicks on Record Audio/Video button following code snippets gets called :
navigator.getUserMedia({ audio: true,video: true }, function (stream) {
mediaRecorder = new MediaRecorder(stream, {mimeType: 'video/webm'});
mediaRecorder.onstop = handleStop;
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start();
}
On Stop record Audio/Video button I'm uploading video to AWS S3 :
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: // . Enter your identity pool
RoleArn: // . Enter RoleArn
});
AWS.config.credentials.get(function(err) {
if (err) alert(err);
console.log(AWS.config.credentials);
});
var bucketName = ''; // Enter your bucket name
var bucket = new AWS.S3({
params: {
Bucket: bucketName
}
});
mediaRecorder.stop();
var blob = new Blob(recordedBlobs, {type: 'video/webm' });
var file = new File([blob], 'testVideo.webm');
var objKey = 'testing/' + file.name;
var params = {
Key: objKey,
ContentType: file.type,
Body: file,
ACL: 'public-read'
};
bucket.putObject(params, function(err, data) {
if (err) {
console.log(" Error while UPLOADING Video :");
} else {
console.log(" Success UPLOADING Video :");
}
});
Everything works perfectly fine. Video gets uploaded successfully when clicked on stop recording.
Video size vary from 100MB to 3GB
Now the problem is while the video is getting uploaded if the user close the browser then the upload fails.
So is there a way to upload video to S3 in realtime ?? i.e during recording phase it should get uploaded.
Or is there any other way to upload it ? before user closes the browser.

Now the problem is while the video is getting uploaded if the user closes the browser then the upload fails.
So is there a way to upload video to S3 in real time?
You're using the wrong technology to achieve this, if you need to make sure the recording is uploaded/recorded serverside simultaneously then you should be looking at WebRTC (Web Real-Time Communications).
Media recorder will very nearly always be of a higher (lossless) quality, the users upload speed will often not keep up so it'll increasingly be buffered for upload even if start uploading chunks immediately. WebRTC on the other hand (should) adjust the quality of the stream uploaded to match the network conditions encountered (lossy).
For an out of the box solution try something like OpenTok or Twilio, both make this pretty straightforward.

Related

chrome PDF viewer can't download file

Here is my situation, I got a server running a PDF generator, when I make a request with some params, it will give me back a PDF file, the PDF is not stored in the server it's generated during the runtime.
Everything goes fine, I can get the PDF open in chrome's PDF viewer, but if want to download the file, an error occurred, like the image shows.
Because Chrome go to the origin URL to request the file, but the file is not a static resource on the server.
I don't know if anybody has run into this problem?
Whenever you leave the website you used to create the object URL (window.URL.createObjectURL(...)) that very object will get garbage collected. So you need to keep a reference to that object somehow.
This works for us in Chrome, Firefox, Safari, iOS Safari & Android to first display the PDF in capable browsers in a new tab and allow a download afterwards (in IE it just starts the download):
function openPdfInNewTab(url,
postData,
description = 'Document',
filename = description + '.pdf') {
if (!window.navigator.msSaveOrOpenBlob) {
var tabWindow = window.open('', '_blank');
var a = tabWindow.document.createElement('a');
a.textContent = 'Loading ' + description + '..';
tabWindow.document.body.appendChild(a);
tabWindow.document.body.style.cursor = 'wait';
} else {
spinnerService.show('html5spinner');
}
$http.post(url, postData, {responseType: 'arraybuffer'})
.then(function showDocument(response) {
var file = new Blob([response.data], {type: 'application/pdf'});
if (window.navigator.msSaveOrOpenBlob) {
spinnerService.hide('html5spinner');
window.navigator.msSaveOrOpenBlob(file, filename);
} else {
tabWindow.document.body.style.cursor = 'auto';
var url = a.href = window.URL.createObjectURL(file);
a.click();
a.download = filename;
}
$timeout(function revokeUrl() {
window.URL.revokeObjectURL(url);
}, 3600000);
}, handleDownloadError);
}
We have been opening PDFs in a new browser-tab and had similar issues.
For us it started working again when we use window.URL.createObjectURL instead of tabWindow.URL.createObject which displayed the PDF but didn't allow the download.
Chrome's built in PDF viewer will download the pdf file through the PDF's origin URL. So if the PDF is generated at server runtime and if it's not stored in the sever, the download could fail.
see link here: https://productforums.google.com/forum/#!topic/chrome/YxyVToLN8ho
Just as an additional comment:
We had the same problem on a project, on Chrome only.
Authenticated GET request would fetch the PDF as an attachment from API, and we would forward it via window.createObjectURL(blob) to the browser viewer.
The Network Error was due us invoking window.revokeObjectURL(url); after opening the PDF. When we removed that line, the blob wasn't garbage collected immediately after opening.
fetch(request)
.then(async response => {
if (!response.ok) {
return reject(response.statusText);
}
const blob = await response.blob();
const url = await window.URL.createObjectURL(blob);
window.open(url, '_blank');
URL.revokeObjectURL(url); // This line was causing the problem
resolve();
})
.catch(reject)

WebAudio streaming with fetch : DOMException: Unable to decode audio data

I'm trying to play an infinite stream coming from the fetch API using Chrome 51. (a webcam audio stream as Microsoft PCM, 16 bit, mono 11025 Hz)
The code works almost OK with mp3s files, except some glitches, but it does not work at all with wav files for some reason i get "DOMException: Unable to decode audio data"
The code is adapted from this answer Choppy/inaudible playback with chunked audio through Web Audio API
Any idea if its possible to make it work with WAV streams ?
function play(url) {
var context = new (window.AudioContext || window.webkitAudioContext)();
var audioStack = [];
var nextTime = 0;
fetch(url).then(function(response) {
var reader = response.body.getReader();
function read() {
return reader.read().then(({ value, done })=> {
context.decodeAudioData(value.buffer, function(buffer) {
audioStack.push(buffer);
if (audioStack.length) {
scheduleBuffers();
}
}, function(err) {
console.log("err(decodeAudioData): "+err);
});
if (done) {
console.log('done');
return;
}
read()
});
}
read();
})
function scheduleBuffers() {
while ( audioStack.length) {
var buffer = audioStack.shift();
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
if (nextTime == 0)
nextTime = context.currentTime + 0.01; /// add 50ms latency to work well across systems - tune this if you like
source.start(nextTime);
nextTime += source.buffer.duration; // Make the next buffer wait the length of the last buffer before being played
};
}
}
Just use play('/path/to/mp3') to test the code. (the server needs to have CORS enabled, or be on the same domain your run script from)
AudioContext.decodeAudioData just isn't designed to decode partial files; it's intended for "short" (but complete) files. Due to the chunking design of MP3, it sometimes works on MP3 streams, but wouldn't on WAV files. You'll need to implement your own decoder in this case.
Making the wav stream sound correctly implies to add WAV headers to the chunks as Raymond suggested, plus some webaudio magic and paquet ordering checks;
Some cool guys helped me to setup that module to handle just that and it works beautifully on Chrome : https://github.com/revolunet/webaudio-wav-stream-player
Now works on Firefox 57+ with some config flags on : https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/getReader#Browser_compatibility

Offline Ready using Service worker

I built an offline first app using the appcache a while ago and wanted to convert it to using the service-worker (my clients all use the latest chrome so I don't have any browser compatibility issues).
I'm using sw-precache to generate a service-worker that caches my local assets (specifically, my html/css/fonts and also some js) and it looks like when the service-worker installs, it does successfully add all the assets to cache storage and it does successfully start (install and activate both fire and complete successfully. And I have the self.skipWaiting() at the end of the install event to start the service-worker (which it does successfully as well)).
The issue is that the "fetch" event doesn't seem to ever fire. As such, if I go offline or open a browser (while already offline) and navigate to the site, I get the Chrome offline dinosaur. When I look at the network tab, it looks like the browser is trying to hit a server to retrieve the pages. I'm not sure what I'm doing wrong and I didn't touch the fetch method that was generated by the sw-precache utility...so I'm not sure what I'm missing. Any help would be greatly appreciated. My fetch event is below:
self.addEventListener('fetch', function(event) {
if (event.request.method === 'GET') {
var urlWithoutIgnoredParameters = stripIgnoredUrlParameters(event.request.url,
IgnoreUrlParametersMatching);
var cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
var directoryIndex = 'index.html';
if (!cacheName && directoryIndex) {
urlWithoutIgnoredParameters = addDirectoryIndex(urlWithoutIgnoredParameters, directoryIndex);
cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
}
var navigateFallback = '';
// Ideally, this would check for event.request.mode === 'navigate', but that is not widely
// supported yet:
// https://code.google.com/p/chromium/issues/detail?id=540967
// https://bugzilla.mozilla.org/show_bug.cgi?id=1209081
if (!cacheName && navigateFallback && event.request.headers.has('accept') &&
event.request.headers.get('accept').includes('text/html') &&
/* eslint-disable quotes, comma-spacing */
isPathWhitelisted([], event.request.url)) {
/* eslint-enable quotes, comma-spacing */
var navigateFallbackUrl = new URL(navigateFallback, self.location);
cacheName = AbsoluteUrlToCacheName[navigateFallbackUrl.toString()];
}
if (cacheName) {
event.respondWith(
// Rely on the fact that each cache we manage should only have one entry, and return that.
caches.open(cacheName).then(function(cache) {
return cache.keys().then(function(keys) {
return cache.match(keys[0]).then(function(response) {
if (response) {
return response;
}
// If for some reason the response was deleted from the cache,
// raise and exception and fall back to the fetch() triggered in the catch().
throw Error('The cache ' + cacheName + ' is empty.');
});
});
}).catch(function(e) {
console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
return fetch(event.request);
})
);
}
}
});

How to Add a file to Google Drive via Docs Add On using App Script? [duplicate]

This question already has answers here:
Uploading Multiple Files to Google Drive with Google App Script
(5 answers)
Closed 6 years ago.
Here is my scenario. I've created an Add-On for Google Docs that acts as a video toolbox.
A feature I'm trying to add is the ability to record a video using the built in web cam (using videojs-recorder) and then link to that video within the doc. I've got the video part working, but not sure how to get the webm JS Blob converted into a Google Blob so I can create a file on the users Google Drive for sharing and linking.
Just to figure out how this might work this is what I've done so far without any luck.
CLIENT SIDE CODE
//event handler for video recording finish
vidrecorder.on('finishRecord', function()
{
// the blob object contains the recorded data that
// can be downloaded by the user, stored on server etc.
console.log('finished recording: ', vidrecorder.recordedData);
google.script.run.withSuccessHandler(function(){
console.log("winning");
}).saveBlob(vidrecorder.recordedData);
});
SERVER SIDE CODE
function saveBlob(blob) {
Logger.log("Uploaded %s of type %s and size %s.",
blob.name,
blob.size,
blob.type);
}
The errors I get seem to be related to serialization of the blob. But really the exceptions aren't very useful - and just point to some minimized code.
EDIT: Note that there is no FORM object involved here, hence no form POST, and no FileUpload objects, as others have indicated that this might be a duplicate, however it's slightly different in that we are getting a Blob object and need to save it to the server.
Thanks goes to Zig Mandel and Steve Webster who provided some insight from the G+ discussion regarding this.
I finally pieced together enough bits to get this working.
CLIENT CODE
vidrecorder.on('finishRecord', function()
{
// the blob object contains the recorded data that
// can be downloaded by the user, stored on server etc.
console.log('finished recording: ', vidrecorder.recordedData.video);
var blob = vidrecorder.recordedData.video;
var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
b64Blob = reader.result;
google.script.run.withSuccessHandler(function(state){
console.log("winning: ", state);
}).saveB64Blob(b64Blob);
};
});
SERVER CODE
function saveB64Blob(b64Blob) {
var success = { success: false, url: null};
Logger.log("Got blob: %s", b64Blob);
try {
var blob = dataURItoBlob(b64Blob);
Logger.log("GBlob: %s", blob);
var file = DriveApp.createFile(blob);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.COMMENT);
success = { success: true, url: file.getUrl() };
} catch (error) {
Logger.log("Error: %s", error);
}
return success;
}
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = Utilities.base64Decode(dataURI.split(',')[1]);
else
byteString = decodeURI(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
return Utilities.newBlob(byteString, mimeString, "video.webm");
}

Live audio via socket.io 1.0

As from socket.io website
Binary streaming
Starting in 1.0, it's possible to send any blob back and forth: image, audio, video.
I'm now wondering, if this couldn't be the solution for something I'm trying to achieve recently.
I'm actually looking for a way how to broadcast live audio stream from (A - ie, mic input..) to all clients connected to a website of mine. Is something like this possible? I've been messing with WebRTC (https://www.webrtc-experiment.com/) examples but I haven't been able to manage the goal for more than few connected clients.
My idea is about something like getUserMedia or any other audio source (PCM, whatever..) on side A being chopped to chunks and provided to client and played for example by html5 audio element or anything.. I need to make that stream as much realtime as possible, no shout/ice cast services werent fast enough (indeed, they arent solution to my problem, they're meant to be used this way) and I don't really care about the audio quality. Crossplatform compatibility would be awesome.
Is something like that possible? By using socket.io as way how to provide those data to clients?
I would be very grateful for any reference, hint or source that could help me achieve this.
Thanks a lot.
This example shows you how to use the MediaRecorder to upload audio and then forward it using socket.io. This code will only broadcast after you're called mediaRecorder.stop(). You can choose to broadcast inside of ondataavailable. If you do that, you might want to pass a timeslice to mediaRecorder.start(), so that it doesn't trigger ondataavailable so often.
This solution isn't truly live, but I think it will help people who come back and find this question.
Client Code
var constraints = { audio: true };
navigator.mediaDevices.getUserMedia(constraints).then(function(mediaStream) {
var mediaRecorder = new MediaRecorder(mediaStream);
mediaRecorder.onstart = function(e) {
this.chunks = [];
};
mediaRecorder.ondataavailable = function(e) {
this.chunks.push(e.data);
};
mediaRecorder.onstop = function(e) {
var blob = new Blob(this.chunks, { 'type' : 'audio/ogg; codecs=opus' });
socket.emit('radio', blob);
};
// Start recording
mediaRecorder.start();
// Stop recording after 5 seconds and broadcast it to server
setTimeout(function() {
mediaRecorder.stop()
}, 5000);
});
// When the client receives a voice message it will play the sound
socket.on('voice', function(arrayBuffer) {
var blob = new Blob([arrayBuffer], { 'type' : 'audio/ogg; codecs=opus' });
var audio = document.createElement('audio');
audio.src = window.URL.createObjectURL(blob);
audio.play();
});
Server Code
socket.on('radio', function(blob) {
// can choose to broadcast it to whoever you want
socket.broadcast.emit('voice', blob);
});
In the Client Code you can write setInterval() instead of setTimeout() and then recursively call mediaRecorder.start() so that every 5 seconds the blob will be emitted continuously.
setInterval(function() {
mediaRecorder.stop()
mediaRecorder.start()
}, 5000);
Client Code
var constraints = { audio: true };
navigator.mediaDevices.getUserMedia(constraints).then(function(mediaStream) {
var mediaRecorder = new MediaRecorder(mediaStream);
mediaRecorder.onstart = function(e) {
this.chunks = [];
};
mediaRecorder.ondataavailable = function(e) {
this.chunks.push(e.data);
};
mediaRecorder.onstop = function(e) {
var blob = new Blob(this.chunks, { 'type' : 'audio/ogg; codecs=opus' });
socket.emit('radio', blob);
};
// Start recording
mediaRecorder.start();
// Stop recording after 5 seconds and broadcast it to server
setInterval(function() {
mediaRecorder.stop()
mediaRecorder.start()
}, 5000);
});
// When the client receives a voice message it will play the sound
socket.on('voice', function(arrayBuffer) {
var blob = new Blob([arrayBuffer], { 'type' : 'audio/ogg; codecs=opus' });
var audio = document.createElement('audio');
audio.src = window.URL.createObjectURL(blob);
audio.play();
});
Server Code
socket.on('voice', function(blob) {
// can choose to broadcast it to whoever you want
socket.broadcast.emit('voice', blob);
});