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笔记——UDP组播接收总结的更多相关文章

  1. Android上UDP组播无法接收数据的问题

    最近,想做一个跨平台的局域网的文件传输软件,思路是组播设备信息,TCP连接传输文件.于是进行了一次简单的UDP组播测试,发现Android对于UDP组播接收数据的支持即极为有限. 部分代码如下 pac ...

  2. 多网卡情况下接收udp组播

    多网卡下接收udp组播 往往会接收失败 因为用错了网卡 例如我想要接收2网段 其他电脑出的udp组播  我电脑有有线网和wifi在window下可以这样 route add 230.0.0.1 mas ...

  3. UDP及其组播,接收发送封装

    1.Receiver public class Receiver { public delegate void HeartBeat(byte[] data); public event HeartBe ...

  4. Android设备一对多录屏直播--(UDP组播连接,Tcp传输)

    原文:https://blog.csdn.net/sunmmer123/article/details/82734245 近期需要学习流媒体知识,做一个Android设备相互投屏Demo,因此找到了这 ...

  5. C# 使用UDP组播实现局域网桌面共享

    最近需要在产品中加入桌面共享的功能,暂时不用实现远程控制:参考了园子里的一些文章,加入了一些自己的修改. 需求:将一台机器的桌面通过网络显示到多个客户端的屏幕上,显示内容可能为PPT,Word文档之类 ...

  6. 【网络开发】UDP组播接收端解析

    UDP组播接收端解析 网络中的一台主机如果希望能够接收到来自网络中其它主机发往某一个组播组的数据报,那么这么主机必须先加入该组播组,然后就可以从组地址接收数据包.在广域网中,还涉及到路由器支持组播路由 ...

  7. (转)C# 使用UDP组播实现局域网桌面共享

    转:http://www.cnblogs.com/mobwiz/p/3715743.html 最近需要在产品中加入桌面共享的功能,暂时不用实现远程控制:参考了园子里的一些文章,加入了一些自己的修改. ...

  8. 调皮的udp组播技术

    2017年本科毕业,经历过千辛万苦的找工作之后,我进入了现在的这家公司.虽是职场小白,但励志成为IT界的一股清流(毕竟开发的妹子少,哈哈).因为公司的业务需要,我负责的部分是利用组播技术实现OSG模型 ...

  9. 桌面共享UDP组播实现

    组播(Multicast)传输:在发送者和每一接收者之间实现点对多点网络连接.如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包.它提高了数据传送效率.减少了骨干网络出现拥塞的 ...

随机推荐

  1. 服务商域名DNS大全

    服务商域名DNS:  服务商 DNS服务器   常用DNS: 英文域名DNS 主DNS名称:ns11.xincache.com 辅DNS名称:ns12.xincache.com 说明:新网共有5组DN ...

  2. echarts geo地图坐标转换为页面Offset坐标

    https://github.com/apache/incubator-echarts/issues/2540 代码示例: // 获取系列 ) // 获取地理坐标系实例 var coordSys = ...

  3. notepad++与ISE/Vivado关联

    转自:http://www.cnblogs.com/ninghechuan/p/6172237.html 1.notepad++与vivado关联 打开vivado软件,选择菜单栏“Tools——&g ...

  4. 理解:Before和:After伪元素

    CSS样式表的主要作用是修饰Web页面上的HTML标记,但有时候,为了实现某个效果而往页面里反复添加某个HTML标记很繁琐,或者是显得多余,或者是由于某种原因而做不到.这就是CSS伪元素(Pseudo ...

  5. C#字符串中特殊字符的转义

    再基础的东西不常用的话就得记下来...不然就忘记了. 比如C#中对字符串中特殊字符的转义,一个是双引号",另一个就是转义符\ 对于同样一个字符串:地址:"C:\Users\E.tx ...

  6. javascript原型继承---constructor篇

    很多人对constructor的理解是指向对象的构造函数,今天才发现这种理解是有偏差的... 其实, constructor指向的不是实例化实例的构造函数,而是实例化该对象的构造函数的原型的构造函数 ...

  7. MAC下cmake安装

    可以参考网上手动下载cmake的源码包进行安装,http://www.cmake.org/download/ 解压后运行sudo ./bootstrap && sudo make &a ...

  8. python学习之pyc,pyo,pyd文件

    pyc:二进制文件,python文件经过编译器编译之后的文件.可以提高文件加载速度. pyo:二进制文件,优化编译后的文件.可以通过`python -O file.py`生成. pyd:python的 ...

  9. python 合并字典,相同 key 的 value 如何相加?

    x = { 'apple': 1, 'banana': 2 } y = { 'banana': 10, 'pear': 11 } 需要把两个字典合并,最后输出结果是: { 'apple': 1, 'b ...

  10. Python ORM框架SQLAlchemy学习笔记之数据添加和事务回滚介绍

    1. 添加一个新对象 前面介绍了映射到实体表的映射类User,如果我们想将其持久化(Persist),那么就需要将这个由User类建立的对象实例添加到我们先前创建的Session会话实例中: 复制代码 ...