使用 javascript 流式传输视频块

问题描述 投票:0回答:1

使用 JavaScript,我尝试接收视频块并将它们显示在 html 视频标签中。但在我的代码中,显示 3 或 4 个块后,它会卡住并且不会移动到下一个块。 你能帮我吗?


// Fetch an encrypted video chunk from the server
async function fetchChunk(chunkIndex) {
    const url = `http://localhost/video/encrypted_chunks/chunk_${chunkIndex}.enc`;
    try {
        const response = await fetch(url);
        if (response.ok) {
            return await response.arrayBuffer();
        } else {
            console.error(`Failed to fetch chunk ${chunkIndex}: Status ${response.status}`);
            return null;
        }
    } catch (error) {
        console.error(`Error fetching chunk ${chunkIndex}:`, error);
        return null;
    }
}

function appendBuffer(sourceBuffer, decryptedData) {
    return new Promise((resolve, reject) => {
        function onUpdateEnd() {
            sourceBuffer.removeEventListener('updateend', onUpdateEnd);
            resolve();
        }

        if (sourceBuffer.updating) {
            sourceBuffer.addEventListener('updateend', onUpdateEnd, { once: true });
        } else {
            sourceBuffer.appendBuffer(new Uint8Array(decryptedData));
            resolve();
        }
    });
}

// Finalize the MediaSource stream
function finalizeStream(mediaSource) {
    if (mediaSource.readyState === 'open') {
        mediaSource.endOfStream();
    }
}

// Main function to handle video streaming
async function streamVideo() {
    const mediaSource = new MediaSource();
    const video = document.getElementById('videoPlayer');
    video.src = URL.createObjectURL(mediaSource);

    mediaSource.addEventListener('sourceopen', async () => {
        const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8, vorbis"');
        let chunkIndex = 1;

        const filename = 'sample-2.webm'; // Replace with the actual filename
        const { key, iv } = await calculateKeyIv(filename);

        // Function to handle fetching, decrypting, and appending chunks
        async function handleChunks() {
            try {
                while (true) {
                    if (sourceBuffer.updating) {
                        await new Promise(resolve => sourceBuffer.addEventListener('updateend', resolve, { once: true }));
                    }

                    const encryptedData = await fetchChunk(chunkIndex);
                    if (!encryptedData) break; // No more data

                    const decryptedData = await decryptChunk(encryptedData, key, iv);
                    await appendBuffer(sourceBuffer, decryptedData);

                    chunkIndex++;
                }

                // Wait for buffer to be done updating before finalizing
                if (!sourceBuffer.updating) {
                    finalizeStream(mediaSource);
                    createWatermark();
                }
            } catch (error) {
                console.error('Error processing chunks:', error);
            }
        }

        // Start processing chunks
        handleChunks();
    });
}

// Call the streamVideo function to start streaming
streamVideo();

我认为问题可能出在缓冲区上,但我是 JavaScript 新手,我无法调试它!

javascript video-streaming html5-video buffering
1个回答
0
投票

看起来您在流媒体视频块方面走在正确的轨道上,但可能需要考虑一些可能导致您的问题的事情。根据您的代码,这里有一些建议来解决显示几个块后卡住的问题: 检查 sourceBuffer 状态:确保 sourceBuffer 未更新或尚未处于可能阻止附加新数据的状态。 正确处理 updateend:确保正确处理 updateend 事件以避免潜在的竞争情况。如果缓冲区更新尚未完成,则在循环内添加事件侦听器可能会产生问题。 谨慎使用 sourceBuffer.remove():如果您在代码中使用 sourceBuffer.remove() (代码片段中未显示),请小心,因为它可能会导致冲突。 确保decryptChunk正确:验证decryptChunk函数是否正常运行并且没有引入任何错误。

    // Fetch an encrypted video chunk from the server
async function fetchChunk(chunkIndex) {
    const url = `http://localhost/video/encrypted_chunks/chunk_${chunkIndex}.enc`;
    try {
        const response = await fetch(url);
        if (response.ok) {
            return await response.arrayBuffer();
        } else {
            console.error(`Failed to fetch chunk ${chunkIndex}: Status ${response.status}`);
            return null;
        }
    } catch (error) {
        console.error(`Error fetching chunk ${chunkIndex}:`, error);
        return null;
    }
}

function appendBuffer(sourceBuffer, decryptedData) {
    return new Promise((resolve, reject) => {
        function onUpdateEnd() {
            sourceBuffer.removeEventListener('updateend', onUpdateEnd);
            resolve();
        }

        if (sourceBuffer.updating) {
            sourceBuffer.addEventListener('updateend', onUpdateEnd, { once: true });
        } else {
            sourceBuffer.appendBuffer(new Uint8Array(decryptedData));
            resolve();
        }
    });
}

// Finalize the MediaSource stream
function finalizeStream(mediaSource) {
    if (mediaSource.readyState === 'open') {
        mediaSource.endOfStream();
    }
}

// Main function to handle video streaming
async function streamVideo() {
    const mediaSource = new MediaSource();
    const video = document.getElementById('videoPlayer');
    video.src = URL.createObjectURL(mediaSource);

    mediaSource.addEventListener('sourceopen', async () => {
        const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8, vorbis"');
        let chunkIndex = 1;

        const filename = 'sample-2.webm'; // Replace with the actual filename
        const { key, iv } = await calculateKeyIv(filename);

        // Function to handle fetching, decrypting, and appending chunks
        async function handleChunks() {
            try {
                while (true) {
                    // Wait for sourceBuffer to be ready
                    if (sourceBuffer.updating) {
                        await new Promise(resolve => sourceBuffer.addEventListener('updateend', resolve, { once: true }));
                    }

                    const encryptedData = await fetchChunk(chunkIndex);
                    if (!encryptedData) break; // No more data

                    const decryptedData = await decryptChunk(encryptedData, key, iv);
                    await appendBuffer(sourceBuffer, decryptedData);

                    chunkIndex++;
                }

                // Wait for buffer to be done updating before finalizing
                await new Promise(resolve => {
                    if (sourceBuffer.updating) {
                        sourceBuffer.addEventListener('updateend', resolve, { once: true });
                    } else {
                        resolve();
                    }
                });

                finalizeStream(mediaSource);
                createWatermark();
            } catch (error) {
                console.error('Error processing chunks:', error);
            }
        }

        // Start processing chunks
        handleChunks();
    });
}

// Call the streamVideo function to start streaming
streamVideo();
© www.soinside.com 2019 - 2024. All rights reserved.