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.mzph.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;蓝。 注意事项 不能使用代码库中…