RTMP 视频数据封装

RTMP 协议

与HTTP(超文本传输协议)同样是一个基于TCP的Real Time Messaging Protocol(实时消息传输协议)。由Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输开发的一种开放协议 。在国内被广泛的应用于直 播领域。HTTP默认端口为80,RTMP则为1935
我们通过阅读Adobe的协议规范,通过与服务器建立TCP通信,根据协议格式生成与解析数据即可使用RTMP进行直播。当然我们也可以借助一些实现了RTMP协议的开源库来完成这一过程。

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

RTMPDump

RTMPDump 是一个用来处理RTMP流媒体的开源工具包。它能够单独使用进行RTMP的通信,也可以集成到FFmpeg中通过FFmpeg接口来使用RTMPDump。

RTMPDump源码下载:http://rtmpdump.mplayerhq.hu/download/rtmpdump-2.3.tgz

交叉编译

在Android中可以直接借助NDK在JNI层调用RTMPDump来完成RTMP通信。但是首先必须得进行交叉编译。 RTMPDump源码结构如下:

在根目录下提供了一个 Makefile 与一些 .c 源文件。这里的源文件将会编译出一系列的可执行文件。然后我们需 要的并不是可执行文件,真正的对RTMP的实现都在librtmp子目录中。在这个子目录中同样包含了一个 Makefile 文件。通过阅读 Makefile 发现,它的源码并不多: OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o 。因此我们 不进行预编译,即直接放入AS中借助 CMakeLists.txt 来进行编译。这么做可以让我们方便的对库本身进行调试或 修改(实际上我们确实会稍微修改这个库的源码)。

在AS中复制librtmp置于: src/main/cpp/librtmp ,并为其编写CMakeLists.txt

 #预编译宏
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_CRYPTO" ) 
#所有源文件放入 rtmp_source 变量
file(GLOB rtmp_source *.c)
#编译静态库
add_library(rtmp STATIC ${rtmp_source} )

在 app/CMakeLists.txt 中导入这个CMakeLists.txt

 cmake_minimum_required(VERSION 3.4.1) #导入其他目录cmakelist add_subdirectory(src/main/cpp/librtmp) add_library(XXX SHARED ...) #XXX需要链接rtmp库 target_link_libraries(XXX rtmp ...)

RTMP视频数据

RTMP视频流格式与FLV很相似,通过查看FLV的格式文档,就能够知道RTMP视频数据应该怎么拼接。

RTMP中的数据就是由FLV的TAG中的数据区构成。

FLV tags 结构

 

如上图,第一个字节0x09 表示此段数据为视频,数据大小为0x00,0x00,0x2F即47,时间戳为 0x00,0x00,0x00,时间戳扩展也为0x00。(第二行)流ID:0x00,0x00,0x00。接下来就是视频数据,通过此处的 数据大小字段得知,数据长为47字节。 则从0x17开始,一直到最后一行的0xC0,就是数据区域,而最后的 0x00,0x00,0x00,0x3A 即58,表示的是这个数据块除最后4个字节的总大小。本处为视频数据,那么从0x17 开始,数据内容则为下面的部分。

视频数据

*字段*

*占位*

*描述*

帧类型

4

1: 关键帧2: 普通帧 ......

编码ID

4

7: 高级视频编码 AVC ......

视频数据

n

AVC则需要下面的AVCVIDEOPACKET

AVCVIDEOPACKET

*字段*

*字节*

*描述*

类型

1

0:AVC 序列头(指导播放器如何解码)1:其他单元(其他NALU)

合成时间

3

对于AVC序列头,全为0

数据

n

类型不同,数据不同

视频数据中 0x17 则表示了1: 关键帧与7: 高级视频编码 AVC,如果是普通帧,则此数据为0x27。而类型为: 0x00表示这段数据为AVC序列头(avc sequence header)。最后三个字节为合成时间。而如果类型为AVC序列 头接下来的数据就是下面的内容:

AVC 序列头

在AVCVIDEOPACKET 中如果类型为0,则后续数据为:

*类型*

*字节*

*说明*

版本

1

0x01

编码规格

3

sps[1]+sps[2]+sps[3] (后面说明)

几个字节表示NALU 的长度

1

0xFF,包长为 (0xFF& 3)+ 1,也就是4字节表示

SPS个数

1

0xE1,个数为0xE1 & 0x1F 也就是1

SPS长度

2

整个sps的长度

sps的内容

n

整个sps

pps个数

1

0x01,不用计算就是1

pps长度

2

整个pps长度

pps内容

n

整个pps内容


0x01为版本,后续数据按照上表记录,最后四字节上面说过:为这个数据块除最后4个字节的总大小。其中 SPS与PPS是编码器在编码H.264视频时,在关键帧前会编码出的关于这个关键帧与需要参考该关键帧的B/P 帧如何解码的内容,如:宽、高等信息。

在AVCVIDEOPACKET 中如果类型为1(非AVC 序列头),则后续数据为:

*类型*

*字节*

*说明*

包长

由AVC序列头中定义

后续长度

数据

n

H.264数据

一般情况下,组装的RTMPPacket(RTMPDump中的结构体)为:

这里的sps与pps表示 AVC序列头

所以对于视频的数据封装,AVC序列头为:

    int i = 0;//固定头packet->m_body[i++] = 0x17;//类型packet->m_body[i++] = 0x00;//composition time 0x000000packet->m_body[i++] = 0x00;packet->m_body[i++] = 0x00;packet->m_body[i++] = 0x00;//版本packet->m_body[i++] = 0x01;//编码规格packet->m_body[i++] = sps[1];packet->m_body[i++] = sps[2];packet->m_body[i++] = sps[3];packet->m_body[i++] = 0xFF;//整个spspacket->m_body[i++] = 0xE1;//sps长度packet->m_body[i++] = (spslen >> 8) & 0xff;packet->m_body[i++] = spslen & 0xff;memcpy(&packet->m_body[i], sps, spslen);i += spslen;//ppspacket->m_body[i++] = 0x01;packet->m_body[i++] = (ppslen >> 8) & 0xff;packet->m_body[i++] = (ppslen) & 0xff;memcpy(&packet->m_body[i], pps, ppslen);packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;packet->m_nBodySize = bodySize;packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;//时间戳  sps与pps(不是图像) 没有时间戳packet->m_nTimeStamp = 0;// 使用相对时间packet->m_hasAbsTimestamp = 0;

而对于非AVC序列头,关键字与非关键字,只有第一个字节0x17与0x27的区别:

 packet->m_body[0] = 0x27;//关键帧if (type == NAL_SLICE_IDR) {packet->m_body[0] = 0x17;}//类型packet->m_body[1] = 0x01;//时间戳packet->m_body[2] = 0x00;packet->m_body[3] = 0x00;packet->m_body[4] = 0x00;//数据长度 int 4个字节 相当于把int转成4个字节的byte数组packet->m_body[5] = (i_payload >> 24) & 0xff;packet->m_body[6] = (i_payload >> 16) & 0xff;packet->m_body[7] = (i_payload >> 8) & 0xff;packet->m_body[8] = (i_payload) & 0xff;//图片数据memcpy(&packet->m_body[9], p_payload, i_payload);

H.264数据

H.264码流在网络中传输时实际是以NALU的形式进行传输的。NALU就是NAL UNIT,NAL单元。NAL全称Network Abstract Layer, 即网络抽象层。在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面 (VCL)和网络抽象层面(NAL)。其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头 信息,以保证数据适合各种信道和存储介质上的传输。我们平时的每帧数据就是一个NAL单元。

往RTMP包中填充的就是NAL数据,但不是直接将编码出来的数据填充进去。

一段包含了N个图像的H.264裸数据,每个NAL之间由:

  • 00 00 00 01 或者 00 00 01 进行分割。

在分割符之后的第一个字节,就是表示这个nal的类型。

  • 0x67:sps
  • 0x68: pps
  • 0x65: IDR

在将数据加入RTMPPacket的时候是需要去除分割符的。

所以完整的封包代码为:

 int bodysize = 9 + i_payload;RTMPPacket_Alloc(packet, bodysize);RTMPPacket_Reset(packet);
//    int type = payload[0] & 0x1f;packet->m_body[0] = 0x27;//关键帧if (type == NAL_SLICE_IDR) {packet->m_body[0] = 0x17;}//类型packet->m_body[1] = 0x01;//时间戳packet->m_body[2] = 0x00;packet->m_body[3] = 0x00;packet->m_body[4] = 0x00;//数据长度 int 4个字节 相当于把int转成4个字节的byte数组packet->m_body[5] = (i_payload >> 24) & 0xff;packet->m_body[6] = (i_payload >> 16) & 0xff;packet->m_body[7] = (i_payload >> 8) & 0xff;packet->m_body[8] = (i_payload) & 0xff;//图片数据memcpy(&packet->m_body[9], p_payload, i_payload);

NALU

NALU就是NAL UNIT,nal单元。NAL全称Network Abstract Layer, 即网络抽象层,H.264在网络上传输的结构。 一帧图片经过 H.264 编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是 NALU 了 。

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/618521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

上海AI实验室等开源,音频、音乐统一开发工具包Amphion

上海AI实验室、香港中文大学数据科学院、深圳大数据研究院联合开源了一个名为Amphion的音频、音乐和语音生成工具包。 Amphion可帮助开发人员研究文本生成音频、音乐等与音频相关的领域,可以在一个框架内完成,以解决生成模型黑箱、代码库分散、缺少评估…

蓝桥杯AcWing学习笔记 8-1数论的学习(上)

蓝桥杯 我的AcWing 题目及图片来自蓝桥杯C AB组辅导课 数论(上) 蓝桥杯省赛中考的数论不是很多,这里讲几个蓝桥杯常考的知识点。 欧几里得算法——辗转相除法 欧几里得算法代码: import java.util.Scanner ;public class Main…

【华为鸿蒙】HarmonyOS概述:技术特性

来源:HarmnyOS 官网 https://developer.harmonyos.com/cn/docs/documentation/doc-guides/harmonyos-features-0000000000011907 技术特性 硬件互助,资源共享 多种设备之间能够实现硬件互助、资源共享,依赖的关键技术包括分布式软总线、分布…

Vant-ui图片懒加载

核心代码 在你的全局顶部引入和初始化 Vue.use(vant.Lazyload, {loading: /StaticFile/img/jiazai.jpg,error: /StaticFile/img/jiazai.jpg,lazyComponent: false, });//图片懒加载 <img v-lazy"https://img-blog.csdnimg.cn/direct/3d2c8a7e2c0040488a8128c3e381d58…

8.云原生存储之Ceph集群

1. 私有云实战之基础环境搭建 2. 云原生实战之kubesphere搭建 3.云原生之kubesphere运维 4. 云原生之kubesphere基础服务搭建 5.云原生安全之kubesphere应用网关配置域名TLS证书 6.云原生之DevOps和CICD 7.云原生之jenkins集成SonarQube 8.云原生存储之Ceph集群 文章目录 为什么…

DynastyPersist:一款功能强大的Linux持久化安全审计与测试工具

关于DynastyPersist DynastyPersist是一款专为红队研究人员和CTF玩家设计的Linux安全测试工具&#xff0c;该工具可以适用于各种安全评估任务和安全测试场景。 DynastyPersist本质上是一个Linux持久化脚本&#xff0c;并提供了大量的安全测试功能&#xff0c;可以为我们展示在…

Java多线程并发篇----第十一篇

系列文章目录 文章目录 系列文章目录前言一、什么是悲观锁二、什么是自旋锁三、Synchronized 同步锁前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、什么是悲观…

【大数据架构】日志采集方案对比

整体架构 日志采集端 Flume Flume的设计宗旨是向Hadoop集群批量导入基于事件的海量数据。系统中最核心的角色是agent&#xff0c;Flume采集系统就是由一个个agent所连接起来形成。每一个agent相当于一个数据传递员&#xff0c;内部有三个组件&#xff1a; source: 采集源&…

Unity中URP下实现深度贴花(雾效支持和BRP适配)

文章目录 前言一、让我们的贴画支持雾效1、我们舍弃内部的MixFog方法2、使用 雾效混合因子 对最后输出颜色进行线性插值相乘 二、在Shader中&#xff0c;限制贴花纹理的采样方式1、申明 纹理 和 限制采样方式的采样器2、在片元着色器进行纹理采样 三、BRP适配1、C#脚本中&#…

仿真验证方法(3)——物理验证

目录 一、物理验证的分类 二、DRC 2.1 设计规则 2.2 规则示例 2.3 线宽违例 2.4 间距违例 2.5 交叠违例 三、金属覆盖图形密度检查 四、天线比率检查 4.1 起因 4.2 计算 4.3 改进 五、LVS检查 六、物理验证常用的EDA工具 七、总结 一、物理验证的分类 对于物理验…

专业课145+合肥工业大学833信号分析与处理考研经验合工大电子信息通信

今年专业课145也是考研科目中最满意的一门&#xff0c;其他基本相对平平&#xff0c;所以这里我总结一下自己的专业课合肥工业大学833信号分析与处理的复习经验。 我所用的教材是郑君里的《信号与系统》&#xff08;第三版&#xff09;和高西全、丁玉美的《数字信号处理》&…

java方法的定义和使用

方法 今日目标&#xff1a; 能够知道方法的好处 能够根据两个明确分析方法的参数和返回值 能够编写方法完成授课案例&#xff0c;并在主方法中完成方法的调用 能够知道方法重载及其特点 1&#xff1a;方法的定义和使用 1.1 方法概述 方法(method)&#xff1a;就是完成特…

Java常用的加密技术

项目结构&#xff1a; 总体代码&#xff1a; package VirtualUtils; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.sp…

深入浅出线程原理

Linux 中的线程本质 线程接口由 Native POSIX Thread Library 提供&#xff0c;即&#xff1a;NPTL 库函数 线程被称为轻量级进程 (Light Weight Process) 每一个线程在内核中都对应一个调度实体&#xff0c;拥有独立的结构体 (task_struct) 内核设计&#xff1a;一个进程对…

【python】matplotlib画图常用功能汇总

目录: 一、matplotlib画图风格二、matplotlib图像尺寸和保存分辨率三、matplotlib子图相关功能创建子图&#xff1a;绘制子图&#xff1a;设置子图属性&#xff1a;调整布局&#xff1a;示例代码&#xff1a; 四、matplotlib字体设置字体族和字体的区别字体选择和设置1. Matplo…

亚马逊云科技 WAF 部署小指南(五):在客户端集成 Amazon WAF SDK 抵御 DDoS 攻击...

方案介绍 在 WAF 部署小指南&#xff08;一&#xff09;中&#xff0c;我们了解了 Amazon WAF 的原理&#xff0c;并通过创建 WEB ACL 和托管规则防护常见的攻击。也了解了通过创建自定义规则在 HTTP 请求到达应用之前判断是阻断还是允许该请求。在 Amazon WAF 自定义规则中&am…

水果音乐编曲软件 FL Studio v21.2.2.3914 中文免费版(附中文设置教程)

FL studio21中文别名水果编曲软件&#xff0c;是一款全能的音乐制作软件&#xff0c;包括编曲、录音、剪辑和混音等诸多功能&#xff0c;让你的电脑编程一个全能的录音室&#xff0c;它为您提供了一个集成的开发环境&#xff0c;使用起来非常简单有效&#xff0c;您的工作会变得…

【书生·浦语】大模型实战营——第四课作业

教程文档&#xff1a;https://github.com/InternLM/tutorial/blob/main/xtuner/self.md 基础作业需要构建数据集&#xff0c;微调模型&#xff0c;让其明白自己的弟位&#xff08;OvO&#xff01;&#xff09; 微调环境准备 进入开发机后&#xff0c;先bash&#xff0c;再创…

列表解析与快速排序

排序是在对文本、数值等数据进行操作时常用的功能&#xff0c;本文介绍两种常用的排序方式&#xff0c;借此学习列表解析&#xff0c;并巩固递归算法。 1 选择排序 说到排序&#xff0c;以数值为例&#xff0c;肯定涉及到值大小的对比&#xff0c;选择排序即通过依次在子集中…

蓝桥杯 python 第二题 数列排序

这里给出一种解法 """ # 错的 n int(input()) dp[int(i) for i in input().split(" ")] dp.sort() print(" ".join(str(i) for i in dp)) """#这个是对的 num int(input())l list(map(int, input().split()))l.sort()pr…