How to get HTML5 video thumbnail without using poster on safari or iOS?

12,574

Solution 1

simply add preload="metadata" in video tag and set #t=0.1 at url src, it will get the frame of 0.1s of your video as thumbnail

however, the disadvantage of this solution is when you click to play the video, it always start at 0.1s

<video preload="metadata" controls>
    <source src="video.mp4#t=0.1" type="video/mp4">
</video>

Solution 2

If you want to do this without storing server side images it is possible, though a bit clunky... uses a canvas to record the first frame of the video and then overlay that over the video element. It may be possible to use the URL for the Canvas as a source for the Poster (eg video.poster = c.toDataURL();) but that requires correct CORS setup (not sure if the video was on your own server, and if you have control over that, so took the safest option). This will work best if video is correctly encoded for streaming (so MOOV atom is at the front of the video, otherwise it will be slow to draw the overlay - see this answer for more detail)

The HEAD contains styling for the video and the overlay (you will need to adjust sizing and position to suit your application)

<head>
  <style>
    video_box{
      float:left;
    }
    #video_overlays {
      position:absolute;
      float:left;
      width:640px;
      min-height:264px;
      background-color: #000000;
      z-index:300000;
    }
  </style>
</head>

In the BODY you will need the div for the overlay and the video. The overlay div has an onclick handler to hide the overlay and start the video playing

<div id="video_box">
  <div id="video_overlays" onclick="this.style.display='none';document.getElementById('video').play()"></div>

    <video id="video" controls width="640" height="264">
      <source src="BigBuck.mp4" type='video/mp4'>
    </video>

  </div>
</div>

Finally you will need code that will load the video, seek to the first frame and load the visual into a canvas that you then insert into the overlay

<script>

function generateOverlay() {
    video.removeEventListener('seeked',generateOverlay);  / tidy up the event handler so it doesn't redraw the overlay every time the user manually seeks the video
    var c = document.createElement("canvas");
    var ctx = c.getContext("2d");
    c.width = 640;
    c.height = 264;
    ctx.drawImage(video, 0, 0, 640, 264); // take the content of the video frame and place in canvas
    overlay.appendChild(c); // insert canvas into the overlay div
}

    // identify the video and the overlay
    var video = document.getElementById("video");
    var overlay = document.getElementById("video_overlays");

    // add a handler that when the metadata for the video is loaded it then seeks to the first frame
    video.addEventListener('loadeddata', function() {
        this.currentTime = 0;
    }, false);

    // add a handler that when correctly seeked, it generated the overlay
    video.addEventListener('seeked', function() {
    // now video has seeked and current frames will show
    // at the time as we expect
        generateOverlay();
    }, false);

    // load the video, which will trigger the event handlers in turn
    video.load();

</script>

Solution 3

this is a bit late but we had the same scenario. I can't use the attribute 'muted' because my videos are podcasts. This is what I came up with and I hope to share it with future visitors. What I did is load the video in the body tag, drew a canvas, retrieved as base64 and applied to the video as the background image.

Since my video should be fixed 150px in height, I computed the aspect ratio so that whatever height and width of the actual video, it will be resized into 150px height and dynamic width.

$('body').append('<video class="checkmeload" style="position:absolute;top:-10000px" controls preload="auto" playsinline src="//src-here"><source src="//src-here" type="//videotype-here"></video>');

$('body').find('.checkmeload').on('loadeddata', function(){
    var video = $('.checkmeload');

    var canvas = document.createElement('canvas');
    aspectratio = (video[0].videoWidth / video[0].videoHeight);
    newwidth = (150 * aspectratio);
    canvas.width = newwidth;
    canvas.height = 150;

    var context = canvas.getContext('2d');
    context.drawImage(video[0], 0, 0, canvas.width, canvas.height);
    var dataURI = canvas.toDataURL('image/jpeg');

    $('body').find('.checkmeload').remove(); 

    $('#myvideo').css('background-image','url("'+dataURI +'")');
});
Share:
12,574
vedankita kumbhar
Author by

vedankita kumbhar

Updated on June 03, 2022

Comments

  • vedankita kumbhar
    vedankita kumbhar almost 2 years

    I have embedded HTML5 video with mp4 format. How to get thumbnail image like poster without using "poster" attribute. This problem coming on Safari and iOS. I have added video like below mentioned code.

    <video height=350 id='TestVideo' webkit-playsinline><source src='test.mp4' type=video/mp4></video>
    

    On Chrome, IE, Firefox first frame of video coming as thumbnail image, but not coming on Safari and iOS.

  • vedankita kumbhar
    vedankita kumbhar over 7 years
    Thanks but I don't want to use separate image or poster for video because video coming dynamically and I don't want to rework by adding separate image. You have any idea to first frame of video set as a poster.
  • vedankita kumbhar
    vedankita kumbhar over 7 years
    No which plugin can I use?
  • Shobhit Srivastava
    Shobhit Srivastava over 7 years
  • Shobhit Srivastava
    Shobhit Srivastava over 7 years
    Were you able to figure out a solution?
  • vedankita kumbhar
    vedankita kumbhar over 7 years
    Thank u I will try popcorn.js.
  • gabrielstuff
    gabrielstuff about 4 years
    This methods works perfectly fine. I have no issue using it on iOS. Tested on Chrome Androïd and desktop and no issue so far. Thanks for the idea.
  • fen1ksss
    fen1ksss over 3 years
    This method does not work if you are using blob.url as video src
  • luke_mclachlan
    luke_mclachlan almost 3 years
    I upvoted because as long as you can trigger a video play via jquery on all platforms then its very neat! Havent tested it however...have you?
  • Koolstr
    Koolstr almost 3 years
    This clever method worked flawlessly, and solved my longstanding problem of getting the native thumbnail to show once the clip finished loading! Thank you so much! It also replaces the poster attribute as desired, and the 0.1s offset doesn't matter in my use case, so this is a perfect solution for my use case.
  • rolandforbes
    rolandforbes over 2 years
    Great recommendation!
  • Regolith
    Regolith over 2 years
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
  • Paolo Moretti
    Paolo Moretti over 2 years
    Note that you can also use a smaller offset, e.g. 0.001 (1ms), it'll still display the thumbnail.