我之前編譯了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 類型。