pjsip内存优化及提升视频呼叫并发数

发布时间 2023-10-24 17:02:09作者: 阿风小子

    工作上的一个上层调度台应用(Windows 7),业务功能上有并发调取多个视频的需求,发现调取30左右路D1视频后会导致崩溃,日志提示:except.c  !!!FATAL: unhandled exception PJLIB/No memory!,内存不足,在开发环境下验证发现内存占用已经达到2G以上(32位程序默认最高给2G内存,通过配置能达到3G),所以确实发生系统内存分配失败的问题,开始着手检查内存消耗的点。

之前为了某个项目能解码3.5K视频,把解码能力提升到了4K(4096*2160@60),profile-level-id增加到"428034"

    一个视频呼叫过程中,大块内存分配的位置有(编码分辨率1280*720,最大解码分辨率4096*2160):
1、video_port 解码渲染buf,YUV420P 4:2:0,直接按照最大解码size,分配4096*2160*1.5 = 13271040,13M

2、video_port 采集buf,YUY2 4:2:2,按照编码size,1280*720*2 = 1843200,1.8M

3、video_port 采集转换buf,YUY2->YUV420P,编码分辨率size,1280*720*1.5 = 1382400,1.3M

4、video_port 本地预览buf,YUV420P,按照编码size,1280*720*1.5 = 1382400,1.3M

5、vid_stream 编码通道buf,按照编码size,1280*720*4+12(RTP头) = 3686412,3M

6、vid_stream 解码帧buf,按照最大解码size,4096*2160*4 = 35389400,35M

7、ffmpeg 编码buf,YUV420P,按照编码size,1280*720 = 1382400 ,1.3M

8、ffmpeg 解码buf,YUV420P,按照最大解码size,4096*2160*1.5 = 13271040,13M

9、语音编解码及其他,忽略

合计:13271040+1843200+1382400+1382400+3686412+35389400+1382400+13271040=71608292,71M
30路则大约2.1G内存开销,已经达到默认32位进程上限,验证了结果(第二路以上视频呼叫时采集buf、采集转换buf、本地预览buf已经不会再次分配了)。

分析及思路:编码方向因为是按需设置分辨率,除非调整架构,很多地方没办法动,重点是解码方向,为了兼容最大解码分辨率,每次都分配足够4KYUV视频的空间是有点不必要的,毕竟4K视频仅仅是偶尔出现,绝大多数分辨率仍然覆盖在是720P~VGA之间,所以完全可以将解码分辨率降低到VGA(profile-level-id仍然设置最大值),相关buf不足时重新分配即可。

具体改动:ffmpeg解码buf已经有了Realloc的逻辑

1、vid_stream编码buf,由

stream->frame_size = vfd_enc->size.w * vfd_enc->size.h * 4;

改为

stream->frame_size = vfd_enc->size.w * vfd_enc->size.h * 1.5;

2、vid_stream解码帧buf,由

stream->dec_max_size = vfd_dec->size.w * vfd_dec->size.h * 4;

改为

stream->dec_max_size = vfd_dec->size.w * vfd_dec->size.h * 1.5;

3、vid_stream成功解码后收到PJMEDIA_EVENT_FMT_CHANGED,在get_frame方法中增加解码帧buf大小的判定逻辑

        //to realloc stream dec frame buf and size
        pj_int32_t new_size = fmt_chg_data->new_fmt.det.vid.size.h*fmt_chg_data->new_fmt.det.vid.size.w*1.5;
        if (stream->dec_max_size < new_size)
        {
            PJ_LOG(5, (THIS_FILE, "Reallocating vid_stream dec_buffer %u --> %u",
                (unsigned)stream->dec_max_size,
                (unsigned)new_size));
            pj_mutex_lock(stream->jb_mutex);

            stream->dec_max_size = new_size;
            stream->dec_frame.buf = pj_pool_alloc(stream->own_pool, stream->dec_max_size);
            pj_mutex_unlock(stream->jb_mutex);
        }

4、同样在vid_port 解码渲染buf,client_port_event_cb方法中增加判断buf大小的逻辑

// realloc mem
        int new_size = vp->conv.conv_param.dst.det.vid.size.h*vp->conv.conv_param.dst.det.vid.size.w*1.5;
        PJ_LOG(5, (THIS_FILE, "Reallocating vid_port frm_buffer %u --> %u",
            (unsigned)vp->frm_buf_size,
            (unsigned)new_size));
        if (vp->frm_buf_size < new_size)
        {
            vp->frm_buf_size = new_size;
            vp->frm_buf->buf = pj_pool_alloc(vp->pool, new_size);
            vp->frm_buf->size = vp->frm_buf_size;
        }

由此显著降低并发视频呼叫情况下的内存占用过多问题,另外64路并发呼叫的宏定义可以这样:

#define PJSIP_MAX_TSX_COUNT         1023    //默认
#define PJSIP_MAX_DIALOG_COUNT         511    //默认
#define PJSUA_MAX_CALLS            64
#define PJSUA_MAX_VID_WINS        64
#define PJ_IOQUEUE_MAX_HANDLES 512    //默认值16,最大呼叫数量15,
#define PJSIP_TPMGR_HTABLE_SIZE 512