zoukankan      html  css  js  c++  java
  • 提取视频中的帧画面

    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>
  • 相关阅读:
    PAT 1010. 一元多项式求导 (25)
    PAT 1009. 说反话 (20) JAVA
    PAT 1009. 说反话 (20)
    PAT 1007. 素数对猜想 (20)
    POJ 2752 Seek the Name, Seek the Fame KMP
    POJ 2406 Power Strings KMP
    ZOJ3811 Untrusted Patrol
    Codeforces Round #265 (Div. 2) 题解
    Topcoder SRM632 DIV2 解题报告
    Topcoder SRM631 DIV2 解题报告
  • 原文地址:https://www.cnblogs.com/web-xu/p/14817338.html
Copyright © 2011-2022 走看看