源码地址:https://github.com/zhouyinfei/rtsp-netty-server

NALU封装成RTP包:

//nalu封装成rtp
public List<byte[]> naluToRtpPack(byte[] nalu, int ssrc, int fps){byte[] pcData = nalu;                      //两个起始码(00 00 00 01)之间的NALU数据int mtu = 1400;                               //最大传输单元int iLen = pcData.length;                  //NALU总长度ByteBuffer bb = null;                 List<byte[]> rtpList = new ArrayList<byte[]>();                //封装后的rtp包集合if (iLen > mtu) { //超过MTU                        分片封包模式byte start_flag = (byte) 0x80;byte mid_flag = 0x00;byte end_flag = 0x40;//获取帧头数据,1bytebyte nalu_type = (byte) (pcData[0] & 0x1f); //获取NALU的5bit 帧类型byte nal_ref_idc = (byte) (pcData[0] & 0x60); //获取NALU的2bit 帧重要程度 00 可以丢 11不能丢//组装FU-A帧头数据 2bytebyte fu_identifier = (byte) (nal_ref_idc + 28);          //F为0 1bit,nri上面获取到2bit,28为FU-A分片类型5bitbyte fu_header = nalu_type;                             //低5位和naluHeader相同boolean bFirst = true;                                   //是否是第一个分片boolean mark = false;                                    //是否是最后一个分片int nOffset = 1;                                        //偏移量,跳过第一个字节naluHeaderwhile (!mark) {bb = ByteBuffer.allocate(mtu + 2);if (iLen < nOffset + mtu) {           //是否拆分结束, 最后一个分片mtu = iLen - nOffset;mark = true;fu_header = (byte) (end_flag + nalu_type);       } else {                if (bFirst == true) {                 //第一个分片fu_header = (byte) (start_flag + nalu_type);bFirst = false;} else {                               //或者中间分片fu_header = (byte) (mid_flag + nalu_type);}}bb.put(fu_identifier);bb.put(fu_header);byte[] dest = new byte[mtu];System.arraycopy(pcData, nOffset, dest, 0, mtu);bb.put(dest);nOffset += mtu;byte[] rtpPackage = makeH264Rtp(bb.array(), mark, seqNum, (int)System.currentTimeMillis(), ssrc);rtpList.add(rtpPackage);seqNum ++;}} else {                //单一NAL 单元模式, 不使用组合模式。mark始终为false//从SPS中获取帧率
//          byte nalu_type = (byte) (pcData[0] & 0x1f); //获取NALU的5bit 帧类型
//          if (nalu_type == 7) {             //如果是sps
//              SpsInfoStruct info = new SpsInfoStruct();
//              SpsUtils.h264ParseSps(nalu, nalu.length, info);
//              if (info.fps != 0) {           //如果sps中存在帧率信息,则使用。如果没有,则使用默认帧率
//                  intervel = 1000/info.fps;
//              }
//          }//根据rtsp传过来的fps参数if (fps != 0) {intervel = 1000/fps; }byte[] rtpPackage = makeH264Rtp(pcData, false, seqNum, (int)System.currentTimeMillis(), ssrc);rtpList.add(rtpPackage);seqNum ++;}try {Thread.sleep(intervel);                   //根据帧率延时发送} catch (InterruptedException e) {e.printStackTrace();}return rtpList;
}public byte[] makeH264Rtp(byte[] pcData, boolean mark, int seqNum, int timestamp, int ssrc){byte b;if (mark) {b = (byte) 0xE0;    } else {b = (byte) 0x60;}ByteBuffer bb = ByteBuffer.allocate(pcData.length + 12);bb.put((byte) 0x80);                //V、P、X、CC, 1000 0000bb.put(b);                          //mark 、payloadType(96)bb.putShort((short) seqNum);bb.putInt(timestamp);bb.putInt(ssrc);                bb.put(pcData);return bb.array();
}

就跟RTP中解析H264的过程类似,将H264文件封装成RTP包时同样要考虑分片封包的情况。只不过这里为了简单处理,当NALU长度大于MTU时,会采取分片封包模式。但是当NALU长度小于MTU很多时,直接将一个NALU封装成一个RTP包,而不是采取组合封包模式。

public List<byte[]> naluToRtpPack(byte[] nalu, int ssrc, int fps)

其中,参数中nalu,指的是两个起始码(00 00 00 01)之间的数据。这里有两种情况,一种是大于MTU,一种是小于或等于MTU。

一、NALU长度<= MTU

先从比较简单的情况开始:NALU长度<= MTU

byte[] rtpPackage = makeH264Rtp(pcData, false, seqNum, (int)System.currentTimeMillis(), ssrc);
rtpList.add(rtpPackage);
seqNum ++;

NALU和RTP包是一一对应的关系。调用了makeH264Rtp方法,其中mark参数表示该RTP包是否是NALU的最后一个单元。

if (mark) {
    b = (byte) 0xE0;    
} else {
    b = (byte) 0x60;
}

b是RTP头的第二个字节,假定我们将H264的payloadType定为96(0110 0000),那么根据RTP的结构,mark为true时,b=1110 0000(即0xE0),而mark为false时,b=0110 0000(即0x60)。

RTP格式如下:

另外几个字段直接使用传进来的参数即可。

bb.putShort((short) seqNum);
bb.putInt(timestamp);
bb.putInt(ssrc);                
bb.put(pcData);

二、NALU长度>MTU

另一种情况就是,NALU长度>MTU。

首先是NALU格式:

可以看出,NALU的头部只有一个字节,但是RTP的分片封包模式除了一个字节的FU header外,还有一个字节的FU Indicator,需要我们手动构造出来。

[RTP header]+[FU Indicator(1字节)]+[FU header(1字节)]+[部分nalu payload]

首先从NALU的头部获取出有用的信息:nalu_type(后5位)和NRI(中间2位):

//获取帧头数据,1byte
byte nalu_type = (byte) (pcData[0] & 0x1f); //获取NALU的5bit 帧类型
byte nal_ref_idc = (byte) (pcData[0] & 0x60); //获取NALU的2bit 帧重要程度 00 可以丢 11不能丢

FU Indicator的NRI字段和NALU的一样,但是后5位的取值是28(FU-A),因此它的值为:

byte fu_identifier = (byte) (nal_ref_idc + 28);            //F为0 1bit,nri上面获取到2bit,28为FU-A分片类型5bit

FU header的后5位和NALU类型一样,但是它的前2位表示起始分片和最后一个分片的标识位,因此在不同情况下是不同的:

byte fu_header = nalu_type;                                //低5位和naluHeader相同

当起始分片时:

fu_header = (byte) (start_flag + nalu_type);

当中间分片时:

fu_header = (byte) (mid_flag + nalu_type);

当最后一个分片时:

fu_header = (byte) (end_flag + nalu_type);

在解析NALU的过程中,需要设置几个标志,表示是否是第一个分片、是否最后一个分片、以及当前解析到哪个位置。

boolean bFirst = true;                                    //是否是第一个分片
boolean mark = false;                                    //是否是最后一个分片
int nOffset = 1;                                        //偏移量,跳过第一个字节naluHeader

然后每次都读取MTU个字节,直到末尾。最后一次只读取iLen - nOffset个字节,然后将这些数据依照前面的规则封装成RTP,因为有多个RTP包,因此存入到一个List中。

将NALU封装成RTP包相关推荐

  1. 将H264码流打包成RTP包

    分类: 流媒体(25) 版权声明:本文为博主原创文章,未经博主允许不得转载. H264码流打包成RTP包的代码如下: [cpp] view plaincopy #include <stdio.h ...

  2. H264码流打包成RTP包

    http://blog.csdn.net/tanningzhong/article/details/53281986 H264码流打包成RTP包的代码如下: [cpp] view plaincopy ...

  3. H265封装成RTP流(一)

    H265封装成RTP流(一) H265封装成RTP流是比较复杂的一件事情,需要一些基础知识才行,首先需要了解一些H265的知识,这里介绍的H265的头信息识识,主要来源于官方的文档<T-REC- ...

  4. Eclipse中如何把自己写的方法封装成jar包供其他项目使用

    Eclipse中如何把自己写的方法封装成jar包供其他项目使用 1.第一步.首先自己写一个方法: 2.右键项目名称(Demo)->选择Export-->选择JAR file,然后选择下一步 ...

  5. 封装成jar包_通用源码阅读指导mybatis源码详解:io包

    io包 io包即输入/输出包,负责完成 MyBatis中与输入/输出相关的操作. 说到输入/输出,首先想到的就是对磁盘文件的读写.在 MyBatis的工作中,与磁盘文件的交互主要是对 xml配置文件的 ...

  6. H265打包成RTP包详解

    在我们的高清视频传输中,一般都需要对视频信息进行指定协议的打包,RTP是比较常用的一种视频打包负载传输方式,那么具体是怎么打包的呢?今天就简单介绍一下. 1.H265的结构 在H265中,每一个前缀码 ...

  7. java tcp数据包_java tcp封装成数据包【相关词_ tcp数据包处理java】

    2-1.数据序号32位,TCP为发送的每一个字节都编一个号码,这里存储当前数据包数据第一 包括 网络编程结构数据JavaTCPIP的信息,所有JAVA网络编程:TCP/IP数据包结构相关内 Java实 ...

  8. java restful项目打包_GitHub - yangguang010/RestfulAPI: 使用java封装萤石开放平台的接口 HTTPS—SDK。可以直接将api封装成jar包...

    RestfulAPI 初步完成了萤石摄像头的一些接口的封装,后面还会陆续更新, 本节包含设备相关的接口,按功能分为设备操作.设备查询.设备升级.云台控制.报警消息查询.探测器相关操作.客流统计相关.开 ...

  9. [live555] RTP包 NALU FU-A等之间的关系

    #前言 RTSP 协议流程 已经介绍在SETUP 确定了传输模式,而在PLAY的时候就开始传输RTP 包 并且,确定了第一个RTP url 序列号 和时间戳 RTP-Info: url=rtsp:// ...

最新文章

  1. Mac打包Android的apk,[Mac][React Native][Android] 打包成apk
  2. CrazePony飞行器--通信部分介绍【转】
  3. libtorch demo
  4. html中源文件回车效果无效,网页制作使用教程第2节初级.ppt
  5. mysql rpl_MySQL管理工具MySQL Utilities — mysqlrplcheck(44)
  6. SyntaxHighlighter3.0.83 配置
  7. FTTP/FTTH理想解决方案(组图)
  8. SpringBoot+vue实现前后端分离的校园志愿者管理系统
  9. 【Ubuntu 20.04 安装中文输入法 谷歌拼音】
  10. Windows 环境搭建 git 服务
  11. 【攻击威胁】女巫攻击(sybil attack)
  12. 随时随地开展绘图工作,还得靠CAD在线!
  13. 论文笔记:Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering
  14. python窗口界面自适应_自适应页面的实现方式
  15. 书生笔记-binlog 的写入机制
  16. LLD-LLVM链接器
  17. YottaChain数据加密的可靠性和安全性有多高?
  18. [世界概览]那些被印度人毁掉或者差一点毁掉的公司(翻译)
  19. 三人表决电路——Verilog HDL语言
  20. vs哪个版本好用_【小巨人分享】VS出品销量神器 沛纳海 441 升级版来了!

热门文章

  1. XNA学习笔记3 鼠标响应
  2. android控件旋转、缩放、平移完美版方案(双指操作、单指操作都有)
  3. 雨课堂提交作业步骤 10步帮你弄好
  4. 悄推换机,日本索尼公告初代PS4今年底停止维修
  5. 开运算、闭运算及其所用
  6. UITextView 关闭键盘
  7. OriginPro 8 简单论文作图并且将多个worksheet表画在同一张图以及图的保存
  8. 自己做量化交易软件(34)小白量化实战7--投资理念与通达信公式回测(2)
  9. Oracle建立索引的原则
  10. 老婆过生日,随写游戏一只,祝生日快乐。