阅读 87

FFmpeg - 模仿B站实现视频预览卡片

B站视频卡片预览

打开浏览器控制台,当鼠标指向某个视频卡片时,会加载一张大图,这张图是当前视频许多帧的合集,所以视频预览的本质是预览图片,鼠标滑动时改变大图在容器的位置,就能实现这种效果。

视频预览实现

1、准备视频素材

下载地址:https://share.weiyun.com/Ps99dLfl

2、生成预览图

ffmpeg -i anoyi.mp4 -vf select='not(mod(n\,60))',scale=200:-1,tile=10x1 -frames:v 1 preview.png
preview 10x1

ffmpeg 命令解释说明:

  • select='not(mod(n\,60))':每60帧取1帧
  • scale=200:-1:按宽度200等比缩放
  • tile=10x1:取10帧合成1帧,排列顺序是 10x1(这里合成一行是为了方便后续前端计算)

如果做成和B站一样的形式,可以使用如下命令:

ffmpeg -i anoyi.mp4 -vf select='not(mod(n\,60))',scale=200:-1,tile=4x3:nb_frames=10 -frames:v 1 preview_4x3_10.png
preview 4x3

服务端源码参考(基于ffmpeg-python):

def get_scaled_preview_frame_by_frame(file_path: str, start, end, interval, count, width):
    """
    获取缩放的视频帧
    :param file_path: 视频地址
    :param start: 起始帧数,默认值:0
    :param end: 结束帧数,默认值:1
    :param width: 缩放宽度,默认值:720
    :param interval: 帧数间隔,默认值:1
    :param count: 帧的数量,默认值:1
    :return:
    """
    out, err = (
        ffmpeg
            .input(file_path)
            .filter('select', f'gte(n, {start})*lt(n, {end})')
            .filter('select', f'not(mod(n, {interval}))')
            .filter('scale', width, -1)
            .filter('tile', f'{count}x1')
            .output('pipe:', vframes=1, format='image2', vcodec='mjpeg', **{'q:v': 0})
            .run(capture_stdout=True)
    )
    return out, err

3、前端实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Video Preview</title>
    <style>
        .video-bg {
            height:100%; width: 100%; background-image: url(preview.png); background-repeat: no-repeat; background-position: 0 0;
        }
    </style>
</head>
<body style="margin:0; padding:0">

    <div style="height: 100vh; width:100%;display: flex; align-items: center; justify-content:center">
        <div style="width: 200px; height:113px; position: relative; cursor: pointer;" onmousemove="mouseMove(event)" onmouseout="mouseOut()">
            <div class="video-bg" id="preview"></div>
        </div>
    </div>

</body>

<script>

    // 节流与防抖
    let ms = 200;
    let lastEvent = Date.now() - ms;

    const mouseMove = (event) => {
        if (Date.now() - lastEvent >= ms) {
            let offset = 200 * parseInt(event.offsetX / 20);
            let style = `background-position: -${offset}px 0;`;
            document.getElementById('preview').setAttribute('style', style);
            lastEvent = Date.now();
        }
    }

    const mouseOut = () => {
        document.getElementById('preview').setAttribute('style', `background-position: 0 0;`);
    }

</script>
</html>

4、效果预览

参考文档

作者:Anoyi

原文链接:https://www.jianshu.com/p/9d2abebd4f35

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐