74

I'm wondering if there's any straightforward way to achieve this effect, without needing backend code to extract a frame, save it as a jpg and database it somewhere.

An effect whereby the first frame of the video just shows up as the poster when the video loads would be so helpful (it would only work if the browser can play back the video obviously, which might be a little different from how poster traditionally works, but that is not a concern.

Damon
  • 10,493
  • 16
  • 86
  • 144

8 Answers8

101

Did you try the following?

just append time in seconds #t={seconds} to source URL:

  <video controls width="360">
    <source src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4#t=0.1" type="video/mp4" />
  </video>

I have chosen a fraction of second (0.1) to keep number of frames small, because I have the suspect that if you put 1 second, it would "preload" the first 1 second of video (i.e. 24 frames or more ....). Just in case ...

Works fine on Chrome and Firefox on desktop :)
Works not on Android mobile, though :(
I did not test on iOS, iPhone, IE yet ??

Edit May 2021:

I realized that many modern browsers now show automatically a poster of first frame.

Seems like they heard us :-)

Jürgen Fink
  • 3,162
  • 2
  • 23
  • 25
  • Thank you very much, that's great. Works for me on iOS Safari, iOS Firefox, macOS Safari, macOS Firefox, macOS Chrome. – David Jul 05 '18 at 08:10
  • 5
    Should be accepted answer. No heavy library required with this simple hack. – Rubenxfd Dec 03 '18 at 13:03
  • 2
    Important is that the parameter preload is not set to `none` either its not given and therewith the default value `auto` or manually set to `preload="auto"`. – wittich Feb 25 '19 at 09:56
  • 2
    This significantly increased page load time for me even even small `t` value. – zerohedge May 16 '19 at 23:21
  • 1
    This apparently is called a "media fragment" https://blog.addpipe.com/10-advanced-features-in-html5-video-player/ – Dmitry Minkovsky Oct 15 '19 at 21:46
  • ... this worked for the first say 90 or so videos linking to, but after that the browser just kind of gave up on going in 1 second. – MeSo2 Jun 02 '21 at 03:05
  • The problem with this approach is performance. If you show first frames of many videos, the page becomes slow. – Ville Dec 20 '22 at 22:45
42

To make it simple you can just add preload="metadata" to your video tag and the second of the first frame #t=0.5 to your video source:

<video width="400" controls="controls" preload="metadata">
  <source src="https://www.w3schools.com/html/mov_bbb.mp4#t=0.5" type="video/mp4">
</video>

Best of luck!

coinhive
  • 451
  • 4
  • 3
24

There is a Popcorn.js plugin called Popcorn.capture which will allow you to create posters from any frame of your HTML5 video.

There is a limitation that is imposed by the browser that prohibits reading pixel data of resources requested across domains (using the canvas API to record the current value of a frame). The source video must be hosted on the same domain as the script and html page that is requesting it for this approach to work.

The code to create poster using this plugin is quite simple:

// This block of code must be run _after_ the DOM is ready
// This will capture the frame at the 10th second and create a poster
var video = Popcorn( "#video-id" );

// Once the video has loaded into memory, we can capture the poster
video.listen( "canplayall", function() {

  this.currentTime( 10 ).capture();

});
Rick
  • 1,391
  • 8
  • 11
  • looks like what I need. Can't get popcorn itself to work unfortunately.. getting `Uncaught Error: INVALID_STATE_ERR: DOM Exception 11 popcorn.js:389 Popcorn.extend.Popcorn.forEach.ret popcorn.js:389` whenever I call it. – Damon Sep 06 '11 at 17:39
  • Looks like you're not waiting for the video to be loaded. I've updated the code example above to wait then set the poster. – Rick Sep 06 '11 at 18:07
  • 1
    thanks.. that got it working. for the most part anyway.. i was under the impression from the docs that `this.capture({at: 1}).currentTime(0);` would grab a poster from 1 second in and then put the video to the beginning, but it leaves it at 1 second in. Do you have any experience with that? – Damon Sep 07 '11 at 12:48
  • Actually, that sounds like a bug to me :) Can you file an issue on the github repo – Rick Sep 08 '11 at 02:14
  • 3
    I just let it start playing and stop at the first frame loaded – Pencilcheck Nov 05 '14 at 23:30
  • what is "#video-id" in Popcorn( "#video-id" ); ? , i have a link to video on my host and using jwplayer to show my videos! how can i use this api? tnx – Saeid Dec 08 '14 at 06:14
18

I recently did this for a recent project that works on desktop and mobile. The trick was getting it to work on iPhone.

Setting preload=metadata works on desktop and android devices but not iPhone.

For iPhones I had to set it to autoplay so the poster image automatically appears on initial load. iPhones will prevent the video from auto playing, but the poster image appears as the result.

I had to do a check for iPhone using Pavan's answer found here. Detect iPhone Browser. Then use the proper video tag with or without autoplay depending on the device.

var agent = navigator.userAgent;
var isIphone = ((agent.indexOf('iPhone') != -1) || (agent.indexOf('iPod') != -1)) ;

$videoTag = "";
if(isIphone()) {
    $videoTag = '<video controls autoplay preload="metadata">';
} else {
    $videoTag = '<video controls preload="metadata">';
}
Community
  • 1
  • 1
drumnbace
  • 211
  • 2
  • 3
  • 1
    This answer explains why the issue happens and provides a work around. Bear in mind that in iOS 10 the autoplay attribute may be honored if some conditions are met. So I'd suggest everyone to immediately pause the video to prevent actual autoplay. – Alessandro Vendruscolo Sep 12 '16 at 09:41
  • @Alessandro can you show some code how we can handle this in ios 10 it would be great Thanks – Mourice Oct 04 '16 at 00:22
  • 5
    something like `var video = document.createElement('video'); video.controls = true; video.preload = 'metadata'; if (isIphone) { video.autoplay = true; video.pause(); }` – Alessandro Vendruscolo Oct 06 '16 at 13:11
  • you save my day man. you worth like a 50 point on that for me :) – pery mimon Sep 04 '17 at 09:26
  • 2
    Do not use this method unless you actually want autoplay, as iOS now supports autoplay inline for muted videos. – Michael Giovanni Pumo Mar 16 '18 at 14:16
  • but doing so will eventually buffer the entire video and consume bandwitch? at least that is what `element.play().then(()=> element.pause());` does – MarcinL Oct 29 '19 at 14:41
8

You can set preload='auto' on the video element to load the first frame of the video automatically.

James Westgate
  • 11,306
  • 8
  • 61
  • 68
  • 2
    Just a word of advice: preload means the video might be downloaded automatically without user action. It is what happens by default on Chrome, Firefox and IE on desktop. So please be sure it won't worsen user experience on your page. – Loïc Lopes Apr 27 '17 at 10:27
  • 1
    Good point although this is the case only on desktop as this is a hint, not a directive. Somewhat out of date but see https://www.stevesouders.com/blog/2013/04/12/html5-video-preload/ – James Westgate May 03 '17 at 14:13
  • It's working in my case where the vídeo have only 8 seconds to play when mouse over! – WesleyMacente Oct 08 '21 at 19:04
5

Solution for #2, #3 etc. frames. We need attach disposable handler .one() for resetting default frame.

<video width="300" height="150" id='my-video'>
   <source src="testvideo.mp4#t=2" type="video/mp4" />
</video>

$(function () {
     let videoJs = videojs('my-video');
     videoJs.one('play', function () {
          this.currentTime(0);
     });
});
Adilet M.
  • 119
  • 1
  • 5
3

I found a great way to dynamically add poster to a video!

To show the desired frame from video (in my case it's the frame at 1.75 seconds) - add preload="metadata" to the video element and #t=1.75 to the end of source URL.

Add eventListener to the video element that will listen for play event only once. Once the event is emitted reset the video time.

<video width="100%" controls="controls" preload="metadata" id="myVid">
    <source src="path/to/your/video#t=1.75" type="video/mp4">
</video>

<script>
    var el = document.getElementById('myVid');
    el.addEventListener('play', () => {
        el.currentTime = 0;
    }, { once: true });
</script>
1

For others, who wants to specify image as poster, use poster attribute:

<video src="/path/to/video.mp4" poster="/path/to/poster.png"></video>
Jurakin
  • 832
  • 1
  • 5
  • 19