playing html5 video from in memory source - javascript

I am retrieving multiple encrypted data with the help of some ajax queries, and performing some manipulation to transform all theses encrypted chunks into a valid video. And now that I have the binary of the video in memory, I am stuck. How can I display it ?
Just to be sure, I have replicated all theses steps on the server side, and the final output is really a playable video. So my only problem is to display my javascript binary object as a video.
I am doing my best to use only web technologies (html5, video tag, javascript) and I would like to avoid developing my own custom player in flash, which is my very last solution.
if you have an idea, I'm interested. For my part, I am out of imagination.

Here's a quick example that just uses a file input instead of the AJAX you'd normally be using. Note that the first input is wired up to a function that will read the file and return a dataURL for it.
However, since you don't have a fileObject, but instead have a stream of data that represents the contents of the file, you can't use this method. So, I've included a second input, which is wired up to a function that just loads the file as a binary string. This string is then base64 encoded 'manually' with a browser function, before being turned into a dataURL. To do this,you need to know what type of file you're dealing with in order to construct the URL correctly.
It's fairly slow to load even on this laptop i7 and probably sucks memory like no-one's business - mobile phones will likely fall over in a stupor (I haven't tested with one)
You should be able to get your data-stream and continue on from the point where I have the raw data (var rawResult = evt.target.result;)
Error checking is left as an exercise for the reader.
<!DOCTYPE html>
<html>
<head>
<script>
"use strict";
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function byId(id,parent){return (parent == undefined ? document : parent).getElementById(id);}
// callback gets data via the .target.result field of the param passed to it.
function loadFileObject(fileObj, loadedCallback)
{
var reader = new FileReader();
reader.onload = loadedCallback;
reader.readAsDataURL( fileObj );
}
// callback gets data via the .target.result field of the param passed to it.
function loadFileAsBinary(fileObj, loadedCallback)
{
var reader = new FileReader();
reader.onload = loadedCallback;
reader.readAsBinaryString( fileObj );
}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded()
{
byId('fileInput1').addEventListener('change', onFileInput1Changed, false);
byId('fileInput2').addEventListener('change', onFileInput2Changed, false);
}
function onFileInput1Changed(evt)
{
if (this.files.length != 0)
{
var curFile = this.files[0];
loadFileObject(curFile, onVideoFileReadAsURL);
function onVideoFileReadAsURL(evt)
{
byId('vidTgt').src = evt.target.result;
byId('vidTgt').play();
}
}
}
function onFileInput2Changed(evt)
{
if (this.files.length != 0)
{
var curFile = this.files[0];
loadFileAsBinary(curFile, onVideoFileReadAsBinary);
function onVideoFileReadAsBinary(evt)
{
var rawResult = evt.target.result;
var b64Result = btoa(rawResult);
var prefaceString = "data:" + curFile.type + ";base64,";
// byId('vidTgt').src = "data:video/mp4;base64," + b64Result;
byId('vidTgt').src = prefaceString + b64Result;
byId('vidTgt').play();
}
}
}
</script>
<style>
</style>
</head>
<body>
<input type='file' id='fileInput1'/>
<input type='file' id='fileInput2'/>
<video id='vidTgt' src='vid/The Running Man.mp4'/>
</body>
</html>

To display your video you would need to get an URL for it so that you are able to pass a reference to the video element.
There is URL.createObjectURL which should provide you with such an URL to refer to your data. See https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL for further explanations and mind the compatibility table.
Mozilla hosts an example at https://developer.mozilla.org/samples/domref/file-click-demo.html which displays local files. It should be possible to adapt this to setting the video element's src property instead. Depending on how you store your data, it should be possible to play your video this way.
I tried it in Firefox for data from a File object which left me with a URL blob:https://developer.mozilla.org/ed2e4f2f-57a6-4b06-8d56-d0a1a47a9ffd that I could use to play a video.

Related

which function should I use? .toDataURL() or .toBlob()?

I want to resize image at client side using JavaScript. I found 2 solutions, one is using .toDataURL() function and the other is using .toBlob() function. Both solutions are worked. Well, I just curious what is the difference between those two functions? which one is better? or when should I use .toDataURL() function or .toBlob() function?
Here is the code I used to output those two function, and I got very slightly different in image size (bytes) and image color (I'm not sure about this one). Is something wrong with the code?
<html>
<head>
<title>Php code compress the image</title>
</head>
<body>
<input id="file" type="file" onchange="fileInfo();"><br>
<div>
<h3>Original Image</h3>
<img id="source_image"/>
<button onclick="resizeImage()">Resize Image</button>
<button onclick="compressImage()">Compress Image</button>
<h1 id="source_image_size"></h1>
</div>
<div>
<h3>Resized Image</h3>
<h1> image from dataURL function </h1>
<img id="result_resize_image_dataURL"/>
<h1> image from toBlob function </h1>
<img id="result_resize_image_toBlob"/>
</div>
<div>
<fieldset>
<legend>Console output</legend>
<div id='console_out'></div>
</fieldset>
</div>
<script>
//Console logging (html)
if (!window.console)
console = {};
var console_out = document.getElementById('console_out');
var output_format = "jpg";
console.log = function(message) {
console_out.innerHTML += message + '<br />';
console_out.scrollTop = console_out.scrollHeight;
}
var encodeButton = document.getElementById('jpeg_encode_button');
var encodeQuality = document.getElementById('jpeg_encode_quality');
function fileInfo(){
var preview = document.getElementById('source_image');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function(e) {
preview.src = e.target.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
function resizeImage() {
var loadedData = document.getElementById('source_image');
var result_image = document.getElementById('result_resize_image_dataURL');
var cvs = document.createElement('canvas'),ctx;
cvs.width = Math.round(loadedData.width/4);
cvs.height = Math.round(loadedData.height/4);
var ctx = cvs.getContext("2d").drawImage(loadedData, 0, 0, cvs.width, cvs.height);
cvs.toBlob(function(blob) {
var newImg = document.getElementById('result_resize_image_toBlob'),
url = URL.createObjectURL(blob);
newImg.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(url);
};
newImg.src = url;
console.log(blob.size/1024);
}, 'image/jpeg', 0.92);
var newImageData = cvs.toDataURL('image/jpeg', 0.92);
var result_image_obj = new Image();
result_image_obj.src = newImageData;
result_image.src = result_image_obj.src;
var head = 'data:image/png;base64,';
var imgFileSize = ((newImageData.length - head.length)*3/4)/1024;
console.log(imgFileSize);
}
Edited:
Based on Result of html5 Canvas getImageData or toDataURL - Which takes up more memory?, its said that
"DataURL (BASE64) is imageData compressed to JPG or PNG, then converted to string, and this string is larger by 37% (info) than BLOB."
What is the string mean? is it same as size in bytes? using the code I provided above, the size difference is less than 1Kb (less than 1%). is .toBlob() always better than .toDataURL() function? or there is a specific condition where it would be better to use .toDataURL() function?
Definitely go with toBlob().
toDataURL is really just an early error in the specs, and had it been defined a few months later, it certainly wouldn't be here anymore, since we can do the same but better using toBlob.
toDataURL() is synchronous and will block your UI while doing the different operations
move the bitmap from GPU to CPU
conversion to the image format
conversion to base64 string
toBlob() on the other hand will only do the first bullet synchronously, but will do the conversion to image format in a non blocking manner. Also, it will simply not do the third bullet.
So in raw operations, this means toBlob() does less, in a better way.
toDataURL result takes way more memory than toBlob's.
The data-URL returned by toDataURL is an USVString, which contains the full binary data compressed in base64.
As the quote in your question said, base64 encoding in itself means that the binary data will now be ~37% bigger. But here it's not only encoded to base64, it is stored using UTF-16 encoding, which means that every ascii character will take twice the memory needed by raw ascii text, and we jump to a 174% bigger file than its original binary data...
And it's not over... Every time you'll use this string somewhere, e.g as the src of a DOM element*, or when sending it through a network request, this string can get reassigned in memory once again.
*Though modern browsers seem to have mechanism to handle this exact case
You (almost) never need a data-url.
Everything you can do with a data-url, you can do the same better with a Blob and a Blob-URI.
To display, or locally link to the binary data (e.g for user to download it), use a Blob-URI, using the URL.createObjectURL() method.
It is just a pointer to the binary data held in memory that the Blob itself points to. This means you can duplicate the blob-URI as many times as you wish, it will always be a ~100 chars UTF-16 string, and the binary data will not move.
If you wish to send the binary data to a server, send it as binary directly, or through a multipart/formdata request.
If you wish to save it locally, then use IndexedDB, which is able to save binary files. Storing binary files in LocalStorage is a very bad idea as the Storage object has to be loaded at every page-load.
The only case you may need data-urls is if you wish to create standalone documents that need to embed binary data, accessible after the current document is dead. But even then, to create a data-url version of the image from your canvas, use a FileReader to which you'd pass the Blob returned by canvas.toBlob(). That would make for a complete asynchronous conversion, avoiding your UI to be blocked for no good reasons.

Web Development Displaying Local Files

Based on research, I've found tutorials, such as this one from Eric Bidelman, that go into FileReader usage. Instead of local files, these tutorials use files provided by the user. Modifying these examples to try and display local files were not successful.
After some research I've found that most browsers by default do not allow access to local files [1]. Some recommend using unsafe modes for testing, but that's not what I'm looking for as that would apply to only my testing [2].
Currently I allow the download of log files. My goal here with FileReader was to provide a way to view the files as well. Is there a way to achieve this? I'm coming up with blanks and have almost resigned to only allowing downloads instead of adding viewing. The following is example code of local file reading. This code fails since 'logpath' is not of type blob. I believe my question doesn't really require code, but I provided an example anyway.
HTML Code
<select name="loglist" id="loglist" onchange="run()" size="2">
<option>stack1.log</option>
<option>stack2.log</option>
</select>
Javascript
function run() {
var reader = new FileReader();
log = document.getElementById( "loglist" ).value;
var logdir = "/var/log/";
var logpath = logdir.concat(log);
reader.onload = function() {
logtext = reader.result;
alert(logtext);
}
reader.readAsText(logpath);
}
There is no way for JavaScript, embedded in a webpage, to access files on the user's local system other than through the File API when the user selects the file using a file input. It is not possible for the page to supply the file name to be opened.
var inp = document.querySelector("input");
inp.addEventListener("change", show);
function show(e) {
var f = this.files[0];
var r = new FileReader();
r.addEventListener("load", display);
r.readAsText(f);
function display(e) {
document.body.appendChild(
document.createTextNode(
e.target.result
)
);
}
}
<input type="file" id="input">

Issue with stock Browser picking photos from Gallery

I am working on a web page for uploading photos from a mobile device, using the <input type="file" accept="image/*"/> tag. This works beautifully on iphone and on chrome on the android, but where we are running into issues is with the stock android browser.
The issue arises when you select a file from your gallery (it works fine when you use the camera to take a photo). And we have narrowed it down even further to seeing that the data MIME type isn't available when taken from the gallery on the stock browser (the photos below show the first 100 characters of the data URL being loaded. The goal was to force JPEG, but without the MIME type we cannot know for sure how to fix this. See code below for how the images are being rendered.
How can an image be rendered without the type? Better yet, does anybody know why the type is not available on the stock android browser?
EDIT
Firstly, these are not the same image, they were taken near the same time, and that's not the issue, that's why the data is different (The MIME type doesn't appear on any images on the stock browser, so that's not the problem.
Update
I confirmed that the MIME type is the issue by inserting image/jpeg into the stock browser where it is on chrome. Unfortunately, we have no way of guaranteeing that it's going to be jpeg, so we again really can't do it that way
_readInputFile: function (file, index) {
var w = this, o = this.options;
try {
var fileReader = new FileReader();
fileReader.onerror = function (event) {
alert(w._translate("There was a problem opening the selected file. For mobile devices, some files created by third-party applications (those that did not ship with the device) may not be standard and cannot be used."))
$('#loadingDots').remove();
return false;
}
fileReader.onload = function (event) {
var data = event.target.result;
//alert(data.substring(0,100));
//var mimeType = data.split(":")[1].split(";")[0];
alert("Load Image"); //I get to this point
$('#' + w.disp.idPrefix + 'hiddenImages').append($('<img />', {
src: data,
id: "dummyImg" + index,
load: function(){
var width = dummy.width();
var height = dummy.height();
$('#dummyImg' + index).remove();
alert("Render"); // I don't get here
var resized = w._resizeAndRenderImage(data, null, null, biOSBugFixRequired, skewRatio, width, height);
alert("Image Rendered"); // I don't get here
}
}));
}
fileReader.readAsDataURL(file);
}
catch (e) {
}
}
Chrome
Stock Browser
Since the issue is probably browser-related, and you can't really fix the browser(you could report a bug to Google though), I'd suggest taking a different path.
Have a look Here:
In Node.js, given a URL, how do I check whether its a jpg/png/gif?
See the comments of the accepted answer, which suggests a method to check the file type using the file stream. I'm pretty sure this would work on browser-implemented Javascript and not only in Node.js.

Read /Process image through javascript by passing its URL - Similar to file_get_contents in PHP?

UPDATE:
For testing purposes, I used
<input type="text" onClick="doProcess(http://www.abc.com/chart.png)" />
That didn't work (used alert to test if url was being passed. The alert box did show up with the url, but decoding failed). In a sense, I answered a part of my question myself. Any ideas on how to read an image file through javascript?tt
Just found that this is HTML 5 related code. Also I figured since I'm passing a url instead of list of files, I'll need to remove the for() loop from the doProcess() function which would be
function doProcess(f)
{
var o=[];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
gCtx.clearRect(0, 0, gCanvas.width, gCanvas.height);
qrcode.decode(e.target.result);
};
})(f);
reader.readAsDataURL(f);
}
But this doesn't work too :(
ORIGINAL POST
I have an input file to which a QR image file is supplied with.
<input type="file" onchange="doProcess(this.files)">
doProcess function
function doProcess(f)
{
var o=[];
for(var i =0;i<f.length;i++)
{
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
gCtx.clearRect(0, 0, gCanvas.width, gCanvas.height);
qrcode.decode(e.target.result);
};
})(f[i]);
reader.readAsDataURL(f[i]);
}
}
This function works perfectly. No problems there. The purpose of this function is to decode a QR image. Now out of sheer interest and curiosity, I'm planning to do something different - remove the need to browse to the image file manually. This brings me to a few queries:
Is it possible for doProcess(f) to perform the same actions when supplied with an image URL instead of a file? At present f parameter is a file. What will happen if f parameter is a URL (for ex: doProcess(http://www.abc.com/abc.jpg))? I realize there are other support functions inside doProcess() also being called to complete the decoding process but I'm assuming those will act accordingly to the type of data being passed through doProcess.
If f parameter is an image file (browsed on local computer and selected) instead of image URL, what data type will f be? I'm guessing it will be an array or in some raw binary form.
My intention is to study the process and know what exactly is happening behind the scenes. By using the 'browse' function, the QR image is being decoded successfully.
In summary, what will happen if I pass the location of the image stored as the argument instead of a file?
Thanks in advance.
There is some problems that I don't know you have or not in here because it's not your full code and I don't know what exactly you are trying to do. Here a working example of something very close of what you are doing. It loads a image on a canvas (you could use your qrcode stuff instead)
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var gCanvas;
var gCtx;
function load(){
console.log("loaded");
gCanvas = document.getElementById("mcanvas");
if (gCanvas.getContext){
gCtx = gCanvas.getContext("2d");
} else console.log("no Canvas?");
}
function doProcess(f){
var o=[];
var reader = new FileReader();
reader.onload = (function(theFile) {
var img = new Image();
img.src = theFile;
img.onload = function(){
gCtx.clearRect(0, 0, gCanvas.width, gCanvas.height);
gCtx.drawImage(img,0,0);
}
return;
})(f);
console.log(reader);
reader.readAsDataURL(f);
}
</script>
</head>
<body onload="load()">
<input type="text" onClick="doProcess('https://www.google.com.br/logos/2012/clara_schuman-2012-hp.jpg')" />
<canvas id="mcanvas" height="500" width="500"></canvas>
</body>
</html>
dont't forget the load function, it grants your javascript to run after body loads.

Reading client side text file using Javascript

I want to read a file (on the client side) and get the content in an array. It will be just one file. I have the following and it doesn't work. 'query_list' is a textarea where I want to display the content of the file.
<input type="file" id="file" name="file" enctype="multipart/form-data"/>
<script>
document.getElementById('file').addEventListener('change', readFile, false);
function readFile (evt) {
var files = evt.target.files;
var file = files[0];
var fh = fopen(file, 0);
var str = "";
document.getElementById('query_list').textContent = str;
if(fh!=-1) {
length = flength(fh);
str = fread(fh, length);
fclose(fh);
}
document.getElementById('query_list').textContent = str;
}
</script>
How should I go about it? Eventually I want to loop over the array and run some SQL queries.
If you want to read files on the client using HTML5's FileReader, you must use Firefox, Chrome or IE 10+. If that is true, the following example reads a text file on the client.
your example attempts to use fopen that I have never heard of (on the client)
http://jsfiddle.net/k3j48zmt/
document.getElementById('file').addEventListener('change', readFile, false);
function readFile (evt) {
var files = evt.target.files;
var file = files[0];
var reader = new FileReader();
reader.onload = function(event) {
console.log(event.target.result);
}
reader.readAsText(file)
}
For IE<10 support you need to look into using an ActiveX Object like ADO.Stream Scripting.FileSystemObject http://msdn.microsoft.com/en-us/library/2z9ffy99(v=vs.85).aspx but you'll run into a security problem. If you run IE allowing all ActiveX objects (for your website), it should work.
There is such thing as HTML5 File API to access local files picked by user, without uploading them anywhere.
It is quite new feature, but supported by most of modern browsers.
I strongly recommend to check out this great article to see, how you can use it.
There is one problem with this, you can't read big files (~400 MB and larger) because straight forward FileAPI functions attempting to load entire file into memory.
If you need to read big files, or search something there, or navigate by line index check my LineNavigator, which allows you to read, navigate and search in files of any size. Try it in jsFiddle! It is super easy to use:
var navigator = new FileNavigator(file);
navigator.readSomeLines(0, function linesReadHandler(err, index, lines, eof, progress) {
// Some error
if (err) return;
// Process this line bucket
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
// Do something with it
}
// End of file
if (eof) return;
// Continue reading
navigator.readSomeLines(index + lines.length, linesReadHandler);
});
Well I got beat to the answer but its different:
<input type="file" id="fi" />
<button onclick="handleFile(); return false;">click</button>
function handleFile() {
var preview = document.getElementById('prv')
var file = document.getElementById('fi').files[0];
var div = document.body.appendChild(document.createElement("div"));
div.innerHTML = file.getAsText("utf-8");
}
This will work in FF 3.5 - 3.6, and that's it. FF 4 and WebKit you need to use the FileReader as mentioned by Juan Mendes.
For IE you may find a Flash solution.
I work there, but still wanted to contribute because it works well: You can use the filepicker.io read api to do exactly this. You can pass in an dom element and get the contents back, for text or binary data, even in IE8+

Categories

Resources