RTP接收部分比较简单(不用考虑jitterbuffer等),先从这里入手。
其实主要就3步:
1 创建一个udp,监听一个端口,比如5200。
2 收到RTP包,送到解包程序,继续收第 二个。
3 收齐一帧后,或保存文件,或解码去播放。
下面详细说一下具体过程:
1 创建UDP,非常非常地简单(这里只是简单地模拟RTP接收,虽然能正常工作,但是没有处理RTCP部分,会影响发送端):
- class CUDPSocket : public CAsyncSocket
- {
- public:
- CUDPSocket();
- virtual ~CUDPSocket();
-
- virtual void OnReceive(int nErrorCode);
-
- };
调用者:CUDPSocket m_udp; m_udp.Create(...);这样就可以了。注意端口,如果指定端口创建不成功,就端口+1或+2重试一下。
重写OnReceive:
- void CUDPSocket::OnReceive(int nErrorCode)
- {
- char szBuffer[1500];
-
- SOCKADDR_IN sockAddr;
- memset(&sockAddr, 0, sizeof(sockAddr));
- int nSockAddrLen = sizeof(sockAddr);
-
- int nResult = ReceiveFrom(szBuffer, 1500, (SOCKADDR*)&sockAddr, &nSockAddrLen, 0);
- if(nResult == SOCKET_ERROR)
- {
- return;
- }
-
-
- USHORT unPort = ntohs(sockAddr.sin_port);
- ULONG ulIP = sockAddr.sin_addr.s_addr;
-
-
-
- Decode((BYTE*)szBuffer, nResult);
-
- }
2 收到了数据,开始Decode,一般通过RTP传输的视频主要有h263 (old,1998,2000),h264,mpeg4-es。mpeg4-es格式最简单,就从它入手。
如果了解RFC3160,直接分析格式写就是了。如果想偷懒,用现成的, 也找的到:在opal项目下,有个plugins目录,视频中包含了h261,h263,h264,mpeg4等多种解包,解码的源码,稍加改动就可以拿来用。
首先看:video\common下的rtpframe.h这个文件,这是对RTP包头的数据和操作的封装:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #ifndef __RTPFRAME_H__
- #define __RTPFRAME_H__ 1
-
- #ifdef _MSC_VER
- #pragma warning(disable:4800) // disable performance warning
- #endif
-
- class RTPFrame {
- public:
- RTPFrame(const unsigned char * frame, int frameLen) {
- _frame = (unsigned char*) frame;
- _frameLen = frameLen;
- };
-
- RTPFrame(unsigned char * frame, int frameLen, unsigned char payloadType) {
- _frame = frame;
- _frameLen = frameLen;
- if (_frameLen > 0)
- _frame [0] = 0x80;
- SetPayloadType(payloadType);
- }
-
- unsigned GetPayloadSize() const {
- return (_frameLen - GetHeaderSize());
- }
-
- void SetPayloadSize(int size) {
- _frameLen = size + GetHeaderSize();
- }
-
- int GetFrameLen () const {
- return (_frameLen);
- }
-
- unsigned char * GetPayloadPtr() const {
- return (_frame + GetHeaderSize());
- }
-
- int GetHeaderSize() const {
- int size;
- size = 12;
- if (_frameLen < 12)
- return 0;
- size += (_frame[0] & 0x0f) * 4;
- if (!(_frame[0] & 0x10))
- return size;
- if ((size + 4) < _frameLen)
- return (size + 4 + (_frame[size + 2] << 8) + _frame[size + 3]);
- return 0;
- }
-
- bool GetMarker() const {
- if (_frameLen < 2)
- return false;
- return (_frame[1] & 0x80);
- }
-
- unsigned GetSequenceNumber() const {
- if (_frameLen < 4)
- return 0;
- return (_frame[2] << 8) + _frame[3];
- }
-
- void SetMarker(bool set) {
- if (_frameLen < 2)
- return;
- _frame[1] = _frame[1] & 0x7f;
- if (set) _frame[1] = _frame[1] | 0x80;
- }
-
- void SetPayloadType(unsigned char type) {
- if (_frameLen < 2)
- return;
- _frame[1] = _frame [1] & 0x80;
- _frame[1] = _frame [1] | (type & 0x7f);
- }
-
- unsigned char GetPayloadType() const
- {
- if (_frameLen < 1)
- return 0xff;
- return _frame[1] & 0x7f;
- }
-
- unsigned long GetTimestamp() const {
- if (_frameLen < 8)
- return 0;
- return ((_frame[4] << 24) + (_frame[5] << 16) + (_frame[6] << 8) + _frame[7]);
- }
-
- void SetTimestamp(unsigned long timestamp) {
- if (_frameLen < 8)
- return;
- _frame[4] = (unsigned char) ((timestamp >> 24) & 0xff);
- _frame[5] = (unsigned char) ((timestamp >> 16) & 0xff);
- _frame[6] = (unsigned char) ((timestamp >> 8) & 0xff);
- _frame[7] = (unsigned char) (timestamp & 0xff);
- };
-
- protected:
- unsigned char* _frame;
- int _frameLen;
- };
-
- struct frameHeader {
- unsigned int x;
- unsigned int y;
- unsigned int width;
- unsigned int height;
- };
-
- #endif /* __RTPFRAME_H__ */
原封不动,可以直接拿来使用。当然,自己写一个也不麻烦。很多人写不好估计是卡在位运算上了。
(sxcong) |