ffmpeg使用x264编码avcodec_open2出现x264_malloc内存泄漏问题分析

问题:

使用x264进行视频编码,利用xcode提供的leak检查工具发现有一处x264_malloc的内存泄漏点,蜜汁泄漏,无从查起。

现象描述:

假设编码参数qmin = n, qmax = m, 则共有(m-n+1)处x264_malloc泄漏。多次反复试验发现此泄漏也不是每次都出现,Google之后发现也有同学遇到过这个问题,但并没有给出具体解决办法或结论,参考链接https://ask.csdn.net/questions/258684。泄漏点如下图:

问题定位:

下面就是艰难的探索之旅。。。。。

首先,因为是以静态库的方式引入x264,泄漏点没有具体调用栈,只可知是x264_malloc泄漏,难以追溯。所以第一步是导入x264源码并再次使用xcode的内存检测工具测试,这样调用栈就很清晰了:

       出于偷懒那就继续换个关键词Goolge吧,能不自己动手就不自己动手。搜索发现提交历史里x264_analyse_init_costs曾经确实有泄漏,但邮件列表显示2016年已经有fix记录了参考https://mailman.videolan.org/pipermail/x264-devel/2016-January/011530.html 。赶紧查看自己的x264源码有没有这次提交,很遗憾,git show 5c6570495f8f1c716b294aee1430d8766a4beb9c 确实显示这个fix已经上master分支了。至此,只好自己看x264源码了,下面就是不知所以的下x264源码了,咬牙看吧:

 

     根据调用栈(x264_analyse_init_costs ---> init_costs ---> CHECKED_MALLOC ---> x264_malloc)提示,最终泄漏点代码在这里:

int x264_analyse_init_costs( x264_t *h )
{
    int mv_range = h->param.analyse.i_mv_range << PARAM_INTERLACED;

    ....
    ....

    for( int qp = X264_MIN( h->param.rc.i_qp_min, QP_MAX_SPEC ); qp <= h->param.rc.i_qp_max; qp++ )
        if( init_costs( h, logs, qp ) )
            goto fail;

    ....
    ....

}
static int init_costs( x264_t *h, float *logs, int qp )
{
    if( h->cost_mv[qp] )
        return 0;
    
    int mv_range = h->param.analyse.i_mv_range << PARAM_INTERLACED;
    ....
    CHECKED_MALLOC( h->cost_mv[qp], (4*4*mv_range + 1) * sizeof(uint16_t) );
    h->cost_mv[qp] += 2*4*mv_range;
    ....
    ....
}

    刚刚好和现象一致,for循环h->param.rc.i_qp_max - h->param.rc.i_qp_min + 1次,每次CHECKED_MALLOC来分配内存,循环次数和检测到的泄漏次数一致。

    找到了分配内存的地方,接下来就得找释放内存的地方,既然有x264_analyse_init_costs,那总该有个相关的free/release啥的吧,又是刚刚好,x264_analyse_init_costs函数下边就是x264_analyse_free_costs函数:

void x264_analyse_free_costs( x264_t *h )
{
    int mv_range = h->param.analyse.i_mv_range << PARAM_INTERLACED;
    for( int i = 0; i < QP_MAX+1; i++ )
    {
        if( h->cost_mv[i] )
            x264_free( h->cost_mv[i] - 2*4*mv_range );
        for( int j = 0; j < 4; j++ )
        {
            if( h->cost_mv_fpel[i][j] )
                x264_free( h->cost_mv_fpel[i][j] - 2*mv_range );
        }
    }
}

      如上,如果调用了x264_analyse_free_costs,理应不会泄露啊,但诡异的地方是init_costs里mallc后又对分配的内存自增了:
                        h->cost_mv[qp] += 2*4*mv_range;

      而x264_analyse_free_costs进行free时又对内存指针做了个减法:

                        x264_free( h->cost_mv[i] - 2*4*mv_range );

      所以如果cost_mv[i]没有在其他地方被修改,而且mv_range也没有被修改,那么应该不会有泄漏才对。况且如果mv_range被修改的话极可能导致野指针崩溃,x264的编码者不至于傻到这水平。那到底咋回事呢?最简单暴力的方法就是添加日志呗,看看指针在mallc和free时地址是不是一致对应的。加日志如下:

printf("init cost_mv[%d]=%p,mv_range=%d\n",qp,h->cost_mv[qp],mv_range);//加在CHECKED_MALLOC后

printf("free cost_mv[%d]=%p mv_range=%d\n",i,(h->cost_mv[i] - 2*4*mv_range),mv_range);//加在x264_free(....)前;

跑起来得到的日志是:

init cost_mv[0]=0x107926040,mv_range=256
init cost_mv[1]=
0x107928240,mv_range=256
init cost_mv[2]=
0x10792a440,mv_range=256
init cost_mv[3]=
0x10792c640,mv_range=256
......
init cost_mv[69]=0x10799d040,mv_range=256

free cost_mv[0]=0x107926040 mv_range=256
free cost_mv[1]=
0x107928240 mv_range=256
free cost_mv[2]=
0x10792a440 mv_range=256
free cost_mv[3]=
0x10792c640 mv_range=256
......
free cost_mv[69]=0x10799d040 mv_range=256

 

       日志里看出malloc和free的地址一一对应,事实上这里并没有泄露。只要调了x264_analyse_init_costs之后确保调用了x264_analyse_free_costs,那么内存泄漏不应该存在才对。而根据调用关系,x264_analyse_init_costs是在avcodec_open2里被调用,x264_analyse_free_costs是在avcodec_close被调用,也就是说只要avcodec_open2只后avcodec_close正确调用,应该不存在内存泄漏。

      日志已经告诉我其实并没有内存泄漏,那就是xcode工具有问题喽,难道工具不是根据mallc和free的统计来检测泄漏的?为了验证这个猜想,我在avcodec_open2下一行加了一句usleep(1000000000),让代码等很久看看xcode内存工具的行为。现象是sleep的过程中就已经报告70处x264_malloc泄漏了,此时这些指针的生命周期还没结束呢,xcode根本就没有等到free检查就是说泄露了!!猜想很可能和h->cost_mv[qp] += 2*4*mv_range这个骚操作有关,认为指向malloc出来内存的指针在赋值后立即自增指向别处了,刚刚malloc出的内存便无人认领,就认定泄漏了,而实际上这些malloc的内存在x264_analyse_free_costs时又通过- 2*4*mv_range的骚操作捡回来。

结论:

     只要正确调用了avcodec_close,内存泄漏并不存在,xcode内存检测有bug

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值