ffmpeg在avformat_open_input里面已经实现了UDP的协议,所以只需要设置好参数,将url传递进去就可以了。
和打开文件的方式基本一样:
01 |
AVCodecContext *pVideoCodecCtx = NULL; |
02 |
AVCodec *pVideoCodec = NULL; |
03 |
avcodec_register_all(); |
05 |
avformat_network_init(); |
06 |
if (m_pConfigManager == NULL) |
13 |
CString url = m_pConfigManager->GetURL(); |
14 |
int portNumber = m_pConfigManager->GetPortNumber(channel); |
15 |
url.Format(_T( "%s:%d" ), url, portNumber); |
17 |
m_pFormatContext[channel] = avformat_alloc_context(); |
18 |
int err = avformat_open_input(&(m_pFormatContext[channel]), url, NULL, NULL); |
21 |
fprintf (stderr, "Can't open %s!" , url); |
25 |
m_pFormatContext[channel]->flags |= AVFMT_FLAG_GENPTS; |
26 |
m_pFormatContext[channel]->flags |= AVFMT_GENERIC_INDEX; |
27 |
m_pFormatContext[channel]->max_index_size = 99; |
29 |
if (av_find_stream_info(m_pFormatContext[channel]) < 0) |
31 |
fprintf (stderr, "Can't find stream info \n" ); |
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