我们知道, RTP(Real-timeTransportProtocol)是用于Internet上针对多媒体数据流的一种传输协议,做流媒体传输方面的应用离不开RTP协议的实现及使用,为了更加快速地在项目中应用RTP协议实现流媒体的传输,我们一般会选择使用一些RTP库,例如使用c++语言编写的JRTPLIB库,网上关于RTP协议以及JRTPLIB库的介绍已经很多了,在此我也不再赘述,文本主要介绍实现了RTP协议的另一种开源库——ORTP库,这个库是纯使用c语言编写,由于我们的项目是基于Linux下的c语言编程,故我们选择了ortp作为我们的第三方库,在此我也对该库进行一个简单地介绍,希望对其他ortp的初学者有所帮助。
一、简介
ORTP是一个支持RTP以及RFC3550协议的库,有如下的特性:
(1)使用C语言编写,可以工作于windows, Linux, 以及 Unix平台
(2)实现了RFC3550协议,提供简单易用的API。支持多种配置,RFC3551为默认的配置。
(3)支持单线程下的多个RTP会话,支持自适应抖动处理。
(4)基于GPL版权声明。
ORTP可以在其官方网站上(http://www.linphone.org/index.php/eng/code_review/ortp)下载,下载解压后得到ORTP的源码包和示例程序(tests)。其帮助文档在docs目录下,也可以在http://download.savannah.gnu.org/releases/linphone/ortp/docs/在线查看。
关于ORTP的资料并不多,主要是其源码、帮助文档以及示例程序,关于示例程序说明如下:
rtprecv.c 和rtpsend.c 展示了如何接收和发送单RTP数据流。
mrtprecv.c mrtpsend.c 展示了如何同时接收和发送多个RTP数据流。
二、主要函数介绍
rtp_session_init
rtp_session_destroy
【原型】: void rtp_session_destroy(RtpSession *session)
【功能】:摧毁rtp会话对象,释放资源
【参数】:session已经创建的RTP会话对象
三、程序示例
编译及运行环境:VS2008,windows
编程语言:c/c++,ortp库为c语言封装,我用c++对其进行了进一步封装,如果需要c语言的封装接口,只需要把类中相关函数提取出来即可使用。
ortp库:ortp-0.9.1(由于是以前写的代码,故用的ortp库比较老,但不影响使用和学习,我附件中的工程中已经把ortp-0.9.1库文件添加进去了)
整个测试代码在工程的附件中,大家下载后直接编译后,在Debug目录下打开2个本程序,一个选择Client,一个选择Server,即可看到测试效果。
下面,我的相关代码发布如下(附件中有完整的工程)。
一、ORTP接收端封装类
- //
- // COPYRIGHT NOTICE
- // Copyright (c) 2011, 华中科技大学 卢俊(版权声明)
- // All rights reserved.
- //
- /// @file CortpClient.h
- /// @brief ortp客户端类声明文件
- ///
- /// 实现和提供ortp的客户端应用接口
- ///
- /// @version 1.0
- /// @author 卢俊
- /// @date 2011/11/03
- //
- //
- // 修订说明:
- //
- #ifndef CORTPCLIENT_H_
- #define CORTPCLIENT_H_
- #include <ortp/ortp.h>
- #include <string>
- /**
- * COrtpClient ortp客户端管理类
- *
- * 负责封装和提供ortp相关接口
- */
- class COrtpClient
- {
- public:
- /** 构造函数/析构函数
- *
- * 在创建/销毁该类对象时自动调用
- */
- COrtpClient();
- ~COrtpClient();
- /** ORTP模块的初始化
- *
- * 在整个系统最开始调用,负责ORTP库的初始化
- * @return: bool 是否成功
- * @note:
- * @see:
- */
- static bool init();
- /** ORTP模块的逆初始化
- *
- * 在系统退出前调用,负责ORTP库的释放
- * @return: bool 是否成功
- * @note:
- * @see:
- */
- static bool deInit();
- /** 创建RTP接收会话
- *
- * 负责产生RTP接收端会话,监听服务器端的数据
- * @param: const char * localip 本地ip地址
- * @param: int localport 本地监听端口
- * @return: bool 是否成功
- * @note:
- * @see:
- */
- bool create(const char * localip, int localport );
- /** 获取接收到的rtp包
- *
- * 将接收到的rtp数据包取出
- * @param: char * pBuffer
- * @param: int & len
- * @return: bool 是否成功
- * @note:
- * @see:
- */
- bool get_recv_data( char *pBuffer, int &len );
- private:
- RtpSession *m_pSession; /** rtp会话句柄 */
- long m_curTimeStamp; /** 当前时间戳 */
- int m_timeStampInc; /** 时间戳增量 */
- };
- #endif // CortpClient_H_
- //
- // COPYRIGHT NOTICE
- // Copyright (c) 2011, 华中科技大学 卢俊(版权声明)
- // All rights reserved.
- //
- /// @file CortpClient.cpp
- /// @brief ortp客户端类实现文件
- ///
- /// 实现和提供ortp的客户端应用接口
- ///
- /// @version 1.0
- /// @author lujun
- /// @date 2011/11/03
- //
- //
- // 修订说明:
- //
- #include "CortpClient.h"
- /* the payload type define */
- #define PAYLOAD_TYPE_VIDEO 34
- /* RTP video Send time stamp increase */
- #define VIDEO_TIME_STAMP_INC 3600
- /** 从rtp接收缓冲区一次读取的字节数 */
- #define READ_RECV_PER_TIME 1024
- COrtpClient::COrtpClient()
- {
- m_pSession = NULL;
- m_timeStampInc = 0;
- m_curTimeStamp = 0;
- }
- COrtpClient::~COrtpClient()
- {
- if (!m_pSession)
- {
- rtp_session_destroy(m_pSession);
- }
- }
- bool COrtpClient::init()
- {
- int ret;
- WSADATA wsaData;
- /** 初始化winsocket */
- if ( WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
- {
- return false;
- }
- ortp_init();
- ortp_scheduler_init();
- return true;
- }
- bool COrtpClient::deInit()
- {
- ortp_exit();
- if (WSACleanup() == SOCKET_ERROR)
- {
- return false;
- }
- return true;
- }
- bool COrtpClient::create( const char * localip, int localport )
- {
- if ( m_pSession != NULL)
- {
- return false;
- }
- /** 创建新会话 */
- m_pSession = rtp_session_new(RTP_SESSION_RECVONLY);
- if ( !m_pSession)
- {
- return false;
- }
- /** 配置相关参数 */
- rtp_session_set_scheduling_mode(m_pSession,1);
- rtp_session_set_blocking_mode(m_pSession,1);
- rtp_session_set_local_addr(m_pSession,localip,localport);
- rtp_session_enable_adaptive_jitter_compensation(m_pSession,1);
- rtp_session_set_jitter_compensation(m_pSession,40);
- rtp_session_set_payload_type(m_pSession,PAYLOAD_TYPE_VIDEO);
- m_timeStampInc = VIDEO_TIME_STAMP_INC;
- return true;
- }
- bool COrtpClient::get_recv_data( char *pBuffer, int &len )
- {
- int recvBytes = 0;
- int totalBytes = 0;
- int have_more = 1;
- while(have_more)
- {
- if ( totalBytes+READ_RECV_PER_TIME > len )
- {
- /** 缓冲区大小不够 */
- return false;
- }
- recvBytes = rtp_session_recv_with_ts(m_pSession,pBuffer+totalBytes,READ_RECV_PER_TIME,m_curTimeStamp,&have_more);
- if (recvBytes <= 0)
- {
- break;
- }
- totalBytes += recvBytes;
- }
- /** 判断是否读取到数据 */
- if (totalBytes == 0)
- {
- return false;
- }
- /** 记录有效字节数 */
- len = totalBytes;
- /** 时间戳增加 */
- m_curTimeStamp += m_timeStampInc;
- return true;
- }
二、ORTP发送端封装类
- //
- // COPYRIGHT NOTICE
- // Copyright (c) 2011, 华中科技大学 卢俊(版权声明)
- // All rights reserved.
- //
- /// @file CortpServer.h
- /// @brief ortp服务器类声明文件
- ///
- /// 实现和提供ortp的服务器端应用接口
- ///
- /// @version 1.0
- /// @author 卢俊
- /// @date 2011/11/03
- //
- //
- // 修订说明:
- //
- #ifndef CORTPSERVER_H_
- #define CORTPSERVER_H_
- #include <ortp/ortp.h>
- /**
- * COrtpServer RTP发送类
- *
- * 负责使用RTP协议进行数据的发送
- */
- class COrtpServer
- {
- public:
- /** 构造函数
- *
- * 该函数为该类的构造函数,在创建该类对象时自动调用
- */
- COrtpServer();
- /** 析构函数
- *
- * 该函数执行析构操作,由系统自动调用
- */
- ~COrtpServer();
- /** ORTP模块的初始化
- *
- * 在整个系统最开始调用,负责ORTP库的初始化
- * @return: bool 是否成功
- * @note:
- * @see:
- */
- static bool init();
- /** ORTP模块的逆初始化
- *
- * 在系统退出前调用,负责ORTP库的释放
- * @return: bool 是否成功
- * @note:
- * @see:
- */
- static bool deInit();
- /** 创建RTP接收会话
- *
- * 负责产生RTP接收端会话,监听服务器端的数据
- * @param: const char * destIP 目的地址的IP
- * @param: int destport 目的地址的监听端口号
- * @return: bool 是否成功
- * @note:
- * @see:
- */
- bool create(const char * destIP, int destport );
- /** 发送RTP数据
- *
- * 将指定的buffer中的数据发送到客户端
- * @param: unsigned char * buffer 需要发送的数据
- * @param: int len 有效字节数
- * @return: int 实际发送的字节数
- * @note:
- * @see:
- */
- int send_data( unsigned char *buffer, int len );
- private:
- RtpSession *m_pSession; /** rtp会话句柄 */
- long m_curTimeStamp; /** 当前时间戳 */
- int m_timeStampInc; /** 时间戳增量 */
- char *m_ssrc; /** 数据源标识 */
- };
- #endif // COrtpServer_H_
- //
- // COPYRIGHT NOTICE
- // Copyright (c) 2011, 华中科技大学 卢俊(版权声明)
- // All rights reserved.
- //
- /// @file CortpServer.cpp
- /// @brief ortp服务器类实现文件
- ///
- /// 实现和提供ortp的服务器端应用接口
- ///
- /// @version 1.0
- /// @author lujun
- /// @date 2011/11/03
- //
- //
- // 修订说明:
- //
- #include "COrtpServer.h"
- /* the payload type define */
- #define PAYLOAD_TYPE_VIDEO 34
- /* RTP video Send time stamp increase */
- #define VIDEO_TIME_STAMP_INC 3600
- COrtpServer::COrtpServer()
- {
- m_ssrc = NULL;
- m_pSession = NULL;
- m_timeStampInc = 0;
- m_curTimeStamp = 0;
- }
- COrtpServer::~COrtpServer()
- {
- if (!m_pSession)
- {
- rtp_session_destroy(m_pSession);
- }
- }
- bool COrtpServer::init()
- {
- int ret;
- WSADATA wsaData;
- /** 初始化winsocket */
- if ( WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
- {
- return false;
- }
- ortp_init();
- ortp_scheduler_init();
- return true;
- }
- bool COrtpServer::deInit()
- {
- ortp_exit();
- if (WSACleanup() == SOCKET_ERROR)
- {
- return false;
- }
- return true;
- }
- bool COrtpServer::create( const char * destIP, int destport )
- {
- m_ssrc = getenv("SSRC");
- m_pSession = rtp_session_new(RTP_SESSION_SENDONLY);
- rtp_session_set_scheduling_mode(m_pSession,1);
- rtp_session_set_blocking_mode(m_pSession,1);
- rtp_session_set_remote_addr(m_pSession,destIP,destport);
- if(m_ssrc != NULL)
- {
- rtp_session_set_ssrc(m_pSession,atoi(m_ssrc));
- }
- rtp_session_set_payload_type(m_pSession,PAYLOAD_TYPE_VIDEO);
- m_timeStampInc = VIDEO_TIME_STAMP_INC;
- return true;
- }
- int COrtpServer::send_data( unsigned char *buffer, int len )
- {
- int sendBytes = 0;
- /** 强转 */
- const char *sendBuffer = (const char*)buffer;
- sendBytes = rtp_session_send_with_ts(m_pSession,sendBuffer,len,m_curTimeStamp);
- if ( sendBytes > 0)
- {
- m_curTimeStamp += m_timeStampInc; /** 增加时间戳 */
- }
- return sendBytes;
- }
三、测试程序
- //
- // COPYRIGHT NOTICE
- // Copyright (c) 2011, 华中科技大学 卢俊 (版权声明)
- // All rights reserved.
- //
- /// @file main.cpp
- /// @brief ortp测试文件
- ///
- /// 测试ortp发送结构体
- ///
- /// @version 1.0
- /// @author 卢俊
- /// @e-mail lujun.hust@gmail.com
- /// @date 2011/10/19
- //
- //
- // 修订说明:
- //
- #include <iostream>
- #include "COrtpClient.h"
- #include "COrtpServer.h"
- /** 本地IP地址 */
- const char * LOCAL_IP_ADDR = "127.0.0.1";
- /** 本地监听端口 */
- const int LOCAL_RTP_PORT = 8000;
- /** 目的监听端口 */
- const int DEST_RTP_PORT = 8000;
- /** 目的IP地址 */
- const char * DEST_IP_ADDR = "127.0.0.1";
- /** 一次发送的数据长度 */
- const int SEND_LEN_PER_TIME = 8*1024;
- /** 接收缓冲区的总大小 */
- const int RECV_BUFFER_LEN = 10*1024;
- /** 一次接收的数据长度 */
- const int RECV_LEN_PER_TIME = 1024;
- bool ortpServer()
- {
- COrtpServer ortpServer;
- COrtpServer::init();
- if (!ortpServer.create(DEST_IP_ADDR,DEST_RTP_PORT))
- {
- std::cout << "ortpServer.create fail!\n";
- getchar();
- getchar();
- return false;
- }
- unsigned char * buffer = new unsigned char[SEND_LEN_PER_TIME];
- while (1)
- {
- if ( ortpServer.send_data(buffer,SEND_LEN_PER_TIME) <= 0)
- {
- std::cout << "send fail!\n";
- }
- Sleep(100);
- std::cout << "send bytes\n";
- }
- delete [] buffer;
- COrtpClient::deInit();
- return true;
- }
- bool ortpClient()
- {
- COrtpClient ortpClient;
- COrtpClient::init();
- if (!ortpClient.create(LOCAL_IP_ADDR,LOCAL_RTP_PORT))
- {
- std::cout << "ortpClient.create fail!\n";
- getchar();
- getchar();
- return false;
- }
- char *buffer = new char[RECV_BUFFER_LEN];
- while(1)
- {
- int len = RECV_BUFFER_LEN;
- if (!ortpClient.get_recv_data(buffer,len))
- {
- Sleep(10);
- continue;
- }
- std::cout << "successful recv,data len =" << len << std::endl;
- }
- COrtpClient::deInit();
- delete [] buffer;
- return true;
- }
- void main()
- {
- std::cout << "enter num,1 ->client, 2->server! \n";
- int num;
- std::cin >> num;
- while(1)
- {
- if (num == 1)
- {
- ortpClient();
- break;
- }
- else if (num == 2)
- {
- ortpServer();
- break;
- }
- else
- {
- std::cout << "please input 1 or 2 !\n";
- }
- }
- getchar();
- getchar();
- }
有关ORTP的介绍、RTP的介绍、RTP的负载类型和时间戳的含义等理论性的东西,都可以在我博客中的其他文章中找到,以上就是整个工程的代码,注释不是很多,因为有些地方我也不是特别清楚,比如jitter、scheduling什么的,如果有什么其他疑问欢迎留言或者E-mail来信交流。