I am trying to create an image from a video by seeking to a certain position in the video but it doesn't always work.
It will always return some data for the image, but sometimes that amount of data is too small to be an acceptable image.
Sometimes the size of the image produced is an acceptable value such as 127619. It always works when seeking to 1.
But, there are times when the size is 2865. This seems to always occur when trying to seek to 3.
Any suggestions are appreciated.
Here is the method to create the image:
private createThumbnail = (
file: File,
seekTo: number,
scaleFactor: number = 0.25,
imageType: string = 'image/png',
imageQuality: number = 0.75
): Observable<Blob | null> => {
return from(
new Promise<Blob | null>((resolve, reject) => {
console.log('getting video cover for file: ', file);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// load the file to a video player
const videoPlayer = document.createElement('video');
videoPlayer.src = URL.createObjectURL(file);
videoPlayer.load();
if (videoPlayer.duration < seekTo) {
reject('Video is too short.');
return;
}
videoPlayer.onerror = (event) => {
console.error('error ', event);
reject('Error when loading video file');
return;
};
videoPlayer.onloadeddata = (_) => {
console.log("Loaded the video's data!");
console.log('Video Source: ' + videoPlayer.src);
console.log('Video Duration: ' + videoPlayer.duration);
// seek to user defined timestamp (in seconds) if possible
// delay seeking or else 'seeked' event won't fire on Safari
setTimeout(() => {
videoPlayer.currentTime = seekTo;
console.log(videoPlayer.currentTime);
}, 200);
};
videoPlayer.onloadedmetadata = (_) => {
console.log('onloadedmetadata ');
// define a canvas to have the same dimension as the video
canvas.width = videoPlayer.videoWidth * scaleFactor;
canvas.height = videoPlayer.videoHeight * scaleFactor;
};
videoPlayer.onseeked = (_) => {
console.log('video is now paused at ');
// draw the video frame to canvas
ctx?.drawImage(videoPlayer, 0, 0, canvas.width, canvas.height);
// return the canvas image as a blob
ctx?.canvas.toBlob(
(blob) => {
// canvas.remove();
// videoPlayer.remove();
console.log(blob);
resolve(blob);
},
imageType,
imageQuality
);
};
})
);
};
Here is the load that calls the method, it is passed a file that is a video, along with the position to seek to:
const seekTos = from([1.0]);
seekTos
.pipe(
concatMap((seekTo) => this.createThumbnail(selectedFiles[0], seekTo))
})
)
.subscribe({
next: (imgBlob) => {
this.imgSrcs.push(imgBlob ? URL.createObjectURL(imgBlob) : null);
},
error: (_) => console.error('An error occurred'),
});