0

当我用ffmpeg转码这个源文件时,ffmpeg可能会占用几十GB的大量内存直到被杀死,为什么?

 the report here:
 ffmpeg started on 2017-12-19 at 10:59:15
 Report written to "ffmpeg-20171219-105915.log"
 Command line:
 ffmpeg -i memory_error_fifo.mp4 -s 854x480 -vcodec libx264 -x264opts
 "keyint=50" -b:v 238k -vf "movie=namei.jpg [watermark]; [vf0] fifo
 [vf1];[vf1][watermark] overlay=main_w-overlay_w-0:0[vf2]" -acodec
 libfdk_aac -ar 44100 -movflags faststart -f mp4 output.mp4 -y -v trace
 -report
 ffmpeg version N-89095-gb3c1172 Copyright (c) 2000-2017 the FFmpeg

 How to reproduce:
 ffmpeg -i "memory_error_fifo.mp4"  -s 854x480  -vcodec libx264 -x264opts
 keyint=50  -b:v 238k -vf "movie=namei.jpg [watermark]; [vf0] fifo
 [vf1];[vf1][watermark] overlay=main_w-overlay_w-0:0[vf2]" -acodec
 libfdk_aac  -ar 44100 -movflags faststart -f mp4 output.mp4 -y -v trace
 -report

当我使用 -an 禁用音频时,结果还可以;当我不使用“fifo”时,结果还可以,但我的项目需要这个“fifo”过滤器。

 n3.5-dev-1292-gce001bb with master commit
 ce001bb8fc6677541c401a614e05e5058d58dde1
 built on linux

ffmpeg report:https://pan.baidu.com/s/1qYNvTes

attached source file: https://pan.baidu.com/s/1pLzbwbp
4

1 回答 1

1

原因:fifo 过滤器在被请求之前不会输出帧。并且 ffmpeg 仅请求属于具有最小时间戳的流的帧(请参见 choose_output() 函数)。所以当媒体有一些异常内容时,比如一小段没有音频的纯图像或一些音频解码错误,ffmpeg 将继续请求音频帧,并在 fifo-filter 中阻塞数千个视频帧,从而占用您的内存。

解决方案:我遇到了同样的问题,并通过在 fifo 过滤器中添加样本限制来解决它。当缓冲的帧数超过限制时,它将强制下一个链接的过滤器请求帧。代码:

libavfilter/fifo.c:

+ #define NB_SAMPLE_MAX 500 // About 1GB memory

typedef struct Buf {
    AVFrame *frame;
    struct Buf        *next;
    AVFrame *out;
    int allocated_samples;      ///< number of samples out was allocated for
+   int buffered_samples;       ///< avoid memory overflow
} FifoContext;


static av_cold int init(AVFilterContext *ctx)
{
    FifoContext *fifo = ctx->priv;
    fifo->last = &fifo->root;
+   fifo->buffered_samples = 0;
    return 0;
}


static int add_to_queue(AVFilterLink *inlink, AVFrame *frame)
{
    FifoContext *fifo = inlink->dst->priv;
+   int i;  
    fifo->last->next = av_mallocz(sizeof(Buf));
    if (!fifo->last->next) {
        av_frame_free(&frame);
        return AVERROR(ENOMEM);
    }

    fifo->last = fifo->last->next;
    fifo->last->frame = frame;

+   fifo->buffered_samples++;
+   if (fifo->buffered_samples > NB_SAMPLE_MAX) {
+       av_log(NULL, AV_LOG_DEBUG, "Too many frames buffered in fifo.\n");
+       for (i = 0; i < inlink->dst->nb_outputs; i++) {
+           inlink->dst->outputs[i]->frame_wanted_out = 1; // it will force the next filter to request frames
+       }
+   }
    return 0;
}


static void queue_pop(FifoContext *s)
{
    Buf *tmp = s->root.next->next;
    if (s->last == s->root.next)
        s->last = &s->root;
    av_freep(&s->root.next);
    s->root.next = tmp;
+   if (s->buffered_samples > 0)
+       s->buffered_samples--;
}

我不确定这些更改是否会导致其他一些问题。如果有人有更好的解决方案,请告诉我。

于 2018-03-19T11:25:39.920 回答