Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New offical preview #7

Merged
merged 11 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/yyeva/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "yyeva",
"version": "1.0.0-beta.18",
"version": "1.0.0-beta.19",
"description": "mp4 gift player",
"files": [
"dist",
Expand Down
24 changes: 13 additions & 11 deletions packages/yyeva/src/helper/logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import config from 'src/helper/config'
import type { LoggerLevelType } from 'src/type/mix'
import { MixEvideoOptions } from 'src/type/mix'
import type {LoggerLevelType} from 'src/type/mix'
import {MixEvideoOptions} from 'src/type/mix'
import Animator from 'src/player/video/animator'
import VideoEntity from 'src/player/render/videoEntity'
import Webgl from 'src/player/render/webglEntity'
Expand Down Expand Up @@ -48,12 +48,12 @@ export class Logger {
]
return rs
}
constructor() { }
constructor() {}
setup(options?: LogParams) {
const { channel, level, showtips } = (self as any).LoggerConfig || {}
this.op = { ...this.op, ...options }
const {channel, level, showtips} = (self as any).LoggerConfig || {}
this.op = {...this.op, ...options}
if (typeof showtips === 'boolean') this.op.showtips = showtips
const lv = level ? this.levels[level] : this.op.level ? this.levels[this.op.level] : (config.mode === 'dev' ? 1 : 2)
const lv = level ? this.levels[level] : this.op.level ? this.levels[this.op.level] : config.mode === 'dev' ? 1 : 2
// console.log(lv,config)
const useChannel = !channel || channel === this.op.channel
const silenceFn = (...args: any[]) => {
Expand Down Expand Up @@ -88,16 +88,18 @@ export const versionTips = (op: MixEvideoOptions) => {
OffScreenRender: !!op.useOfsRender,
UseFrameCache: !!op.useFrameCache,
UseVideoDBCache: !!op.useVideoDBCache,
AnimationType: Animator.animationType
AnimationType: Animator.animationType,
//
})
}
console.log(
`%c ${prefixName} ${config.version} %c ${op.renderType === 'canvas2d' ? op.renderType : `WebGL.${Webgl.version}`}${self.devicePixelRatio ? ` DPR.${self.devicePixelRatio}` : ''
}${` FPS.${VideoEntity.fps}`}${op.mode ? ` ${op.mode}` : ''}${op.usePrefetch ? ' MSE' : ''}${op.useOfsRender ? ' OfsRender' : ''
`%c ${prefixName} ${config.version} %c ${op.renderType === 'canvas2d' ? op.renderType : `WebGL.${Webgl.version}`}${
self.devicePixelRatio ? ` DPR.${self.devicePixelRatio}` : ''
}${` FPS.${VideoEntity.fps}`}${op.mode ? ` ${op.mode}` : ''}${op.usePrefetch ? ' MSE' : ''}${
op.useOfsRender ? ' OfsRender' : ''
}${op.useFrameCache ? ` FrameCache` : ''}${op.useVideoDBCache ? ` VideoCache` : ''}${
// op.useAccurate && 'requestVideoFrameCallback' in HTMLVideoElement.prototype ? ' Accurate' : ''
` ${Animator.animationType}`
// op.useAccurate && 'requestVideoFrameCallback' in HTMLVideoElement.prototype ? ' Accurate' : ''
` ${Animator.animationType}`
} %c`,
'background:#34495e ; padding: 1px; border-radius: 2px 0 0 2px; color: #fff',
'background:#16a085 ; padding: 1px; border-radius: 0 2px 2px 0; color: #fff',
Expand Down
131 changes: 85 additions & 46 deletions packages/yyeva/src/player/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class EVideo {
private eventsFn: {[key: string]: (...args: any[]) => void} = {}
private animator: Animator
private blobUrl: string
private polyfillCreateObjectURL: boolean
//
public onStart: EventCallback
public onResume: EventCallback
Expand All @@ -33,15 +34,29 @@ export default class EVideo {
public onError: EventCallback
//
public isPlay = false
private videoFile?: HTMLInputElement
static url?: string
/**
* 记录当前播放资源的 base64,当blob url播放失败时播放
*/
constructor(op: MixEvideoOptions) {
if (!op.container) throw new Error('container is need!')
if (!op.videoUrl) throw new Error('videoUrl is need!')
//TODO 考虑到 除了 htmlinputelement http 应该还有 dataUrl 后续拓展
if (typeof op.videoUrl !== 'string') {
op.useVideoDBCache = false
//TODO filename 作为 url 可能会很多重复问题 考虑是否默认屏蔽帧缓存
// op.useFrameCache = false
this.videoFile = op.videoUrl
EVideo.url = this.videoFile.name
} else {
EVideo.url = op.videoUrl
}
this.op = op
this.video = this.videoCreate()
this.animator = new Animator(this.video, this.op)
// 是否创建 object url
this.polyfillCreateObjectURL = (polyfill.baidu || polyfill.quark || polyfill.uc) && this.op.forceBlob === false
//
if (this.op.renderType === 'canvas2d') {
this.renderer = new Render2D(this.op)
Expand Down Expand Up @@ -93,7 +108,7 @@ export default class EVideo {
}
}
} catch (e) {
this.onEnd?.()
this.onEnd?.(e)
this.onError?.(e)
this.destroy()
logger.error(e)
Expand Down Expand Up @@ -147,7 +162,7 @@ export default class EVideo {
* 暂时自动切换静音
*/
if (polyfill.safari && polyfill.mac) {
logger.debug('切换到静音播放', this.op.videoUrl)
logger.debug('切换到静音播放', EVideo.url)
this.video.muted = true
this.video.play().catch(e => {
logger.debug(e)
Expand All @@ -156,7 +171,7 @@ export default class EVideo {
return
}
//
logger.debug(`切换到静音播放`, this.op.videoUrl, e)
logger.debug(`切换到静音播放`, EVideo.url, e)
this.clickToPlay()
// 增加弹窗 手动触发 video.play
if (e?.code === 0 && e?.name === EPlayError.NotAllowedError) {
Expand All @@ -183,7 +198,7 @@ export default class EVideo {
this.eventsFn[e.type] && this.eventsFn[e.type]()
}
private videoCreate() {
const videoID = this.op.videoID || getVIdeoId(this.op.videoUrl, polyfill.weixin)
const videoID = this.op.videoID || getVIdeoId(EVideo.url, polyfill.weixin)
logger.debug('[videoID]', videoID)
const videoElm = document.getElementById(videoID)
let video: HTMLVideoElement
Expand Down Expand Up @@ -309,13 +324,18 @@ export default class EVideo {
if (this.op.usePrefetch) {
const url = await this.prefetch()
video.src = url
logger.debug('[prefetch url]', url.length)
logger.debug('[prefetch url]', url)
} else {
video.src = this.op.videoUrl
logger.debug('[prefetch url]', this.op.videoUrl.length)
video.src = EVideo.url
logger.debug('[prefetch url]', EVideo.url)
}
//判断是否存在 audio 默认为 false
video.muted = typeof this.op.mute !== 'undefined' ? this.op.mute : !VideoEntity.hasAudio
if (!VideoEntity.hasAudio) {
video.muted = true
} else {
video.muted = typeof this.op.mute !== 'undefined' ? this.op.mute : false
}
// video.muted = typeof this.op.mute !== 'undefined' ? this.op.mute : !VideoEntity.hasAudio
//
video.load()
logger.debug('[video load]')
Expand Down Expand Up @@ -375,10 +395,12 @@ export default class EVideo {
this.renderer = undefined as any
this.animator = undefined as any
this.version = undefined as any
// 释放 file 文件
this.videoFile = undefined
}
private async checkVideoCache(): Promise<string | undefined> {
try {
const d = await db.model().find(this.op.videoUrl)
const d = await db.model().find(EVideo.url)
if (d) {
const {blob, data} = d
if (data) this.renderer.videoEntity.setConfig(data)
Expand All @@ -394,53 +416,70 @@ export default class EVideo {
async prefetch(): Promise<string> {
// const URL = (window as any).webkitURL || window.URL
// const polyfillCreateObjectURL = polyfill.baidu || ((polyfill.quark || polyfill.uc) && polyfill.android)
const polyfillCreateObjectURL = (polyfill.baidu || polyfill.quark || polyfill.uc) && this.op.forceBlob === false
//
if (this.op.useVideoDBCache && !polyfillCreateObjectURL) {
// const polyfillCreateObjectURL = (polyfill.baidu || polyfill.quark || polyfill.uc) && this.op.forceBlob === false
//
if (this.op.useVideoDBCache && !this.polyfillCreateObjectURL) {
const url = await this.checkVideoCache()
if (url) return url
}
//
let file
if (!this.videoFile) {
file = await this.getVideoByHttp()
} else {
file = this.videoFile
}
const url = await this.readFileToBlobUrl(file)
logger.debug('[prefetch result]', url)
return url
}
private readFileToBlobUrl(file): Promise<string> {
return new Promise((resolve, reject) => {
const fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.onloadend = () => {
const rs = fileReader.result as string
/**
* 根据 useMetaData 获取 yy视频 metadata 信息
*/
let data
if (this.op.useMetaData) {
data = parser.getdata(rs)
if (data) {
this.renderer.videoEntity.setConfig(data)
}
}
//
if (!this.polyfillCreateObjectURL) {
const raw = atob(rs.slice(rs.indexOf(',') + 1))
const buf = Array(raw.length)
for (let d = 0; d < raw.length; d++) {
buf[d] = raw.charCodeAt(d)
}
const arr = new Uint8Array(buf)
const blob = new Blob([arr], {type: 'video/mp4'})
// 返回 metadata 数据
if (this.op.useVideoDBCache) {
db.model().insert(EVideo.url, {blob, data})
}
this.blobUrl = this.createObjectURL(blob)
resolve(this.blobUrl)
} else {
//获取 data 后 原路返回
resolve(EVideo.url)
}
}
})
}
private getVideoByHttp() {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', this.op.videoUrl, true)
xhr.open('GET', EVideo.url, true)
xhr.responseType = 'blob'
xhr.onload = () => {
if (xhr.status === 200 || xhr.status === 304) {
const fileReader = new FileReader()
fileReader.onloadend = () => {
const rs = fileReader.result as string
/**
* 根据 useMetaData 获取 yy视频 metadata 信息
*/
let data
if (this.op.useMetaData) {
data = parser.getdata(rs)
if (data) {
this.renderer.videoEntity.setConfig(data)
}
}
//
if (!polyfillCreateObjectURL) {
const raw = atob(rs.slice(rs.indexOf(',') + 1))
const buf = Array(raw.length)
for (let d = 0; d < raw.length; d++) {
buf[d] = raw.charCodeAt(d)
}
const arr = new Uint8Array(buf)
const blob = new Blob([arr], {type: 'video/mp4'})
// 返回 metadata 数据
if (this.op.useVideoDBCache) {
db.model().insert(this.op.videoUrl, {blob, data})
}
this.blobUrl = this.createObjectURL(blob)
resolve(this.blobUrl)
} else {
//获取 data 后 原路返回
resolve(this.op.videoUrl)
}
}
fileReader.readAsDataURL(xhr.response)
resolve(xhr.response)
} else {
reject(new Error('http response invalid' + xhr.status))
}
Expand Down
4 changes: 2 additions & 2 deletions packages/yyeva/src/player/render/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export default class WglRender {
const ofs = this.webgl.canvas ? this.webgl.canvas : (this.webgl.ofs as HTMLCanvasElement)
const canvasAspect = ofs.clientWidth / ofs.clientHeight
const videoAspect = ofs.width / ofs.height
// logger.debug(this.op.videoUrl, this.op.mode)

ofs.setAttribute('class', `e-video-${this.op.mode.toLocaleLowerCase()}`)
switch (this.op.mode) {
case 'AspectFill':
Expand Down Expand Up @@ -289,7 +289,7 @@ export default class WglRender {
gl.enableVertexAttribArray(aAlphaTexCoord)
// 将缓冲区对象分配给a_position变量、a_texCoord变量
const size = view.BYTES_PER_ELEMENT
// console.log(view, size, this.videoEntity.op.videoUrl)

gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, size * 6, 0) // 顶点着色器位置
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, size * 6, size * 2) // rgb像素位置
gl.vertexAttribPointer(aAlphaTexCoord, 2, gl.FLOAT, false, size * 6, size * 4) // rgb像素位置
Expand Down
10 changes: 5 additions & 5 deletions packages/yyeva/src/player/render/mCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import {logger} from 'src/helper/logger'
import Animator from 'src/player/video/animator'
import VideoEntity from './videoEntity'
import {isDataUrl} from 'src/helper/utils'

import EVideo from 'src/player'
export type MCacheItem = {[frames: number]: ImageBitmap | HTMLImageElement | undefined}
//
function setStoreName(op: MixEvideoOptions) {
const {videoUrl, effects, mode, container} = op
let storeName = isDataUrl(videoUrl) ? videoUrl.substring(22, 88) : videoUrl
const {effects, mode, container} = op
// let storeName = isDataUrl(videoUrl) ? videoUrl.substring(22, 88) : videoUrl
let storeName = EVideo.url
if (effects) {
const eUrl = Object.keys(effects)
.map(key => key + '=' + effects[key])
Expand Down Expand Up @@ -38,9 +39,8 @@ export default class MCache {
requestAnimationFramePercent = 0.95
private hasRequestAnimationFrame = false
constructor(op: MixEvideoOptions) {
if (!op.videoUrl || !op.useFrameCache) return
if (!EVideo.url || !op.useFrameCache) return
this.op = op
// this.storeName = op.videoUrl
this.storeName = setStoreName(op)
if (!MCache.caches[this.storeName]) {
logger.debug('[mCache]', '[实例化]')
Expand Down
2 changes: 1 addition & 1 deletion packages/yyeva/src/player/render/videoEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export default class VideoEntity {
return d
} catch (e) {
logger.error(e)
this.op?.onEnd?.()
this.op?.onEnd?.(e)
return undefined
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/yyeva/src/type/mix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ export type MixEvideoOptions = VideoEvent & {
*/
container: HTMLElement
/**
* 视频连接
* video http 连接 或 file 对象
*/
videoUrl: string
videoUrl: string | HTMLInputElement
/**
* video id
* 用来做唯一播放对象处理 不重复注销video标签 适配微信 (目前是支持多video标签)
Expand Down
Loading