How can I get the first frame of a video file in javascript as an image?
It can be done with HTML 5 video and canvas tags:
HTML:
<input type="file" id="file" name="file">
<video id="main-video" controls>
<source type="video/mp4">
</video>
<canvas id="video-canvas"></canvas>
Javascript:
var _CANVAS = document.querySelector("#video-canvas");
var _CTX = _CANVAS.getContext("2d");
var _VIDEO = document.querySelector("#main-video");
document.querySelector("#file").addEventListener('change', function() {
// Object Url as the video source
document.querySelector("#main-video source").setAttribute('src', URL.createObjectURL(document.querySelector("#file").files[0]));
// Load the video and show it
_VIDEO.load();
// Load metadata of the video to get video duration and dimensions
_VIDEO.addEventListener('loadedmetadata', function() {
// Set canvas dimensions same as video dimensions
_CANVAS.width = _VIDEO.videoWidth;
_CANVAS.height = _VIDEO.videoHeight;
});
_VIDEO.addEventListener('canplay', function() {
_CANVAS.style.display = 'inline';
_CTX.drawImage(_VIDEO, 0, 0, _VIDEO.videoWidth, _VIDEO.videoHeight);
});
});
View demo
Just add the video tag to the page with no controls and no auto-play.
<video width="96" height="54" class="clip-thumbnail"> ... </video>
Drawback is that users can play the video by right clicking the thumbnail and selecting "play" in the context-menu. To avoid that you'd need a little javascript listening for click events and cancelling them (removing context-menu from thumbnails).
As mentioned, Javascript is not able to do this.
If you want to create thumbnails for your videos you have to create the thumbnail server side and then simply serve up the image on the client as you would any other image.
My method of choice for accomplishing this is the ffmpeg decoder. It can handle a multitude of file formats and is able to do what you want. So if you have a video named hello.avi, you might do:
ffmpeg -itsoffset -1 -i /path/to/hello.avi -vcodec mjpeg -vframes 1 -an -f rawvideo -s 200x150 /path/to/hello.jpg
You can run this command (fixing the paths and dimensions...) with whatever server-side language you are using and it would create a thumbnail of the video file.
Javascript is not capable of doing this.
It may be possible if the video is a file selected by the user into an <input type="file">, you can get the base-64 video data using the FileReader API:
https://developer.mozilla.org/en-US/docs/DOM/FileReader
From there you're just left with the extremely intractable problem of decoding the video and somehow picking out and rendering a single frame, in javascript. Alternatively, you could just include the entire video as the "thumbnail preview" (I assume that's why you're doing this?), as demonstrated here:
http://hacks.mozilla.org/2009/12/file-drag-and-drop-in-firefox-3-6/
Not sure about the compatability of that last example though, or how well it work with larger video files (I hear you can easily bump up against URL length restrictions)
Related
I am working on an example of ffmpeg-wasm which will accept 3 files as input [song1.mp3, song2.mp3, image.png] and combine them into a video file [cool-output-video.mkv] which I want to be displayed as a playable video for the user in the html webpage.
https://github.com/MartinBarker/ffmpeg-wasm-node
My code is hosted in the above repo, right now I have a placeholder video element, which I want to change to the finished video after the user uploads the 3 files and clicks the render button.
inside server.js, I have the route app.post('/render' , which will render the video using ffmpeg, and return the video using the line res.end(Buffer.from(outputData, 'binary'));
outputData = ffmpeg.FS('readFile', outputFileName);
ffmpeg.FS('unlink', outputFileName);
res.writeHead(200, {
'Content-Type': 'image/png',
'Content-Disposition': `attachment;filename=${outputFileName}`,
'Content-Length': outputData.length
});
//res.end(btoa(Buffer.from(outputData, 'binary')));
//res.end(outputData)
res.end(Buffer.from(outputData, 'binary'));
I can see in my node server console log that the ffmpeg command finished and successfully renders the mkv video file
and inside client.js is where my code recieves the binary buffer for my outputted video file, and tries to make it appear on the webpage by changing the src attribute for the html video element, but no matter what code I try, I cant get the video file to appear on the html page
<video width="320" height="240" controls>
<source id='mp4source' src="" type="video/mp4">
Your browser does not support the video tag.
</video>
...
const mp4source = document.querySelector('#mp4source')
...
console.log('call renderVideo()')
const renderResult = await renderVideo(files);
console.log('renderVideo() finished. renderResult=',renderResult)
mp4source.src = renderResult
I can see in my html file in my chrome webpage devtools console, that data is being returned and gets printed out, I'm just not sure how to handle this string data for my video file to make it appear on the webpage:
renderVideo() finished. renderResult= data:image/png;base64,GkXfo6NChoEBQveBAULygQRC84EIQoKIbWF0cm9za2FCh4EEQoWBAhhTgGcBAAAAC0APWxFNm3TCv4T+wiwATbuLU6uEFUmpZlOsgaFNu4tTq4QWVK5rU6yB8U27jFOrhBJUw2dTrIIB1E27jlOrhBxTu2tTrIQLQA797AEAAAAAAABRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUmpZsu/hLA0O5Qq17GDD0JATYCNTGF2ZjU4LjQ1LjEwMFdBjUxhdmY1OC40NS4xMDBzpJAgTn1WHXX52GUlw+lW90wkRImIQRzeoAAAAAAWVK5rQN2/hEsGuLSuAQAAAAAAAD3XgQFzxYjtk28iSHTlJJyBACK1nIN1bmSGj............. long text was truncated etc
EDIT changed my ffmpeg command to export an mp4 video, changed header to be video/mp4, and verified it is getting added to the html src element but the video still does not appear
solved by reading the comment on this post:
Base64 value is not working with video tag?
had to move src tag from the element to the element instead and now it works!
If playing an MPEG Dash video using dash.js, is it possible to make it loop, i.e. when the video has downloaded fully, replay the downloaded content, without having to download any more data or possibly just raising the resolution if possible, but this is not essential.
The standard code for displaying this is:
<div>
<video id="videoPlayer"></video>
</div>
<script src="https://cdn.dashjs.org/v2.0.0/dash.all.min.js"></script>
<script>
(function(){
var url = "./Videos/146252.mpd";
var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true);
})();
</script>
The video element has a loop attribute. Have you tried setting it?
I'm trying to embed a video.js video player on my website. I'm running a debian 7.5 / chrome / IceWeasel (thunderbird) station with jquery-1.10.2 for the test.
Awaited features
Playing a video from a list index.
I list the video files contained in a directory, setup the hyperlink for each of them as :
<a class="IndexItem" href="#" id="404467" type="video/mime-type" data-video="video-file_name.mp4">My video #1</a>
....
The click event is managed BY the javascript code.
when a video index is clicked, the corresponding source tag is dynamically created and the video player loaded.
Issue
The video is loaded and played succesfully but when I click the player stop button, the progress bar still move forward until the end of the stream is reached.
How can I fixed that ?
Code excerpt
html
<video class="video-js vjs-default-skin" height="480" id="video-player" width="640" poster="/images/bubble-logo.png">
<source src="/path/to/my/video" type="video/mime-type"/>
<h3>Your browser does not support the video tag</h3>
</video>
the video/mime_type can be video/mp4, video/webm or video/ogg
javascript
$(document).ready(function() {
//---------------------
// Edit click event
//---------------------
$.each($(".IndexItem"), function(){
$(this).click(function(event){
event.preventDefault();
// href data-video attribute contains the video file name
// href type attribute contains the video mime-type
var filename = $(this).attr("data-video");
var mime = $(this).attr("type");
// removing previous sources
$('#video-player').empty();
playVideo(filename, mime);
});
});
});
function playVideo(filename, mime) {
// set set html source tag inside the video player div
loadSource(filename, mime);
videojs("video-player", {"controls":true}, function(){
// Player (this) is initialized and ready.
});
}
function loadSource(filename, mime){
var source = $('<source src="/videos/' + filename + '" type="' + mime + '"/>');
$('#video-player').append(source);
var warning = $('<h3>Your browser does not support the video tag</h3>');
$('#video-player').append(warning);
console.log("Creating new source :" + source);
}
Perhaps my method is not the right one as I create a video player instance for each request (Doesn't seem to be really clean ...). Assuming it's not, the problem regarding my issue is still relevant as it raised the first time I try to open a video after having the page reloaded.
Thanks in advance for your help.
Fix
It's an encoding issue of my video files with ffmpeg, frankly I don't remenber what was exactly the problem but now everything works fine !
First thing: to ensure the widest compliance it's advised to encode your original video in three different format :
mp4
webm
ogg (ogv extension)
and then declaring them in the html tags as
<video height="480" id="video-1" width="640" poster="/images/video-1.jpg" controls="true" preload="true"><source src="/videos/video-1.ogv" type="video/ogg"/><source src="/videos/video-1.webm" type="video/webm/><source src="/videos/video-1.mp4" type="video/mp4"/><h3>Your browser does not support the video tag</h3></video>
/videos must be a static web server mapping to /path/to/ (see bellow)
The more difficult thing to do is to encode the video with the correct ffmpeg options. The video bit rate must not exceed the link capacity and the video display must be as smooth as possible.
I found out this settings after several days of testing that finally fit my needs ... and also perharps yours.
I use .mp4 format as input as that's providen by my Contour Camera but ffmpeg should convert any other known format.
To check the available formable type in:
$ ffmpeg -formats
To encode my video with multiple outputs, I run
$ ffmpeg -i camvideo.mp4 -c:v libvpx -c:a libvorbis -s 4cif -b:v 256K -f webm /path/to/video-1.webm
$ ffmpeg -i camvideo.mp4 -c:v libx264 -s 4cif -preset slow -bufsize 4M -maxrate 256K -tune fastdecode,zerolatency -c:a copy -b:a 16K -f mp4 path/to/video-1.mp4
$ ffmpeg -y -i $1 -codec:v libtheora -s 4cif -qscale:v 7 -codec:a libvorbis -qscale:a 3 -f ogg /path/to/video-1.ogv
Note that my environment is Debian 7.5 with ffmpeg 2.2.2 (compiled from sources) and that the result may differ regarding your ffmpeg version ... I think.
Note : the mp4 source is also re-encoded to make it usable by web browser plugins and Be patient .. encoding is a huge CPU task.
Hope that helps.
In a HTML5/JS app (backbone.js / jQuery) i'm having trouble embedding a m3u8 video stream delivered by a wowza media server.
The app is supposed to run in mobile safari only, so the format would be allright.
The video gets embedded with a tag, generated by Javascript.
<video src="http://someotherdomain.net:[customport]/path/playlist.m3u8" controls>
</video>
When embedding the video tag with a test stream-URL in a static test page, the video works perfectly up and down all tested iOS Versions.
The Problem is when generating the tag with JS, the video stalls in loading state.
Desktop Safari however plays the video just fine.
I couldn't figure out what's causing this so far. Could this be some security restriction on the mobile browser?
UPDATE
as this is some closed and rather complex app i'm unfortunately not able to provide a non-working example, but this is the method that renders the video tag into the page:
/**
* add <video> tag and add source
*
* #param src
*/
loadVideoSource: function (src) {
this.$el.find('video').remove();
$('<video/>')
.attr('src', src)
.prop('autoplay', true)
.prop('controls', true)
.css('width', '100%')
.css('height', '60%')
.appendTo(this.$el.find('div.video'));
}
that port does work and renders the following markup:
<video
src="https://somedomain.com.tr:1935/live/12345678/playlist.m3u8"
autoplay
controls
style="width: 100%; height: 60%;"
></video>
i also tried initially having the video tag in the page an just adding the src attribute or <source> child.
I also tried to call load() and play() explicitly on the video element but that didn't help either...
Hi i am posting a small snippet which i always use for video..
videoElement = document.createElement('video'); // this audioelement is used to create an audio tag
$(videoElement )
.attr('id', 'video');
function videoElement_Int_fn(currentPos) {
var src = currentPos+'.mp4';
videoElement .setAttribute('src', src);
videoElement .load();
videoElement .play();
}
ok, after hours of debugging it simply turned out to be a CSS bug :P
Since you have to start a video explicitly in iOS, you'll have to press the play button in the video after loading the markup.
Strangely enough, this was prevented by
-webkit-overflow-scrolling: touch;
...which was used on the parent container of the video, and prevented the native play button of the video element to be clickable.
Huge WTF that I thought was a bug hidden in the semicomplex web app that I'm making, but I have pared it down to the simplest code possible, and it is still replicable in Firefox, Chrome, and Safari, unpredictably but more than 1/2 of the time.
http://jsfiddle.net/cDpV9/7/
var v = $("<video id='v' src='http://ia600401.us.archive.org/18/items/ForrestPlaysTaik/forresto-plays-taik-piano-360.webm' autobuffer='auto' preload autoplay controls></video>");
$("#player").append(v);
Add a video element.
Video starts to load and play.
Video audio sounds like it is doubled.
Pause the visible video, and one audio track continues.
Delete the video element; the ghost audio keeps playing.
Delete the frame, and the ghost audio stops (though once in Firefox it continued to play after closing the window, and didn't stop until quitting Firefox).
Here is a screen capture to maybe show that I'm not completely crazy: http://www.youtube.com/watch?v=hLYrakKagRY
It doesn't seem to happen when making the element with .html() instead of .append(), so that's my only clue: http://jsfiddle.net/cDpV9/6/
$("#player").html("<video id='v' src='http://ia600401.us.archive.org/18/items/ForrestPlaysTaik/forresto-plays-taik-piano-360.webm' autobuffer='auto' preload autoplay controls></video>");
I'm on OS X 10.6.7.
I think that I have it. Even just creating the JQuery object without adding it to the page causes the ghost player to play: http://jsfiddle.net/cDpV9/8/
var v = $("<video id='v' src='http://ia600401.us.archive.org/18/items/ForrestPlaysTaik/forresto-plays-taik-banjo-360.webm' autobuffer='auto' preload autoplay controls></video>");
For now I can work around this by using .html(). I'll report the issue to JQuery.
Maybe jQuery caches the content of $() before appending it to your player div? So there is another instance of the video tag. It could be an error in jQuery. Have you tried this without Jquery/js?
I would try adding the autoplay attribute after you append the video player. This should then instantiate the play function.
That would be something like this:
var v = $("<video id='v' src='videofile.webm' autobuffer='auto' preload controls></video>");
$("#player").append(v);
v.attr('autoplay','autoplay');
When you create elements in JavaScript i.e. image elements, objects etc, they are loaded instantly and stored in memory as objects. That is why you can preload images before you load a page. It is therefore not a jQuery bug.
Ref: http://www.w3.org/TR/html5/video.html#attr-media-autoplay
When present, the user agent (as described in the algorithm described
herein) will automatically begin playback of the media resource as
soon as it can do so without stopping.
I've got the same problem over here. This seems to be an issue with using the "autoplay" attribute on your video markup.
Remove the autoplay attribute
append your video DOMNode to any node
if you want autoplay behavior, call videodomnode.play() - using jquery this would be $('video')[0].play()
You could get the html of #player and append the video your self, then add the total with .html() like this:
var v = $("#player").html() + "<video id='v' src='http://ia600401.us.archive.org/18/items/ForrestPlaysTaik/forresto-plays-taik-piano-360.webm' autobuffer='auto' preload autoplay controls></video>";
$("#player").html(v);
It's not as good as the .append() function, but if the .append() doesn't work, you might be forced to do something like this.
This one worked best in my case:
var v = '<audio hidden name="media"><source src="text.mp3"><\/audio>';
$('#player').append(v);
$("audio")[$('audio').size()-1].play();
I solved mine by loading video after dom loaded:
$(window).bind("load", function() {
var v = $("<video id='bgvid' loop muted>
<source src='/blabla.mp4' type='video/mp4'>
</source>
</video>");
$(".video-container").append(v);
v.attr('autoplay','autoplay');
});