ffmpeg笔记——UDP组播接收总结

ffmpeg在avformat_open_input里面已经实现了UDP的协议,所以只需要设置好参数,将url传递进去就可以了。

       和打开文件的方式基本一样:

 

01 AVCodecContext *pVideoCodecCtx = NULL;
02 AVCodec *pVideoCodec = NULL;
03 avcodec_register_all();
04 av_register_all();
05 avformat_network_init();
06 if(m_pConfigManager == NULL)
07 {
08      return E_POINTER;
09 }
10  
11 int videoStream = -1;
12  
13 CString url = m_pConfigManager->GetURL();
14 int portNumber = m_pConfigManager->GetPortNumber(channel);
15 url.Format(_T( "%s:%d"), url, portNumber);
16  
17 m_pFormatContext[channel] = avformat_alloc_context();
18 int err = avformat_open_input(&(m_pFormatContext[channel]), url, NULL, NULL);
19 if(err != 0)
20 {
21    fprintf(stderr, "Can't open %s!", url);
22    return E_FAIL;
23 }
24  
25 m_pFormatContext[channel]->flags |= AVFMT_FLAG_GENPTS;
26 m_pFormatContext[channel]->flags |= AVFMT_GENERIC_INDEX;
27 m_pFormatContext[channel]->max_index_size = 99;
28  
29 if(av_find_stream_info(m_pFormatContext[channel]) < 0)
30 {
31    fprintf(stderr,"Can't find stream info \n" );
32                                  
33    return E_FAIL;
34 }
35  
36 av_dump_format(m_pFormatContext[channel], 0, url,false);

       之后就可以通过av_read_frame来获取通过UPD组播协议发送来的packet,并发送至自己的一个队列A进行缓存,再由DirectShow等视频开发框架从该队列A获取数据,并进行解码显示。

 

       与读取文件不同,读取UDP组播协议数据包必须不断调用av_read_frame。这是因为ffmpeg在读取UDP时会新建一个数据缓冲区B,数据通过UDP组播协议发送出来后,就被存储到这个缓冲区B里;如果长时间不调用av_read_frame(例如在读取文件时,如果自己建立的队列A已满,则不再调用av_read_frame,而是使本线程休眠若干毫秒,等待DirectShow等前端框架从队列A取出数据包进行解码),那么不断到来的UDP数据包就会将缓冲区B写爆,之后再调用av_read_frame就会返回-5,代表IO错误。

 

       因为UDP组播一般是与实时源(Live Source)相关联,因此过时的数据包是没有用的,因此当队列A写满时,采取从队列A弹出若干个数据包,之后再进行av_read_frame操作。这样的代价是在播放过程中会产生丢包(视频中出现马赛克、花屏),但是延时会控制在一个合理的范围内;也可以直接丢弃av_read_frame得到的数据包,这样视频中的马赛克较少,但是这样会产生无法估计的延时。

 

        另外,在同时读取多路组播数据包时,也会遇到与缓冲区B相关的问题:av_find_stream_info会在视频流中寻找并解析PPS、SPS,是非常耗时的。在做好第一路的初始化之后,就应该立即新建一个线程用于调用av_read_frame函数获取数据(否则,第一路的缓冲区B会被写爆);与此同时,第二路的初始化工作也将开始;由于此时有一个读取第一路UDP数据包的线程来争夺CPU时间,因此第二路的av_find_stream_info将会花费更多的时间,这将导致第二路的缓冲区B被写爆。

 

        上述问题的解决思路有两个,第一个是同时初始化两路UDP连接,但是在我的程序里由于有一些共享资源的限制,线程同步比较麻烦,所以后来放弃了;第二个是尽量减少av_find_stream_info的时间或者加大缓冲区B的大小。看了AVFormatContext的属性,经过试验发现,减小probesize和max_analyze_duration可以减少av_find_stream_info的时间,但是没有找到可以增大缓冲区B的办法。。。上面两个属性也不可以减少的太多,而要依据视频源的SPS和PPS的特点来确定,否则会找不到stream_info,也就没有分辨率等信息。

 

        解决了缓冲区的问题后,我发现播放高清视频(1080i,720p,1080p)时播放时丢包非常严重。查了半天,发现播放时队列A几乎是时时处于满状态,导致程序主动丢弃了很多数据包。再看CPU的状态,几乎是满负荷运转。输出解码和缓存的调试信息后,发现基本上解码一帧,会缓存五帧……看来是CPU太慢了(四年前的机器,AMD Turion64×2)。换到旁边同学的i3-2300(盒)上运行就非常流畅了……

 

        看来我该换笔记本了。等28号HKUST出学生机计划,看看价钱靠不靠谱吧~谢谢阳总工提供的消息!

        欢迎转载,转载请注明出处:http://guoyb.com/Tech/26.html

你可能感兴趣的:(网络,ffmpeg,c#)