UNPKG

tav-media

Version:

Cross platform media editing framework

233 lines (232 loc) 8.99 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; /* eslint-disable no-param-reassign */ import { tav } from '../tav'; import { Http } from '../io/http'; import { setMediaSource, waitForEvent } from './media-element-utils'; const FarLessThanOneFrameTime = 0.01; export class MediaReader { constructor(path) { this.contentTime = 0; this.textureId = 0; this.released = false; this.playbackRate = 1; this.alwaysPause = false; this.volume = 1; this.adjustRange = 0.16; /** * Browser, it will be 1 in WeChat reader */ this.type = 0; this.useCount = 0; this.isPaused = true; this.startTime = 0; this.duration = -1; this.resetTask = -1; this.onMediaTimeUpdate = () => { this.checkEndTimeAndPause(); }; this.path = path; MediaReader.readers.push(this); MediaReader.preload(path); this.videoEl = MediaReader.videos[path]; if (this.videoEl.videoHeight) { this.updateSize(); } else { this.videoEl.addEventListener('loadedmetadata', () => this.updateSize()); } MediaReader.videos[path] = null; this.reuse(); } static preload(clipAssetPath) { if (this.videos[clipAssetPath] && this.videos[clipAssetPath].videoWidth > 0) { return Promise.resolve(this.videos[clipAssetPath]); } if (this.videoPreloadTask[clipAssetPath]) { return this.videoPreloadTask[clipAssetPath]; } const videoEl = document.createElement('video'); const src = Http.getUrl(clipAssetPath); setMediaSource(videoEl, src); videoEl.preload = 'auto'; videoEl.crossOrigin = 'anonymous'; videoEl.volume = 1; videoEl.loop = false; this.videos[clipAssetPath] = videoEl; const task = new Promise((res, rej) => { videoEl.onloadedmetadata = _e => { delete this.videoPreloadTask[clipAssetPath]; res(videoEl); }; videoEl.onerror = _e => rej(videoEl); videoEl.load(); }); this.videoPreloadTask[clipAssetPath] = task; return task; } static Make(filePath) { const existing = MediaReader.readers.find(reader => reader.path === filePath); if (existing) { existing.reuse(); return existing; } return new MediaReader(filePath); } static MakeFromBytes(bytesOffset, length) { const module = tav; const blob = new Blob([new Uint8Array(module.HEAPU8.buffer, bytesOffset, length)]); const url = URL.createObjectURL(blob); return this.Make(url); } decodeAudio() { return Promise.resolve(this); } updateSize() { this.width = this.videoEl.videoWidth || this.width; this.height = this.videoEl.videoHeight || this.height; } renderToTexture(GL, textureId) { const gl = GL.currentContext.GLctx; if (this.videoEl.readyState >= 1) { gl.bindTexture(gl.TEXTURE_2D, GL.textures[textureId]); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.videoEl); } } setOptions(options) { if (typeof (options.playbackRate) !== 'undefined') { this.alwaysPause = options.playbackRate === 0 || (options.playbackRate < 0.01); const playbackRate = Math.max(0.125, Math.min(options.playbackRate, 4)); this.videoEl.playbackRate = playbackRate; } if (typeof (options.startTime) !== 'undefined' && (this.duration !== options.duration || this.startTime !== options.startTime) && options.duration >= 0 && options.startTime >= 0) { const startTime = options.startTime / 1000000; const duration = options.duration / 1000000; if (this.duration < 0 || this.videoEl.currentTime > (startTime + duration) || this.videoEl.currentTime < startTime) { this.videoEl.currentTime = startTime; } this.startTime = startTime; this.duration = duration; } if (typeof (options.volume) !== 'undefined') { this.videoEl.volume = options.volume; } } getNearestKeyFrame(time) { return Math.ceil(1.0 * time / (0.8 * 1000000)); } seekToSync(contentTime) { const targetTime = contentTime / 1000000; this.contentTime = contentTime; if (Math.abs(targetTime - this.videoEl.currentTime) < this.adjustRange) { return; } this.videoEl.currentTime = targetTime; } pause() { this.isPaused = true; this.videoEl.pause(); this.reset(); return Promise.resolve(true); } continue() { this.isPaused = false; } seekTo(contentTime, forceSeek = false) { return __awaiter(this, void 0, void 0, function* () { if (contentTime === this.contentTime && !forceSeek) { return Promise.resolve(true); } if (this.resetTask !== -1) { clearTimeout(this.resetTask); this.resetTask = -1; } const targetTime = contentTime / 1000000; this.contentTime = contentTime; // Get the nearest key frame to the target time when paused if ((this.isPaused && Math.abs(this.videoEl.currentTime - targetTime) > FarLessThanOneFrameTime) || forceSeek || Math.abs(this.videoEl.currentTime - targetTime) > 2 || (this.videoEl.currentTime === 0 && targetTime !== 0) || this.videoEl.readyState < this.videoEl.HAVE_FUTURE_DATA) { this.videoEl.currentTime = targetTime; yield waitForEvent(this.videoEl, 'seeked'); } // Playing if (!(this.isPaused || this.alwaysPause)) { if (this.videoEl.paused) { this.videoEl.currentTime = targetTime; this.videoEl.play().catch(() => { }); } if (this.videoEl.readyState < this.videoEl.HAVE_FUTURE_DATA) { yield waitForEvent(this.videoEl, 'playing'); } // Keep one frame from the target time if (this.videoEl.currentTime < (targetTime - 1 / 30 * 1.5)) { this.videoEl.playbackRate = this.playbackRate * 2; } else if (this.videoEl.currentTime > (targetTime - 1 / 30 * 0.5)) { this.videoEl.playbackRate = this.playbackRate * 0.5; } else { this.videoEl.playbackRate = this.playbackRate; } if (Math.abs(targetTime - (this.startTime + this.duration)) < 0.05) { this.videoEl.pause(); } } return true; }); } reset() { this.resetTask = window.setTimeout(() => { this.videoEl.pause(); this.videoEl.currentTime = this.startTime; }, 60); } release() { this.useCount -= 1; if (this.useCount > 0) { return; } MediaReader.readers.splice(MediaReader.readers.indexOf(this), 1); this.released = true; this.isPaused = true; this.duration = -1; this.videoEl.pause(); this.videoEl.removeEventListener('timeupdate', this.onMediaTimeUpdate); if (!MediaReader.videos[this.path]) { MediaReader.videos[this.path] = this.videoEl; } else { this.videoEl.remove(); } } reuse() { this.useCount += 1; this.released = false; this.videoEl.volume = 1; this.videoEl.playbackRate = this.playbackRate; this.videoEl.addEventListener('timeupdate', this.onMediaTimeUpdate); } checkEndTimeAndPause() { if (this.videoEl.currentTime > this.startTime + this.duration) { this.videoEl.pause(); } } } MediaReader.videos = {}; MediaReader.videoPreloadTask = {}; MediaReader.readers = [];