首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-02-16 05:01
  • 2770
  • 12564
blog.csdn.net

文章目录

  • 一、 基本封装数据格式说明
  • 二、 封装 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7


参考博客 : 参考之前的两篇分析 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 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
  • 1
  • 2
  • 3
  • 4
  • 01 PPS 个数, 1 字节
  • 00 05 PPS 长度, 2 字节
  • ppsLen 字节的 PPS 数据
                            68 E9 7B 2C
0x000001ba	:   8B
  • 1
  • 2
  • 后面的 00 00 00 39 是视频标签的总长度 , 这里在 RTMP 标签中可以不用封装 ;


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


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

② 封装 SPS 数据 : SPS 个数 , SPS 长度 , SPS 数据 , 分别有 1 + 2 + s p s L e n 1 + 2 + spsLen 1+2+spsLen 字节 ;

③ 封装 PPS 数据 : PPS 个数 , PPS 长度 , PPS 数据 , 分别有 1 + 2 + p p s L e n 1 + 2 + ppsLen 1+2+ppsLen 字节 ;

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




三、 封装头数据



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

    // 帧类型数据 : 分为两部分;
    // 前 4 位表示帧类型, 1 表示关键帧, 2 表示普通帧
    // 后 4 位表示编码类型, 7 表示 AVC 视频编码
    rtmpPacket->m_body[nextPosition++] = 0x17;

    // 数据类型, 00 表示 AVC 序列头
    rtmpPacket->m_body[nextPosition++] = 0x00;

    // 合成时间, 一般设置 00 00 00
    rtmpPacket->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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23




四、 封装 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;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15




五、 封装 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10




六、 设置 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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12




七、 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 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 标签中可以不用封装
     */
    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 00
    rtmpPacket->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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
注:本文转载自blog.csdn.net的韩曙亮的文章"https://blog.csdn.net/shulianghan/article/details/106737015"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top