我之前编译了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
cpp源码:
#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 类型。