文章目录

  • 一、 基本封装数据格式说明
  • 二、 封装 SPS PPS 数据总体说明
  • 三、 封装头数据
  • 四、 封装 SPS 数据
  • 五、 封装 PPS 数据
  • 六、 设置 RTMP 数据包其它参数
  • 七、 SPS PPS 数据封装代码示例

Android 直播推流流程 : 手机采集视频 / 音频数据 , 视频数据使用 H.264 编码 , 音频数据使用 AAC 编码 , 最后将音视频数据都打包到 RTMP 数据包中 , 使用 RTMP 协议上传到 RTMP 服务器中 ;

Android 端中主要完成手机端采集视频数据操作 , 并将视频数据传递给 JNI , 在 NDK 中使用 x264 将图像转为 H.264 格式的视频 , 最后将 H.264 格式的视频打包到 RTMP 数据包中 , 上传到 RTMP 服务器中 ;

本篇博客中介绍如下内容 , Java 层将 Camera 采集的 NV21 格式的数据传入 JNI 层 , 在 JNI 中使用 x264 编码器将 NV21 图像数据编码为 H.264 视频数据 ;

本篇博客中主要封装 AVC 序列头数据 , 将 帧类型 , AVC 数据类型 , 合成时间 , 版本信息 , 编码规格 , NALU 长度 , SPS 个数 , SPS 长度 , SPS 数据 , PPS 个数 , PPS 长度 , PPS 数据 , 封装到 RTMP 包中 ;

一、 基本封装数据格式说明


1 . 这是完整的视频标签数据内容 : 这是 FLV 中完整视频标签数据 ;

0x00000182   :   09 00 00 2E 00 00 00 00
0x0000018a  :   00 00 00 17 00 00 00 00
0x00000192  :   01 64 00 32 FF E1 00 19
0x0000019a  :   67 64 00 32 AC D9 80 78
0x000001a2  :   02 27 E5 84 00 00 03 00
0x000001aa  :   04 00 00 1F 40 3C 60 C6
0x000001b2  :   68 01 00 05 68 E9 7B 2C
0x000001ba  :   8B 00 00 00 39

2 . 标签头 : 前 111111 个字节是标签头数据 , 存储有 标签类型 , 标签数据大小 , 时间戳 , 时间戳扩展位 , 流编号 等 111111 字节信息 ;

0x00000182   :   09 00 00 2E 00 00 00 00
0x0000018a  :   00 00 00

3 . 标签数据 ( 重点 ) : 这就是本篇博客要封装的内容 , 基本上是封装一个格式一模一样的 RTMP 数据包 ,

                         17 00 00 00 00
0x00000192  :   01 64 00 32 FF E1 00 19
0x0000019a  :   67 64 00 32 AC D9 80 78
0x000001a2  :   02 27 E5 84 00 00 03 00
0x000001aa  :   04 00 00 1F 40 3C 60 C6
0x000001b2  :   68 01 00 05 68 E9 7B 2C
0x000001ba  :   8B 00 00 00 39

参考博客 : 参考之前的两篇分析 RTMP 数据格式的博客 , 分析了与 RTMP 格式几乎一致的 FLV 视频数据格式 ;

  • 【Android RTMP】RTMP 数据格式 ( FLV 视频格式分析 | 文件头 Header 分析 | 标签 Tag 分析 | 视频标签 Tag 数据分析 )

  • 【Android RTMP】RTMP 数据格式 ( FLV 视频格式分析 | AVC 序列头格式解析 )

这两篇博客一定要 , 并且明白 FLV 视频标签数据格式 , 才能看懂今天写的 RTMP 数据包封装的内容 ;

二、 封装 SPS PPS 数据总体说明


1 . 数据示例 :

                         17 00 00 00 00
0x00000192  :   01 64 00 32 FF E1 00 19
0x0000019a  :   67 64 00 32 AC D9 80 78
0x000001a2  :   02 27 E5 84 00 00 03 00
0x000001aa  :   04 00 00 1F 40 3C 60 C6
0x000001b2  :   68 01 00 05 68 E9 7B 2C
0x000001ba  :   8B 00 00 00 39
  • 17 帧类型, 1 字节
  • 00 数据类型, 1 字节
  • 00 00 00 合成时间, 3 字节
  • 01 版本信息, 1 字节
  • 64 00 32 编码规则, 3 字节
  • FF NALU 长度, 1 字节
  • E1 SPS 个数, 1 字节
  • 00 19 SPS 长度, 2 字节

截止到当前位置有 13 字节数据

  • spsLen 字节数据, 这里是 25 字节
                67 64 00 32 AC D9 80 78
0x000001a2  :   02 27 E5 84 00 00 03 00
0x000001aa  :   04 00 00 1F 40 3C 60 C6
0x000001b2  :   68
  • 01 PPS 个数, 1 字节
  • 00 05 PPS 长度, 2 字节
  • ppsLen 字节的 PPS 数据
                            68 E9 7B 2C
0x000001ba  :   8B
  • 后面的 00 00 00 39 是视频标签的总长度 , 这里在 RTMP 标签中可以不用封装 ;

2 . 计算整个 SPS 和 PPS 数据的大小 :

① 封装头 : 帧类型 , 数据类型 , 合成时间 , 版本信息 , 编码规则 , NALU 长度 , 总共有 101010 字节 ;

② 封装 SPS 数据 : SPS 个数 , SPS 长度 , SPS 数据 , 分别有 1+2+spsLen1 + 2 + spsLen1+2+spsLen 字节 ;

③ 封装 PPS 数据 : PPS 个数 , PPS 长度 , PPS 数据 , 分别有 1+2+ppsLen1 + 2 + ppsLen1+2+ppsLen 字节 ;

int rtmpPackagesize = 10 + 3 + spsLen + 3 + ppsLen;

三、 封装头数据


向 RTMP 数据包中 , 封装 帧类型 , 数据类型 , 合成时间 , 版本信息 , 编码规则 , NALU 长度 , 总共有 101010 字节 ;

    // 帧类型数据 : 分为两部分;// 前 4 位表示帧类型, 1 表示关键帧, 2 表示普通帧// 后 4 位表示编码类型, 7 表示 AVC 视频编码rtmpPacket->m_body[nextPosition++] = 0x17;// 数据类型, 00 表示 AVC 序列头rtmpPacket->m_body[nextPosition++] = 0x00;// 合成时间, 一般设置 00 00 00rtmpPacket->m_body[nextPosition++] = 0x00;rtmpPacket->m_body[nextPosition++] = 0x00;rtmpPacket->m_body[nextPosition++] = 0x00;// 版本信息rtmpPacket->m_body[nextPosition++] = 0x01;// 编码规格rtmpPacket->m_body[nextPosition++] = sps[1];rtmpPacket->m_body[nextPosition++] = sps[2];rtmpPacket->m_body[nextPosition++] = sps[3];// NALU 长度rtmpPacket->m_body[nextPosition++] = 0xFF;

四、 封装 SPS 数据


将 SPS 数据封装到 RTMP 数据包中 , 包含 SPS 个数 , SPS 长度 , SPS 数据 ;

    // SPS 个数rtmpPacket->m_body[nextPosition++] = 0xE1;// SPS 长度, 占 2 字节// 设置长度的高位rtmpPacket->m_body[nextPosition++] = (spsLen >> 8) & 0xFF;// 设置长度的低位rtmpPacket->m_body[nextPosition++] = spsLen & 0xFF;// 拷贝 SPS 数据// 将 SPS 数据拷贝到 rtmpPacket->m_body[nextPosition] 地址中memcpy(&rtmpPacket->m_body[nextPosition], sps, spsLen);// 累加 SPS 长度信息nextPosition += spsLen;

五、 封装 PPS 数据


将 PPS 数据封装到 RTMP 数据包中 , 包含 PPS 个数 , PPS 长度 , PPS 数据 ;

    // PPS 个数rtmpPacket->m_body[nextPosition++] = 0x01;// PPS 数据的长度, 占 2 字节// 设置长度的高位rtmpPacket->m_body[nextPosition++] = (ppsLen >> 8) & 0xFF;// 设置长度的低位rtmpPacket->m_body[nextPosition++] = (ppsLen) & 0xFF;// 拷贝 SPS 数据memcpy(&rtmpPacket->m_body[nextPosition], pps, ppsLen);

六、 设置 RTMP 数据包其它参数


设置 RTMP 包类型 , RTMP 包长度 , RTMP 通道 , 时间戳 等信息 ;

    // 设置 RTMP 包类型, 视频类型数据rtmpPacket->m_packetType = RTMP_PACKET_TYPE_VIDEO;// 设置 RTMP 包长度rtmpPacket->m_nBodySize = rtmpPackagesize;// 分配 RTMP 通道, 随意分配rtmpPacket->m_nChannel = 10;// 设置视频时间戳, 如果是 SPP PPS 数据, 没有时间戳rtmpPacket->m_nTimeStamp = 0;// 设置绝对时间, 对于 SPS PPS 赋值 0 即可rtmpPacket->m_hasAbsTimestamp = 0;// 设置头类型, 随意设置一个rtmpPacket->m_headerType = RTMP_PACKET_SIZE_MEDIUM;

七、 SPS PPS 数据封装代码示例


/*** 将 SPS / PPS 数据发送到 RTMP 服务器端* @param sps       SPS 数据* @param pps       PPS 数据* @param spsLen    SPS 长度* @param ppsLen    PPS 长度*/
void VedioChannel::sendSpsPpsToRtmpServer(uint8_t *sps, uint8_t *pps, int spsLen, int ppsLen) {// 创建 RTMP 数据包, 将数据都存入该 RTMP 数据包中RTMPPacket *rtmpPacket = new RTMPPacket;/*计算整个 SPS 和 PPS 数据的大小数据示例 :17 00 00 00 000x00000192   :   01 64 00 32 FF E1 00 190x0000019a   :   67 64 00 32 AC D9 80 780x000001a2   :   02 27 E5 84 00 00 03 000x000001aa   :   04 00 00 1F 40 3C 60 C60x000001b2   :   68 01 00 05 68 E9 7B 2C0x000001ba   :   8B 00 00 00 3917 帧类型, 1 字节00 数据类型, 1 字节00 00 00 合成时间, 3 字节01 版本信息, 1 字节64 00 32 编码规则, 3 字节FF NALU 长度, 1 字节E1 SPS 个数, 1 字节00 19 SPS 长度, 2 字节截止到当前位置有 13 字节数据spsLen 字节数据, 这里是 25 字节67 64 00 32 AC D9 80 780x000001a2  :   02 27 E5 84 00 00 03 000x000001aa   :   04 00 00 1F 40 3C 60 C60x000001b2   :   6801 PPS 个数, 1 字节00 05 PPS 长度, 2 字节ppsLen 字节的 PPS 数据68 E9 7B 2C0x000001ba   :   8B后面的 00 00 00 39 是视频标签的总长度这里再 RTMP 标签中可以不用封装*/int rtmpPackagesize = 10 + 3 + spsLen + 3 + ppsLen;// 为 RTMP 数据包分配内存RTMPPacket_Alloc(rtmpPacket, rtmpPackagesize);// 记录下一个要写入数据的索引位置int nextPosition = 0;// 帧类型数据 : 分为两部分;// 前 4 位表示帧类型, 1 表示关键帧, 2 表示普通帧// 后 4 位表示编码类型, 7 表示 AVC 视频编码rtmpPacket->m_body[nextPosition++] = 0x17;// 数据类型, 00 表示 AVC 序列头rtmpPacket->m_body[nextPosition++] = 0x00;// 合成时间, 一般设置 00 00 00rtmpPacket->m_body[nextPosition++] = 0x00;rtmpPacket->m_body[nextPosition++] = 0x00;rtmpPacket->m_body[nextPosition++] = 0x00;// 版本信息rtmpPacket->m_body[nextPosition++] = 0x01;// 编码规格rtmpPacket->m_body[nextPosition++] = sps[1];rtmpPacket->m_body[nextPosition++] = sps[2];rtmpPacket->m_body[nextPosition++] = sps[3];// NALU 长度rtmpPacket->m_body[nextPosition++] = 0xFF;// SPS 个数rtmpPacket->m_body[nextPosition++] = 0xE1;// SPS 长度, 占 2 字节// 设置长度的高位rtmpPacket->m_body[nextPosition++] = (spsLen >> 8) & 0xFF;// 设置长度的低位rtmpPacket->m_body[nextPosition++] = spsLen & 0xFF;// 拷贝 SPS 数据// 将 SPS 数据拷贝到 rtmpPacket->m_body[nextPosition] 地址中memcpy(&rtmpPacket->m_body[nextPosition], sps, spsLen);// 累加 SPS 长度信息nextPosition += spsLen;// PPS 个数rtmpPacket->m_body[nextPosition++] = 0x01;// PPS 数据的长度, 占 2 字节// 设置长度的高位rtmpPacket->m_body[nextPosition++] = (ppsLen >> 8) & 0xFF;// 设置长度的低位rtmpPacket->m_body[nextPosition++] = (ppsLen) & 0xFF;// 拷贝 SPS 数据memcpy(&rtmpPacket->m_body[nextPosition], pps, ppsLen);// 设置 RTMP 包类型, 视频类型数据rtmpPacket->m_packetType = RTMP_PACKET_TYPE_VIDEO;// 设置 RTMP 包长度rtmpPacket->m_nBodySize = rtmpPackagesize;// 分配 RTMP 通道, 随意分配rtmpPacket->m_nChannel = 10;// 设置视频时间戳, 如果是 SPP PPS 数据, 没有时间戳rtmpPacket->m_nTimeStamp = 0;// 设置绝对时间, 对于 SPS PPS 赋值 0 即可rtmpPacket->m_hasAbsTimestamp = 0;// 设置头类型, 随意设置一个rtmpPacket->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}

【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 封装 SPS / PPS 数据包 )相关推荐

  1. 【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 关键帧数据格式 | 非关键帧数据格式 | x264 编码后的数据处理 | 封装 H.264 视频数据帧 )

    文章目录 安卓直播推流专栏博客总结 一. x264 编码后的 H.264 数据帧 二. RTMP 协议中 关键帧 / 非关键帧 数据格式 说明 三. 判定 H.264 帧数据分隔符 四. 初始化 RT ...

  2. 【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频采样数据 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )

    文章目录 安卓直播推流专栏博客总结 一. FAAC 编码器编码 AAC 音频采样数据 二. 封装 RTMP 音频数据头 三. 封装 RTMP 音频数据类型 四. 拷贝 AAC 音频数据到 RTMPPa ...

  3. 【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频解码信息 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )

    文章目录 安卓直播推流专栏博客总结 一. FAAC 编码器编码 AAC 音频解码信息 二. 封装 RTMP 音频数据头 三. 封装 RTMP 音频数据类型 四. 拷贝 AAC 音频数据到 RTMPPa ...

  4. 【Android RTMP】安卓直播推流总结 ( 直播服务器搭建 | NV21 图像采集 | H.264 视频编码 | PCM 音频采集 | AAC 音频编码 | RTMP 包封装推流 )

    文章目录 一. 安卓直播推流专栏博客总结 二. 相关资源介绍 三. GitHub 源码地址 四. 整体 Android 直播推流数据到服务器并观看直播演示过程 Android 直播推流流程 : 手机采 ...

  5. 【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )

    文章目录 安卓直播推流专栏博客总结 一. Java 层传入的 RTMP 推流地址处理 二. RTMPDump 推流线程 三. 创建 RTMP 对象 四. 初始化 RTMP 对象 五. 设置 RTMP ...

  6. 【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )

    文章目录 安卓直播推流专栏博客总结 一. AAC 音频格式解析 二. FLV 音频数据标签解析 1. 分析 FLV 格式中的 AAC 音频格式数据 2. AAC 音频特殊配置 3. AAC 音频数据标 ...

  7. 【Android RTMP】x264 图像数据编码 ( NV21 格式中的 YUV 数据排列 | Y 灰度数据拷贝 | U 色彩值数据拷贝 | V 饱和度数据拷贝 | 图像编码操作 )

    文章目录 安卓直播推流专栏博客总结 一. NV21 图像数据中的 YUV 数据简介 二.向 x264 编码图片 三. 提取 NV21 数据中的灰度数据 Y 四. 提取 NV21 数据中的饱和度数据 U ...

  8. 【Android RTMP】x264 编码器初始化及设置 ( 获取 x264 编码参数 | 编码规格 | 码率 | 帧率 | B帧个数 | 关键帧间隔 | 关键帧解码数据 SPS PPS )

    文章目录 安卓直播推流专栏博客总结 一. x264 编码器参数设置引入 二. 获取 x264 编码器参数 三. 设置 x264 编码器编码规格 四. 设置 x264 编码器编码图像数据格式 五. 设置 ...

  9. 【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )

    文章目录 安卓直播推流专栏博客总结 一. Android 端数据采集涉及到的相关概念 二. Camera 预览图像尺寸设置 三. 获取摄像头采集的数据格式 安卓直播推流专栏博客总结 Android R ...

最新文章

  1. (转载)MyEclipse github
  2. ARKit从入门到精通(5)-ARScnView介绍
  3. 联邦家私:用宜搭一周上线售后管理系统,打通信息孤岛,提升协同效率
  4. 判别学习与生成学习的区别
  5. 动态数据交换 python_如何用 Python 和 Streamlit 做交互式数据分析产品?
  6. pytorch load state dict_PyTorch 学习笔记(五):Finetune和各层定制学习率
  7. mysql拦截器实现crud_Mybatis自定义SQL拦截器
  8. html5酷炫表白代码_七夕表白代码,樱花特效+爱心特效+花瓣+评论留言功能等
  9. python __getattribute__
  10. ButterKnife的安装与使用以及ButterKnife右键不显示的大坑
  11. php Hash Table(二) Hash函数
  12. setuna截图怎么放大缩小_手机中的望远镜 华为P30pro是怎么做到50倍变焦?
  13. 怎么做好企业网站关键词优化
  14. 未来IT技术展现——虚拟化技术大盘点
  15. 2021年下半年软件设计师下午真题试题(案例分析)及答案
  16. Retinex网络模型学习笔记
  17. windows存储空间清理,C盘空间清理教程,磁盘清理方法
  18. 计算机学业水平考试反思总结8百,考试反思与总结
  19. 英语面试技巧以及准备工作
  20. 项目管理中软件项目文档的分类管理

热门文章

  1. HTML 5 令人期待的 5 项功能
  2. 黑马lavarel教程---13、分页
  3. 记录MNIST采用卷积方式实现与理解
  4. 人生中五个遗憾,共勉
  5. Mysql存储过程中的事务回滚
  6. 绑定变量窥测(Bind Variable Peeking)
  7. 我是如何去了解需求的
  8. Java-JPA:JPA
  9. android设置程序开机自启动
  10. 基于子类的动态代理: