用 AVCodec 做自定义播放器,初始化、配置、启动,回调也注册了,但 outputBufferAvailable 一直不触发,画面停在第一帧。日志里 inputBufferAvailable 来过,说明解码器在等数据,数据也 PushInputBuffer 了,但输出就是不来。查了两天才发现,问题不在解码器配置,在输入缓冲区的归还时机:我把同一个 buffer 的 index 连续 push 了多次,解码器认为这个 index 还在被占用,不会再把它交还给我,整条输入链就此死锁。

输入缓冲区按 index 追踪,push 之前确认它已经“还给你”了

VideoDecoder 的输入缓冲区模型是 index 驱动的。inputBufferAvailable 回调告诉你“index 为 N 的缓冲区现在可以填数据了”,你填完调用 PushInputBuffer 交回去,然后必须等到下一次 inputBufferAvailable 再次报告同一个 index,才能重新使用它。如果你在第一次 push 之后,还没收到新一轮回调就再次 push 同一个 index,解码器内部的占用状态不会更新,后续的回调也可能停掉。

typescript

decoder.on('inputBufferAvailable', (index: number, buffer: ArrayBuffer) => {
  if (this.pendingIndex !== -1) {
    // 上一个 index 还没被解码器交还,不能复用
    this.queue.push({ index, buffer });
    return;
  }
  this.fillAndPush(index, buffer);
});

private fillAndPush(index: number, buffer: ArrayBuffer): void {
  this.pendingIndex = index;
  // 填充压缩数据...
  decoder.pushInputBuffer(index);
}

pendingIndex 是一次只允许一个输入在途的闸门。解码器处理完会再次回调 inputBufferAvailable,那时候 pendingIndex 才应该被清掉。如果你需要更高的吞吐,可以维护一个有限大小的在途队列,但同一个 index 绝对不能在没有新回调的情况下重复 push。

Buffer 模式和 Surface 模式选错了,输出回调的行为不一样

AVCodec 输出有两种模式:Buffer 模式把解码后的 YUV/RGB 拷到用户空间,适合逐帧处理、滤镜、分析;Surface 模式解码后直接送 Surface,数据不经过用户空间,适合纯播放。如果你用的是 Surface 模式,outputBufferAvailable 的行为和 Buffer 模式不完全相同,送显和释放的接口是 RenderOutputBuffer 或 RenderOutputBufferAtTime,不是手动拷贝。模式选错了,回调看起来“没来”,实际上是输出路径和你预期的不一致。

花屏绿屏先查送进去的码流,不是解码器参数

点播场景出现块状花屏或绿屏,大概率不是解码器配置错了,而是送进去的压缩数据不完整。HEVC 和 AVC 都是块编码,某个数据块缺了,解码出来就是纯色方块。先用一个已知完好的本地文件走完整链路,确认 Demuxer 抽出来的码流长度和关键帧对齐是否正确,再去调解码器参数。解码器参数能调的东西不多,码流完整性才是更常见的根因。

收尾时送显和释放的调用顺序也要对:Surface 模式下每一帧解码输出都必须调用 RenderOutputBuffer 或 FreeOutputBuffer,不送显就释放,不释放就再也不会有新的输出回调。输入 index 管死锁,输出 buffer 管泄漏,两头都收住,解码链路才会稳定跑下去。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐