转自:http://blog.csdn.net/kof98765/article/details/17733701

1 音视频实时传输
1.1 Jrtplib库介绍
本系统采用开源库Jrtplib进行RTP传输模块的开发。Jrtplib库是由比利时Hasselt大学EDM(Expertise Centre for Digital Media)开发的一个用C++语言实现的完全开源的RTP库,目前已经可以运行在Windows、Linux、FreeBSD、Solaris、Unix和VxWorks等多种操作系统上,其现有的最新版本为Jrtplib3.7.1。
Jrtplib完全遵循RFC3550标准设计,提供了丰富的接口,使用win32 socket实现网络通讯。应用该库进行开发设计时,开发人员只需要关心RTP负载、RTCP负载、时间戳增量、发送目标地址及端口、RTCP带宽及发送间隔等最基本的问题,而不必考虑一些琐碎的细节,可以从Jrtplib的网站(http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.html)下载更新的源码包。
1.2 Jrtplib初始化
在使用Jrtplib进行实时流媒体数据传输之前,首先应该生成RTPSession类的一个实例来表示此次RTP会话,然后调用Create() 方法来对其进行初始化操作。RTPSession类的Create()方法只有一个参数,用来指明此次RTP会话所采用的端口号。示例1中给出了一个最简单的初始化框架,它只是完成了RTP会话的初始化工作,还不具备任何实际的功能。
1、initial.cpp
#include "rtpsession.h"
int main(void)
{
RTPSession sess;
sess.Create(5000);
return 0;
}
如果RTP会话创建过程失败,Create()方法将会返回一个负数,通过它虽然可以很容易地判断出函数调用究竟是成功的还是失败的,但却很难明白出错的原因到底什么。Jrtplib采用了统一的错误处理机制,它提供的所有函数如果返回负数就表明出现了某种形式的错误,而具体的出错信息则可以通过调用 RTPGetErrorString()函数得到。RTPGetErrorString()函数将错误代码作为参数传入,然后返回该错误代码所对应的错误信息。示例2给出了一个更加完整的初始化框架,它可以对RTP会话初始化过程中所产生的错误进行更好的处理:
2、framework.cpp
#include
#include "rtpsession.h"
int main(void)
{
RTPSession sess;
int status;
char* msg;
sess.Create(6000);
msg = RTPGetErrorString(status);
printf("Error String:%s\\n",msg);
return 0;
}
设置恰当的时戳单元,是RTP会话初始化过程所要进行的另外一项重要工作,这是通过调用RTPSession类的SetTimestampUnit ()方法来实现的,该方法同样也只有一个参数,表示的是以秒为单元的时戳单元。例如,当使用RTP会话传输8000Hz采样的音频数据时,由于时戳每秒钟将递增8000,所以时戳单元相应地应该被设置成1/8000:
sess.SetTimestampUnit(1.0/8000.0);
1.3 Jrtplib数据发送
当RTP会话成功建立起来之后,接下去就可以开始进行流媒体数据的实时传输了。首先需要设置好数据发送的目标地址,RTP协议允许同一会话存在多个目标地址,这可以通过调用RTPSession类的AddDestination()、DeleteDestination()和 ClearDestinations()方法来完成。例如,下面的语句表示的是让RTP会话将数据发送到本地主机的6000端口:
unsigned long addr = ntohl(inet_addr("127.0.0.1"));
sess.AddDestination(addr, 6000);
目标地址全部指定之后,接着就可以调用RTPSession类的SendPacket()方法,向所有的目标地址发送流媒体数据。SendPacket()是RTPSession类提供的一个重载函数,它具有下列多种形式:
int SendPacket(void *data,int len)
int SendPacket(void *data,int len,unsigned char pt,bool mark,unsigned long timestampinc)
int SendPacket(void *data,int len,unsigned short hdrextID,void *hdrextdata,int numhdrextwords)
int SendPacket(void *data,int len,unsigned char pt,bool mark,unsigned long timestampinc,unsigned short hdrextID,void *hdrextdata,int numhdrextwords)
SendPacket()最典型的用法是类似于下面的语句,其中第一个参数是要被发送的数据,而第二个参数则指明将要发送数据的长度,再往后依次是RTP负载类型、标识和时戳增量。
sess.SendPacket(buffer,5,0,false,10);
对于同一个RTP会话来讲,负载类型、标识和时戳增量通常来讲都是相同的,Jrtplib允许将它们设置为会话的默认参数,这是通过调用 RTPSession类的SetDefaultPayloadType()、SetDefaultMark()和 SetDefaultTimeStampIncrement()方法来完成的。为RTP会话设置这些默认参数的好处是可以简化数据的发送,例如,如果为 RTP会话设置了默认参数:
sess.SetDefaultPayloadType(0);
sess.SetDefaultMark(false);
sess.SetDefaultTimeStampIncrement(10);
之后在进行数据发送时只需指明要发送的数据及其长度就可以了:
sess.SendPacket(buffer,5);
1.4 Jrtplib数据接收
对于流媒体数据的接收端,首先需要调用RTPSession类的PollData()方法来接收发送过来的RTP或者RTCP数据报。由于同一个 RTP会话中允许有多个参与者(源),既可以通过调用RTPSession类的GotoFirstSource()和GotoNextSource() 方法来遍历所有的源,也可以通过调用RTPSession类的GotoFirstSourceWithData()和 GotoNextSourceWithData()方法来遍历那些携带有数据的源。在从RTP会话中检测出有效的数据源之后,接下去就可以调用 RTPSession类的GetNextPacket()方法从中抽取RTP数据报,当接收到的RTP数据报处理完之后,一定要记得及时释放。下面的代码示范了该如何对接收到的RTP数据报进行处理:
if (sess.GotoFirstSourceWithData()) {
do {
RTPPacket *pack;
pack = sess.GetNextPacket(); // 处理接收到的数据
delete pack;
} while (sess.GotoNextSourceWithData());
}
Jrtplib为RTP数据报定义了三种接收模式,其中每种接收模式都具体规定了哪些到达的RTP数据报将会被接受,而哪些到达的RTP数据报将会被拒绝。通过调用RTPSession类的SetReceiveMode()方法可以设置下列这些接收模式:
a) RECEIVEMODE_ALL 缺省的接收模式,所有到达的RTP数据报都将被接受;
b) RECEIVEMODE_IGNORESOME 除了某些特定的发送者之外,所有到达的RTP数据报都将被接受,而被拒绝的发送者列表可以通过调用AddToIgnoreList()、DeleteFromIgnoreList()和ClearIgnoreList()方法来进行设置;
c) RECEIVEMODE_ACCEPTSOME 除了某些特定的发送者之外,所有到达的RTP数据报都将被拒绝,而被接受的发送者列表可以通过调用AddToAcceptList ()、DeleteFromAcceptList()和ClearAcceptList ()方法来进行设置。
2 音视频同步
2.1 RTCP控制参数
由于音视频流作为不同的RTP会话传送,它们在RTP层无直接关联。尽管由一个数据源发出的不同的流具有不同的同步源标识(SSRC),为能进行流同步,RTCP要求发送方给接收方传送一个唯一的标识数据源的规范名(canonical name),应用层藉此关联音视频流,以便实现同步。
RTP/ RTCP中有时间戳(相对和绝对)和序列号等信息,可以利用它实现基于时间戳的多媒体流同步。使用相对时间戳和序列号实现流内同步;使用相对和绝对时间戳的对应关系实现流间同步。获得相对与绝对时间戳的算法如下:
while ((pack = GetNextPacket()) != NULL)
{
if(srcdat->SR_HasInfo() && srcdat->SR_GetRTPTimestamp() != app->mvideortcprtp)
{
app->mvideortcprtp = srcdat->SR_GetRTPTimestamp();
app->mvideortcpntp = srcdat->SR_GetNTPTimestamp().GetMSW();
srcdat->FlushPackets(); }
DeletePacket(pack);
}

2.2 音视频流间同步实现

发送端在发送音视频数据时,同时也会发送SR包,这样可以使接收方能够正确使音视频同步播放。具体实现方法是在接收方每次接收数据包后,再遍历一次数据源,获取所有源端的SS_RTPTime与SS_NTPTime这两则数据,通过获取音频端与视频端的数据,可以利用下面的公式进行计算。先描述变量如表2.1所列:
表 2.1 变量描述表
类型
RTP数据
NTP数据
RTP时戳频率
音频
Audio_SRRTPTime
Audio_SRNTPTime
Audio_Fre
视频
Video_SRRTPTime
Video_SRNTPTime
Video_Fre
从SR包中可以读出音频与视频的RTP与NTP数据,而需要计算的是时戳频率,利用下述公式:
Audio_Fre=(AudioSRRTPtime2- AudioSRRTPtime1)/( AudioSRNTPtime2- AudioSRNTPtime1); (4.3)
Video_Fre=( VideoSRRTPtime2- VideoSRRTPtime1)/( VideoSRNTPtime2- Video SRNTPtime1); (4.4)
然后计算视频RTP的时间,即:
Video_RTPTime=Video_SRRTPTime+(Audio_SRNTP-Video_SRNTP)×Video_Fre;
(4.5)
最后按式 (4.6)即可计算出Video_TRUERTP时间,将其与从RTP包中读出的时间进行比较,就可以进行同步播放了。
(Video_TRUERTP-Video_RTPTime)/Video_Fre=(Audio_TRUERTP-Audio_SRRTPTime)/Audio_Fre; (4.6)
下面采用临时缓冲区的方法来同步音视频。因为要保证音频优先正常传输,所以本系统以音频为主轴,视频为辅轴。以最大延迟时间为缓冲区大小,存放M个音频帧数据。当接收端获得M个音频帧后开始播放音频,每次获得视频帧时,就计算出当前视频应当播放RTP时间与现有RTP时间进行比对,如若在120ms以内,迅速播放;延迟120ms以上,则扔掉,然后比对下一帧;还在120ms以内,放入视频缓冲区内进行储存,如果视频缓冲区的大小超过了最大临时缓冲区的数值,依旧开始播放。

RTP RTCP在音视频传输与同步方面的使用的更多相关文章

  1. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(六)RTP音视频传输解析层之音视频数据传输格式

    一.差异 本地音视频数据格式和用来传输的音视频数据格式存在些许差异,由于音视频数据流到达客户端时,需要考虑数据流的数据边界.分包.组包顺序等问题,所以传输中的音视频数据往往会多一些字节. 举个例子,有 ...

  2. 树莓派+android things+实时音视频传输demo之遥控小车

    做了个测试小车,上面安装了摄像头,通过外网进行视频传输: https://www.bilibili.com/video/av23817880/

  3. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(八)RTP音视频传输解析层之MPA传输格式

    一.MPEG RTP音频传输 相较H264的RTP传输格式,MPEGE音频传输格式则简单许多. 每一包MPEG音频RTP包都前缀一个4字节的Header,如下图(RFC2550) “MBZ”必须为0( ...

  4. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(七)RTP音视频传输解析层之H264传输格式

    一.H264传输封包格式的2个概念 (1)组包模式(Packetization Modes) RFC3984中定义了3种组包模式:单NALU模式(Single Nal Unit Mode).非交错模式 ...

  5. 基于RTP的h.264视频传输系统设计(一)

    一.H.264 的层次介绍 H.264 定义三个层次,每一个层次支持一组特定的编码功能.而且按照各个层次指定所指定的功能.基础层次(baselineprofile)支持I 帧和 P 帧[1]的帧内和帧 ...

  6. [SimplePlayer] 8. 音视频同步

    音频与视频在播放当中可能会由于种种原因(如:音视频并非在同一时间开始播放,或视频由于解码任务繁重导致输出图像延迟等)导致音频与视频的播放时间出现偏差,这种就是音视频的同步问题,本文会对音视频同步进行讨 ...

  7. 了不起的WebRTC:生态日趋完善,或将实时音视频技术白菜化

    本文原文由声网WebRTC技术专家毛玉杰分享. 1.前言 有人说 2017 年是 WebRTC 的转折之年,2018 年将是 WebRTC 的爆发之年,这并非没有根据.就在去年(2017年),WebR ...

  8. Android多媒体框架总结(1) - 利用MediaMuxer合成音视频数据流程分析

    场景介绍: 设备端通过服务器传向客户端(Android手机)实时发送视频数据(H.264)和音频数据(g711a或g711u), 需要在客户端将音视频数据保存为MP4文件存放在本地,用户可以通过APP ...

  9. Android IOS WebRTC 音视频开发总结(八十六)-- WebRTC中RTP/RTCP协议实现分析

    本文主要介绍WebRTC中的RTP/RTCP协议,作者:weizhenwei ,文章最早发表在编风网,微信ID:befoio 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID ...

随机推荐

  1. 【架构】How To Use HAProxy to Set Up MySQL Load Balancing

    How To Use HAProxy to Set Up MySQL Load Balancing Dec  2, 2013 MySQL, Scaling, Server Optimization U ...

  2. 一个很详细的web.xml讲解

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "- ...

  3. Zigzag Iterator

    Given two 1d vectors, implement an iterator to return their elements alternately. For example, given ...

  4. Unity3d 防止内存修改工具的小方法

    一个非常简单的方法,直接上代码. private int curATK; private int curAtkKey; public int CurATK { get { return curATK ...

  5. ffmpeg-20160508-git-bin-v2

    ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...

  6. Django~Databases

    更换数据库 migrate 下哈 admin 要重新建立 修改\mysite\settings.py DATABASES = {    'default': {        'ENGINE': 'd ...

  7. centos搭建svn服务器并在windows实验

    安装步骤如下: 1.yum install subversion   2.输入rpm -ql subversion查看安装位置,如下图:   我们知道svn在bin目录下生成了几个二进制文件. 输入 ...

  8. algorithm 中的常用函数

    非修改性序列操作(12个) 循环         对序列中的每个元素执行某操作         for_each() 查找         在序列中找出某个值的第一次出现的位置         fin ...

  9. Android Canvas绘图详解(图文)

    编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! Andr ...

  10. 【leetcode】Permutations (middle)

    Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the follow ...