VideoDecoder解码输出未调用

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

我正在尝试解码后端发送的 H264 帧,如下所示:

/*
packet = {
  type: "frame",
  keyframe: <Boolean>,
  pts: <BigInt>,
  data: <ArrayBuffer/Uint8Array>
}
*/
const chunk = new EncodedVideoChunk({
  type: packet.keyframe === false ? 'delta' : 'key',
  timestamp: 0,
  data: data
});
console.debug("CHUNK");
this.decoder.decode(chunk);

解码器看起来像这样:

this.decoder = new VideoDecoder({
  output: (frame) => {
    console.debug("DECODE")
    <...>
  error: (error) => {
    console.error(error);      
  }
});

我遇到的问题是我的日志

DECODE
从未打印过,而
CHUNK
是,但与此同时,也没有抛出任何错误。

如果有人知道我还能尝试什么,我将不胜感激。

javascript h.264 webcodecs
2个回答
0
投票

“我正在尝试解码后端发送的 H264 帧,就像这样……”

解码器设置取决于您输入的 H.264 数据:
假设它以 AnnexB 格式提供数据,其编解码器名称已知(eg:

"avc1.42C01E"
)。

你可以找到“avc1”后面的部分。通过提取 SPS 中的前 3 个字节值并将它们分别转换为 hex string.

AnnexB 意味着...

  • 每一帧以字节序列开始:
    00 00 00 01
    .
  • 如果是第一帧,那么它还必须包括SPSPPS数据。

H.264 关键帧字节示例(来自 AnnexB 文件):

00 00 00 01 67 42 C0 1E DD EC 04 40 00 00 03 00 40 00 00 0F 03 C5 8B E0 00 00 00 01 68 CE 0F 2C 80 00 00 01 65 88 84 04 BC 46 28 00 0C 7D 47 00 01 7D 78 E0 00 22 3D 80

以下是解码上述 H.264 关键帧字节所需的最低代码。

  • 未来的帧只需要使用以下方法更新新的帧数据:

    myOutputFrameData.data =  new Uint8Array( some frame bytes )

  • 同时更新框架“类型”,可以是

    "key"
    (IDR&I框架)或
    "frame"
    (P&B框架):

    myOutputFrameData.type = "key"

  • 然后解码新的帧数据:

    const chunk_frame = new EncodedVideoChunk( myOutputFrameData );

    decoder.decode( chunk_frame );

解码某些 H.264 帧的示例代码...

<!DOCTYPE html>
<html>
<body>
<!-- Canvas to display a decoded frame -->
<canvas id="myCanvas" width="320" height="240">
</canvas>

<br>A decoded frame (eg: yellow fill)

<script>
    
//# for displaying decoded frame in Canvas
const myCanvas = document.getElementById("myCanvas");
const ctx = myCanvas.getContext("2d");

//# STRING for codec format... 
str_codec_format = "annexb";

//# STRING for codec name... is: "avc1." + 0xAA + 0xBB + 0xCC
var str_codec_name = "avc1.42C01E";

///////////////////////////////////////////

//# AnnexB needs SPS + PPS at front then followed by keyframe..
let myFrameData_Key = new Uint8Array( 
                                        [   
                                            //# SPS (0x67 ... etc )
                                            0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xC0, 0x1E, 0xDD, 0xEC, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0F, 0x03, 0xC5, 0x8B, 0xE0, 
                                            
                                            //# PPS (0x68 ... etc )
                                            0x00, 0x00, 0x00, 0x01, 0x68, 0xCE, 0x0F, 0x2C, 0x80,
                                            
                                            //# Keyframe data (0x65 ... etc )                       
                                            0x00, 0x00, 0x00, 0x01, 0x65, 0x88, 0x84, 0x04, 0xBC, 0x46, 0x28, 0x00, 0x0C, 0x7D, 0x47, 0x00, 0x01, 0x7D, 0x78, 0xE0, 0x00, 0x22, 0x3D, 0x80 
                                        ] 
                                    );
                                    
//# setup decoder initialize...
let decoder_init = {
  output: ( videoFrame ) => {  handleFrame( videoFrame ); },
  error: ( evt ) => { console.log( evt.message ); },
};

//# setup decoder config...
let decoder_config = {};
decoder_config.codec = str_codec_name;
decoder_config.avc = { format: str_codec_format }
decoder_config.hardwareAcceleration = "prefer-hardware";

//# start Video decoder...
const decoder = new VideoDecoder( decoder_init );
decoder.configure( decoder_config );

//#  Object to send to Video decoder (holds frame data + frame settings)
let myOutputFrameData = { }; 
myOutputFrameData.type = "key";
myOutputFrameData.timestamp = 0;

//# Add frames bytes into Object sent to the VideoDecoder
myOutputFrameData.data =  new Uint8Array( myFrameData_Key );

/////# Try to decode a keyframe

//# convert Object into EncodedVideoChunk...
const chunk_frame = new EncodedVideoChunk( myOutputFrameData );

//# Decoder only accepts Objects from EncodedVideoChunk...
decoder.decode( chunk_frame );
  
async function handleFrame( input )
{
    alert("Decode success : Got Frame = " + input );

    //# draw frame inside a Canvas object
    ctx.drawImage(input, 0, 0, myCanvas.width, myCanvas.height);

    //# close frame and flush decoder (send to display)
    input.close(); //decoder.flush();
    await decoder.flush();
}

</script>

</body>
</html> 

0
投票

单个

decode()
通常不会导致帧被回调。原因是(我假设)解码器不知道下一个
decode()
是否将用于显示顺序较早的帧(字节流中的帧顺序可能与它们的顺序不同)显示,
VideoDecoder
按显示顺序(呈现顺序)输出帧。

所以解决方案是让

VideoDecoder
知道你已经完成了调用:

await this.decoder.flush()

(或者

this.decoder.flush().then(() => console.log("FLUSHED"))
如果它不是异步函数)

注意,如果你想调用

this.decoder.close()
,这必须在after冲洗完成(即等待
flush
承诺,或将其放在
then()
部分。

© www.soinside.com 2019 - 2024. All rights reserved.