主要介绍JRTPLIB 2.x系列和3.x系列两种版本,它们的区别是2.x系列代码量少使用简单,但是只支持RFC 1889不支持RFC 3550,3.x支持RFC 3550,但代码量稍多,以及使用也稍显复杂。

一、JRTPLIB的编译及安装

1. JRTPLIB-2.X系列版本:

1.1.下载

下载链接:http://research.edm.uhasselt.be/jori/jrtplib/jrtplib_old.html

或我的github: https://github.com/fengweiyu/JRTPLIB

1.2.解压

Linux下使用命令:tar –xvf  jrtplib-2.9.tar.bz2

1.3.编译及安装

分别执行:

./configure

make

make install

1.4查看编译及安装的结果

查看编译完的文件

1. /usr/local/lib 目录下放置着编译的jrtplib的库,主要的是libjrtp.a  静态库, libjrtp.so.2.9动态库,libjrtp.so链接

2. /usr/local/incude  多了两个文件夹:jrtplib

2. JRTPLIB-3.X系列版本:

2.1.下载三个文件

I、jrtplib:http://research.edm.uhasselt.be/jori/page/CS/Jrtplib.html

II、jthread:http://research.edm.uhasselt.be/jori/page/CS/Jthread.html

III、cmake:http://www.cmake.org

或我的github: https://github.com/fengweiyu/JRTPLIB

2.2.解压

把下载的文件全部放到Linux下并解压:tar –xvf  XXX.tar.bz2

2.3.编译及安装

I、cmake:

进入cmake目录:分别输入命令

./bootstrap

make

make install

make -version查看安装是否成功

II、jthread和jrtplib:

jthread和jrtplib是配合使用的,jthread负责线程调用函数和mutex,所以要先编译jthread,因为编译jrtplib时会用到jthread的编译好的头文件。

a.进入jthread目录分别执行下列命令

cmake  CMakeLists.txt   生成makefile

make

make install

b.进入jrtplib目录,执行命令同上。

2.4查看编译及安装的结果

查看编译完的文件

1. /usr/local/lib 目录下放置着编译的jrtplib和jthread的库,主要的是libjrtp.a  libjthread.a静态库, libjrtp.so.3.9.1 libjthread.so.1.3.1动态库,libjrtp.so libjthread.so 链接

2. /usr/local/incude  多了两个文件夹:jrtplib3  jthread

二、基于JRTPLIB编程

1. JRTPLIB-2.X系列版本:

1.1.初始化

在使用JRTPLIB进行实时流媒体数据传输之前,首先应该生成RTPSession类的一个实例来表示此次RTP会话,然后调用Create()方法来对其进行初始化操作。RTPSession类的Create()方法只有一个参数,用来指明此次RTP会话所采用的端口号。清单1给出了一个最简单的初始化框架,它只是完成了RTP会话的初始化工作,还不具备任何实际的功能。

代码清单1initial.cpp

#include "rtpsession.h"

int main(void)

{

RTPSession sess;

sess.Create(5000);

return 0;

}

如果RTP会话创建过程失败,Create()方法将会返回一个负数,通过它虽然可以很容易地判断出函数调用究竟是成功的还是失败的,但却很难明白出错的原因到底什么。JRTPLIB采用了统一的错误处理机制,它提供的所有函数如果返回负数就表明出现了某种形式的错误,而具体的出错信息则可以通过调用 RTPGetErrorString()函数得到。RTPGetErrorString()函数将错误代码作为参数传入,然后返回该错误代码所对应的错误信息。清单2给出了一个更加完整的初始化框架,它可以对RTP会话初始化过程中所产生的错误进行更好的处理:

代码清单2framework.cpp

#include <stdio.h>

#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.2.数据发送

当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.3.数据接收

对于流媒体数据的接收端,首先需要调用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()方法可以设置下列这些接收模式:

  • RECEIVEMODE_ALL  缺省的接收模式,所有到达的RTP数据报都将被接受;
  • RECEIVEMODE_IGNORESOME  除了某些特定的发送者之外,所有到达的RTP数据报都将被接受,而被拒绝的发送者列表可以通过调用AddToIgnoreList()、DeleteFromIgnoreList()和ClearIgnoreList()方法来进行设置;
  • RECEIVEMODE_ACCEPTSOME  除了某些特定的发送者之外,所有到达的RTP数据报都将被拒绝,而被接受的发送者列表可以通过调用AddToAcceptList ()、DeleteFromAcceptList和ClearAcceptList ()方法来进行设置。

 

1.4.控制信息

JRTPLIB 是一个高度封装后的RTP库,程序员在使用它时很多时候并不用关心RTCP数据报是如何被发送和接收的,因为这些都可以由JRTPLIB自己来完成。只要 PollData()或者SendPacket()方法被成功调用,JRTPLIB就能够自动对到达的 RTCP数据报进行处理,并且还会在需要的时候发送RTCP数据报,从而能够确保整个RTP会话过程的正确性。

而另一方面,通过调用RTPSession类提供的SetLocalName()、SetLocalEMail()、 SetLocalLocation()、SetLocalPhone()、SetLocalTool()和SetLocalNote()方法, JRTPLIB又允许程序员对RTP会话的控制信息进行设置。所有这些方法在调用时都带有两个参数,其中第一个参数是一个char型的指针,指向将要被设置的数据;而第二个参数则是一个int型的数值,表明该数据中的前面多少个字符将会被使用。例如下面的语句可以被用来设置控制信息中的电子邮件地址:

sess.SetLocalEMail("
xiaowp@linuxgam.comxiaowp@linuxgam.com
 
",19);

在RTP 会话过程中,不是所有的控制信息都需要被发送,通过调用RTPSession类提供的 EnableSendName()、EnableSendEMail()、EnableSendLocation()、EnableSendPhone ()、EnableSendTool()和EnableSendNote()方法,可以为当前RTP会话选择将被发送的控制信息。

1.5.实际应用

最后通过一个简单的流媒体发送-接收实例,介绍如何利用JRTPLIB来进行实时流媒体的编程。清单3给出了数据发送端的完整代码,它负责向用户指定的IP地址和端口,不断地发送RTP数据包:

代码清单3sender.cpp

#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数据接收端

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;

}

也可以参考官方自带的例子,解压后在examples目录下。

使用examples目录下的sample1,

I、编译:

g++ -rdynamic /usr/local/lib/libjrtp.so.2.9 -o sample1 sample1.cpp

或 g++ -L/usr/local/lib -ljrtp -o sample1 sample1.cpp

说明:

a、出现错误:undefined reference to jrtplib::RTPGetErrorString(int) ...........................................

解决:这是因为编译的时候链接库没有找到所致

所以编译时添加上链接库:-rdynamic /usr/local/lib/libjrtp.so.2.9或-L/usr/local/lib -ljrtp

b、出现错误:rtpsession.h 没有那个文件或目录........................

原因:Linux系统默认查找的头文件在usr/include下面,我们编译后的头文件在usr/local/include下面,所以找不到是情有可源的

解决1:直接将usr/local/include下的两个文件夹复制到/usr/include目录下

解决2:或者做个软连接:

ln -s /usr/local/include/jrtplib   /usr/include/jrtplib

通过1/2这两种其实还是解决不了的,因为默认需找的是/usr/include文件夹下的内容,现在相当于放在/usr/include/jrtplib目录下了,所以要讲源程序sample1.cpp中的“”头文件加上jrtplib/

或者,简单点:直接在编译的时候指定,即:-I/usr/local/include/jrtplib

II、执行:

export LD_LIBRARY_PATH=/usr/local/lib

./sample1

说明:

a、LD_LIBRARY_PATH 这个环境变量是大家最为熟悉的,它告诉loader:在哪些目录中可以找到共享库。可以设置多个搜索目录,这些目录之间用冒号分隔开。export LD_LIBRARY_PATH=/usr/local/lib,然后再运行编译,即可通过。这种方法只是暂时修改路径,在重启shell后会失效。或者是配置在环境变量文件/etc/profile中,重启或者source /etc/profile 生效,source 只在本控制台生效。

b、永久生效的方法为修改动态链接库配置文件/etc/ld.so.conf,或者在/etc/ld.so.conf.d里创建一个新文件,并把需要的目录加到这个文件里。具体方法如下:(说明:这种修改动态链接库配置的方式需要使用超级用户权限,不然没有对共享库配置文件的写权限)

#cd /etc/ld.so.conf.d

#vim jrtplib在编辑环境下加入/usr/local/lib,保存退出。

#ldconfig 重新加载动态链接库。

然后再执行编译链接,成功生成sample1文件。

然后运行./ sample1成功。

III、实测JRTPLIB发送出来的数据如下:

RTP:

17:51:01 收到数据:{80 00 C4 96 FE D5 2B CD 4D B9 72 1F 31 32 33 34 35 36 37 38 39 30 }

17:51:23 收到数据:{80 00 C4 97 FE D5 2B D7 4D B9 72 1F 31 32 33 34 35 36 37 38 39 30 }

17:51:25 收到数据:{80 00 C4 98 FE D5 2B E1 4D B9 72 1F 31 32 33 34 35 36 37 38 39 30 }

17:51:26 收到数据:{80 00 C4 99 FE D5 2B EB 4D B9 72 1F 31 32 33 34 35 36 37 38 39 30 }

RTCP:

17:51:01 收到数据:{80 C8 00 06 4D B9 72 1F DD 66 2F 0C 05 6D 3B 6C FE EF A0 E5 00 00 00 07 00 00 00 46 81 CA 00 06 4D B9 72 1F 01 11 62 6F 6F 6B 40 62 6F 6F 6B 2D 64 65 73 6B 74 6F 70 00 }

17:51:23 收到数据:{80 C8 00 06 4D B9 72 1F DD 66 2F 22 1B F5 E8 4F FE F2 53 26 00 00 00 08 00 00 00 50 81 CA 00 06 4D B9 72 1F 01 11 62 6F 6F 6B 40 62 6F 6F 6B 2D 64 65 73 6B 74 6F 70 00 }

17:51:26 收到数据:{80 C8 00 06 4D B9 72 1F DD 66 2F 25 09 57 8A 2A FE F2 AE A0 00 00 00 0A 00 00 00 64 81 CA 00 06 4D B9 72 1F 01 11 62 6F 6F 6B 40 62 6F 6F 6B 2D 64 65 73 6B 74 6F 70 00 81 CB 00 01 4D B9 72 1F }

2. JRTPLIB-3.X系列版本:

程序流程图
发送:获得接收端的 IP 地址和端口号       
创建 RTP 会话       
指定 RTP 数据接收端 设置 RTP 会话 默认参数   发送流媒体数据
接收:获得用户指定的端口号  创建RTP会话  设置接收模式 
接受RTP数据 
检索RTP数据源 
获取RTP数据报 删除RTP数据报

2.1.初始化

I、在使用 JRTPLIB 进行实时流媒体数据传输之前,首先应该生成 RTPSession 类的一个实例来表示此次 RTP会话,然后调用 Create() 方法来对其进行初始化操作。RTPSession 类的
Create() 方法只有一个参数,用来指明此次 RTP 会话所采用的端口号。
RTPSession sess;  sess.Create(5000);

JRTPLIB-3.7中已经修改了Create(prot)方法。新的Create方法被修改为Create(sessparams,&transparams)。其中的两个参数需要如下先定义:

RTPUDPv4TransmissionParams
transparams;
RTPSessionParams sessparams;

sessparams.SetOwnTimestampUnit(1.0/8000.0);/*设置时间戳,1/8000表示1秒钟采样8000次,即录音时的8KHz*/

sessparams.SetAcceptOwnPackets(true);

transparams.SetPortbase(portbase);/*本地通讯端口*/

II、设置恰当的时戳单元,是 RTP 会话初始化过程所要进行的另外一项重要工作,这是通过调用 RTPSession类的
SetTimestampUnit() 方法来实现的,前面已经提过。

2.2.数据发送

I、当 RTP 会话成功建立起来之后,接下去就可以开始进行流媒体数据的实时传输了。首先需要设置好数据发送的目标地址,RTP 协议允许同一会话存在多个目标地址,这可以通过调用 RTPSession 类的AddDestination()、DeleteDestination() 和 ClearDestinations() 方法来完成。例如,下面的语句表示的是让 RTP 会话将数据发送到本地主机的 6000 端口:

unsigned
long addr = ntohl(inet_addr("127.0.0.1")); 
sess.AddDestination(addr, 6000);

II、目标地址全部指定之后,接着就可以调用 RTPSession 类的
SendPacket() 方法,向所有的目标地址发送流媒体数据。SendPacket() 是
RTPSession 类提供的一个重载函数对于同一个 RTP 会话来讲,负载类型、标识和时戳增量通常来讲都是相同的,JRTPLIB 允许将它们设置为会话的默认参数,这是通过调用 RTPSession 类的
SetDefaultPayloadType()、SetDefaultMark()
和SetDefaultTimeStampIncrement()
方法来完成的。为 RTP 会话设置这些默认参数的好处是可以简化数据的发送,例如,如果为 RTP 会话设置了默认参数:

sess.SetDefaultPayloadType(0);
sess.SetDefaultMark(false);  
sess.SetDefaultTimeStampIncrement(10);

之后在进行数据发送时只需指明要发送的数据及其长度就可以了:

sess.SendPacket(buffer, 5);

在真正的语音传输中,上面的buffer就是我们录音时所得到的buffer。使用上面的函数可以简单的发送,但无法真正的实现RTP传输,我们需要调用另一个接口:sess.SendPacket((void
*)buffer,sizeof(buffer),0,false,8000);详细的说明可以查看JRTPLIB的说明文档。

2.3.数据接收

对于流媒体数据的接收端,首先需要调用 RTPSession 类的
PollData() 方法来接收发送过来的 RTP 或者RTCP 数据报。

JRTPLIB-3.7中修改PollData()方法为Poll(),使用都一样

由于同一个 RTP 会话中允许有多个参与者(源),你既可以通过调用
RTPSession 类的

GotoFirstSource() 和 GotoNextSource() 方法来遍历所有的源,也可以通过调用
RTPSession 类的GotoFirstSourceWithData()
和 GotoNextSourceWithData() 方法来遍历那些携带有数据的源。在从 RTP 会话中检测出有效的数据源之后,接下去就可以调用
RTPSession 类的
GetNextPacket() 方法从中抽取 RTP 数据报,当接收到的 RTP 数据报处理完之后,一定要记得及时释放。

JRTPLIB 为 RTP 数据报定义了三种接收模式,其中每种接收模式都具体规定了哪些到达的 RTP 数据报将会被接受,而哪些到达的 RTP 数据报将会被拒绝。通过调用 RTPSession 类的
SetReceiveMode() 方法可以设置下列这些接收模式: 
RECEIVEMODE_ALL  缺省的接收模式,所有到达的 RTP 数据报都将被接受; 
RECEIVEMODE_IGNORESOME  除了某些特定的发送者之外,所有到达的 RTP 数据报都将被接受,而被拒绝的发送者列表可以通过调用 AddToIgnoreList()、DeleteFromIgnoreList() 和 ClearIgnoreList() 方法来进行设置; 
RECEIVEMODE_ACCEPTSOME  除了某些特定的发送者之外,所有到达的 RTP 数据报都将被拒绝,而被接受的发送者列表可以通过调用 AddToAcceptList ()、DeleteFromAcceptList 和 ClearAcceptList () 方法来进行设置。 下面是采用第三种接收模式的程序示例。
if (sess.GotoFirstSourceWithData()) {   
 do {   
          sess.AddToAcceptList(remoteIP,
allports,portbase);
           sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);

RTPPacket
*pack;         
          pack =
sess.GetNextPacket();           
// 处理接收到的数据    
           delete
pack;   } 
 while (sess.GotoNextSourceWithData()); 
 }

完整的代码中,首先需调用Poll()方法接收RTP数据报,然后在BeginDataAccess()和EndDataAccess()之间进行数据接收的操作。此时,我们设定程序一直do-while等待并处理数据

do{

#ifndef
RTP_SUPPORT_THREAD
               
error_status = sess_client.Poll();
               
checkerror(error_status);
#endif // RTP_SUPPORT_THREAD
               
sess_client.BeginDataAccess();

// check incoming packets
               
if (sess_client.GotoFirstSourceWithData())
               
{
                         printf("Begin
play/n");
                       
do
                       
{
                               
RTPPacket *pack;

while ((pack = sess_client.GetNextPacket()) != NULL)
                               
{
                                       
// You can examine the data here
                                       
printf("Got packet !/n");
                                       
timestamp1 = pack->GetTimestamp();
                                       
lengh=pack->GetPayloadLength();
                                       
RawData=pack->GetPayloadData();   //得到数据

printf(" 
timestamp: %dlengh=%d/n",timestamp1,lengh);

int fd = open("/dev/dsp", O_RDWR);
                                           
int status = write(fd, RawData,lengh );
                                           
printf("Play bytes:%d/n",status);
                                           
if (status != lengh)
                                             
perror("wrote wrong number of bytes");

status = ioctl(fd, SOUND_PCM_SYNC, 0);
                                           
if (status == -1)
                                            perror("SOUND_PCM_SYNC
ioctl failed");
                                           
printf("Play end/n");
                                            
close(fd);
                                       
sess_client.DeletePacket(pack);

}
                       
} while (sess_client.GotoNextSourceWithData());
                        
//return 0;

}

sess_client.EndDataAccess();
            }while(1);

说明 : jrtp-3.x 中有两种数据接收方式:第一种是用 jthread 库提供的线程自动在后台执行对数据的接收。第二种是用户自己调用
RTPSession 中的 Poll
方法。如果采取第一种方法则要安装
jthread 库,则安装
jthread-1.2.1.tar.gz ,而且 jthread-1.2.1 必须先与 jrtp-3.7.1 的安装。因为在 jrtp-3.7.1 的
configure 中,会查找系统是否有编译了 jthread 库,如果有,那么编译的 jrtp 库会开启对 jthread 的支持。因此如果先编译jrtp 在编译 jthread ,编译出来的 jrtp 是没有开启对 jthread 的支持的。如果采用第二种方法,那么可以不用编译 jthread 库,而直接编译 jrtp 库。

2.4.实际应用

可以参考官方自带的例子,解压后在examples目录下。

使用examples目录下的example1,

I、编译:

g++ -rdynamic
/usr/local/lib/libjrtp.so.3.11.1 -I/usr/local/include/jrtplib3 -o example
example1.cpp

说明:

a、出现错误:undefined reference to
jrtplib::RTPGetErrorString(int) ...........................................

解决:这是因为编译的时候链接库没有找到所致

所以编译时添加上链接库:-rdynamic /usr/local/lib/libjrtp.so.3.11.1或-l jrtp

b、出现错误:rtpsession.h 没有那个文件或目录........................

原因:Linux系统默认查找的头文件在usr/include下面,我们编译后的头文件在usr/local/include下面,所以找不到是情有可源的

解决1:直接将usr/local/include下的两个文件夹复制到/usr/include目录下

解决2:或者做个软连接:

ln -s /usr/local/include/jrtplib3   /usr/include/jrtplib

ln -s /usr/local/include/jthread   /usr/include/jthread

通过1/2这两种其实还是解决不了的,因为默认需找的是/usr/include文件夹下的内容,现在相当于放在/usr/include/jrtplib目录下了,所以要讲源程序example1.cpp中的“”头文件加上jrtplib/     eg:"jrtplib/rtpsession.h"当有调用jthread头文件的地方要添加“jthread/”

或者,简单点:直接在编译的时候指定,即:-I/usr/local/include/jrtplib3

II、执行:

export
LD_LIBRARY_PATH=/usr/local/lib

./ example

说明:

a、LD_LIBRARY_PATH 这个环境变量是大家最为熟悉的,它告诉loader:在哪些目录中可以找到共享库。可以设置多个搜索目录,这些目录之间用冒号分隔开。export LD_LIBRARY_PATH=/usr/local/lib,然后再运行编译,即可通过。这种方法只是暂时修改路径,在重启shell后会失效。或者是配置在环境变量文件/etc/profile中,重启或者source /etc/profile 生效,source 只在本控制台生效。

b、永久生效的方法为修改动态链接库配置文件/etc/ld.so.conf,或者在/etc/ld.so.conf.d里创建一个新文件,并把需要的目录加到这个文件里。具体方法如下:(说明:这种修改动态链接库配置的方式需要使用超级用户权限,不然没有对共享库配置文件的写权限)

#cd /etc/ld.so.conf.d

#vim jrtplib在编辑环境下加入/usr/local/lib,保存退出。

#ldconfig 重新加载动态链接库。

然后再执行编译链接,成功生成example文件。

然后运行./example成功。

III、实测JRTPLIB发送出来的数据如下:

RTP:

16:36:22 收到数据:{80 00 89
1F AB 94 D4 1F 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:23 收到数据:{80 00 89
20 AB 94 D4 29 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:24 收到数据:{80 00 89
21 AB 94 D4 33 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:25 收到数据:{80 00 89
22 AB 94 D4 3D 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:26 收到数据:{80 00 89
23 AB 94 D4 47 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:27 收到数据:{80 00 89
24 AB 94 D4 51 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:28 收到数据:{80 00 89
25 AB 94 D4 5B 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:29 收到数据:{80 00 89
26 AB 94 D4 65 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:30 收到数据:{80 00 89
27 AB 94 D4 6F 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

16:36:31 收到数据:{80 00 89
28 AB 94 D4 79 18 D7 D0 74 31 32 33 34 35 36 37 38 39 30 }

RTCP:

16:36:25 收到数据:{80 C8 00
06 18 D7 D0 74 DD 6A 03 FF 5F 27 DC BD AB 94 D4 3D 00 00 00 04 00 00 00 28 81
CA 00 06 18 D7 D0 74 01 11 62 6F 6F 6B 40 62 6F 6F 6B 2D 64 65 73 6B 74 6F 70
00 }

16:36:31 收到数据:{80 C8 00
06 18 D7 D0 74 DD 6A 04 05 64 AD 03 D9 AB 94 D4 79 00 00 00 0A 00 00 00 64 81
CA 00 06 18 D7 D0 74 01 11 62 6F 6F 6B 40 62 6F 6F 6B 2D 64 65 73 6B 74 6F 70
00 }

16:36:32 收到数据:{80 C8 00
06 18 D7 D0 74 DD 6A 04 06 65 7A 02 FB AB 94 D4 83 00 00 00 0A 00 00 00 64 81
CA 00 06 18 D7 D0 74 01 11 62 6F 6F 6B 40 62 6F 6F 6B 2D 64 65 73 6B 74 6F 70
00 81 CB 00 01 18 D7 D0 74 }

三、参考的原文:

使用JRTPLIB

2.9系列版本:

http://blog.csdn.net/pu1030/article/details/7619908

http://research.edm.uhasselt.be/jori/jrtplib/jrtplib_old.html

3.11系列版本:

http://research.edm.uhasselt.be/jori/page/CS/Jrtplib.html

http://jrtplib.readthedocs.io/en/stable/

http://blog.csdn.net/zhangjikuan/article/details/27974733

http://blog.sina.com.cn/s/blog_712a04260101c2bm.html

http://blog.csdn.net/asklw/article/details/73555796

http://www.cnblogs.com/einyboy/archive/2012/12/01/2796950.html

http://yebaoshan.blog.163.com/blog/static/204231167201232455825452/

流媒体协议之JRTPLIB的使用20170919的更多相关文章

  1. [转]流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls)

    [转]流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls) http://blog.csdn.net/tttyd/article/details/12032357 RTP       ...

  2. 流媒体协议(RTMP、RTSP、UDP、HTTP、MMS)转换小工具(RTSP转成RTMP案例展示)(转)

    源: 流媒体协议(RTMP.RTSP.UDP.HTTP.MMS)转换小工具(RTSP转成RTMP案例展示)

  3. 部分流媒体协议及流媒体开发框架vitamio

    流媒体协议部分RTP.RTCP.RTSP.MMS.HLS.HTTP progressive streaming   流媒体协议:(RTP.RTCP.RTSP.MMS.HLS.HTTP progress ...

  4. 所有流媒体协议,编解码规范和媒体封装格式的datasheet的下载地址

    https://github.com/jiayayao/DataSheet All datasheet about stream protocol, encode-decode spec and me ...

  5. 流媒体协议RTMP,RTSP与HLS有什么不同

    转载自:http://www.cuplayer.com/player/PlayerCode/Wowza/2015/0204/1774.html HLS (HTTP Live Streaming) Ap ...

  6. 流媒体协议部分RTP、RTCP、RTSP、MMS、HLS、HTTP progressive streaming

    流媒体协议:(RTP.RTCP.RTSP.MMS.HLS.HTTP progressive streaming) 当前在internet上传送音频和视频等信息主要有两种方式: 下载,完整下载一个视频, ...

  7. 网络直播流媒体协议的选择讨论,RTSP,RTMP,HTTP,私有协议?

    最近有不少人在EasyDarwin的交流群里面问关于花椒.映客手机直播技术的问题,还有RTSP.RTMP协议选择的问题,这里个人谈一下自己的愚见. 1.不管是RTSP/RTP.RTMP.HTTP,亦或 ...

  8. 网络流媒体协议的联系与区别(RTP RTCP RTSP RTMP HLS)

    目录 网络流媒体协议的联系与区别(RTP RTCP RTSP RTMP HLS) 简结 RTP RTCP RTSP 区别与联系 RTSP.RTMP.HLS 区别与联系 关于直播 流媒体各协议层次图 基 ...

  9. 流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls)

    RTP           参考文档 RFC3550/RFC3551 Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输层协议.RTP协议详细 ...

随机推荐

  1. IDEA 配置Junit4

    Junit4 主要用来执行java程序的单元测试: 1 安装junit4插件 因为我安装过了,没有安装的再输入框搜索,然后安装就行 2 选择默认使用Junit4 3 红框中的test去掉,变为“$en ...

  2. Spine with Unity Mecanim

    前言 最近这两天刚刚接触Spine,研究了一下Unity Mecanim Animator如何控制Spine,在此分享记录一下,如有不当之处,请留言指出,欢迎讨论. Unity & Spine ...

  3. NO.05--谈一谈Angular 和 Vue.js 的对比。

    几天的vue之后,给需要的盆友们带来一篇对比,也算是我近期之内业余时间的大工程,现在开始: Vue.js 是开源的 JavaScript 框架,能够帮助开发者构建出美观的 Web 界面.当和其它网络工 ...

  4. spring-boot断点调试(IDEA)

  5. JQuery点击打开再点击关闭

    $("#03").click(function() { $("#03").show(speed); $("#03").css("c ...

  6. Python坑系列:可变对象与不可变对象

    在之前的文章 http://www.cnblogs.com/bitpeng/p/4748148.html 中,大家看到了ret.append(path) 和ret.append(path[:])的巨大 ...

  7. Python 装饰器Decorator(二)

    对于上一篇“”Python闭包“”随笔中提到的make_averager()函数的如下实现,我们把历史值保存在列表里,每次计算平均值都需要重新求和,当历史值较多时,需要占用比较多的空间并且效率也不高. ...

  8. 如何理解IPD+CMMI+Scrum一体化研发管理解决方案之IPD篇

    如何快速响应市场的变化,如何推出更有竞争力的产品,如何在竞争中脱颖而出,是国内研发企业普遍面临的核心问题,为了解决这些问题,越来越多的企业开始重视创新与研发管理,加强研发过程的规范化,集成产品开发(I ...

  9. Mininet介绍及安装

    什么是Mininet Mininet是由一些虚拟的终端节点(end-hosts).交换机.路由器连接而成的一个网络仿真器,它采用轻量级的虚拟化技术使得系统可以和真实网络相媲美. Mininet可以很方 ...

  10. 第一次c++团队合作项目第二篇随笔

    随着时间的推移,项目也逐渐展开.我的地图也通过按钮的拼接完成了一小部分.这部分我是用了QT上的按钮类来实现的.接下来就是给按钮贴上图片,然后最重要也是最困难的是实现参数的传递,如何实现点击一个英雄或小 ...