opencv拉流出现missing picture in access unit with size 4错误解决

发布时间:2024年01月12日

0、应用场景问题

我们使用opencv作为拉流客户端,获取画面后进行图像处理并推流(使用ffmpeg库)。 opencv解码同样使用ffmpeg库。

我们要求opencv能根据业务不断进行拉流操作,等效的逻辑代码如下:

while(1) {
    printf("start open rtmp\n");
    cv::VideoCapture cap;
	
    if(!cap.open("rtmp://192.168.3.100:1935/live/1581F5FHB228R00200S3", cv::CAP_FFMPEG))  // // 无流时会有20-30s超时时间
    {
        printf("reopen rtmp\n");
        continue;
    }

    // 推流初始化 ...

    while(1) {
        cap.read(frame);  // 无流时会有20-30s超时时间

        if(frame.empty()) {
            printf("frame empty\n");
            break; 
        }
        cv::imshow("v", frame);
        cv::waitKey(1);

        // 图像处理 ...
        // 推流 ...
    }

    // 推流反初始化 ...
}

注意代码中 // 无流时会有20-30s超时时间 注释的地方cap.open() 和 cap.read(frame),后面会用到。

在一次偶然测试中,发现opencv拉流时,控制台会出现如下错误:

[NULL @ 000001bb1f721400] missing picture in access unit with size 4
[h264 @ 000001bb1f71f880] Error splitting the input into NAL units.

导致后续获取frame为空,认为流断开触发重新进行拉流的操作,导致后续视频处理、推流业务有短暂停顿,客户端播放会出现响应的短暂黑屏问题。
在这里插入图片描述

1、解决思路

出现前面情况的可能原因:

可能是因为网络问题引起数据丢失,使得解码出现问题,是一段(秒级以上)时间的发生频率。 我们不去处理视频源、opencv底层的亲在问题,仅从应用层上规避该原因带来的问题,也就是不将这个报错信息作为拉流断开来处理。

1.1、方式一:

我们简单操作修改代码如下。流存在时,如果frame为空不作为错误,直接continue进行下一次读取。

        if(frame.empty()) {
            printf("frame empty\n");
            // break; 
            continue; // 不进行重新拉流
        }

实际测试时,能够跳过当前空帧,并能再次重新成功获取新的frame。
在这里插入图片描述

但是,会存一下可能的问题:

  • 1)无法区分流真正断开的情况,导致程序会卡在读取frame的循环中

  • 2)断开流超过 cap.read(frame) 超时时间后,再次连接后无法解码

    这个错误在1)的基础之上出现,导致流不能解码(需要重新拉流?或其他未知设置?)。如下图
    在这里插入图片描述

1.2、方式二:

我们设置一个frame.empty()为空的次数限制,当达到一定次数(如3次)认为是流断开连接。

    while(1) {
        cap.read(frame); // 无流时会有20-30s超时时间
        
    	if(frame.empty()) {
        	printf("frame empty\n"); 

        	if(cnt++ == 3) {
            	cnt = 0;
            	printf("grab failed at 3 times, reopen rtmp\n");
            	break;
        	}
        	continue;
        }
		// 其他操作...
    }

当流正常确认断开后,cap.read(frame) 会阻塞,阻塞3次(约60~90s)后将退出当前while循环,进行重新拉流。这种逻辑是符合业务的,新拉流可能参数配置改变,需要重新进行推流参数的调整(推流反初始化、推流初始化)。

在这里插入图片描述

当以cap.read(frame) 超时以20s,不同时间端重连时,说明如下情况:

  • 若在 20s 内流重新连接时,能正常获取非空的 frame。

  • 在 20~60s 之内重新获取流时,cap.read(frame) 不再超时且以流帧率进行执行,例如 30fps 时会再后续第 3x20 = 60ms 后 break 退出当前循环、重新拉流。

  • 在 60s 后,break 退出当前循环、重新拉流。

存在问题:

前面代码中,若正常获取非空 frame 后未将 cnt 置零,会出现每次累计出现文首错误(读到空frame )到第 3 次 必将 break 退出当前循环、重新拉流。因此需要在 if 语句后面后对 cnt 重置。

1.3、方式三:

方式二的等效代码,只不过是使用了 VideoCapture 的另外一种接口方式。

    while(1) { 
        if(!cap.grab()) {  // 无流时会有20-30s超时时间
	        if(cnt++ == 3) {
	            cnt = 0;
	            printf("grab failed at 10 times, reopen rtmp\n");
	            break;
	        }
	        continue;
	    }
	    cnt = 0; // 重置
        
	    if(!cap.retrieve(frame)) {
	        printf("retrieve failed\n");
	        continue;
	    }

		// 其他操作...
    }
文章来源:https://blog.csdn.net/wanggao_1990/article/details/135365544
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。