461 lines
13 KiB
JavaScript
461 lines
13 KiB
JavaScript
|
import { isH5Renderer, isSupportVideoPlayer, isPc, isDevtools } from '../../check-version';
|
||
|
let FrameworkData = null;
|
||
|
const isWebVideo = isH5Renderer || isPc || isDevtools;
|
||
|
const isDebug = false;
|
||
|
const needCache = true;
|
||
|
const cacheVideoDecoder = [];
|
||
|
const videoInstances = {};
|
||
|
function _JS_Video_CanPlayFormat(format, data) {
|
||
|
|
||
|
|
||
|
FrameworkData = data;
|
||
|
return !!isSupportVideoPlayer;
|
||
|
}
|
||
|
let videoInstanceIdCounter = 0;
|
||
|
function dynCall_vi(...args) {
|
||
|
if (FrameworkData) {
|
||
|
FrameworkData.dynCall_vi(...args);
|
||
|
}
|
||
|
}
|
||
|
function dynCall_vii(...args) {
|
||
|
if (FrameworkData) {
|
||
|
FrameworkData.dynCall_vii(...args);
|
||
|
}
|
||
|
}
|
||
|
function jsVideoEnded() {
|
||
|
if (isDebug) {
|
||
|
console.log('jsVideoEnded');
|
||
|
}
|
||
|
|
||
|
if (this.onendedCallback) {
|
||
|
|
||
|
dynCall_vi(this.onendedCallback, this.onendedRef);
|
||
|
}
|
||
|
}
|
||
|
function _JS_Video_Create(url) {
|
||
|
let source = '';
|
||
|
if (FrameworkData) {
|
||
|
source = FrameworkData.UTF8ToString(url);
|
||
|
}
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_Create', source);
|
||
|
}
|
||
|
if (isWebVideo) {
|
||
|
|
||
|
const video = GameGlobal.manager.createWKVideo(source, FrameworkData.GLctx);
|
||
|
|
||
|
videoInstances[++videoInstanceIdCounter] = video;
|
||
|
}
|
||
|
else {
|
||
|
let videoDecoder;
|
||
|
if (cacheVideoDecoder.length > 0) {
|
||
|
videoDecoder = cacheVideoDecoder.pop();
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
videoDecoder = wx.createVideoDecoder({
|
||
|
type: 'wemedia',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
const videoInstance = {
|
||
|
videoDecoder,
|
||
|
videoWidth: 0,
|
||
|
videoHeight: 0,
|
||
|
isReady: false,
|
||
|
paused: false,
|
||
|
ended: false,
|
||
|
seeking: false,
|
||
|
duration: 1,
|
||
|
};
|
||
|
|
||
|
videoInstances[++videoInstanceIdCounter] = videoInstance;
|
||
|
videoDecoder.on('start', (res) => {
|
||
|
if (isDebug) {
|
||
|
console.warn('wxVideoDecoder start:', res);
|
||
|
}
|
||
|
videoInstance.paused = false;
|
||
|
if (!videoInstance.isReady) {
|
||
|
videoInstance.duration = res.video?.duration ?? 0;
|
||
|
videoInstance.videoWidth = res.width ?? 0;
|
||
|
videoInstance.videoHeight = res.height ?? 0;
|
||
|
videoInstance.isReady = true;
|
||
|
videoDecoder.stop();
|
||
|
}
|
||
|
});
|
||
|
videoDecoder.on('stop', (res) => {
|
||
|
if (isDebug) {
|
||
|
console.warn('wxVideoDecoder stop:', res);
|
||
|
}
|
||
|
videoInstance.paused = true;
|
||
|
});
|
||
|
videoDecoder.on('seek', (res) => {
|
||
|
if (isDebug) {
|
||
|
console.warn('wxVideoDecoder seek:', res);
|
||
|
}
|
||
|
});
|
||
|
videoDecoder.on('bufferchange', (res) => {
|
||
|
if (isDebug) {
|
||
|
console.warn('wxVideoDecoder bufferchange:', res);
|
||
|
}
|
||
|
});
|
||
|
videoDecoder.on('ended', (res) => {
|
||
|
if (isDebug) {
|
||
|
console.warn('wxVideoDecoder ended:', res);
|
||
|
}
|
||
|
if (videoInstance.loop) {
|
||
|
videoInstance.seek(0);
|
||
|
}
|
||
|
else {
|
||
|
videoInstance.ended = true;
|
||
|
videoInstance.onended?.();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
videoDecoder.on('frame', (res) => {
|
||
|
|
||
|
|
||
|
videoInstance.currentTime = res.pts / 1000;
|
||
|
videoInstance.frameData = new Uint8ClampedArray(res.data);
|
||
|
});
|
||
|
videoInstance.play = () => videoDecoder.start({
|
||
|
source,
|
||
|
});
|
||
|
videoInstance.pause = () => {
|
||
|
videoDecoder.stop();
|
||
|
};
|
||
|
videoInstance.seek = (time) => {
|
||
|
|
||
|
videoDecoder.avSync.seek({ stamp: time });
|
||
|
};
|
||
|
videoInstance.play();
|
||
|
videoInstance.destroy = () => {
|
||
|
if (needCache) {
|
||
|
videoDecoder.stop();
|
||
|
cacheVideoDecoder.push(videoDecoder);
|
||
|
}
|
||
|
else {
|
||
|
videoDecoder.remove();
|
||
|
}
|
||
|
if (videoInstance.loopEndPollInterval) {
|
||
|
clearInterval(videoInstance.loopEndPollInterval);
|
||
|
}
|
||
|
delete videoInstance.videoDecoder;
|
||
|
delete videoInstance.onendedCallback;
|
||
|
delete videoInstance.frameData;
|
||
|
videoInstance.paused = false;
|
||
|
videoInstance.ended = false;
|
||
|
videoInstance.currentTime = 0;
|
||
|
videoInstance.onended = null;
|
||
|
};
|
||
|
}
|
||
|
return videoInstanceIdCounter;
|
||
|
}
|
||
|
function _JS_Video_Destroy(video) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_Destroy', video);
|
||
|
}
|
||
|
videoInstances[video].destroy();
|
||
|
delete videoInstances[video];
|
||
|
}
|
||
|
function _JS_Video_Duration(video) {
|
||
|
return videoInstances[video].duration;
|
||
|
}
|
||
|
function _JS_Video_EnableAudioTrack(video, trackIndex, enabled) {
|
||
|
const v = videoInstances[video];
|
||
|
|
||
|
if (!v.enabledTracks) {
|
||
|
v.enabledTracks = [];
|
||
|
}
|
||
|
while (v.enabledTracks.length <= trackIndex) {
|
||
|
v.enabledTracks.push(true);
|
||
|
}
|
||
|
v.enabledTracks[trackIndex] = enabled;
|
||
|
const tracks = v.audioTracks;
|
||
|
if (!tracks) {
|
||
|
return;
|
||
|
}
|
||
|
const track = tracks[trackIndex];
|
||
|
if (track) {
|
||
|
track.enabled = !!enabled;
|
||
|
}
|
||
|
}
|
||
|
function _JS_Video_GetAudioLanguageCode(video, trackIndex) {
|
||
|
|
||
|
const tracks = videoInstances[video].audioTracks;
|
||
|
if (!tracks) {
|
||
|
return '';
|
||
|
}
|
||
|
const track = tracks[trackIndex];
|
||
|
return track ? track.language : '';
|
||
|
}
|
||
|
function _JS_Video_GetNumAudioTracks(video) {
|
||
|
const tracks = videoInstances[video].audioTracks;
|
||
|
// console.log('_JS_Video_GetNumAudioTracks', tracks);
|
||
|
return tracks ? tracks.length : 1;
|
||
|
}
|
||
|
function _JS_Video_Height(video) {
|
||
|
return videoInstances[video].videoHeight;
|
||
|
}
|
||
|
function _JS_Video_IsPlaying(video) {
|
||
|
if (isWebVideo) {
|
||
|
const v = videoInstances[video];
|
||
|
return v.isPlaying;
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
return v.isReady && !v.paused && !v.ended;
|
||
|
}
|
||
|
function _JS_Video_IsReady(video) {
|
||
|
const v = videoInstances[video];
|
||
|
return !!v.isReady;
|
||
|
}
|
||
|
function _JS_Video_IsSeeking(video) {
|
||
|
const v = videoInstances[video];
|
||
|
return !!v.seeking;
|
||
|
}
|
||
|
function _JS_Video_Pause(video) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_Pause');
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
v.pause();
|
||
|
if (v.loopEndPollInterval) {
|
||
|
clearInterval(v.loopEndPollInterval);
|
||
|
}
|
||
|
}
|
||
|
function _JS_Video_SetLoop(video, loop = false) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetLoop', video);
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
if (v.loopEndPollInterval) {
|
||
|
clearInterval(v.loopEndPollInterval);
|
||
|
}
|
||
|
v.loop = loop;
|
||
|
if (loop) {
|
||
|
|
||
|
v.loopEndPollInterval = setInterval(() => {
|
||
|
if (typeof v.currentTime !== 'undefined' && typeof v.lastSeenPlaybackTime !== 'undefined') {
|
||
|
if (v.currentTime < v.lastSeenPlaybackTime) {
|
||
|
jsVideoEnded.apply(v);
|
||
|
}
|
||
|
}
|
||
|
v.lastSeenPlaybackTime = v.currentTime;
|
||
|
}, 1e3 / 30);
|
||
|
v.lastSeenPlaybackTime = v.currentTime;
|
||
|
v.onended = null;
|
||
|
}
|
||
|
else {
|
||
|
v.onended = jsVideoEnded;
|
||
|
}
|
||
|
}
|
||
|
function jsVideoAllAudioTracksAreDisabled(v) {
|
||
|
if (isDebug) {
|
||
|
console.log('jsVideoAllAudioTracksAreDisabled');
|
||
|
}
|
||
|
if (!v.enabledTracks) {
|
||
|
return false;
|
||
|
}
|
||
|
for (let i = 0; i < v.enabledTracks.length; ++i) {
|
||
|
if (v.enabledTracks[i]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
function _JS_Video_Play(video, muted) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_Play', video, muted);
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
v.muted = muted || jsVideoAllAudioTracksAreDisabled(v);
|
||
|
v.play();
|
||
|
_JS_Video_SetLoop(video, v.loop);
|
||
|
}
|
||
|
function _JS_Video_Seek(video, time) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_Seek', video, time);
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
v.seek(time);
|
||
|
}
|
||
|
function _JS_Video_SetEndedHandler(video, ref, onended) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetEndedHandler', video, ref, onended);
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
v.onendedCallback = onended;
|
||
|
v.onendedRef = ref;
|
||
|
}
|
||
|
function _JS_Video_SetErrorHandler(video, ref, onerror) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetErrorHandler', video, ref, onerror);
|
||
|
}
|
||
|
if (isWebVideo) {
|
||
|
videoInstances[video].on('error', (errMsg) => {
|
||
|
dynCall_vii(onerror, ref, errMsg);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
function _JS_Video_SetMute(video, muted) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetMute', video, muted);
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
v.muted = muted || jsVideoAllAudioTracksAreDisabled(v);
|
||
|
}
|
||
|
function _JS_Video_SetPlaybackRate(video, rate) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetPlaybackRate', video, rate);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
return;
|
||
|
|
||
|
|
||
|
}
|
||
|
function _JS_Video_SetReadyHandler(video, ref, onready) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetReadyHandler', video, ref, onready);
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
if (isWebVideo) {
|
||
|
v.on('canplay', () => {
|
||
|
dynCall_vi(onready, ref);
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
const fn = () => {
|
||
|
console.log('_JS_Video_SetReadyHandler onCanPlay');
|
||
|
dynCall_vi(onready, ref);
|
||
|
v.videoDecoder?.off('bufferchange', fn);
|
||
|
};
|
||
|
v.videoDecoder?.on('bufferchange', fn);
|
||
|
}
|
||
|
}
|
||
|
function _JS_Video_SetSeekedOnceHandler(video, ref, onseeked) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetSeekedOnceHandler', video, ref, onseeked);
|
||
|
}
|
||
|
const v = videoInstances[video];
|
||
|
if (isWebVideo) {
|
||
|
v.on('seek', () => {
|
||
|
dynCall_vi(onseeked, ref);
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
v.videoDecoder?.on('seek', () => {
|
||
|
dynCall_vi(onseeked, ref);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
function _JS_Video_SetVolume(video, volume) {
|
||
|
if (isDebug) {
|
||
|
console.log('_JS_Video_SetVolume');
|
||
|
}
|
||
|
videoInstances[video].volume = volume;
|
||
|
}
|
||
|
function _JS_Video_Time(video) {
|
||
|
return videoInstances[video].currentTime;
|
||
|
}
|
||
|
function _JS_Video_UpdateToTexture(video, tex) {
|
||
|
|
||
|
const v = videoInstances[video];
|
||
|
if (!(v.videoWidth > 0 && v.videoHeight > 0)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (v.lastUpdateTextureTime === v.currentTime) {
|
||
|
return false;
|
||
|
}
|
||
|
v.lastUpdateTextureTime = v.currentTime;
|
||
|
if (!FrameworkData) {
|
||
|
return false;
|
||
|
}
|
||
|
const { GL, GLctx } = FrameworkData;
|
||
|
GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, true);
|
||
|
|
||
|
|
||
|
const internalFormat = GLctx.RGBA;
|
||
|
const format = GLctx.RGBA;
|
||
|
const width = v.videoWidth;
|
||
|
const height = v.videoHeight;
|
||
|
if (v.previousUploadedWidth !== width || v.previousUploadedHeight !== height) {
|
||
|
GLctx.deleteTexture(GL.textures[tex]);
|
||
|
const t = GLctx.createTexture();
|
||
|
t.name = tex;
|
||
|
GL.textures[tex] = t;
|
||
|
GLctx.bindTexture(GLctx.TEXTURE_2D, t);
|
||
|
GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_WRAP_S, GLctx.CLAMP_TO_EDGE);
|
||
|
GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_WRAP_T, GLctx.CLAMP_TO_EDGE);
|
||
|
GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_MIN_FILTER, GLctx.LINEAR);
|
||
|
if (isWebVideo) {
|
||
|
v.render();
|
||
|
}
|
||
|
else {
|
||
|
GLctx.texImage2D(GLctx.TEXTURE_2D, 0, internalFormat, v.videoWidth, v.videoHeight, 0, format, GLctx.UNSIGNED_BYTE, v.frameData);
|
||
|
}
|
||
|
v.previousUploadedWidth = width;
|
||
|
v.previousUploadedHeight = height;
|
||
|
}
|
||
|
else {
|
||
|
GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[tex]);
|
||
|
if (isWebVideo) {
|
||
|
v.render();
|
||
|
}
|
||
|
else {
|
||
|
GLctx.texImage2D(GLctx.TEXTURE_2D, 0, internalFormat, v.videoWidth, v.videoHeight, 0, format, GLctx.UNSIGNED_BYTE, v.frameData);
|
||
|
}
|
||
|
}
|
||
|
GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, false);
|
||
|
return true;
|
||
|
}
|
||
|
function _JS_Video_Width(video) {
|
||
|
return videoInstances[video].videoWidth;
|
||
|
}
|
||
|
function _JS_Video_SetSeekedHandler(video, ref, onseeked) {
|
||
|
const v = videoInstances[video];
|
||
|
if (isWebVideo) {
|
||
|
v.on('seek', () => {
|
||
|
dynCall_vi(onseeked, ref);
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
v.videoDecoder?.on('seek', () => {
|
||
|
dynCall_vi(onseeked, ref);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
function _JS_Video_GetPlaybackRate(video) {
|
||
|
return videoInstances[video].playbackRate;
|
||
|
}
|
||
|
export default {
|
||
|
_JS_Video_CanPlayFormat,
|
||
|
_JS_Video_Create,
|
||
|
_JS_Video_Destroy,
|
||
|
_JS_Video_Duration,
|
||
|
_JS_Video_EnableAudioTrack,
|
||
|
_JS_Video_GetAudioLanguageCode,
|
||
|
_JS_Video_GetNumAudioTracks,
|
||
|
_JS_Video_Height,
|
||
|
_JS_Video_IsPlaying,
|
||
|
_JS_Video_IsReady,
|
||
|
_JS_Video_IsSeeking,
|
||
|
_JS_Video_Pause,
|
||
|
_JS_Video_SetLoop,
|
||
|
_JS_Video_Play,
|
||
|
_JS_Video_Seek,
|
||
|
_JS_Video_SetEndedHandler,
|
||
|
_JS_Video_SetErrorHandler,
|
||
|
_JS_Video_SetMute,
|
||
|
_JS_Video_SetPlaybackRate,
|
||
|
_JS_Video_SetReadyHandler,
|
||
|
_JS_Video_SetSeekedOnceHandler,
|
||
|
_JS_Video_SetVolume,
|
||
|
_JS_Video_Time,
|
||
|
_JS_Video_UpdateToTexture,
|
||
|
_JS_Video_Width,
|
||
|
_JS_Video_SetSeekedHandler,
|
||
|
_JS_Video_GetPlaybackRate,
|
||
|
};
|