流媒体相关

本文介绍了流媒体的基本概念,包括流媒体协议如HLS、RTSP和RTMP的工作原理,以及RTP、RTCP在传输中的作用。此外,还探讨了H264编码中的NALU结构,并举例说明了FFmpeg在流媒体推拉流中的应用。
摘要由CSDN通过智能技术生成

一、流概念

流媒体技术是将网络一端的媒体数据及时有效的传输给另一端显示的控制手段。如果没有流媒体技术就必须在使用前下载整个媒体文件之后才能观看。

流媒体协议是一种标准化的视频传递方法,用于将视频分解为多个块,将其发送给视频播放器,播放器重新组合播放。

ES--Elementary Streams (原始流)是直接从编码器出来的数据流,也叫基本码流,包含视频、音频或数据的连续码流。可以是编码过的视频数据流(H.264,MJPEG等),音频数据流(AAC),或其他编码数据流的统称。ES流经过PES打包器之后,被转换成PES包。

PES:PES--Packetized Elementary Streams (分组的ES),也叫打包的基本码流,是将基本的码流ES流根据需要分成长度不等的数据包,并加上包头就形成了打包的基本码流PES流。ES形成的分组称为PES分组,是用来传递ES的一种数据结构。PES流是ES流经过PES打包器处理后形成的数据流,在这个过程中完成了将ES流分组、打包、加入包头信息等操作(对ES流的第一次打包)。PES流的基本单位是PES包。PES包由包头和payload组成。

PES流封装成MP4、flv、ts等存储为可播放的文件。

AvPacket里的是PES,不是ES。网络传输过来的H264裸流是ES数据。纯ES数据可在ESEYE中播放,PES数据不可以在ESEYE中播放

PS:PS--Program Stream(节目流)PS流由PS包组成,而一个PS包又由若干个PES包组成(到这里,ES经过了两层的封装)。PS包的包头中包含了同步信息与时钟恢复信息。一个PS包最多可包含具有同一时钟基准的16个视频PES包和32个音频PES包。

TS:TS--Transport Stream(传输流)由定长的TS包组成(188字节),而TS包是对PES包的一个重新封装(到这里,ES也经过了两层的封装)。PES包的包头信息依然存在于TS包中。

二、常用协议

1. HLS协议

由苹果公司提出的基于HTTP的流媒体网络传输协议。由HTTP、M3U8、TS 三部分组成。HTTP 是传输协议,M3U8 是索引文件,TS 是音视频的媒体信息。每一个 .m3u8 文件,分别对应若干个 ts 文件,这些 ts 文件才是真正存放视频的数据,m3u8 文件只是存放了一些 ts 文件的配置信息和相关路径。

2. RTSP

RTSP(Real Time Streaming Protocol 实时流协议),应用层协议,建立并控制一个或几个时间同步的连续流媒体,对媒体流提供了诸如开始、暂停、快进、停止等控制,RTSP的作用相当于流媒体服务器的远程控制,而它本身并不传输数据。

 不特别强调时间同步,比较能容忍网络延迟。

具有重新导向功能,可视实际负载情况来转换提供服务的服务器,以避免过大的负载集中于同一服务器而造成延迟。

RTSP协议 = RTSP(字符串协议,类似HTTP)数据+ RTP(二进制协议)数据。

形式类似HTTP,但是有状态的双向的协议。

协议格式

 开源RTSP服务代码 (C++11实现):

https://github.com/ZLMediaKit/ZLMediaKit
https://gitee.com/ZLMediaKit/ZLMediaKit

简单RTSP服务的demo:从零开始写一个RTSP服务器(二)RTSP协议的实现 - 知乎 (zhihu.com)

RTSP常用的方法包括:OPTIONS、DESCRIBE、ANNOUNCE、SETUP、TEARDOWN、PLAY、PAUSE、GET_PARAMETER和SET_PARAMETER,通过这些方法的请求和响应完成RTSP的控制。

推流端:ffmpeg -re -i test.mp4 -vcodec h264 -acodec aac -f rtsp rtsp_transport tcp rtsp://192.168.32.128/live/test

拉流端:ffplay -rtsp_transport tcp rtsp://192.168.32.128/live/test

3. RTCP和RTP

见 (42条消息) 网络传输相关_tangcpp的博客-CSDN博客

4. RTMP协议

建立在TCP的基础上的应用层协议,通过TCP3次握手建立端口连接之后开始RTMP自己的3次握手。

RTMP的3次握手是为了RTMP自己协商自己应用层的事情,主要2个事:版本时间戳

C端发送自己的rtmp版本C0消息,S端收到并验证OK之后发送自己的版本S0消息;

C端在发送C0之后接着发C1时间戳消息,S端在发S0之后接着发自己的时间戳S1消息;(C端和S端的时间戳都是随着C0和S0后面发出去的)

C端在收到S1之后发确认消息C2,S端在收到C1消息之后发S2确认消息;(此后双方都处于等待状态)

C端收到S2之后结束等待状态开始发数据,S端收到C2之后结束等待状态等待接收下面的数据。

后面的数据怎么发?

因为都是一帧帧比较大的音视频数据,所以要考虑C端播的时候怎么识别,怎么播并且是怎么快速的播的问题。

S端把帧拆分成块chunk进行发送,C端收到之后组帧然后显示。

消息头:

rtmp、flv 和 m3u8

 协议的出现顺序RTMP(FLASH)-------FLV(FLASH,支持http传输)-------HLS(H5)
RTMP是由FLASH支持的,视频封装格式为flv的传输协议
http-flv出现在RTMP之后,其发送的还是flv封装格式的音视频,但其可以在http上进行传输
后来出现HLS支持h5

rtmp 和 flv需要 Flash 的支持 延时在 1-3s 之间,Windows浏览器支持,在移动端需要额外处理支持

点播Nignx配置

点播 rtmp://192.168.32.99/av/jg.mp4 #直接通过Nginx 1935端口的av路由请求磁盘上存储的MP4文件。

直播 rtmp://192.168.32.99/live/gh123 FFmpeg实时的把磁盘存储的MP4文件读取拷贝到内存转换成flv数据(flv跟ts一样每一段有自己的codec参数),并实时推送给Nginx?当客户端以指定的rtmp地址请求Nginx时,可拉取实时数据进行播放。

rtmp {
    server
   {              
    listen 1935;
    chunk_size 4096;

    application av {
        play /home/guohua/av/;
    }
    application live {
        live on;
    }
    }
}

ffmpeg -re -i /home/guohua/av/jg.mp4 -c copy -f flv rtmp://192.168.32.99/live/gh123 通过FFmpeg把MP4文件的流数据以拷贝数据转换成flv的方式推到Nginx的1935端口,客户端通过地址 rtmp://192.168.32.99/live/gh123 请求到Nginx的1935端口live路由拉取数据。

通过vlc请求rtmp://192.168.32.99/live/gh123之后不关闭vlc,结束推流或者中断推流之后从头再推流时vlc端也会重新接收新的数据,即rtmp建立连接之后就会一直保持连接,以最初的连接传输数据。

export PKG_CONFIG_PATH=/tang/lib/pkgconfig:/usr/lib/pkgconfig:/home/guohua/ffmpeg/ffbuild/lib/pkgconfig/

export LD_LIBRARY_PATH=/usr/lib/:/usr/local/lib:/tang/lib/ffmpeg41/lib:/home/guohua/ffmpeg/ffbuild/lib/:$LD_LIBRARY_PATH

推流

root@ubuntu:/home/guohua/ffmpeg# /tang/lib/ffmpeg41/bin/ffmpeg -re -i /home/guohua/av/jg.mp4 -c copy -f flv rtmp://192.168.32.99/live/gh123
ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
configuration:  --prefix=/tang/lib/ffmpeg41 --bindir=/tang/lib/ffmpeg41/bin/ --enable-pic --target-os=linux --arch=x86_64 --enable-gpl --enable-nonfree --disable-stripping --enable-ffmpeg --enable-ffplay --enable-ffprobe --enable-libmp3lame --enable-libx264 --enable-decoder=h264 --enable-decoder=mp3 --enable-decoder=mjpeg --enable-decoder=mjpegb --enable-decoder=jpeg2000 --enable-decoder=jpegls --enable-demuxer=mjpeg --enable-protocol=file --enable-avfilter --enable-bsf=aac_adtstoasc --enable-libfdk-aac --disable-x86asm --enable-shared --extra-cflags=-I/home/guohua/ffmpeg/ffbuild/include/ --extra-ldflags=-L/home/guohua/ffmpeg/ffbuild/lib/
libavutil 56. 22.100 / 56. 22.100
libavcodec 58. 35.100 / 58. 35.100
libavformat 58. 20.100 / 58. 20.100
libavdevice 58. 5.100 / 58. 5.100
libavfilter 7. 40.101 / 7. 40.101
libswscale 5. 3.100 / 5. 3.100
libswresample 3. 3.100 / 3. 3.100
libpostproc 55. 3.100 / 55. 3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/guohua/av/jg.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp42mp41
creation_time : 2020-03-14T03:27:28.000000Z
Duration: 00:01:03.57, start: 0.000000, bitrate: 18959 kb/s
Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 18652 kb/s, 25 fps, 25 tbr, 25k tbn, 50 tbc (default)
Metadata:
creation_time : 2020-03-14T03:27:28.000000Z
handler_name : ?Mainconcept Video Media Handler
encoder : AVC Coding
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 317 kb/s (default)
Metadata:
creation_time : 2020-03-14T03:27:28.000000Z
handler_name : #Mainconcept MP4 Sound Media Handler
Output #0, flv, to 'rtmp://192.168.32.99/live/gh123':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp42mp41
encoder : Lavf58.20.100
Stream #0:0(eng): Video: h264 (High) ([7][0][0][0] / 0x0007), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 18652 kb/s, 25 fps, 25 tbr, 1k tbn, 25k tbc (default)
Metadata:
creation_time : 2020-03-14T03:27:28.000000Z
handler_name : ?Mainconcept Video Media Handler
encoder : AVC Coding
Stream #0:1(eng): Audio: aac (LC) ([10][0][0][0] / 0x000A), 48000 Hz, stereo, fltp, 317 kb/s (default)
Metadata:
creation_time : 2020-03-14T03:27:28.000000Z
handler_name : #Mainconcept MP4 Sound Media Handler
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
[flv @ 0x13efe40] Failed to update header with correct duration.ate=18989.3kbits/s speed= 1x
[flv @ 0x13efe40] Failed to update header with correct filesize.
frame= 1588 fps= 25 q=-1.0 Lsize= 147170kB time=00:01:03.50 bitrate=18983.4kbits/s speed= 1x
video:144628kB audio:2461kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.055035%

 加参数推流:/tang/lib/ffmpeg41/bin/ffmpeg -i /home/guohua/av/720P-20.mp4 -threads 1 -c:v libx264 -profile:v baseline -b:v 350K -s 640x360 -f flv -c:a aac -ac 1 -strict -2 -b:a 56k rtmp://192.168.32.99/live/gh123

flv 拉流

地址:

http://122.112.245.213/flv?port=1935&app=live&stream=gh123

live为拉流地址路由匹配,
port等于推流的端口号
app等于推流的路由
stream等于推流后自定义的文件名

三、NALU

|0 |1 |2 |3 |4 |5 |6 |7 |
|F |NRI |     Type     |

一个图像序列的组成:SPS+PPS+SEI+一个I帧+若干个P帧

NALU是对RBSP的封装。而RTP是对NALU的封装。

上行RTP:传输层 → NAL:网络适配层 → VCLU:适配编解码层 

接受到RTP包之后,要先去除RTP头解出NALU,然后再通过NALU的头信息解出负载数据RBSP。

从大到小依次为:GOP、帧(I、P、B帧)、片组、片(I、P、B、SP、SI片)、NALU、宏块、亚宏块、块、像素。

一个宏块由几乘几如16x16的小宏块组成,每个SLICE可以分成1个或者多个宏块。slice data 组成的流就是SODB——原始数据比特串(String Of Data Bit)。由于SODB数据不一定是8的整数倍,所以会在SODB的末尾填充数据形成RBSP数据——原始字节序列载荷(Raw Byte Sequence Payload)。

RBSP 是上行传输NAL的body,EBSP是存储(或者下行)PES数据时的body。

NALU的每一个数据单元都有一个起始码,一帧图像可以分成多个NALU,一帧图像的起始NALU起始码为 0x00 00 00 01,非一帧的第一个NALU起始码为 0x00 00 01,如果NALU里面万一也出现了0x00 00 01或者0x00 00 00 01的数据怎么办,可不能也把它当初起始码了,所以定义NALU内一旦出现出现00 00,则在00 00 的后面加上一个03,即00 00 03 ,这样处理后的数据就是EBSP——扩展字节序列载荷(Extension Byte Sequence Payload),解码时把多余的03去掉得到RBSP数据叫做脱壳。

nal_unit_type的值在1到5之间的NALU称为VCL NALU,其余的称为非VCL NALU。

接收到的H264数据都是一个一个的NALU数据,NALU是H264 ES数据的组成单元。

起始码之后是每一个NALU的header,H264之所以要定义NALU,是因为NALU的头里面有H264相关的信息,NALU头里面主要带着这个数据单元的类型,如是否IDR、是否SPS、PPS,是否是分块的SLICE ABC,指示是否码流结束,指示这个NALU的重要程度等等。具体定义如下:

NALU的第1个字节是NUAL类型字节,其格式如下
|0 |1 |2 |3 |4 |5 |6 |7 |
|F |NRI|Type |
F: 1 bit forbidden_zero_bit H.264规范声明值设置为1表示语法违例
NRI: 2 bit nal_ref_idc 表示NALU的优先级。0–3,取值越大,该NALU越重要,需要优先保护。SPS、PPS、关键帧:3(011),P帧:2(010),B帧(001),其他不重要信息:0(000),同后面5bit组织起来关键帧是 01100101 (65),SPS: 01100111 (0x67)、PPS: 01101000 (0x68)补充增强信息SEI为 00000110(0x06),流结束:00001011(0x0b).
00值表示该NALU不用于帧间图像预测重构参考图像,可以丢弃不用冒参考图像完整性风险。
如果NALU是参考帧slice、PPS、SPS时,该值必须大于0
Type: 5 bit nal_unit_type 表示NALU载荷类型。h264仅用1-23,24以后的用在RTP H264负载类型头中。

类型值定义如下:
0:未定义
1:非IDR图像不采用数据划分片段
2:非IDR图像采用数据划分片段A部分
3:非IDR图像采用数据划分片段B部分
4:非IDR图像采用数据划分片段C部分
5:IDR图像片段
6:补充增强信息 SEI
7:序列参数集 SPS
8:图像参数集 PPS
9:分隔符
10:序列结束符
11:流结束符
12:填充数据
13:序列参数集扩展
14:带前缀的NALU
15:子序列参数集
16-18:保留
19:不采用数据划分的辅助编码图像片段
20:编码片段扩展
21-23:保留
24-31:未定义

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tangcpp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值