avcodec_send_packet内存占用高

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

有一段时间我一直在制作类似视频播放器应用程序的东西。视频播放器接收视频流,对其进行解码并显示其内容。我正在使用 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 MB 的内存。考虑到一个解码的 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 时,帧会损坏。我将附上两张图片来比较框架的预期外观和实际外观。What its supposed to look like What it looks like when given to the decoder 如果有人问这个问题,这并不是因为我显示框架的方式。我可以 100% 肯定地说,因为我已经尝试过 3 种不同的视频帧渲染方式。第一个是 CanvasFrame,它附带了我用于 ffmpeg 的包装库,然后我切换到 javafx ImageView,现在我使用 vulkan。所有三种方法的结果完全相同。解码器几乎在所有情况下都是 vp9 并且帧是 yuv420p。

我尝试浏览 stackoverflow 和其他一些地方,我看到了一篇有点类似的帖子,但不幸的是我在那里找不到答案。

ffmpeg decoding
1个回答
0
投票

使用完毕后,您应该确保使用

av_frame_free
正确取消引用视频帧。

每个解码器都需要一些开销来存储参考帧,这可能是您观察到的高内存使用率的根源之一。从消费者方面影响这一点几乎是不可能的。当您控制该视频的编码步骤时,您可以修改一些参数(例如关键帧距离)以降低解码器的内存开销。

我注意到的另一件事:当您从

AVERROR_EAGAIN
收到
avcodec_send_packet
时,您需要确保在使用
avcodec_receive_frame
收集帧后再次发送此数据包。否则,您将在后续帧中收到伪影(但是,这并不能解释您提到的损坏的帧)。

损坏的帧很可能是由于错误的 YUV -> RGB 转换造成的。也许某些数据丢失(U 和/或 V 平面)。然而,没有代码可以给出任何判断。

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