一、说明
RTP 现在的问题是要解决的流媒体的实时传输的问题的最佳方法。和JRTPLIB 是一个用C++语言实现的RTP库。包含UDP通讯。刚使用JRTPLIB,对JRTPLIB的理解还不够深,当做使用时,积累的一些经验写个笔记吧。
二、RTP协议
实时传送协议(Real-time Transport Protocol或简写RTP,也能够写成RTTP)是一个网络传输协议,RTP协议具体说明了在互联网上传递音频和视频的标准数据包格式。它一開始被设计为一个多播协议。但后来被用在非常多单播应用中。
RTP协议经常使用于流媒体系统(配合RTCP协议或者RTSP协议)。由于RTP自身具有Time stamp所以在ffmpeg 中被用做一种formate。
RTP协议的具体介绍,请參考这篇文章http://www.360doc.com/content/11/1009/15/496343_154624612.shtml
三、RTPSession类
这里不介绍jrtplib的编译安装。这个非常easy,网上非常多地方都有解说。
jrtplib的使用中,主要是环绕这个类来实现的,因此大家有必要去查看源代码,看这类的实现。为了方便使用,我在这做了RTPSession的继承封装。以下直接贴代码了。
RTPSessionUtils.h
#include "rtpsession.h" #include "rtppacket.h" #include "rtpudpv4transmitter.h" #include "rtpipv4address.h" #include "rtpsessionparams.h" #include "rtperrors.h" #ifndef WIN32 #include <netinet/in.h> #include <arpa/inet.h> #else #include <winsock2.h> #endif // WIN32 #include "rtpsourcedata.h" #include <stdlib.h> #include <stdio.h> #include <iostream> #include <string> //jrtplib应用需链接的lib #pragma comment(lib,"ws2_32.lib") #pragma comment(lib, "jrtplib_d.lib") #pragma comment(lib,"jthread_d.lib") namespace jrtplib { class RTPSessionUtils : public RTPSession { typedef RTPSession base_type; public: RTPSessionUtils(); ~RTPSessionUtils(); int AddDestination(const std::string& ip, uint16_t port); int DeleteDestination(const std::string& ip, uint16_t port); int CreateDefault(uint16_t port); protected: void OnNewSource(RTPSourceData *dat); void OnBYEPacket(RTPSourceData *dat); void OnRemoveSource(RTPSourceData *dat); void OnRTPPacket(RTPPacket *pack,const RTPTime &receivetime, const RTPAddress *senderaddress); void OnRTCPCompoundPacket(RTCPCompoundPacket *pack,const RTPTime &receivetime, const RTPAddress *senderaddress); void OnPollThreadStep(); private: int GetAddrFromSource(RTPSourceData *dat, uint32_t& ip, uint16_t& port); }; } //整形的ip转成字符串ip static std::string IPToString(const unsigned int iIP) { struct in_addr inaddr; inaddr.s_addr = htonl(iIP); return std::string(inet_ntoa(inaddr)); } //字符串ip转成整形ip static unsigned int IPToInt(const std::string& sIP) { return inet_addr(sIP.c_str()); }
#include "RTPSessionUtils.h" namespace jrtplib{ RTPSessionUtils::RTPSessionUtils() { #ifdef WIN32 WSADATA dat; WSAStartup(MAKEWORD(2,2),&dat); #endif // WIN32 } RTPSessionUtils::~RTPSessionUtils() { #ifdef WIN32 WSACleanup(); #endif // WIN32 } int RTPSessionUtils::CreateDefault(uint16_t port) { RTPUDPv4TransmissionParams transparams; RTPSessionParams sessparams; sessparams.SetOwnTimestampUnit(1.0/10.0);//必须设置 transparams.SetPortbase(port);//port必须是偶数 return base_type::Create(sessparams, &transparams); base_type::SetDefaultPayloadType(0); base_type::SetDefaultTimestampIncrement(0); base_type::SetDefaultMark(false); } int RTPSessionUtils::AddDestination(const std::string& ip, uint16_t port) { return base_type::AddDestination(RTPIPv4Address(ntohl(inet_addr(ip.c_str())), port)); } int RTPSessionUtils::DeleteDestination(const std::string& ip, uint16_t port) { return base_type::DeleteDestination(RTPIPv4Address(ntohl(inet_addr(ip.c_str())), port)); } int RTPSessionUtils::GetAddrFromSource(RTPSourceData *dat, uint32_t& ip, uint16_t& port) { if (dat->IsOwnSSRC()) return -1; if (dat->GetRTPDataAddress() != 0) { const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress()); ip = addr->GetIP(); port = addr->GetPort(); } else if (dat->GetRTCPDataAddress() != 0) { const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress()); ip = addr->GetIP(); port = addr->GetPort()-1; } return 0; } void RTPSessionUtils::OnNewSource(RTPSourceData *dat) { uint32_t ip; uint16_t port; if (GetAddrFromSource(dat, ip, port)) return; RTPIPv4Address dest(ip,port); base_type::AddDestination(dest); std::cout << "OnNewSource Adding destination " << IPToString(ip) << ":" << port << std::endl; } void RTPSessionUtils::OnRemoveSource(RTPSourceData *dat) { if (dat->ReceivedBYE()) return; uint32_t ip; uint16_t port; if (GetAddrFromSource(dat, ip, port)) return; RTPIPv4Address dest(ip,port); base_type::DeleteDestination(dest); std::cout << "OnRemoveSource Deleting destination " << IPToString(ip) << ":" << port << std::endl; } void RTPSessionUtils::OnBYEPacket(RTPSourceData *dat) { uint32_t ip; uint16_t port; if (GetAddrFromSource(dat, ip, port)) return; RTPIPv4Address dest(ip,port); base_type::DeleteDestination(dest); std::cout << "OnBYEPacket Deleting destination " << IPToString(ip) << ":" << port << std::endl; } //仅仅要有rtp包就会触发 void RTPSessionUtils::OnRTPPacket(RTPPacket *pack,const RTPTime &receivetime, const RTPAddress *senderaddress) { std::cout << "OnRTPPacket: data:" << pack->GetPayloadData() << std::endl; } //收到rtcp包触发 void RTPSessionUtils::OnRTCPCompoundPacket(RTCPCompoundPacket *pack,const RTPTime &receivetime, const RTPAddress *senderaddress) { std::cout << "OnRTCPCompoundPacket: data:" << pack->GetCompoundPacketData() << std::endl; } //隔段时间就会触发,也能够用于收包回调函数 //void RTPSessionUtils::OnPollThreadStep() //{ // BeginDataAccess(); // // check incoming packets // if (GotoFirstSourceWithData()) // { // do // { // RTPPacket *pack; // RTPSourceData *srcdat; // srcdat = GetCurrentSourceInfo(); // while ((pack = GetNextPacket()) != NULL) // { // std::cout << "Got packet " << pack->GetExtendedSequenceNumber() << " from SSRC " << srcdat->GetSSRC() << std::endl; // DeletePacket(pack); // } // } while (GotoNextSourceWithData()); // } // EndDataAccess(); //} }
#include <iostream> #include "RTPSessionUtils.h" using namespace jrtplib; void main() { int status; RTPSessionUtils sess; status = sess.CreateDefault(8888); if(status) { std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl; return; } while (1) { std::string buf; std::cout << "Input send data:" ; std::cin >> buf; sess.SendPacket((void*)buf.c_str(), buf.length(), 0, false, 0); if(status) { std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl; continue; } } system("pause"); }
client.cpp
#include <iostream> #include "RTPSessionUtils.h" using namespace jrtplib; void main() { int status; RTPSessionUtils sess; status = sess.CreateDefault(6666); if(status) { std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl; return; } status = sess.AddDestination("127.0.0.1", 8888); if(status) { std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl; return; } while (1) { std::string buf; std::cout << "Input send data:" ; std::cin >> buf; sess.SendPacket((void*)buf.c_str(), buf.length(), 0, false, 0); if(status) { std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl; continue; } } system("pause"); }