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

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.pswp.cn/news/453787.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/453787.shtml
英文地址,請注明出處:http://en.pswp.cn/news/453787.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JavaScript eval() 函數,計算某個字符串,并執行其中的的 JavaScript 代碼。

JavaScript eval() 函數&#xff0c;計算某個字符串&#xff0c;并執行其中的的 JavaScript 代碼。 適合用于計算器的計算&#xff0c;等。 例子&#xff1a; eval("x10;y20;document.write(x*y)") document.write(eval("22")) var x10 document.write(eva…

vb整合多個excel表格到一張_[Excel]同一工作簿中多個工作表保存成獨立的表格

一個工作簿中有多個表格&#xff0c;如何將其表格單獨保存成一個獨立的文檔呢&#xff1f;如果表格少&#xff0c;操作如下&#xff1a;選中要導出表格的標簽名--鼠標郵件--移動或復制表格--新建工作簿。當如果表格太多呢&#xff0c;以上方法就太羅嗦了。簡單方法用VBA,步驟如…

sqlserver字符串多行合并為一行

1 --創建測試表2 CREATE TABLE [dbo].[TestRows2Columns](3 [Id] [int] IDENTITY(1,1) NOT NULL,4 [UserName] [nvarchar](50) NULL,5 [Subject] [nvarchar](50) NULL,6 [Source] [numeric](18,0) NULL7 )8 GO9 10 --插入測試數據 11 INSERT INTO [TestRows2C…

OpenCore 的代碼結構

OpenCore的代碼結構 以開源Android 的代碼為例&#xff0c;Open Core 的代碼在Android 代碼的External/Opencore 目錄 中。這個目錄是OpenCore 的根目錄&#xff0c;其中包含的子目錄如下所示&#xff1a; android&#xff1a;這里面是一個上層的庫&#xff0c;它基于PVPlaye…

深度學習之卷積神經網絡 GoogleNet

GoogLeNet Incepetion V1 這是GoogLeNet的最早版本&#xff0c;出現在2014年的《Going deeper with convolutions》。之所以名為“GoogLeNet”而非“GoogleNet”,文章說是為了向早期的LeNet致敬。 深度學習以及神經網絡快速發展&#xff0c;人們不再只關注更給力的硬件、更大…

名詞解釋 算法的有限性_數據結構與算法期中考試卷(含答案)

玉林師范學院期中課程考試試卷(2010——2011學年度第一學期)命題教師&#xff1a;劉恒 命題教師所在系&#xff1a;數計系 課程名稱&#xff1a;數據結構與算法 考試專業&#xff1a;信計 考試年級&#xff1a;09級一、單項選擇題(每題2分&#xff0c;共30分&#xff0c;把正確…

Jzoj4348 打擊目標

又是被水題坑了。。。 一直想不出來看題解說要什么主席樹&#xff0c;于是開始打離線算法 結果打到一半發現要強制在線。。No!!! 發現直接AC自動機似乎可做&#xff1f;樹剖之后在AC自動機上跑的時候判斷一下不就好了嗎!連線段樹都不要 讓后快樂切掉&#xff0c;速度還可以&…

深度學習之卷積神經網絡 VGGNet

2014年&#xff0c;牛津大學計算機視覺組&#xff08;Visual Geometry Group&#xff09;和Google DeepMind公司的研究員一起研發出了新的深度卷積神經網絡&#xff1a;VGGNet&#xff0c;并取得了ILSVRC2014比賽分類項目的第二名&#xff08;第一名是GoogLeNet&#xff0c;也是…

SpringMVC 返回json的兩種方式

前后臺數據交互使用json是一種很重要的方式.本文主要探討SpringMVC框架使用json傳輸的技術. 請注意,本文所提到的項目使用Spring 版本是4.1.7,其他版本在具體使用上可能有不一樣的情況. 一、最常見——使用RequestBody的注解返回一個實體對象; 使用方式如下: 1:引入jar包&#…

word上怎么把圖片拼接到一起_如何用Word把自己插入的兩張圖片合在一起?

例如上面效果的設置方法&#xff1a;1、單擊插入----圖片按鈕&#xff1b;2、彈出插入圖片對話框&#xff0c;按住Ctrl鍵&#xff0c;同時選擇所需要的圖片&#xff1b;3、選中圖片&#xff0c;單擊圖片工具格式----文字環繞----緊密型環繞&#xff1b;4、此時&#xff0c;用鼠…

深度學習之卷積神經網絡 ResNet

論文 Identity Mappings in Deep Residual Networks 2015年&#xff0c;ResNet&#xff08;Residual Neural Network&#xff09;由微軟研究院的Kaiming He等四名華人提出&#xff0c;并在ILSVRC2015比賽中取得冠軍&#xff0c;在top5上的錯誤率為3.57%&#xff0c;同時參數量…

按照RFC3984協議實現H264視頻流媒體 RTSP H264

轉自&#xff1a;http://topic.csdn.net/u/20100104/16/0fd992e8-b0a6-4c2b-85a4-d9513d3b1491.html 相信有不少人和我一樣&#xff0c;希望實現H264格式視頻的流媒體播放。但是對于一個新手來說&#xff0c;往往不知道從何入手。利用百度&#xff0c;GOOGLE等搜索資料真是沙里…

搭建SSM框架之Spring

作為一枚大四準備畢業的學生&#xff0c;最重要的事便是畢業設計&#xff0c;前些日子剛剛拿到畢設題目&#xff1a;“3D網絡圖&#xff1a;面向網絡結構數據的可視化軟件設計”&#xff0c;(⊙o⊙)…&#xff0c;怎么說哪&#xff0c;看到題目就是一頭霧水&#xff08;前幾屆不…

audio unity 加速_淺談Unity中Android、iOS音頻延遲

在Unity上面做音游&#xff0c;當在移動端實機運行起來&#xff0c;會發現&#xff0c;音頻的發出會有一定的延遲&#xff0c;無論是長音效還是短音效&#xff0c;Unity內置的Audio內部使用的是FMOD&#xff0c;有以下手段改善通過設置稍微改善其延遲的問題Edit → Project Set…

深度學習之 hard negative mining (難例挖掘)

Hard Negative Mining Method 思想 hard是困難樣本&#xff0c;negative是負樣本&#xff0c;hard negative就是說在對負樣本分類時候&#xff0c;loss比較大&#xff08;label與prediction相差較大&#xff09;的那些樣本&#xff0c;也可以說是容易將負樣本看成正樣本的那些…

單列表_使用Excel中的quot;記錄單quot;功能快速錄入數據

在Excel中進行數據錄入的時候&#xff0c;平常都是一行一行地錄入數據&#xff0c;但是有時候在單元格之間&#xff0c;行與行&#xff0c;列與列之間頻繁地切換去錄入數據&#xff0c;費事費力還容易出錯。今天給你推薦一個既好用又有效率的Excel中的隱藏功能——“記錄單”。…

CentOS 6.9下的Setup工具(用于管理服務/防火墻/網絡配置/驗證服務)

說明&#xff1a;Setup工具套件好像是CentOS下特有的用于管理服務/防火墻/網絡配置等&#xff0c;其實就是基于命令行模式界面的GUI工具。唯一特點就是方便。 安裝&#xff1a; #安裝Setup命令工具 yum -y install setuptool #安裝Setup工具配套的系統服務組件 yum -y insta…

wireshark解析rtp協議,流媒體中的AMR/H263/H264包的方法

原文教程&#xff1a;http://hi.baidu.com/zjxiaoyu3/blog/item/22f9f18f32b45de5f11f3670.html 抓到完整的流媒體包之后&#xff0c;用wireshark打開&#xff0c;其中的包可能不會自動映射成RTP&#xff0b;AMR&#xff0f;H263&#xff0f;H264的包&#xff0c;做如下修改操作…

深度學習之非極大值抑制(Non-maximum suppression,NMS)

非極大值抑制&#xff08;Non-maximum suppression&#xff0c;NMS&#xff09;是一種去除非極大值的算法&#xff0c;常用于計算機視覺中的邊緣檢測、物體識別等。 算法流程 給出一張圖片和上面許多物體檢測的候選框&#xff08;即每個框可能都代表某種物體&#xff09;&…

148. 顏色分類

給定一個包含紅&#xff0c;白&#xff0c;藍且長度為 n 的數組&#xff0c;將數組元素進行分類使相同顏色的元素相鄰&#xff0c;并按照紅、白、藍的順序進行排序。 我們可以使用整數 0&#xff0c;1 和 2 分別代表紅&#xff0c;白&#xff0c;藍。 注意事項 不能使用代碼庫中…