1

I have an issue with getting a snapshot from a video to use as a header image. Underneath I have the code I currently use, but the output I get is only a black image. In the console I don't get any issues. I do this after I have uploaded the video to the fileserver I use.

    let video = $(videoPath).find('#newVideo').get(0);
    let canvas = document.createElement('canvas');
    video.onloadedmetadata = function() {
        video.play();
        canvas.width = 640;
        canvas.height = 480;
        var ctx = canvas.getContext('2d');
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        var dataURI = canvas.toDataURL('image/jpeg');
        let $previewBox = $(".post-preview-box");
        let $previewCardImg = $previewBox.find(".card-img");

I have used How to take a snapshot of HTML5-JavaScript-based video player? as a base to get a snapshot and then added some other things in the hope it would work, but so far I have been unable to do.

Any help or input on what to try is welcome.

Edit 04-03-2020 (april third): To give more clarification I will add a console out of the dataURI and changes to code I made.

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCACWASwDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8qqACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//2Q==

Added the currentTime of the video and set it to 3 (I also have tried it with 5).

    let video = $(videoPath).find('#newVideo').get(0);
    let canvas = document.createElement('canvas');
    video.onloadedmetadata = function() {
        video.currentTime = 3;
        video.play();
        var ctx = canvas.getContext('2d');
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        var dataURI = canvas.toDataURL('image/jpeg');
        let $previewBox = $(".post-preview-box");
        console.log(dataURI);
        let $previewCardImg = $previewBox.find(".card-img");
nev3rm0re
  • 60
  • 5
  • 1
    skip forward a few seconds, all videos are black around the first frames – Lawrence Cherone Apr 02 '20 at 13:03
  • @LawrenceCherone Thank you for the comment, I put it 3 seconds forward, but the image still turns out black. – nev3rm0re Apr 03 '20 at 10:25
  • Edit your code above, to show what you tried. – CBroe Apr 03 '20 at 11:30
  • Does using the `loadedmetadata` event to trigger this actually make much sense? You don’t want a “screenshot” of the meta data, so why should _that_ having finished loading be the trigger here? – CBroe Apr 03 '20 at 11:36
  • @CBroe in the past I have had some issues with code being executed while the metadata of the video was not properly loaded in yet causing data to be missed out. – nev3rm0re Apr 03 '20 at 11:40
  • I wasn’t trying to say _don’t_ wait for the meta data to be loaded, but wait for actual _image_ data to be loaded. Meta data loaded might still be _too early_. – CBroe Apr 03 '20 at 11:42

1 Answers1

0

Without seeing all your code, here is an example which gets 10 screens.

var screens = [];

var video = document.getElementById('video');
var video_preview = document.getElementById('video_preview');

var loadingContainer = document.getElementById('loading-container')

function loadVideo(event) {
  loadingContainer.style.display = 'block'
  screens = [];

  var reader = new FileReader()
  reader.onload = function(e) {
    video.src = video_preview.src = e.target.result;
    video.autoplay = video_preview.autoplay = true;
    video.hasLoaded = video_preview.hasLoaded = false;

    video_preview.addEventListener('canplay', function() {
      video_preview.hasLoaded = true;
      video.play();
    })

    video.addEventListener('canplay', function() {

      // first time
      if (!video.hasLoaded) {
        console.log('Loaded: video duration: ', this.duration, event.target.files[0].size)

        loadingContainer.innerText = 'Generating screens...'

        video.hasLoaded = true;

        var self = this;

        (function repeat(i) {
          setTimeout(function() {
            var timestamp = ((self.duration / 10) * i) / 1.1; // fudge abit so dont get start/end frames

            console.log('seeking to:', timestamp)
            self.currentTime = timestamp

            if (--i) {
              // next
              repeat(i);
            } else {
              //
              loadingContainer.style.display = 'none'

              // all screens grabbed
              var str = '<div style="position:relative;width:calc(100% + .25rem)">'
              screens.reverse().forEach(function(screen) {
                str += '<img src="' + screen + '" style="width:32%" class="img-thumbnail m-1" />';
              });
              str += '</div>';
              document.getElementById("screens-container").innerHTML = str;
            }
          }, 500) // how fast to attempt to grab screens
        })(11); // iterations i.e how many screens
      }
    }, false);

    video.addEventListener('seeked', function() {
      console.log('grabbing screen for', this.currentTime)
      takeScreen();
    }, false);
  }

  reader.readAsDataURL(event.target.files[0]);
}

function takeScreen() {
  var filename = video.src;
  var w = video.videoWidth;
  var h = video.videoHeight;
  var canvas = document.createElement('canvas');
  canvas.width = w;
  canvas.height = h;
  var ctx = canvas.getContext('2d');
  ctx.drawImage(video, 0, 0, w, h);
  var data = canvas.toDataURL("image/jpg");

  screens.push(data)

  loadingContainer.innerText = 'Generated ' + screens.length + ' out of 10 screens...'
}

function failed(e) {
  switch (e.target.error.code) {
    case e.target.error.MEDIA_ERR_ABORTED:
      console.log('You aborted the video playback.');
      break;
    case e.target.error.MEDIA_ERR_NETWORK:
      console.log('A network error caused the video download to fail part-way.');
      break;
    case e.target.error.MEDIA_ERR_DECODE:
      console.log('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.');
      break;
    case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
      console.log('The video could not be loaded, either because the server or network failed or because the format is not supported.');
      break;
    default:
      console.log('An unknown error occurred.');
      break;
  }
}
body,
html {
  margin: 0;
  padding: 0
}

li {
  display: inline-block;
  list-style-type: none
}

.jumbotron {
  margin-bottom: 1rem;
}
<div class="container">

  <div class="card">
    <div class="card-header" style="background-color: #e9ecef">
      <h5 class="card-title">Video Preview</h5>
      <video id="video_preview" onerror="failed(event)" controls="controls" preload="none" muted style="width: 100%"></video>
      <video id="video" onerror="failed(event)" controls="controls" preload="none" muted style="display:none"></video>
      <div class="card-body">
        <a href="javascript:void(0)" class="btn btn-sm btn-primary btn-block" onClick="$('[type=\'file\']').trigger('click')">
          <i class="fa fa-upload"></i> Load Video
        </a>
        <form id="uploadForm" ref="uploadForm" action='/upload' method='post' enctype="multipart/form-data">
          <input type="file" name="file" accept="video/*" style="display: none" onchange="loadVideo(event)">
        </form>
        <div id="loading-container" style="display:none">Loading...</div>
      </div>

      <div class="card-body">
        <h5 class="card-title">Screens</h5>
        <div id="screens-container"></div>
      </div>
    </div>
  </div>

  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

For a more complete example see: https://codesandbox.io/s/clipseed-hfhee

Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106
  • This isn't exactly what I'm looking for. This has to do with the existing file upload I have to work with. This upload puts the file on an external third party filestorage and returns the url to it. Would this code work, with some tweaks, using the timeupdate event from the video element? Which basically says timeupdate, function(){ //snap shot code//} – nev3rm0re Apr 03 '20 at 13:59
  • Yeah it would work the same and a lot less code, its just using the ` – Lawrence Cherone Apr 03 '20 at 14:51
  • With some changes I was able to figure it out, I let out the html and css, because that was not needed in my case. – nev3rm0re Apr 06 '20 at 10:34