WebCodecs - 从 jpg 图像创建 mp4 视频

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

我开始使用 WebCodecs API。我尝试从一组 jpg 图像创建 mp4 视频。 从简单的事情开始,我尝试从一张 jpg 图像创建 mp4。当调用输出回调时,它将视频块保存到 mp4 文件中。

   const downloadVideo = (data) => {
     const link = document.createElement("a");
     const file = new Blob([data], { type: 'video/mp4' });
     link.href = URL.createObjectURL(file);
     link.download = "video.mp4";
     link.click();
     URL.revokeObjectURL(link.href);
  };

  $('#create_movie').click( function(ev) {
            
            const videoEncoder = new VideoEncoder({
                output(chunk, metadata) {
                    console.log("timestamp:" + chunk.timestamp);
                    console.log("length:" + chunk.byteLength);
                    console.log("video chunk type:" + chunk.type);
                    console.log("duration:" + chunk.duration)
                    console.log(JSON.stringify(metadata));
                    var videoBlob = new ArrayBuffer(chunk.byteLength);
                    chunk.copyTo(videoBlob);
                    downloadVideo(videoBlob);
                },
                error(error) {
                    console.log(error);
                },
            });
            
            videoEncoder.configure( {
                codec: "avc1.42001E",
                height: 480,
                width: 640,
                framerate: 1,
                bitrate: 2_000_000, // 2 Mbps
            });

            console.log("video encoder created! " + videoEncoder);

            const myImage = new Image();
            myImage.src = "samples/pic0.jpg";

            var imageBitmap = null;
            var imageBitmapPromise = null;


            myImage.onload = () => {

                imgContext.drawImage(myImage,0,0);
                imageBitmapPromise = createImageBitmap(myImage);

                imageBitmap = imageBitmapPromise.then( result => {
                    const ms = 1_000_000; // 1µs
                    const fps = 10;
                    return new VideoFrame(result, {
                        timestamp: (ms * 1) / fps,
                        duration: ms / fps
                    },false);
                }).then(vidFrame => {
                    console.log("imageBitmap:" + vidFrame);
                    console.log("video frame timestamp:" + vidFrame.timestamp);
                    console.log("format:" + vidFrame.format);
                    videoEncoder.encode(vidFrame, { keyFrame: true });
                    videoEncoder.flush();

                    vidFrame.close();
                });

                console.log("imageBitmap:" + imageBitmap);
            }


            
        });
        
        
    });

不幸的是,视频打开时显示错误消息:您尝试播放的文件是空文件 我使用 ffmpeg 工具从同一个 jpg 文件生成了 mp4:

ffmpeg  -i pic%d.jpg -vcodec mpeg4 test.mp4

两个文件的结构完全不同:

左边是ffmpeg生成的视频。它包含我的代码生成的 vido 文件中缺少的一些元数据。所以我的问题是:如何使用 WebCodecs API 正确生成视频文件?

javascript jpeg encode mp4 webcodecs
1个回答
0
投票

“我尝试从一组 jpg 图像创建 mp4 视频”

WebCodecs 不会对 MP4 或 AVI 等容器进行任何混合。它只提供 原始编码帧(视频或音频),然后代码编写者自己将决定输出容器格式(如果需要)。

您是否假设您需要 MP4 才能显示它?如果是:H.264 可以按原样播放,因为它是一种视频格式。

或者您确定需要 MP4 输出(例如: HTML5)?如果是:祝你好运。不难,但是很乏味。

是为了预览吗?

如果您只是想检查您的代码是否正常工作,请使用像 VLC 这样的播放器来播放您保存的 H.264 文件。
VLC 在桌面上运行(因此只需将文件拖到其窗口中即可检查视觉效果)。

对于在线播放,您可以找到一个支持 JS 的复用器,例如:JMuxer,或者喜欢学习 MP4 结构,然后编写代码。我试图在另一个问题上解释一个起点,但他们从未回应。

您首先需要一个正确编码的 H.264 文件,否则没有播放器会接受它(请参阅下面的代码修复)。

对可播放的原始 H.264 进行编码:

(1) 在函数

downloadVideo()
中,您需要作为二进制流下载..

替换:

const file = new Blob([data], { type: 'video/mp4' });

与:

let file = new Blob(  
                        [ Uint8Array.from( data ) ] , 
                        {type: "application/octet-stream"} 
                    );

使用

"application/octet-stream"
将保证您获得实际的 binary 字节值(没有任何不需要的格式转换,例如 ASCII 或 UTF 等)。另外更安全(问题更少)的方法是将输出字节包装到新的
Uint8Array.from
缓冲区中。

(2) 保存时您需要包含元数据。 Webcodecs 将其提供为

metadata.decoderConfig

const videoEncoder = new VideoEncoder(
                                        {
                                            output(chunk, metadata) 
                                            {
                                                console.log("timestamp:" + chunk.timestamp);
                                                console.log("length:" + chunk.byteLength);
                                                console.log("video chunk type:" + chunk.type);
                                                console.log("duration:" + chunk.duration)
                                                console.log(JSON.stringify(metadata));
                                                
                                                var myMetadata; var videoBlob
                                                
                                                if (metadata.decoderConfig) 
                                                {
                                                    //# save the decoder's description/config ...
                                                    //# is SPS and PPS metadata needed by players to display H.264)
                                                    myMetadata = new Uint8Array( metadata.decoderConfig.description );
                                                }
                                                else
                                                { alert("WARNING : No Metadata Found --> Saved bytes will not be playable" );
                                                
                                                videoBlob = new ArrayBuffer(chunk.byteLength);
                                                chunk.copyTo(videoBlob);
                                                
                                                //# combine the two arrays (in the shown order of appearance)
                                                var output_Bytes = [...myMetadata, ...videoBlob];
                                                
                                                downloadVideo( output_Bytes );
                                            },
                                            
                                            error(error) { console.log(error); },
                                        }
                                    );
© www.soinside.com 2019 - 2024. All rights reserved.