提取視頻中的幀畫面


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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM