我之前编译了jrtplib 3.9.1,并且在项目中使用,结果发现在用这个库时,程序体积有增加了300多K,感觉实在是有点笨重,我无法就是用来发送rtp包而已。想想还是自己重新实现一个简单的类用用拉倒了,所以有了下面的代码。
头文件:
/*! @brief 简单rtp库 @file easy_rtp.h */ #ifndef _EASY_RTP_H #define _EASY_RTP_H #include <string> #include <stdint.h> #ifdef _WIN32 #include <winsock2.h> #else #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <errno.h> #ifndef INVALID_SOCKET #define INVALID_SOCKET (SOCKET)(~0) #endif #ifndef SOCKET_ERROR #define SOCKET_ERROR (-1) #endif #ifndef closesocket #define closesocket(x) close(x) #endif typedef int SOCKET; #endif // 默认最大包大小(MTU 1500 - IP头 20 - UDP头 8) #define DEFAULT_MAX_PACKET_SIZE 1472 /*! @brief 简单rtp数据包装发送库 */ class EasyRtp { public: /*! @brief 构造 @param destIp 目标ip地址 @param port 目标端口 @param localport 本地帮定端口,默认端口采用随机值 */ EasyRtp(const std::string& destIp, uint16_t port, uint16_t localPort = 0, int16_t maxpacketsize = DEFAULT_MAX_PACKET_SIZE); /*! @brief 构造 @param destIp 目标ip地址 @param port 目标端口 @param localport 本地帮定端口,默认端口采用随机值 */ EasyRtp(uint32_t destIp, uint16_t port, uint16_t localPort = 0, int16_t maxpacketsize = DEFAULT_MAX_PACKET_SIZE); ~EasyRtp(); public: /*! @brief 发送rtp包给目标 @param buf 发送的缓冲 @param len 发送的缓冲大小 @param pt 负载类型 @param mark 标记位 @param timestampInc 时间戳增量 @param 错误为-1 */ int32_t sendPacket(const char* buf, int32_t len, int8_t pt, bool mark, int32_t timestampInc); private: /// 简单rtp头12字节,不含扩展头,csrc列表等信息 typedef struct { uint8_t ver; /// 版本号(2bit) bool p; /// 填充位,一直置0(1bit) bool x; /// 扩充头位,一直置0(1bit) uint8_t cc; /// csrc列表数量,一直置0(4bit) bool mark; /// 标记位(1bit) int8_t pt; /// 负载类型(7bit) uint16_t sn; /// 序列号(16bit) uint32_t ts; /// 时间戳(32bit) uint32_t ssrc; /// 来源标示(32bit) }RtpHeader; // 最大包大小 int16_t _maxPacketSize; // 发送的缓冲 char* _sbuf; // 序列号 uint16_t _sn; // 时间戳 uint32_t _ts; // 源标示 uint32_t _ssrc; // 句柄 SOCKET _socket; // 目标地址 struct sockaddr_in _destTo; }; #endif // _EASY_RTP_H
#include <stdio.h> #include <string.h> #include <stdexcept> #include "easy_rtp.h" #include "byte_write.h" #include "utils.h" // 默认的rtp版本 #define RTP_VERSION 2 // rtp头大小 #define RTP_HEADER_SIZE 12 EasyRtp::EasyRtp( const std::string& destIp, uint16_t port, uint16_t localPort /*= 0*/, int16_t maxpacketsize /*= 1500*/ ) :_maxPacketSize(maxpacketsize), _sbuf(NULL), _sn(Utils::createRandam32()), _ts(Utils::createRandam32()), _ssrc(Utils::createRandam32()) { if (maxpacketsize >= RTP_HEADER_SIZE) _sbuf = new char[maxpacketsize]; else throw std::runtime_error("[EasyRtp] too small packet size, must more than 12 Byte"); _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (_socket == INVALID_SOCKET) throw std::runtime_error("[EasyRtp] invalid socket"); _destTo.sin_family = AF_INET; _destTo.sin_port = htons(port); _destTo.sin_addr.s_addr = inet_addr(destIp.c_str()); if (localPort != 0) { struct sockaddr_in sockAddr; sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(localPort); sockAddr.sin_addr.s_addr = INADDR_ANY; if (bind(_socket, (const sockaddr*)&sockAddr, sizeof(sockAddr)) == SOCKET_ERROR) { #ifndef NPRINT #ifdef _WIN32 printf("[EasyRtp] bind error: %d\n", WSAGetLastError()); #else printf("[EasyRtp] bind error: %d\n", errno); #endif #endif closesocket(_socket); throw std::runtime_error("[EasyRtp] bind error"); } } } EasyRtp::EasyRtp( uint32_t destIp, uint16_t port, uint16_t localPort /*= 0*/, int16_t maxpacketsize /*= DEFAULT_MAX_PACKET_SIZE*/ ) :_maxPacketSize(maxpacketsize), _sbuf(NULL), _sn(Utils::createRandam32()), _ts(Utils::createRandam32()), _ssrc(Utils::createRandam32()) { if (maxpacketsize >= RTP_HEADER_SIZE) _sbuf = new char[maxpacketsize]; else throw std::runtime_error("[EasyRtp] too small packet size, must more than 12 Byte"); _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (_socket == INVALID_SOCKET) throw std::runtime_error("[EasyRtp] invalid socket"); _destTo.sin_family = AF_INET; _destTo.sin_port = htons(port); _destTo.sin_addr.s_addr = htonl(destIp); if (localPort != 0) { struct sockaddr_in sockAddr; sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(localPort); sockAddr.sin_addr.s_addr = INADDR_ANY; if (bind(_socket, (const sockaddr*)&sockAddr, sizeof(sockAddr)) == SOCKET_ERROR) { #ifndef NPRINT #ifdef _WIN32 printf("[EasyRtp] bind error: %d\n", WSAGetLastError()); #else printf("[EasyRtp] bind error: %d\n", errno); #endif #endif closesocket(_socket); throw std::runtime_error("[EasyRtp] bind error"); } } } EasyRtp::~EasyRtp() { if (_socket != INVALID_SOCKET) closesocket(_socket); if (_sbuf != NULL) delete [] _sbuf; } int32_t EasyRtp::sendPacket( const char* buf, int32_t len, int8_t pt, bool mark, int32_t timestampInc ) { if ((len + RTP_HEADER_SIZE) > _maxPacketSize) return -1; ++_sn; _ts += timestampInc; // 只设置版本号,其它的全是默认0 _sbuf[0] = 0; _sbuf[0] |= RTP_VERSION << 6; _sbuf[1] = 0; _sbuf[1] |= mark << 7; _sbuf[1] |= pt; write_be_w(_sbuf + 2, _sn); write_be_dw(_sbuf + 4, _ts); write_be_dw(_sbuf + 8, _ssrc); // 保存数据 memcpy(_sbuf + RTP_HEADER_SIZE, buf, len); int32_t ret = sendto(_socket, (const char*)_sbuf, len + RTP_HEADER_SIZE, 0, (const sockaddr*)&_destTo, sizeof(_destTo)); #ifndef NPRINT if (ret < 0) { #ifdef _WIN32 printf("[EasyRtp] sendto error: %d\n", WSAGetLastError()); #else printf("[EasyRtp] sendto error: %d\n", errno); #endif } #endif return ret; }
stdint.h是新c++标准中的头文件,定义了int32_t int8_t等typedef 类型。