关于1078实时监控的另一种做法,就是自己编码处理音视频裸流.这种形式需要了解更多的音视频基础知识,因为之前的处理过程是JavaCV帮我们做了很多东西.本文只是做个准备工作,先了解一些基础知识在,然后才能知道该方案的可行性
参考资料:
- https://blog.csdn.net/u013008311/article/details/80249586
- https://blog.csdn.net/u013008311/article/details/80251479
- https://blog.csdn.net/jefry_xdz/article/details/8461343
- https://www.cnblogs.com/wainiwann/p/7477794.html
- https://blog.csdn.net/bsplover/article/details/7426511
- https://blog.csdn.net/byxdaz/article/details/53993791
视频编解码技术有两套标准:
- 国际电联(ITU-T)的标准H.263、H.263+等;
- ISO 的MPEG标准Mpeg1、Mpeg2、Mpeg4等等。
H.264/AVC是两大组织集合H.263+和Mpeg4的优点联合推出的最新标准,最具价值的部分无疑是更高的数据压缩比。在同等的图像质量条件下,H.264的数据压缩比能比H.263高2倍,比MPEG-4高1.5倍。 所以说H264和AVC 其实是指同一个东西.
H.264/AVC标准是由ITU-T和ISO/IEC联合开发的,定位于覆盖整个视频应用领域,包括:低码率的无线应用、标准清晰度和高清晰度的电视广播应用、Internet上的视频流应用,传输高清晰度的DVD视频以及应用于数码相机的高质量视频应用等等。 ITU-T给这个标准命名为H.264(以前叫做H.26L),而ISO/IEC称它为MPEG-4 高级视频编码(Advanced Video Coding,AVC),并且它将成为MPEG-4标准的第10部分。既然AVC是当前MPEG-4标准的拓展,那么它必然将受益于MPEG-4开发良好的基础结构(比如系统分层和音频等)。很明显,作为MPEG-4高级简洁框架(Advanced Simple Profile,ASP)的MPEG-4 AVC将会优于当前的MPEG-4视频压缩标准,它将主要应用在具有高压缩率和分层次质量需求的方向。
H264原始码流是一个接一个的NALU(网络抽象层单元)租成的,每个单元之间是由 0x00 0x00 0x00 0x01 或者 0x00 0x00 0x01分割而成的,紧接着就是NALU的单元头和数据体.(注意这里的NALU是很重要的,因为后面我会一定要现货区NALU才能进行编码操作与封装操作.--cuiyaonan2000@163.com,注意这里的分隔符~~很重要,我们在收到终端的推流数据的时候就会遇到)
NAL
在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:
- 视频编码层面(VCL):负责有效表示视频数据的内容
- 网络抽象层面(NAL):负责格式化数据并提供头信息,保证数据适合各种信道和存储介质上的传输(这个是我们要关注的重点)
因此我们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧…… 注意这里的分隔符.cuiyoanan2000@163.com
SPS(参数集合)SPS即Sequence Paramater Set,又称作序列参数集。SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。所谓的编码视频序列即原始视频的一帧一帧的像素数据经过编码之后的结构组成的序列。而每一帧的编码后数据所依赖的参数保存于图像参数集中。一般情况SPS和PPS的NAL Unit通常位于整个码流的起始位置。但在某些特殊情况下,在码流中间也可能出现这两种结构,主要原因可能为:(这个特殊情况不考虑)
- 解码器需要在码流中间开始解码;
- 编码器在编码的过程中改变了码流的参数(如图像分辨率等)
除了序列参数集SPS之外,H.264中另一重要的参数集合为图像参数集Picture Paramater Set(PPS)。通常情况下,PPS类似于SPS,在H.264的裸码流中单独保存在一个NAL Unit中,只是PPS NAL Unit的nal_unit_type值为8;而在封装格式中,PPS通常与SPS一起,保存在视频文件的文件头中。(这个是我们的1078的环境,不考虑真实环境)
解析H264数据最上面图的码流对应的数据来层层分析,以00 00 00 01分割之后的下一个字节就是NALU类型,将其转为二进制数据后,解读顺序为从左往右算,如下:
- 第1位禁止位,值为1表示语法出错
- 第2~3位为参考级别
- 第4~8为是nal单元类型(单元类型如下图所示)
例如上面00000001后有67,68以及65
其中0x67的二进制码为: 0110 0111 4-8为00111,转为十进制7,参考第二幅图:7对应序列参数集SPS
其中0x68的二进制码为: 0110 1000 4-8为01000,转为十进制8,参考第二幅图:8对应图像参数集PPS
其中0x65的二进制码为: 0110 0101 4-8为00101,转为十进制5,参考第二幅图:5对应IDR图像中的片(I帧)
所以判断是否为I帧的算法为: (NALU类型 & 0001 1111) = 5 即 NALU类型 & 31 = 5
FLV封装格式
FLV文件是由 FLV文件头(FLV header)和 FLV文件体(FLV body)组成的。
FLV body又是由 (previous tag size 和 flv tag)* N 组成的。
FLV header 中包含的信息有: 文件类型、 FLV版本 、stream信息、 FLV header 长度。这个Header长度一般都是固定的9个字节:(注意这个头信息需要搞明白,因为后面我们的推流会要自己写入头信息,根据你的数据情况-----cuiyaonan2000@163.com)
Flv Body(这个数据的生成过程其实不复杂,但是需要先搞清楚它原始的数据结构,比较耗费时间--心累)
FLV body中包含很多个FLV tag,tag的分类有三种:0x8音频、0x9视频、0x12脚本。(后面会对着3中类型的数据分别讲解,每个类型的tag的作用不一样音频和视频很容易区分,主要是脚本的作用)
每个tag中包含tag头和tag体.
每个tag前面还有一个previous size包. 记录着上一个tag的数据的长度(这个长度就是header 和 body 的长度总和)
Tag HeaderTag Body---0x12脚本
脚本tag 0x12(script tag),脚本tag描述了媒体信息,比如视频的width 和 height ,帧率等等信息. (看到这感觉崩溃,不知道后面是否需要自己写入脚本信息, 非常希望终端推送的数据已经包含了脚本tag-------cuiyaonan2000@163.com)
图中为一个脚本tag的数据截图,脚本tag在tag头后面的tag数据中一般会包含两个AMF包,AMF是Adobe设计的一种通用数据封装格式。在第一个AMF包中有一个字符串类型的值,该值为onMetaDate。第二个AMF包封装一个“数据组”,每一组数据都有自己的元素名、元素类型、元素值、这里不详细描述(现在又引出了AMF的封装格式---------痛哭流涕,这个后面空了在研究)
视频tag 0x9 视频tag头都是一样的,1byte Tag类型,3bytes数据长度记录,3bytes时间戳,一个扩展时间戳bype,3bytes StreamID。(后面跟了一个字节用于区分视频的数据类型)
视频tag头后面的第一byte 的4bit表示视频数据的类型,后4bit表示视频的编码器ID( 跟在这后面的才是视频帧的数据,如:I帧, P帧,B帧 等等---等等很牵强)
数据类型:1keyframe、2innerframe、3disposable inner frame(h.263)、4generated keyframe
编码器ID:2h.263、3screen video、4On2 VP6、5On2 VP6 with alpha channel、6Screen video version、7AVC/h264 (注意这里的 avc h264 其实是一个编码器的不同称呼-----希望终端推送的流数据中包含了~~~ =.=)
音频tag0x8 和 视频相比tagtype为0x8 tag header 都是一样的后面跟着的 tag数据类型,后续就是音频的数据了。
音频tag占1byte 前4bit表示音频格式(比如MP3),接着2bit表示采样率,接着1bit表示采样长度接着1bit表示音频类型
音频格式:0未压缩、1ADPCM、2MP3、4Nellymoser 16-Hz mono、5Nellymoser 8-kHz mono、10AAC (竟然缺少了我们的G711A的编码格式,但是字典表中肯定有的)
采样率: 05.5kHz、111kHz、222kHz、344kHz (缺少了8kHz,字典表中肯定有的)
采样位数:0snd8bit、116bit(这个使我们要的)
音配类型:0sndMono(单声道) 1sndStereo(立体声)
稍微详细一点的FLV文件格式说明
- Signature: FLV 文件的前3个字节为固定的‘F’‘L’‘V’,用来标识这个文件是flv格式的.在做格式探测的时候,
- 如果发现前3个字节为“FLV”,就认为它是flv文件.
- Version: 第4个字节表示flv版本号.
- Flags: 第5个字节中的第0位和第2位,分别表示 video 与 audio 存在的情况.(1表示存在,0表示不存在)
- DataOffset : 最后4个字节表示FLV header 长度.
FLV header之后,就是 FLV File Body.
FLV File Body是由一连串的back-pointers + tags构成.back-pointers就是4个字节数据,表示前一个tag的size.
所以第一个PreviousTagSize0 是 Flv Header的长度.
FieldTypeCommentPreviousTagSize0UI32Always 0Tag1FLVTAGFirst tagPreviousTagSize1UI32Size of previous tag, including its header, in bytes. For FLV version1,
this value is 11 plus the DataSize of the previous tag.
Tag2FLVTAGSecond tag.........PreviousTagSizeN-1UI32Size of second-to-last tag, including its header, in bytes.TagNFLVTAGLast tagPreviousTagSizeNUI32Size of last tag, including its header, in bytesFLVTAG
- TagType: TAG中第1个字节中的前5位表示这个TAG中包含数据的类型,8 = audio,9 = video,18 = script data.
- DataSize:StreamID之后的数据长度.
- Timestamp和TimestampExtended组成了这个TAG包数据的PTS信息,记得刚开始做FVL demux的时候,并没有考虑TimestampExtended的值,直接就把Timestamp默认为是PTS,后来发生的现 象就是画面有跳帧的现象,后来才仔细看了一下文档发现真正数据的PTS是PTS= Timestamp | TimestampExtended
关注打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?