Search code examples
javascripthtmltypescriptionic-frameworkionic4

Capturing the first video frame


I found many article in stackoverflow talking about how to capture the first video image frame, but I don't see what I'm doing wrong on the code to make it not working. So if someone can help me, thanks a lot !

HTML code:

<ion-button expand="block" color="primary" (click)="onPickVideo()">
   <ion-icon name="videocam" slot="start"></ion-icon>
   <ion-label>Select video</ion-label>
 </ion-button>

  <input type="file" (change)="onFileChosen($event)" #filePicker/>

<div class="wrapper" *ngIf="flag">
  <video controls >
    <source [src]="videoDetail.dataString" type="video/mp4">
    unsupported video
  </video>
</div>

TS Code:

import { Component, ViewChild, ElementRef } from '@angular/core';

export interface VideoDetail {
  ... Some code ...
}

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  ... Some code ...

  onPickVideo() {
    this.filePickerRef.nativeElement.click();
  }


  onFileChosen(ev: Event) {
    const files: FileList = (ev.target as HTMLInputElement).files;

  ... Some code ...      

    this.videoMetadataReader(this.videoBuffer, this.videoDetail);

    this.convertToDataString(this.videoBuffer, this.videoDetail);
  }

  // Metadata video reader
  videoMetadataReader(buffer: File, detail: VideoDetail) {
    const fileReader = new FileReader();
    this.videoBuffer = files[0];   // get video file

    fileReader.onload = () => {
      const blob = new Blob([fileReader.result], {type});

      const url = (URL || webkitURL).createObjectURL(blob);
      const video = document.createElement('video');  // create video element

      video.preload = 'metadata';                     // preload setting
      video.addEventListener('loadedmetadata', () => {

       ... Some code ...

        // Get first frame
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const img = new Image();


        canvas.height = video.videoHeight;
        canvas.width = video.videoWidth;

        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);


        img.src = canvas.toDataURL();
        detail.videoFrame = img.src;

        console.log('img :', img.src);

      });
      video.src = url; // start video load
    };
    fileReader.readAsArrayBuffer(buffer);
  }

  // File to dataUrl
  convertToDataString(buffer: File, detail: VideoDetail) {
    const fileReader = new FileReader();
    fileReader.onload = () => {
      const dataString = fileReader.result.toString();
      detail.dataString = dataString;
      this.flag = true;
    };
    fileReader.readAsDataURL(buffer);
  }
}

When I see the img in console.log I get an image, but totally white, and it should not be white.

Edit to show screenshot: In red the video frame capture, white.

enter image description here

Thanks again for help :)

Edited to add app link download :

Download source code here


Solution

  • I've reproduced your problem: you should replace preload type from preload to auto and event loadedmetadata should be changed to loadeddata or canplay.

    BTW, I've used function below to convert blob to dataUri (can be changed to async or promises):

    function blobToDataURL(blob, callback) {
      var a = new FileReader();
      a.onload = function(e) {callback(e.target.result);}
      a.readAsDataURL(blob);
    }