How to capture screenshot from a video playing on different server - html5-canvas

I am trying to capture a screen image from a video, which src is on a different server. So far I got to blank image, which as far as I understand is expectable, because of the tainted canvas or CORS policy error if I give the video tag 'anonymous' crossorgin property. I am using html2canvas, but also tried and with simple canvas context like this:
let ctx = canvas.getContext('2d');
ctx.drawImage( video, 0, 0, canvas.width, canvas.height );
let image = canvas.toDataURL('image/jpeg');
and with html2canvas, trying multiple combinations of options with Vue proxy configurated to the server of the video:
let el = this.$refs.videoPlayer
el.setAttribute('crossOrigin', 'anonymous')
this.output = (
await html2canvas(el, {
allowTaint: true,
useCORS: true,
proxy: '/two',
async: true,
foreignObjectRendering: true,
})
).toDataURL()
Is there any workaround around this? Because I have it working with a local video and now this is quite unfortunate. Is it possible to first download the video on the user system and then play it in order to take the screenshot?
Edit:
The only working workaround I found is to download the video as Blob via axios and then use it, which works perfectly, but uses the user cache for the video.

Related

read image data from web page to a firefox addon insecure?

I'm currently doing a plugin on firefox, which should be very simple. But since I am new on that, some problems have occured:
The purpose is to bind an item on the context menu when user clicking on an image(). I want to manipulate this image a lot(some work like visual encryption) using a canvas, and display the result in a new panel or dialog.
To bind the new item onto the menu, following code is used.
cm.Item({
label: _("menu-label-encrypt"),
context: cm.SelectorContext("img"),
contentScriptFile: [
data.url('jquery.js'),
data.url('encrypt.menu.js'),
],
onMessage: function(cmd){
var cryptWorker = imgcrypt();
cryptWorker.key(cmd.password);
var ret = cryptWorker.encrypt( cmd.width, cmd.height, cmd.data);
console.log(ret.length);
},
});
My idea is to use the contextScriptFile encrypt.menu.js to inject a code into the page, and fetch the canvas data as an array, which will then be posted using self.postMessage to the addon and get processed:
self.on('click', function(node){
var canvasID = 'cache';
var img = $(node)[0];
$('<canvas>', {id: canvasID}).appendTo('body').hide();
var canvas = $('#' + canvasID)[0];
var ctx = canvas.getContext('2d');
ctx.canvas.width = img.width;
ctx.canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
var data = ctx.getImageData(0, 0, img.width, img.height).data,
dataAry = new Array(data.length);
for(var i=0; i<data.length; i++)
dataAry[i] = data[i];
var command = {
'password': 'test',
'width': img.width,
'height': img.height,
'data': dataAry,
};
self.postMessage(command);
});
and now the problem came to my surprise: when I tried this addon on some page hosted at localhost:4000, it works. On some real web page, it shows:
menu.js:14 - SecurityError: The operation is insecure.
I know that this may be caused by a violation of some same-origin policy, but this is a content-script injected by an addon. Is it also impossible to read the image data without some help of a external server, or am I doing something totally wrong?
Thank you.
I've not found any solution to this approach(that being said, to inject a content to get the data from an remotely retrived image). But the net/xhr in mozilla's sdk works at the side of addon well. Alternatively I have wrote a XHR working in main.js and used it as a proxy.
At the side of addon, this XMLHttpRequest have no same-domain restrictions and can be used to retrive the binary data of the image given by a URL. The retrived data can be returned in a form of ArrayBuffer by setting xhr.responseType='arraybuffer';
The URL given to the addon's XHR is simply the src of <IMG>, which is being clicked. The browser seems to be caching the image and using XHR to retrive this image is very fast and doesn't need another request to the server.

PhoneGap camera getPictue - get full image quality

I am making an app where an photo is taken and sent to the server, but the files that get sent are of low quality. It seems that when I take a photo using iOS native camera app it come out as a very sharp picture, but when using PhoneGap it reduces the quality massively.
I have set the image quality to 100 etc:
function capturePhoto() {
// Take picture using device camera and retrieve image as base64-encoded string
navigator.camera.getPicture(onPhotoURISuccess, onPhotoURIFail, {
quality: 100,
destinationType: Camera.DestinationType.FILE_URI,
//destinationType: Camera.DestinationType.DATA_URL,
//destinationType: Camera.DestinationType.NATIVE_URI,
sourceType : Camera.PictureSourceType.CAMERA,
correctOrientation: true,
saveToPhotoAlbum: true,
encodingType: Camera.EncodingType.PNG
});
}
But still seems to give a real blurry image.
Has anyone else had this problem? I have also tried using the DATA_URL to get the base64 encoded image, but this makes the app go so slow that I couldn't get to a stage to send to the server.
Is there a way to use the original LOSSLESS file?
Thanks
function capturePhoto() {
navigator.camera.getPicture(onPhotoURISuccess, onPhotoURIFail, {
quality: 50,
destinationType: Camera.DestinationType.FILE_URI,
sourceType : Camera.PictureSourceType.CAMERA,
correctOrientation: false,
encodingType: Camera.EncodingType.PNG,
targetWidth: 1500,
targetHeight: 1500
});
}
Im not sure why mine wouldn't work before, but it may have been because I was trying it on my iPad? I can't quite remember if I had the same problem with my iPhone also at that time.
Also the way the imageURI is retrieved may have made a difference, not sure about that either, but here is my code to get the image URI:
function onPhotoURISuccess(imageURI){
$('#imagePreview img.smallImage').attr("src", imageURI);
}
And then just get the URI from the image when you want to send. It prety much exactly like all the examples but for some reason didn't work before.
Make sure you have your plugins using the latest version too. That's about it!

Skipper in SailsJS (beta) image resize before upload

I'm using SailsJS (beta). I'm trying to find a way to use graphicsmagick to take the stream parsed by Skipper in SailsJS-beta to resize the image before calling the Skipper-function req.file('inputName').upload().
My goal is to take my large, original image, and resize it before uploading it. Sails beta have introduced the Skipper-file-parser which are poorly documented (at least I don't understand it). Please help me understand how to resize the image before upload.
This works (code in my controller action):
req.file('fileName').upload('storedImage.png', function(err, files){
// File is now uploaded to storedImage.png
});
What I want is something like:
// Read the file stream into a file upload
var stream = req.file('fileName');
gm(stream).resize(200, 200).write('storedImage.png', function(err){
// File is now resized to 200x200 px and uploaded to storedImage.png
});
My problem is: how do I properly fetch the stream from req.file('fileName') to send it to gm?
This should work for you:
var Writable = require('stream').Writable;
var resize = require('image-resize-stream')(100); // Or any other resizer
// The output stream to pipe to
var output = require('fs').createWriteStream('storedImage.png');
// Let's create a custom receiver
var receiver = new Writable({objectMode: true});
receiver._write = function(file, enc, cb) {
file.pipe(resize).pipe(output);
cb();
};
Now in your action you just have to use your receiver:
req.file('fileName').upload(receiver, function(err, files){
// File is now resized to 100px width and uploaded to ./storedImage.png
});
I have a feeling that Skipper's API is going to change, a lot, but this will work for now (with v0.1.x).
UPDATE
Specifically, if using gm for resizing, it'll be something like this:
var gm = require('gm');
var Writable = require('stream').Writable;
// The output stream to pipe to
var output = require('fs').createWriteStream('storedImage.png');
// Let's create a custom receiver
var receiver = new Writable({objectMode: true});
receiver._write = function(file, enc, cb) {
gm(file).resize('200', '200').stream().pipe(output);
cb();
};
I had problems with #bredikhin solution so I dig deeper into this and found this thread very helpful: Uploading files using Skipper with Sails.js v0.10 - how to retrieve new file name
I just changed one line of his Uploader:
[...]
file.pipe(outputs);
[...]
into:
gm(file).resize(200, 200).stream().pipe(outputs);
and this does the trick.
I wrote this answer because it may be helpful for someone.

mediaelement.js multi video gallery flash fallback

Does anyone have a URL of a successful multivideo gallery using mediaelement.js where the same instance of MediaElementPlayer is reused and have it working with the Flash fallback for IE8 & 7?
I have partial success changing the setSrc as a function after the new MediaElementPlayer is created for the first time. This is robust for the HTML5 component but failing for the flash fallback. setSrc is consoling as not available in IE8 & 7. It fails to recognise the object.
Moving the setSrc to the "success" part of the new MediaElementPlayer does load the Flash fallback and HTML5 video as expected. On attempting to change the source of the player I have attempted to "destroy" and recreate the MediaElementPlayer object on the fly without success. I have not declared player using var=player so reasonably have expected to delete it but without success:
player = false;
delete player;
//make a new instance of the mediaelement video player
player = new MediaElementPlayer('#videoPlayer', {
pluginPath: ''+basePath+'_Includes/JS/',
success: function (player, node) {
//set the size (for flash otherwise no video just sound!)
if($("#rg-gallery.smallGallery").length > 0){
player.setVideoSize(400, 225);
} else{
player.setVideoSize(640, 360);
}
player.setSrc([
{ src: mp4, type: 'video/mp4' },
{ src: webm, type: 'video/webm' }
]);
player.load();
player.pause();
//if the video plays stop the slideshow
player.addEventListener('play', function () {
videoPlaying.push('playing');
stopSlideshow();
}, false);
}
});
Research both here and on the web shows that others are attempting to try this type of dynamic gallery but I am unable to find an example that shows it as technically viable.
Any example URLs where someone's cracked it would be lovely. Thanks :)
Solved.
Created a JS variable of video code:
var playerView = '<video width="640" height="360" id="videoPlayer" class="video-js vjs-default-skin" poster="" controls preload="auto"><source id="mp4" type="video/mp4" src="" /><source id="webm" type="video/webm" src="" /></video>';
On initialization removed any DOM rendering of any existing player, set it to false and deleted it:
//remove any existing video player
$(".mejs-container").remove();
player = false;
delete player;
Appended new video view to DOM:
//add a new one
$(".rg-video").append(playerView);
Created new instance of player and set src and load as normal:
//make a new instance of the mediaelement video player
player = new MediaElementPlayer('#videoPlayer', { ...
HTML5 video and Flash video fallback for IE8 & 7 now working as part of dynamic mixed media gallery.
Off for tea and medals.
:)

Images turning sideways/upside down after being uploaded via PhoneGap (iOS)

Not sure what would be causing this, but when I upload some images to my remote server via FileTransfer(), the images sometimes show up either sideways or upside down. However, when I view the images locally on the iPhone, they are positioned in the correct way.
For example, when I select an image like this to upload: http://sharefa.st/view/WBe2QNSK8r8z
It will turn out like this: http://sharefa.st/view/EWdW1Z4G8r8z
I am using the local path to transfer the file, so I don't understand why the image would rotate "randomly".
Here is my upload function:
function uploadPhoto() {
var options = new FileUploadOptions();
options.fileKey = 'file';
options.fileName = imgURI.substr(imgURI.lastIndexOf('/')+1);
options.mimeType = 'image/jpeg';
var params = new Object();
if(logged_in == true) {
params.unique_id = app_unique_id;
params.secret_key = user_secret_key;
}
options.params = params;
loadingStart();
var ft = new FileTransfer();
ft.upload(imgURI, 'http://' + remote_server + '/API/upload', uploadDetails, fail, options);
}
imgURI value looks like this:
file://localhost/var/mobile/Applications/<snip>/tmp/photo_015.jpg
Any insight is appreciated.
Thanks to Humanoidism pointing out that the issue was in fact with the iPhone, and the way it stored images, I was able to figure out a solutuion.
To upload photos in the correct orientation you must add the correctOrientation setting to the options array in getPicture(), and set it to true.
Here are two examples:
function capturePhoto() {
navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 30, correctOrientation: true });
}
function getPhoto(source) {
navigator.camera.getPicture(onPhotoURISuccess, onFail, { quality: 30,
destinationType: destinationType.FILE_URI,
sourceType: source,
correctOrientation: true });
}
The problem is not PhoneGap but iPhone. The iPhone was designed to be used as a wide lens camera. Turn the phone sideways when taking pictures or capturing video if you intend to view them on desktop. Your phone will display them correctly, because it "knows" how you took them, but the computer that you're viewing it on dosen't.
What you could do to prevent this is to rotate the picture before the upload. This is not a recommended fix but at least people on desktop computers will be able to see it. Though when viewing them on iPhone they'll be rotated - maybe a check for mobile devices wether or not to rotate the image could come in handy - but still again, not recommended.

Resources