代码之家  ›  专栏  ›  技术社区  ›  iexav

avcodec_send_packet内存使用率高

  •  0
  • iexav  · 技术社区  · 1 年前

    所以一段时间以来,我一直在制作类似视频播放器的应用程序。视频播放器接收视频流,对其进行解码并显示其内容。我是使用ffmpeg包装器用java编写的(这应该没有任何实际意义,我只是提到这一点,因为我提供的代码将用java编写,但我使用的函数只是调用ffmpeg库的相关c/c++函数)。问题是,当我调用avcodec_send_packet时,它会使我获得非常高的内存使用率,这也会随着视频的分辨率而变化。最近,我尝试解码一个4k视频流,每次调用avcodec_send_packet都会为我节省大约40 mb的内存。这在第一帧之后稳定下来。换言之,当我接收到帧时,对于随后的帧,内存使用停止攀升。对于特定的4k视频流,它攀升到了大约800mb,对于随后的avcodec_send_packet和avcodec_receive_frame调用,内存使用率没有显著攀升。如果分辨率是1080p,它会为我节省大约300兆字节的内存。考虑到一个解码的4k YUV420帧大约是12兆字节,这是一个相当高的内存使用率

        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种不同的视频帧渲染方式。第一个是我用于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");
    

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

    0 回复  |  直到 1 年前
        1
  •  0
  •   imikbox    1 年前

    您应该确保使用正确地取消引用视频帧 av_frame_free 当你用完它的时候。

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

    我注意到的另一件事是:当你收到 AVERROR_EAGAIN 从…起 avcodec_send_packet ,您需要确保,在收集到具有 avcodec_receive_frame 。否则,您将在后续帧中收到伪影(但是,这并不能解释您提到的损坏帧)。

    损坏的帧最有可能是由于错误的YUV->RGB转换。可能缺少一些数据(U平面和/或V平面)。然而,没有任何准则可以作出任何判断。