SIP與RTP綜合應用5-RTP解包過程

來源:互聯網
上載者:User

RTP接收部分比較簡單(不用考慮jitterbuffer等),先從這裡入手。

其實主要就3步:

1 建立一個udp,監聽一個連接埠,比如5200。

2 收到RTP包,送到解包程式,繼續收第 二個。

3 收齊一幀後,或儲存檔案,或解碼去播放。

下面詳細說一下具體過程:

1 建立UDP,非常非常地簡單(這裡只是簡單地類比RTP接收,雖然能正常工作,但是沒有處理RTCP部分,會影響發送端):

lass 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;
    }

//如果必要可以處理對方IP連接埠

   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包頭的資料和操作的封裝:

/*****************************************************************************/
/* The contents of this file are subject to the Mozilla Public License       */
/* Version 1.0 (the "License"); you may not use this file except in          */
/* compliance with the License.  You may obtain a copy of the License at     */
/* http://www.mozilla.org/MPL/                                               */
/*                                                                           */
/* Software distributed under the License is distributed on an "AS IS"       */
/* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the  */
/* License for the specific language governing rights and limitations under  */
/* the License.                                                              */
/*                                                                           */
/* The Original Code is the Open H323 Library.                               */
/*                                                                           */
/* The Initial Developer of the Original Code is Matthias Schneider          */
/* Copyright (C) 2007 Matthias Schneider, All Rights Reserved.               */
/*                                                                           */
/* Contributor(s): Matthias Schneider (ma30002000@yahoo.de)                  */
/*                                                                           */
/* Alternatively, the contents of this file may be used under the terms of   */
/* the GNU General Public License Version 2 or later (the "GPL"), in which   */
/* case the provisions of the GPL are applicable instead of those above.  If */
/* you wish to allow use of your version of this file only under the terms   */
/* of the GPL and not to allow others to use your version of this file under */
/* the MPL, indicate your decision by deleting the provisions above and      */
/* replace them with the notice and other provisions required by the GPL.    */
/* If you do not delete the provisions above, a recipient may use your       */
/* version of this file under either the MPL or the GPL.                     */
/*                                                                           */
/* The Original Code was written by Matthias Schneider <ma30002000@yahoo.de> */
/*****************************************************************************/

#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__ */

原封不動,可以直接拿來使用。當然,自己寫一個也不麻煩。很多人寫不好估計是卡在位元運算上了。

然後,進入video/MPEG4-ffmpeg目錄下看mpeg4.cxx,這裡包含了完整的RFC解包重組及MPEG4解碼的源碼。直接編譯可能通不過,好在代碼寫的非常整齊,提取出來就是了。解包解碼只要看這一個函數:

bool MPEG4DecoderContext::DecodeFrames(const BYTE * src, unsigned & srcLen,
                                       BYTE * dst, unsigned & dstLen,
                                       unsigned int & flags)

{
    if (!FFMPEGLibraryInstance.IsLoaded())
        return 0;

    // Creates our frames
    RTPFrame srcRTP(src, srcLen);
    RTPFrame dstRTP(dst, dstLen, RTP_DYNAMIC_PAYLOAD);
    dstLen = 0;
    flags = 0;
   
    int srcPayloadSize = srcRTP.GetPayloadSize();
    SetDynamicDecodingParams(true); // Adjust dynamic settings, restart allowed
   
    // Don't exceed buffer limits.  _encFrameLen set by ResizeDecodingFrame
    if(_lastPktOffset + srcPayloadSize < _encFrameLen)
    {
        // Copy the payload data into the buffer and update the offset
        memcpy(_encFrameBuffer + _lastPktOffset, srcRTP.GetPayloadPtr(),
               srcPayloadSize);
        _lastPktOffset += srcPayloadSize;
    }
    else {

        // Likely we dropped the marker packet, so at this point we have a
        // full buffer with some of the frame we wanted and some of the next
        // frame. 

        //I'm on the fence about whether to send the data to the
        // decoder and hope for the best, or to throw it all away and start
        // again.

        // throw the data away and ask for an IFrame
        TRACE(1, "MPEG4/tDecoder/tWaiting for an I-Frame");
        _lastPktOffset = 0;
        flags = (_gotAGoodFrame ? PluginCodec_ReturnCoderRequestIFrame : 0);
        _gotAGoodFrame = false;
        return 1;
    }

    // decode the frame if we got the marker packet
    int got_picture = 0;
    if (srcRTP.GetMarker()) {
        _frameNum++;
        int len = FFMPEGLibraryInstance.AvcodecDecodeVideo
                        (_avcontext, _avpicture, &got_picture,
                         _encFrameBuffer, _lastPktOffset);

        if (len >= 0 && got_picture) {
#ifdef LIBAVCODEC_HAVE_SOURCE_DIR
            if (DecoderError(_keyRefreshThresh)) {
                // ask for an IFrame update, but still show what we've got
                flags = (_gotAGoodFrame ? PluginCodec_ReturnCoderRequestIFrame : 0);
                _gotAGoodFrame = false;
            }
#endif
            TRACE_UP(4, "MPEG4/tDecoder/tDecoded " << len << " bytes" << ", Resolution: " << _avcontext->width << "x" << _avcontext->height);
            // If the decoding size changes on us, we can catch it and resize
            if (!_disableResize
                && (_frameWidth != (unsigned)_avcontext->width
                   || _frameHeight != (unsigned)_avcontext->height))
            {
                // Set the decoding width to what avcodec says it is
                _frameWidth  = _avcontext->width;
                _frameHeight = _avcontext->height;
                // Set dynamic settings (framesize), restart as needed
                SetDynamicDecodingParams(true);
                return true;
            }

            // it's stride time
            int frameBytes = (_frameWidth * _frameHeight * 3) / 2;
            PluginCodec_Video_FrameHeader * header
                = (PluginCodec_Video_FrameHeader *)dstRTP.GetPayloadPtr();
            header->x = header->y = 0;
            header->width = _frameWidth;
            header->height = _frameHeight;
            unsigned char *dstData = OPAL_VIDEO_FRAME_DATA_PTR(header);
            for (int i=0; i<3; i ++) {
                unsigned char *srcData = _avpicture->data[i];
                int dst_stride = i ? _frameWidth >> 1 : _frameWidth;
                int src_stride = _avpicture->linesize[i];
                int h = i ? _frameHeight >> 1 : _frameHeight;
                if (src_stride==dst_stride) {
                    memcpy(dstData, srcData, dst_stride*h);
                    dstData += dst_stride*h;
                }
                else
                {
                    while (h--) {
                        memcpy(dstData, srcData, dst_stride);
                        dstData += dst_stride;
                        srcData += src_stride;
                    }
                }
            }
            // Treating the screen as an RTP is weird
            dstRTP.SetPayloadSize(sizeof(PluginCodec_Video_FrameHeader)
                                  + frameBytes);
            dstRTP.SetPayloadType(RTP_DYNAMIC_PAYLOAD);
            dstRTP.SetTimestamp(srcRTP.GetTimestamp());
            dstRTP.SetMarker(true);
            dstLen = dstRTP.GetFrameLen();
            flags = PluginCodec_ReturnCoderLastFrame;
            _gotAGoodFrame = true;
        }
        else {
            TRACE(1, "MPEG4/tDecoder/tDecoded "<< len << " bytes without getting a Picture...");
            // decoding error, ask for an IFrame update
            flags = (_gotAGoodFrame ? PluginCodec_ReturnCoderRequestIFrame : 0);
            _gotAGoodFrame = false;
        }
        _lastPktOffset = 0;
    }
    return true;
}

寫的非常非常的明白:if (srcRTP.GetMarker()),到了這裡表示收滿了一包,開始去解碼。

mpeg4-es的RFC還原重組就這麼簡單,下一步的解碼,就涉及到用libavcodec.dll了。

 

更多技術文章請參看施昌權的個人網站: http://www.joyvc.cn

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.