avcodec_send_packet高内存使用率

avcodec_send_packet high memory usage

提问人:iexav 提问时间:10/26/2023 最后编辑:iexav 更新时间:11/12/2023 访问量:101

问:

所以一段时间以来,我一直在制作类似视频播放器应用程序的东西。视频播放器接收视频流,对其进行解码并显示其内容。我使用 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");

但这也给我留下了损坏的框架。(毕竟应该是相同的编解码器)。

FFMPEG 解码

评论


答:

0赞 imikbox 11/1/2023 #1

使用完后,应确保正确取消引用视频帧。av_frame_free

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

我注意到的另一件事:当您收到来自 时,您需要确保在收集了带有 的帧后再次发送此数据包。否则,您将在随后的帧中收到伪影(但是,这并不能解释您提到的损坏的帧)。AVERROR_EAGAINavcodec_send_packetavcodec_receive_frame

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

评论

0赞 iexav 11/3/2023
我在应用程序的整个过程中重复使用相同的 AVFrame。不幸的是,这不是原因,因为这个问题甚至在首先收到帧之前就发生了。我在avcodec_send_packet行上暂停调试器,并观察内存使用情况。当我跨过该行时,内存使用量为 +40mb。这重复了几次,直到对avcodec_receive_frame的调用最终成功
0赞 iexav 11/3/2023
哦,还有我在之前的评论中忘记提到的一件事:我无法控制编码过程,但我可以向你保证,这不是因为这个。更不用说这仍然留下了框架损坏的谜团,我觉得如果其中一个问题得到解决,也许那也会解决另一个问题。
0赞 imikbox 11/4/2023
您可以尝试使用具有不同编解码器的其他视频,看看是否观察到相同的行为
0赞 iexav 11/4/2023
我尝试了两件事。我下载了一个最初用 h264 编码的单独视频并播放了它。内存使用率仍然很高(尽管它低了大约 100-200 mb),但更有趣的部分是不再发生碎片。图像本身看起来不错。之后,我将原始视频从 vp9 转码为 h264 并播放。同样的结果,不知道发生了什么。我知道使用率不应该那么高,因为 youtube 使用 vp9,当您在 yt 上播放视频时,您不会看到您的内存飙升至 800mb
0赞 iexav 11/5/2023
刚刚注意到您编辑了您的评论(我也编辑了我的帖子)YUV->RGB 转换很好,我可以 100% 保证。此外,如果它返回 EAGAIN,我将发送流中的下一个数据包(decodeVideoFrame 函数与流中的下一个数据包一起调用)如果您查看我帖子的更新,您会看到完整的硬件解码器 (vp9_cuvid) 工作得很好。如果我只对 vp9 使用硬件加速,我仍然会遇到奇怪的帧损坏。我很确定软件 vp9 解码器有问题。