import { Injectable } from '@angular/core' ; import { concat , fromEvent , Observable , Observer } from 'rxjs' ; @ Injectable ( { providedIn : 'root' } ) export class FrameExtractionService { private createVideoPlayer ( source ) { const videoPlayer = document . createElement ( 'video' ) ; videoPlayer . preload = 'auto' ; videoPlayer . src = source ; return videoPlayer ; } private createCanvas ( width , height ) { const canvas = document . createElement ( 'canvas' ) ; canvas . width = width ; canvas . height = height ; return canvas ; } private goToFrame ( videoPlayer , time ) { videoPlayer . currentTime = time ; return fromEvent ( videoPlayer , time === 0 ? 'loadeddata' : 'seeked' ) ; } private getThumbnail ( videoPlayer , canvas , time , width = 160 , height = 90 ) { return Observable . create ( ( observer ) => { const subscription = this . goToFrame ( videoPlayer , time ) . subscribe ( ( ) => { canvas . getContext ( '2d' ) . drawImage ( videoPlayer , 0 , 0 , width , height ) ; observer . next ( { image : canvas . toDataURL ( ) , width , height , time } ) ; observer . complete ( ) ; subscription . unsubscribe ( ) ; } ) ; } ) ; } getFrameThumbnail ( source , width , height , time ) { let videoPlayer = this . createVideoPlayer ( source ) ; let canvas = this . createCanvas ( width , height ) ; const frameSubscription = this . getThumbnail ( videoPlayer , canvas , time , width , height ) ; frameSubscription . subscribe ( null , null , ( ) => { videoPlayer = null ; canvas = null ; } ) ; return frameSubscription ; } getFrameThumbnails ( source , width , height , start , end , skip = 5 ) { let videoPlayer = this . createVideoPlayer ( source ) ; let canvas = this . createCanvas ( width , height ) ; return Observable . create ( ( observer ) => { fromEvent ( videoPlayer , 'loadedmetadata' ) . subscribe ( ( ) => { const frames = [ ] ; for ( let idx = start ; idx < end ; idx += skip ) { frames . push ( this . getThumbnail ( videoPlayer , canvas , idx , width , height ) ) ; } concat ( ... frames ) . subscribe ( ( frame ) => observer . next ( frame ) , null , ( ) => { videoPlayer = null ; canvas = null ; observer . complete ( ) ; } ) ; } ) ; } ) ; } } export interface ExtractedFrame { image : string ; width : number ; height : number ; time : number ; }