提问人:iexav 提问时间:10/26/2023 最后编辑:iexav 更新时间:11/12/2023 访问量:101
avcodec_send_packet高内存使用率
avcodec_send_packet high memory usage
问:
所以一段时间以来,我一直在制作类似视频播放器应用程序的东西。视频播放器接收视频流,对其进行解码并显示其内容。我使用 ffmpeg 包装器在 java 中编写它(这不应该有任何实际意义,我只是提到这一点,因为我提供的代码将是 java,但我使用的函数只是调用相关的 c/c++ 函数ffmpeg库)。问题是,当我打电话给avcodec_send_packet时,它会给我带来非常高的内存使用率,该内存使用率也会随着视频的分辨率而扩展。最近,我尝试解码 4k 视频流,每次调用avcodec_send_packet都会为我带来大约 40 MB 的内存使用量。这在第一帧之后稳定下来。换句话说,当我收到帧时,后续帧的内存使用量会停止攀升。对于特定的 4k 视频流,它攀升至大约 800 mb,对于随后的 avcodec_send_packet 和 avcodec_receive_frame 通话,内存使用率不会显着攀升。如果分辨率为 1080p,则为我提供大约 300 兆字节的内存。考虑到一个解码的 4k YUV420 帧约为 12 MB,这是一个相当高的内存使用率
public Optional<Boolean> decodeVideoFrame(AVPacket pkt,boolean readPacket,boolean keyFrames,boolean doProcessing) throws Exception {
int ret;
// Decode video frame
if (readPacket) {
ret = avcodec_send_packet(video_c, pkt);
if (pkt.data() == null && pkt.size() == 0) {
pkt.stream_index(-1);
}
if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {
// The video codec may have buffered some frames
} else if (ret < 0) {
// Ignore errors to emulate the behavior of the old API
// throw new Exception("avcodec_send_packet() error " + ret + ": Error sending a video packet for decoding.");
}
}
// Did we get a video frame?
while (true) {
ret = avcodec_receive_frame(video_c, picture);
if (ret == AVERROR_EAGAIN() || ret == AVERROR_EOF()) {
if (pkt.data() == null && pkt.size() == 0) {
return Optional.empty();
} else {
return Optional.of(true);
}
} else if (ret < 0) {
// Ignore errors to emulate the behavior of the old API
// throw new Exception("avcodec_receive_frame() error " + ret + ": Error during video decoding.");
return Optional.of(true);
}
这几乎是所有相关代码以及数据包分配:
AVPacket pkt = av_packet_alloc();
pkt.stream_index(-1);
int ret;
while(true){
if((ret = av_read_frame(grabber.getFormatContext(),pkt))<0){
if (ret == AVERROR_EAGAIN()) {
try {
Thread.sleep(10);
continue;
} catch (InterruptedException ex) {
// reset interrupt
Thread.interrupted();
}
}
av_packet_unref(pkt);
pkt.deallocate();
return null;
}
break;
}
return pkt;
(这是在单独的方法中)。此外,由此产生的另一个奇怪效果是,当视频分辨率高于 1080p 时,帧会损坏。我将附上两张图片,比较框架的外观和实际外观。它应该是什么样子 当它交给解码器时是什么样子 如果有人问这个问题,这不是因为我如何显示帧。我可以 100% 肯定地说,因为我已经使用了 3 种不同的视频帧渲染方式。第一个是我用于 ffmpeg 的包装器库附带的 CanvasFrame,然后我切换到 javafx ImageView,现在我正在使用 vulkan。所有三种方法的结果完全相同。解码器几乎在所有情况下都是 vp9,帧是 yuv420p。
我尝试浏览 stackoverflow 和其他一些地方,我看到了一个有点类似的帖子,但不幸的是我在那里找不到答案。
更新:我决定在我的程序中实现硬件加速,而这个问题仍然存在,看看它是否会有所帮助。通过硬件加速,内存使用率会大大降低,但这是意料之中的。我遇到的更奇怪的事情是:当分辨率高于 1080p 时,帧仍然损坏,除非我非常明确地说
avcodec_find_decoder_by_name("vp9_cuvid");
.如果我使用这个:
av_find_best_stream(oc, AVMEDIA_TYPE_VIDEO, -1, -1, vidCodec, 0);
这也分配了视频编解码器,它使用 VP9,但帧会损坏。不知道发生了什么。我也试过做
avcodec_find_decoder_by_name("vp9");
但这也给我留下了损坏的框架。(毕竟应该是相同的编解码器)。
答:
使用完后,应确保正确取消引用视频帧。av_frame_free
每个解码器都需要一些开销来存储参考帧,这可能是您观察到的高内存使用率的一个来源。从消费者方面影响这一点几乎是不可能的。当您控制此视频的编码步骤时,您可以修改一些参数(例如关键帧距离)以降低解码器的内存开销。
我注意到的另一件事:当您收到来自 时,您需要确保在收集了带有 的帧后再次发送此数据包。否则,您将在随后的帧中收到伪影(但是,这并不能解释您提到的损坏的帧)。AVERROR_EAGAIN
avcodec_send_packet
avcodec_receive_frame
损坏的帧很可能是由于错误的 YUV -> RGB 转换造成的。也许缺少一些数据(您和/或 V 平面)。但是,没有代码可以给出任何判断。
评论