赞
踩
最近写了一个小功能,是在前端播放视频流(格式flv/mp4,编码h.264/h.265),一开始找到了LivePlayer。但很遗憾后续被告知需要支持h.265(chrome目前支持h.264编码但不支持h.265);于是又搜寻了一番找到了Easyplayer,照着上面Vue集成的部分搞了一下,WTF居然不行?出现了跟这个issue一样的问题,于是就换了个思路,直接把它用原生方式引入,成功了,具体操作如下:
若不配置,vue-loader会报warning,提示此组件不是Vue的组件也不是标准组件库里的组件。
configureWebpack->module->rules下:
按照文档上HTML集成示例使用即可。
这个时候已经能用了,但而后又发现了一个问题,直播推h.265的流没有任何问题,但是录播的流不知道为什么无法解析。后来领导从别人那顺了一份旧版本的EasyPlayer说是可以,我就又看了一下,写完发现果然可以。
旧版本集成方式和新版本一样,就是使用起来不太一样,使用方式可以看文档旧版EasyPlayer。由于项目上了ts,于是又自己写了一个声明文件,很简陋,凑活着用:
declare class WasmPlayer { constructor( url: string | undefined | null, id: string, cb?: (e: any) => void, opts?: { decodeType?: 'auto' | 'soft'; openAudio?: boolean; BigPlay?: boolean; Height?: boolean; HideKbs?: boolean; cbUserPtr?: any; cfKbs?: (e: any) => void; } ) { return } play(url: string, autoplay = 0, currentTime = 0) { return } pause() { return } destroy() { return } openAudio() { return } closeAudio() { return } startLoading() { return } endLoading() { return } fullScreen() { return } setSnap(url: string) { return } endSnap() { return } }
另外旧版本的EasyPlayer下面自带一条无法清除的toolbar,toolbar上还有一个无法清除的logo,点击会跳转到官网。这个就只能在源码里修改了,由于源代码已经做过混淆,让我找了半天。清除logo全局搜索this.logo.style=
,并将其中的display改为none;toolbar全局搜索timeBox.style
,这两者前者还有验证函数,会验证logo的样式,因此再搜索LogoTimer
修改其中的验证条件;后者会定时修改样式为初始样式,因此需要再搜索setTimeout
,找到其中有timeBox.style
的一条代码,修改其样式。至此大功告成。
本来我觉得这个问题大概不是什么问题,因为本身新版EasyPlayer是自带的,但找了找发现旧版并没有这个方法。没办法只能自己写了。
一开始我以为EasyPlayer它是默认将视频流渲染成canvas的。因此只做了对canvas的截图和录制。
截图方法:
async function snapshot() { const playerEle = document.getElementById(id.value) as HTMLElement const children = playerEle.children const canvas = Object.values(children).find( child => child.tagName.toLocaleLowerCase() === 'canvas' ) as HTMLCanvasElement let url = '' if (canvas) { // 由于视频流有空白帧,渲染到canvas上后导致canvas截图空白,故如果截图时机不好会截出来空白的图,因此判断图片大小以避免空白图(空白图大小<100kb) const interval = setInterval(() => { url = canvas.toDataURL('image/jpg') if (url.length > 204800) { clearInterval(interval) const aEle = document.createElement('a') aEle.setAttribute('href', url.replace('image/jpg', 'image/octet-stream')) aEle.setAttribute( 'download', `${props.videoOpts.channel_no}-${props.videoOpts.guid}.jpg` ) aEle.click() aEle.remove() } }, 50) } }
甚至本来截图我是没想到它会能截出来空白的,后来发现之后给它加了个循环,直到截出正常图片为止。
录制方法:
let recorder: MediaRecorder let blobData: Blob[] = [] function record() { const playerEle = document.getElementById(id.value) as HTMLElement const children = playerEle.children const canvas = Object.values(children).find((child) => child.tagName.toLocaleLowerCase() === 'canvas') as HTMLCanvasElement if (canvas) { const stream = canvas.captureStream(30) recorder = new MediaRecorder(stream, { mimeType: 'video/webm;codecs=vp9'}) recorder.ondataavailable = (e) => { blobData.push(e.data) } recorder.start(10) } } function stopRecord() { recorder.stop() const blob = new Blob(blobData) const url = window.URL.createObjectURL(blob) const aEle = document.createElement('a') aEle.setAttribute('href', url) aEle.setAttribute('download', `${props.videoOpts.channel_no}-${props.videoOpts.guid}.webm`) aEle.click() aEle.remove() blobData = [] }
本来到这也就告一段落了,结果某一天,又发现h.264视频无法截图也无法录制,查找了一番发现是EasyPlayer会分析视频编码格式,如果是h.264格式就会利用<video>标签简化操作,只有h.265编码才会转成canvas。于是我又改了下截图方法,至于录制方法,我暂时不知道怎么录制<video>播放的视频,唯一能想到的就是每秒截图30次拼接起来,但考虑到性能开销太大,于是让后端处理了,如果有大佬会的话麻烦告知一下。
改进后的截图:
async function snapshot() { const playerEle = document.getElementById(id.value) as HTMLElement const children = playerEle.children const canvas = Object.values(children).find( child => child.tagName.toLocaleLowerCase() === 'canvas' ) as HTMLCanvasElement const video = Object.values(children).find( child => child.tagName.toLocaleLowerCase() === 'video' ) as HTMLVideoElement let url = '' if (canvas) { // 由于视频流有空白帧,渲染到canvas上后导致canvas截图空白,故如果截图时机不好会截出来空白的图,因此判断图片大小以避免空白图(空白图大小<100kb) const interval = setInterval(() => { url = canvas.toDataURL('image/jpg') if (url.length > 204800) { clearInterval(interval) const aEle = document.createElement('a') aEle.setAttribute('href', url.replace('image/jpg', 'image/octet-stream')) aEle.setAttribute( 'download', `${props.videoOpts.channel_no}-${props.videoOpts.guid}.jpg` ) aEle.click() aEle.remove() } }, 50) } else if (video) { const target = await html2canvas(video) url = target.toDataURL('image/jpg') const aEle = document.createElement('a') aEle.setAttribute('href', url.replace('image/jpg', 'image/octet-stream')) aEle.setAttribute('download', `${props.videoOpts.channel_no}-${props.videoOpts.guid}.jpg`) aEle.click() aEle.remove() } else { message.error('不支持的dom类型') return } }
其中video的截图使用了html2canvas。
至此整个视频流播放问题都解决了,以后说不定还会再用ffmpeg自己写一个视频流解析的组件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。