vue3videplayer播放m3u8视频流注意事项

发布时间 2023-09-15 15:50:35作者: DAmarkday

前言

使用vue3开发项目时,碰上需要做一个视频流列表的页面,最开始是想获取所有列表数据后创建对应的video标签,这样默认获取第一帧作为封面,同时暂停视频减轻不断请求的压力。但开发后发现视频就算暂停后也会继续请求视频流,多个视频反而会导致页面卡顿。

方案

手动获取视频的第一帧,通过canvas将其绘画出来后作为封面,隐藏销毁video标签。

部分代码块

<div ref="videoContainerRef" absolute top-5px bottom-5px left-5px right-5px>
    <vue3VideoPlay v-if="!video.isSingleShowPoster" :id="video.id"
        v-bind="createVideoParams(video.name, video.url, false, 'auto')"
        @loadeddata="getVideoPoster(video)" />
    <img v-else w-full h-full :src="video.poster" alt="preview">
</div>


<script setup lang="ts">

const video =  reactive({
        name: "video",
        id: "id",                   // 用作video标签的id
        isSingleShowPoster: false,  // 是否显示poster
        poster: '',                 // 自动获取poster第一帧
        url:"",
}) // 由于修改数据后dom树能够及时变化,涉及到双向绑定,所以数据需要是响应式

// preload要选择 auto 预加载,否则可能会获取不到第一帧
function createVideoParams(title: string, url: string, autoPlay: boolean = false, preload: 'auto' | 'metadata' | 'none' = 'none') {
    return {
        width: "100%", // 播放器高度
        height: "100%", // 播放器高度
        color: "#5B8BB3", // 主题色
        title: title, // 视频名称
        type: "m3u8",
        src: url, // 视频源
        muted: true, // 静音
        webFullScreen: false,
        autoPlay: autoPlay, // 自动播放
        volume: 0, // 默认音量大小
        control: false, // 是否显示控制
        preload: preload,
        controlBtns: ["fullScreen"], // 显示所有按钮,
    };
}


const videoDialogContainerRef = ref()
async function getVideoPoster(info: any) {
    const video = document.getElementById(info.id)! as HTMLVideoElement;
    video.currentTime = 0

    const canvas = document.createElement('canvas');

    // 绘制时画布大小最好和video标签大小一样(大小不一样可能绘制出来会糊)(代码关系可能会导致获取不到video大小导致canvas宽高为0,
    // 如果画布的高度或宽度是0,那么绘制出来的图片字符为"data:,")
    // 如果获取不到video标签的 绘制出来的图片会黑屏
    // 如果能直接获取获取video大小最好,这里通过video外部容器的高度近似获取video大小
    const { clientWidth, clientHeight } = videoDialogContainerRef.value;
    canvas.width = clientWidth;
    canvas.height = clientHeight;
    const ctx = canvas.getContext('2d', { alpha: false })!

    ctx?.drawImage(video, 0, 0, clientWidth, clientHeight);
    const img = canvas.toDataURL('image/jpeg', 1);
    canvas.remove();

    video.src = ""; // 绘制完成后停止video流请求,通过v-if移除dom树
    info.isSingleShowPoster = true;
    info.poster = img;
}
</script>

注意

监听函数一定得是"onloadeddata",如果preload=none: 页面加载时视频不加载可能绘制出来的图片就会使纯黑。
虽然设置了onloadeddata事件才去画它的第一帧,按理说应该视频已经加载出了第一帧了,但是由于没有设置preload=auto并没有截图到第一帧。(好像不管有没有preload都会执行onloadeddata)
所以要想截图第一帧就必须让它自动加载。因为各浏览器对preload不设置时的默认值可能不同还是手动设置一下比较好。

相关文章