UNPKG

tav-media

Version:

Cross platform media editing framework

158 lines (157 loc) 6.67 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; 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()); }); }; import { destroyVerify } from '../utils/decorators'; import { MP4_CACHE_PATH } from './constant'; import { touchDirectory, writeFile } from './file-utils'; const BUFFER_MAX_SIZE = 6; const BUFFER_MIN_SIZE = 2; const GET_FRAME_DATA_INTERVAL = 2; // ms const frameDataOptions2FrameData = (id, options) => { const data = new ArrayBuffer(options.data.byteLength); new Uint8Array(data).set(new Uint8Array(options.data)); return { id: id, data: data, width: options.width, height: options.height, }; }; let VideoReader = class VideoReader { constructor(mp4Data, width, height, frameRate, staticTimeRanges) { this.frameData = null; this.frameDataBuffers = []; this.bufferIndex = 0; // next frameData id this.getFrameDataLooping = false; this.getFrameDataResolve = null; this.getFrameDataLoopTimer = null; this.seeking = false; this.frameRate = frameRate; this.currentFrame = -1; this.mp4Path = `${MP4_CACHE_PATH}${new Date().getTime()}.mp4`; touchDirectory(MP4_CACHE_PATH); writeFile(this.mp4Path, mp4Data.buffer.slice(mp4Data.byteOffset, mp4Data.byteLength + mp4Data.byteOffset)); this.videoDecoder = wx.createVideoDecoder(); this.videoDecoder.on('ended', () => { var _a; (_a = this.videoDecoder) === null || _a === void 0 ? void 0 : _a.seek(0).then(() => { this.bufferIndex = 0; }); }); this.videoDecoderPromise = this.videoDecoder.start({ source: this.mp4Path, mode: 1 }).then(() => { this.startGetFrameDataLoop(); }); } prepare(targetFrame) { return __awaiter(this, void 0, void 0, function* () { if (targetFrame === this.currentFrame) { return true; } // Wait for videoDecoder ready yield this.videoDecoderPromise; if (this.frameDataBuffers.length > 0) { const index = this.frameDataBuffers.findIndex((frameData) => frameData.id === targetFrame); if (index !== -1) { this.frameDataBuffers = this.frameDataBuffers.slice(index); this.frameData = yield this.getFrameData(); this.currentFrame = targetFrame; return true; } this.frameDataBuffers = []; } if (targetFrame !== this.bufferIndex) { this.seeking = true; yield this.videoDecoder.seek(Math.floor((targetFrame / this.frameRate) * 1000)); this.seeking = false; } this.frameData = yield this.getFrameData(); this.currentFrame = targetFrame; return true; }); } renderToTexture(GL, textureID) { const gl = GL.currentContext.GLctx; gl.bindTexture(gl.TEXTURE_2D, GL.textures[textureID]); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.frameData.width, this.frameData.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(this.frameData.data)); } onDestroy() { this.videoDecoder.remove(); } getFrameData() { return new Promise((resolve) => { if (this.frameDataBuffers.length <= BUFFER_MIN_SIZE && !this.getFrameDataLooping) { this.startGetFrameDataLoop(); } if (this.frameDataBuffers.length === 0) { this.getFrameDataResolve = resolve; return; } const res = this.frameDataBuffers.shift(); if (!res) { this.getFrameDataResolve = resolve; return; } resolve(res); }); } startGetFrameDataLoop() { this.getFrameDataLooping = true; this.getFrameDataLoopTimer = setInterval(() => { this.getFrameDataLoop(); }, GET_FRAME_DATA_INTERVAL); } getFrameDataLoop() { if (this.seeking) return; if (!this.videoDecoder) { this.clearFrameDataLoop(); throw new Error('VideoDecoder is not ready!'); } if (this.frameDataBuffers.length >= BUFFER_MAX_SIZE) { this.getFrameDataLooping = false; this.clearFrameDataLoop(); return; } const frameDataOptions = this.videoDecoder.getFrameData(); if (frameDataOptions !== null) { if (this.getFrameDataResolve) { this.getFrameDataResolve(frameDataOptions2FrameData(this.bufferIndex, frameDataOptions)); this.getFrameDataResolve = null; } else { this.frameDataBuffers.push(frameDataOptions2FrameData(this.bufferIndex, frameDataOptions)); } this.bufferIndex += 1; } } clearFrameDataLoop() { if (this.getFrameDataLoopTimer) { clearInterval(this.getFrameDataLoopTimer); this.getFrameDataLoopTimer = null; } this.getFrameDataLooping = false; } }; VideoReader.isIOS = () => { // need't to check platform on wechat miniprogram return false; }; VideoReader.isAndroidMiniprogram = () => { return wx.getSystemInfoSync().platform === 'android'; }; VideoReader = __decorate([ destroyVerify ], VideoReader); export { VideoReader };