• 提取视频中的帧画面


    demo仅测试了以file对象做为源,格式为mp4

    有几点可以考虑做成配置项:画布宽高;转 base64 或 blob 时,图片的格式以及质量;是否需要返回 base64 或 blob 的数组以及视频时长;

    我在实际应用中仅使用了base64的数组,选择某一个图片时,再将单个的 base64转blob

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>提取视频帧</title>
            <style type="text/css">
                #imgBox img {
                    width: 50%;
                }
            </style>
        </head>
    
        <body>
            <input type="file" id="input" accept="video/*" />
            <br>
            <br>
            <div id="imgBox"></div>
        </body>
    
        <script type="text/javascript">
            /**
             * 在视频中提取帧画面
             * @param { File | String } videoSource
             * @param { String } [interval = 1 / 30] - 以30fps提取每一帧,或传入指定间隔(s)
             * @returns { Object } obj: { base64Frames, blobFrames, duration }
             */
            function extractFramesFromVideo(videoSource, interval = 1 / 30) {
                return new Promise(async (resolve) => {
                    let videoBlob;
    
                    // 如果视频源是视频文件对象,直接赋值
                    if (typeof videoSource === "object") {
                        videoBlob = videoSource
                    } else {
                        // 如果是url路径,要先完全下载(无缓冲)
                        videoBlob = await fetch(videoSource).then(r => r.blob());
                    }
    
                    const videoObjectUrl = URL.createObjectURL(videoBlob);
                    const video = document.createElement("video");
    
                    let seekResolve;
                    video.addEventListener('seeked', async function() {
                        // 音视频移动/跳跃到新的位置,并寻址完成后执行此函数
                        if (seekResolve) seekResolve();
                    });
    
                    // 当前帧的数据可用时执行
                    video.addEventListener('loadeddata', async function() {
                        const canvas = document.createElement('canvas');
                        const context = canvas.getContext('2d');
    
                        // 画布宽高为视频原始宽高(考虑要不要做成配置项)
                        const [w, h] = [video.videoWidth, video.videoHeight]
                        canvas.width = w;
                        canvas.height = h;
    
                        // base64格式与blob对象格式的帧数组
                        const base64Frames = [],
                            blobFrames = [];
    
                        let currentTime = 0;
                        const duration = video.duration;
    
                        while (currentTime < duration) {
                            video.currentTime = currentTime;
                            // 设置完时间点后等待寻址完成
                            await new Promise(r => seekResolve = r);
    
                            context.drawImage(video, 0, 0, w, h);
                            let base64ImageData = canvas.toDataURL();
                            base64Frames.push(base64ImageData);
    
                            canvas.toBlob((blob) => {
                                blobFrames.push(blob)
                            })
    
                            // 提取画面的时间步进(间隔)
                            currentTime += interval;
                        }
                        resolve({
                            base64Frames, // base64格式的字符串数组
                            blobFrames, // blob对象格式的文件对象数组
                            duration // 视频总时长
                        });
                    });
    
                    // 在设置视频路径前先注册好监听事件,防止资源加载太快,事件发生在注册监听之前
                    video.src = videoObjectUrl;
                });
            }
    
    
            const input = document.getElementById("input")
            input.onchange = function(e) {
                const file = input.files[0]
    
                console.log("视频处理中……");
                console.time("耗时")
    
    
                // 以10秒的间隔取帧(取每一帧非常耗显卡算力)
                extractFramesFromVideo(file, 10).then(data => {
                    console.timeEnd("耗时");
                    console.log(data);
    
                    const box = document.getElementById("imgBox")
                    data.base64Frames.forEach(item => {
                        const img = new Image()
                        img.src = item
                        box.appendChild(img)
                    })
                })
            }
        </script>
    </html>
  • 相关阅读:
    第二十篇 sys模块
    第十九章 Python os模块,pathlib 判断文件是目录还是文件
    第三篇 Postman之 Tests(后置处理器,断言)
    第十八篇 模块与包--time&random模块&模块导入import(os.path.dirname(os.path.abspath(__file__)))
    Sublime text3最全快捷键清单
    第十七篇 Python函数之闭包与装饰器
    第二篇 Postman的高阶使用之配置全局变量及局部变量的调用及设置方法(手动方法)
    第六篇 常用请求协议之post put patch 总结
    第十六篇 Python之迭代器与生成器
    PCL—低层次视觉—关键点检测(Harris)
  • 原文地址:https://www.cnblogs.com/web-xu/p/14817338.html
Copyright © 2020-2023  润新知