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

举报
韩曙亮 发表于 2022/01/10 23:33:07 2022/01/10
【摘要】 文章目录 一、 基本封装数据格式说明二、 封装 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 视频数据格式 ;


这两篇博客一定要 , 并且明白 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

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/106737015

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。