??xml version="1.0" encoding="utf-8" standalone="yes"?>
RTP 是目前解x媒体实时传输问题的最好办法,要在 Linux q_上进行实时传送编E,可以考虑使用一些开放源代码?nbsp;RTP 库,?nbsp;LIBRTP、JRTPLIB {。JRTPLIB 是一个面向对象的 RTP 库,它完全遵?nbsp;RFC 1889 设计Q在很多场合下是一个非怸错的选择。JRTPLIB 是一个用 C++ 语言实现?nbsp;RTP 库,q个库用socket 机制实现|络通讯 因此可以q行?nbsp;Windows、Linux、FreeBSD、Solaris、Unix和VxWorks {多U操作系l上?br>二、JRTPLIB 库的使用Ҏ(gu)及程序实?br> (1)JRTPLIB 函数 的?br> a、在使用 JRTPLIB q行实时媒体数据传输之前,首先应该生成 RTPSession cȝ一个实例来表示此次 RTP 会话Q然后调?nbsp;Create() Ҏ(gu)来对其进行初始化操作。RTPSession cȝ Create() Ҏ(gu)只有一个参敎ͼ用来指明此次 RTP 会话所采用的端口号?br> RTPSession sess; sess.Create(5000);
b、设|恰当的时戳单元Q是 RTP 会话初始化过E所要进行的另外一w要工作,q是通过调用 RTPSession cȝ SetTimestampUnit() Ҏ(gu)来实现的Q该Ҏ(gu)同样也只有一个参敎ͼ表示的是以秒为单元的时戳单元?br> sess.SetTimestampUnit(1.0/8000.0);
c、当 RTP 会话成功建立h之后Q接下去可以开始进行流媒体数据的实时传输了。首先需要设|好数据发送的目标地址QRTP 协议允许同一会话存在多个目标地址Q这可以通过调用 RTPSession cȝ AddDestination()、DeleteDestination() ?nbsp;ClearDestinations() Ҏ(gu)来完成。例如,下面的语句表C的是让 RTP 会话数据发送到本地L?nbsp;6000 端口Q?nbsp;
unsigned long addr = ntohl(inet_addr("127.0.0.1"));
sess.AddDestination(addr, 6000);
d、目标地址全部指定之后Q接着可以调?nbsp;RTPSession cȝ SendPacket() Ҏ(gu)Q向所有的目标地址发送流媒体数据。SendPacket() ?nbsp;RTPSession cL供的一个重载函?br>对于同一?nbsp;RTP 会话来讲Q负载类型、标识和时戳增量通常来讲都是相同的,JRTPLIB 允许它们设|ؓ会话的默认参敎ͼq是通过调用 RTPSession cȝ SetDefaultPayloadType()、SetDefaultMark() ?nbsp;SetDefaultTimeStampIncrement() Ҏ(gu)来完成的。ؓ RTP 会话讄q些默认参数的好处是可以化数据的发送,例如Q如果ؓ RTP 会话讄了默认参敎ͼ
sess.SetDefaultPayloadType(0);
sess.SetDefaultMark(false);
sess.SetDefaultTimeStampIncrement(10);
之后在进行数据发送时只需指明要发送的数据及其长度可以了Q?nbsp;
sess.SendPacket(buffer, 5);
e、对于流媒体数据的接收端Q首先需要调?nbsp;RTPSession cȝ PollData() Ҏ(gu)来接收发送过来的 RTP 或?nbsp;RTCP 数据报。由于同一?nbsp;RTP 会话中允许有多个参与者(源)Q你既可以通过调用 RTPSession cȝ GotoFirstSource() ?nbsp;GotoNextSource() Ҏ(gu)来遍历所有的源,也可以通过调用 RTPSession cȝ GotoFirstSourceWithData() ?nbsp;GotoNextSourceWithData() Ҏ(gu)来遍历那些携带有数据的源。在?nbsp;RTP 会话中检出有效的数据源之后Q接下去可以调?nbsp;RTPSession cȝ GetNextPacket() Ҏ(gu)从中抽取 RTP 数据报,当接收到?nbsp;RTP 数据报处理完之后Q一定要记得及时释放?br>
JRTPLIB ?nbsp;RTP 数据报定义了三种接收模式Q其中每U接收模式都具体规定了哪些到辄 RTP 数据报将会被接受Q而哪些到辄 RTP 数据报将会被拒绝。通过调用 RTPSession cȝ SetReceiveMode() Ҏ(gu)可以讄下列q些接收模式Q?nbsp;
? RECEIVEMODE_ALL ~省的接收模式,所有到辄 RTP 数据报都被接受Q?nbsp;
? RECEIVEMODE_IGNORESOME 除了某些特定的发送者之外,所有到辄 RTP 数据报都被接受Q而被拒绝的发送者列表可以通过调用 AddToIgnoreList()、DeleteFromIgnoreList() ?nbsp;ClearIgnoreList() Ҏ(gu)来进行设|;
? RECEIVEMODE_ACCEPTSOME 除了某些特定的发送者之外,所有到辄 RTP 数据报都被拒绝Q而被接受的发送者列表可以通过调用 AddToAcceptList ()、DeleteFromAcceptList ?nbsp;ClearAcceptList () Ҏ(gu)来进行设|?nbsp;下面是采用第三种接收模式的程序示例?br> if (sess.GotoFirstSourceWithData()) {
do {
sess.AddToAcceptList(remoteIP, allports,portbase);
sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);
RTPPacket *pack;
pack = sess.GetNextPacket(); // 处理接收到的数据
delete pack; }
while (sess.GotoNextSourceWithData());
}
Q?Q程序流E图
发送:获得接收端的 IP 地址和端口号 创徏 RTP 会话 指定 RTP 数据接收?nbsp;讄 RTP 会话默认参数 发送流媒体数据
接收Q获得用h定的端口?nbsp; 创徏RTP会话 讄接收模式 接受RTP数据 索RTP数据?nbsp; 获取RTP数据?nbsp; 删除RTP数据?br>
三、环境搭建及~译Ҏ(gu)
Q?QToolchain的安?br> 首先扑ֈxscale-arm-toolchain.tgz文gQ假设该文g包放?tmp/?br> #cd /
#tar -zxvf /tmp/xscale-arm-toolchain.tgz
再设|环境变?br> #export PATH=/usr/local/arm-linux/bin:$PATH
最后检查一下交叉编译工h否安装成?br> #arm-linux-g++ --version
看是否显Carm-linux-g++的版本,如有则安装成功?br>Q?QJRTPLIB 库的交叉~译及安?br> 首先?nbsp;JRTPLIB 的网站(http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.htmllQ?nbsp;下蝲最新的源码包,此处使用的是jrtplib-2.8.tarQ假设下载后的源码包攑֜/tmp下,?nbsp;行下面的命o对其解压~:
#cd /tmp
#tar -zxvf jrtplib-2.8.tar
然后要对jrtplibq行配置和编?br> #cd jrtplib-2.8
#./configure CC=arm-linux-g++ cross-compile=yes
修改Makefile文g
链接命令ld 和ar改ؓarm-linux-ld?nbsp;arm-linux-ar
#make
最后再执行如下命o可以完?nbsp;JRTPLIB 的安装:
#make install
(3)E序~译
a、配|编译环?br> 可以用export来配|,也可以用~写Makefile的方法。这里采用Makefile?br> ~写Makefile&:
INCL = -I/usr/local/include
CFLAGS = -pipe -O2 -fno-strength-reduce
LFLAGS = /usr/local/lib/libjrtp.a -L/usr/X11R6/lib
LIBS = -LX11 -LXext /usr/local/lib/libjrtp.a
CC = arm-linux-g++
main:main.o
$(CC) $(LFLAGS) $(INCL) -o main main.o $(LIBS)
main.o:main.cpp
clean:
rm -f main
rm -f *.o
.SUFFIXES:.cpp
.cpp.o:
$(CC) -c $(CFLAGS) $(INCL) -o $@ $< /* $@表示目标的完整名?nbsp; */
/* $<表示W一个依赖文件的名字 */
b、编?br> 假设发送和接收E序分别攑֜/tmp/send?tmp/receive目录?br> #cd /tmp/send
#make
#cd /tmp/receive
#make
四、易出错误及注意问题
1、找不到一些标准的最 基本的一些头文g?br> 主要是因为Toolchain路径没安装对Q要 严格按照步骤安装?br> 2、找不到使用的jrtplib库中的一些头文g?br> ?nbsp;jrtplib的安装目录下Qinclude路径下不能再有别的目录?br> 3、recieve函数接收数据包不能正提出所要数据?br> ׃每一个RTP数据报都由头部(HeaderQ和负蝲QPayloadQ两个部分组成,若用getrawdata()是返回整个数据包的数据,包含传输媒体的类型、格式、序列号、时间戳以及是否有附加数据等信息。getpayload()函数是返回所发送的数据。两者一定要分清?br> 4、设|RECEIVEMODE_ACCEPTSOME 接收模式后,q行E序接收端不能接包?br> IP地址格式Z问题。iner_addr()与ntohl()函数要用对,否则参数传不q去Q接受列表中无|当然接收不了数据包?br> 5、编译通过Q但试时接收端不能接收到数据?br> 可能是接收机防火墙未关闭。运行:
#iptables -F
也可能是IP地址没有讄好。运行:
#ifocnfig eth0 *.*.*.* netmask *.*.*.*
6、用jrtolib库时Q在E序中include 后最好加上库所在的路径?br>五、程?br>
send:
#include <stdio.h>
#include <string.h>
#include "rtpsession.h"
// 错误处理函数
void checkerror(int err)
{
if (err < 0) {
char* errstr = RTPGetErrorString(err);
printf("Error:%s\\n", errstr);
exit(-1);
}
}
int main(int argc, char** argv)
{
RTPSession sess;
unsigned long destip;
int destport;
int portbase = 6000;
int status, index;
char buffer[128];
if (argc != 3) {
printf("Usage: ./sender destip destport\\n");
return -1;
}
// 获得接收端的IP地址和端口号
destip = inet_addr(argv[1]);
if (destip == INADDR_NONE) {
printf("Bad IP address specified.\\n");
return -1;
}
destip = ntohl(destip);
destport = atoi(argv[2]);
// 创徏RTP会话
status = sess.Create(portbase);
checkerror(status);
// 指定RTP数据接收?br> status = sess.AddDestination(destip, destport);
checkerror(status);
// 讄RTP会话默认参数
sess.SetDefaultPayloadType(0);
sess.SetDefaultMark(false);
sess.SetDefaultTimeStampIncrement(10);
// 发送流媒体数据
index = 1;
do {
sprintf(buffer, "%d: RTP packet", index ++);
sess.SendPacket(buffer, strlen(buffer));
printf("Send packet !\\n");
} while(1);
return 0;
}
receive:
#include <stdio.h>
#include "rtpsession.h"
#include "rtppacket.h"
// 错误处理函数
void checkerror(int err)
{
if (err < 0) {
char* errstr = RTPGetErrorString(err);
printf("Error:%s\\n", errstr);
exit(-1);
}
}
int main(int argc, char** argv)
{
RTPSession sess;
int localport,portbase;
int status;
unsigned long remoteIP;
if (argc != 4) {
printf("Usage: ./sender localport\\n");
return -1;
}
// 获得用户指定的端口号
remoteIP = inet_addr(argv[1]);
localport = atoi(argv[2]);
portbase = atoi(argv[3]);
// 创徏RTP会话
status = sess.Create(localport);
checkerror(status);
//RTPHeader *rtphdr;
unsigned long timestamp1;
unsigned char * RawData;
unsigned char temp[30];
int lengh ,i;
bool allports = 1;
sess.AddToAcceptList(remoteIP, allports,portbase);
do {
//讄接收模式
sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);
sess.AddToAcceptList(remoteIP, allports,portbase);
// 接受RTP数据
status = sess.PollData();
// 索RTP数据?br> if (sess.GotoFirstSourceWithData()) {
do {
RTPPacket* packet;
// 获取RTP数据?br> while ((packet = sess.GetNextPacket()) != NULL) {
printf("Got packet !\n");
timestamp1 = packet->GetTimeStamp();
lengh=packet->GetPayloadLength();
RawData=packet->GetPayload();
for(i=0;i<lengh;i++){
temp[i]=RawData[i];
printf("%c",temp[i]);
}
temp[i]='\0';
printf(" timestamp: %d lengh=%d data:%s\n",timestamp1,lengh,&temp);
// 删除RTP数据?br>
delete packet;
}
} while (sess.GotoNextSourceWithData());
}
} while(1);
return 0;
}
]]>
一、流媒体?/span>
随着Internet的日益普及,在网l上传输的数据已l不再局限于文字和图形,而是逐渐向声韛_视频{多媒体格式q渡。目前在|络上传输音?span>/视频Q?span>Audio/VideoQ简U?span>A/VQ等多媒体文件时Q基本上只有下蝲和流式传输两U选择。通常说来Q?span>A/V文g占据的存储空间都比较大,在带宽受限的|络环境中下载可能要耗费数分钟甚x时Q所以这U处理方法的延迟很大。如果换用流式传输的话,声音、媄像、动ȝ多媒体文件将׃门的媒体服务器负责向用戯l、实时地发送,q样用户可以不必{到整个文g全部下蝲完毕Q而只需要经q几U钟的启动g时就可以了,当这些多媒体数据在客h上播放时Q文件的剩余部分l从媒体服务器下蝲?/span>
(StreamingQ是q年?span>Internet上出现的新概念,其定义非常广泛,主要是指通过|络传输多媒体数据的技术ȝ。流媒体包含q义和狭义两U内涵:q义上的媒体指的是佉K频和视频形成E_和连l的传输和回放的一pd技术、方法和协议的ȝQ即媒体技术;狭义上的媒体是相对于传l的下蝲-回放方式而言的,指的是一U从Internet上获取音频和视频{多媒体数据的新Ҏ(gu)Q它能够支持多媒体数据流的实时传输和实时播放。通过q用媒体技术,服务器能够向客户机发送稳定和q箋的多媒体数据,客户机在接收数据的同时以一个稳定的速率回放Q而不用等数据全部下蝲完之后再q行回放?/span>
׃受网l带宽、计机处理能力和协议规范等斚w的限Ӟ要想?span>Internet上下载大量的音频和视频数据,无论从下载时间和存储I间上来讲都是不太现实的Q而流媒体技术的出现则很好地解决了这一N。目前实现流媒体传输主要有两U方法:序(progressive streamingQ传输和实时(realtime streamingQ传输,它们分别适合于不同的应用场合?/span>
序传?/span>
序传输采用顺序下载的方式q行传输Q在下蝲的同时用户可以在U回攑֤媒体数据Q但l定时刻只能观看已经下蝲的部分,不能跛_未下蝲的部分,也不能在传输期间Ҏ(gu)|络状况对下载速度q行调整。由于标准的HTTP服务器就可以发送这UŞ式的媒体,而不需要其他特D协议的支持Q因此也常常被称?span>HTTP 式传输。顺序流式传输比较适合于高质量的多媒体片段Q如片头、片或者广告等?/span>
实时传?/span>
实时式传输保证媒体信号带宽能够与当前网l状늛匚wQ从而得流媒体数据L被实时地传送,因此特别适合于现Z件。实时流传输支持随机讉KQ即用户可以通过快进或者后退操作来观看前面或者后面的内容。从理论上讲Q实时流媒体一l播攑ְ不会停顿Q但事实上仍有可能发生周期性的暂停现象Q尤其是在网l状冉|化时更是如此。与序传输不同的是,实时传输需要用到特定的媒体服务器Q而且q需要特定网l协议的支持?/span>
二、流媒体协议
实时传输协议Q?span>Real-time Transport ProtocolQ?span>PRTQ是?span>Internet上处理多媒体数据的一U网l协议,利用它能够在一对一Q?span>unicastQ单播)或者一对多Q?span>multicastQ多播)的网l环境中实现传流媒体数据的实时传输?span>RTP通常使用UDP来进行多媒体数据的传输,但如果需要的话可以?span>TCP或?span> ATM{其它协议,整个RTP协议׃个密切相关的部分l成Q?span>RTP数据协议?span>RTP控制协议。实时流协议Q?span>Real Time Streaming ProtocolQ?span>RTSPQ最早由Real Networks?span>Netscape公司共同提出Q它位于RTP?span>RTCP之上Q其目的是希望通过IP|络有效C输多媒体数据?/span>
2.1 RTP数据协议
RTP数据协议负责Ҏ(gu)媒体数据q行包q实现媒体流的实时传输,每一?span>RTP数据报都由头部(HeaderQ和负蝲Q?span>PayloadQ两个部分组成,其中头部?span>12个字节的含义是固定的Q而负载则可以是音频或者视频数据?span>RTP数据报的头部格式如图1所C:
其中比较重要的几个域及其意义如下Q?nbsp;
?span>RTP 数据报的格式不难看出Q它包含了传输媒体的cd、格式、序列号、时间戳以及是否有附加数据等信息Q这些都为实时的媒体传输提供了相应的基?span>RTP协议的目的是提供实时数据Q如交互式的音频和视频)的端到端传输服务Q因此在RTP中没有连接的概念Q它可以建立在底层的面向q接或面向非q接的传输协议之上;RTP也不依赖于特别的|络地址格式Q而仅仅只需要底层传输协议支持组帧(FramingQ和分段Q?span>SegmentationQ就_了;另外RTP 本nq不提供M可靠性机Ӟq些都要׃输协议或者应用程序自己来保证。在典型的应用场合下Q?span>RTP 一般是在传输协议之上作为应用程序的一部分加以实现的,如图2所C:
2.2 RTCP控制协议
RTCP 控制协议需要与RTP数据协议一起配合用,当应用程序启动一?span>RTP会话时将同时占用两个端口Q分别供RTP ?span>RTCP使用?span>RTP本nq不能ؓ按序传输数据包提供可靠的保证Q也不提供流量控制和拥塞控制Q这些都?span>RTCP来负责完成。通常RTCP会采用与 RTP相同的分发机Ӟ向会话中的所有成员周期性地发送控制信息,应用E序通过接收q些数据Q从中获取会话参与者的相关资料Q以及网l状c分l丢失概率等反馈信息Q从而能够对服务质量q行控制或者对|络状况q行诊断?/span>
RTCP协议的功能是通过不同?span>RTCP数据报来实现的,主要有如下几U类型:
RTCP数据报携带有服务质量监控的必要信息,能够Ҏ(gu)务质量进行动态的调整Qƈ能够对网l拥塞进行有效的控制。由?span>RTCP数据报采用的是多播方式,因此会话中的所有成员都可以通过RTCP数据报返回的控制信息Q来了解其他参与者的当前情况?/span>
在一个典型的应用场合下,发送媒体流的应用程序将周期性地产生发送端报告SRQ该RTCP数据报含有不同媒体流间的同步信息Q以及已l发送的数据报和字节的计敎ͼ接收端根据这些信息可以估计出实际的数据传输速率。另一斚wQ接收端会向所有已知的发送端发送接收端报告RRQ该RTCP数据报含有已接收数据报的最大序列号、丢q数据报数目、g时抖动和旉戳等重要信息Q发送端应用Ҏ(gu)q些信息可以估计出往q时Ӟq且可以Ҏ(gu)数据报丢失概率和时g抖动情况动态调整发送速率Q以改善|络拥塞状况Q或者根据网l状况^滑地调整应用E序的服务质量?/span>
2.3 RTSP实时协?/span>
作ؓ一个应用层协议Q?span>RTSP提供了一个可供扩展的框架Q它的意义在于得实时流媒体数据的受控和Ҏ(gu)变得可能。ȝ说来Q?span>RTSP是一个流媒体表示协议Q主要用来控制具有实时特性的数据发送,但它本nq不传输数据Q而是必须依赖于下层传输协议所提供的某些服务?span>RTSP 可以Ҏ(gu)媒体提供诸如播放、暂停、快q等操作Q它负责定义具体的控制消息、操作方法、状态码{,此外q描qC?span>RTP间的交互操作?/span>
RTSP 在制定时较多地参考了HTTP/1.1协议Q甚臌多描qCHTTP/1.1完全相同?span>RTSP之所以特意用与HTTP/1.1cM的语法和操作Q在很大E度上是Z兼容现有?span>Web基础l构Q正因如此,HTTP/1.1的扩展机制大都可以直接引入到RTSP 中?/span>
?span>RTSP 控制的媒体流集合可以用表C描qͼPresentation DescriptionQ来定义Q所谓表C是指流媒体服务器提供给客户机的一个或者多个媒体流的集合,而表C描q则包含了一个表CZ各个媒体的相关信息Q如数据~码/解码法、网l地址、媒体流的内容等?/span>
虽然RTSP服务器同样也使用标识W来区别每一连接会话(SessionQ,?span>RTSPq接q没有被l定C输层q接Q如TCP{)Q也是说在整个 RTSPq接期间Q?span>RTSP用户可打开或者关闭多个对RTSP服务器的可靠传输q接以发?span>RTSP h。此外,RTSPq接也可以基于面向无q接的传输协议(?span>UDP{)?/span>
RTSP协议目前支持以下操作Q?nbsp;
三、流媒体~程
RTP 是目前解x媒体实时传输问题的最好办法,如果需要在Linuxq_上进行实时流媒体~程Q可以考虑使用一些开放源代码?span>RTP库,?span>LIBRTP?span> JRTPLIB{?span>JRTPLIB是一个面向对象的RTP库,它完全遵?span>RFC 1889设计Q在很多场合下是一个非怸错的选择Q下面就?span>JRTPLIBZQ讲q如何在Linuxq_上运?span>RTP协议q行实时媒体编E?/span>
3.1 环境搭徏
JRTPLIB 是一个用C++语言实现?span>RTP库,目前已经可以q行?span>Windows?span>Linux?span>FreeBSD?span> Solaris?span>Unix?span>VxWorks{多U操作系l上。要?span>Linux pȝ安装JRTPLIBQ首先从JRTPLIB的网站(http: //lumumba.luc.ac.be/jori/jrtplib/jrtplib.htmlQ下载最新的源码包,此处使用的是jrtplib- 2.7b.tar.bz2。假设下载后的源码包保存?span>/usr/local/src目录下,执行下面的命令可以对其进行解压羃Q?/span>
|