浅尝实时流媒体传输

geepair

技术分享|2022-8-3|最后更新: 2023-3-13|
type
Post
status
Published
date
Aug 3, 2022
slug
summary
tags
工具
category
技术分享
icon
password
notion image

低延迟实时流媒体传输要求

  1. 数据可靠性
  1. 流控算法
  1. 网络传输协议

传输协议区别 RTP RTMP RTSP HLS FLV WebRTC

RTP:实时传输协议(Real-time Transport Protocol)

RTP是一种基于包的传输协议,它用来传输实时数据。在网络上传输数据包的延迟和误差是不可避免的,对此RTP包头包含时间戳、丢失保护、载荷标识、源标识和安全性信息。这些信息用于在应用层实现数据包丢失恢复、拥塞控制等。 RTP通常运行于UDP的上层,以利用UDP的复用和求和校验功能。RTP是在两个主机之间提供基于连接的、稳定的数据流,而UDP是在网络上提供一种非连接数据报服务。
 
notion image
Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输层协议。RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式。RTP协议常用于流媒体系统(配合RTCP协议),视频会议和一键通(Push to Talk)系统(配合H.323或SIP),使它成为IP电话产业的技术基础。RTP协议和RTP控制协议RTCP一起使用,而且它是建立在UDP协议上的。 RTP 本身并没有提供按时发送机制或其它服务质量(QoS)保证,它依赖于低层服务去实现这一过程。 RTP 并不保证传送或防止无序传送,也不确定底层网络的可靠性。 RTP 实行有序传送, RTP 中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置,例如:在视频解码中,就不需要顺序解码。 RTP 由两个紧密链接部分组成: RTP ― 传送具有实时属性的数据;RTP 控制协议(RTCP) ― 监控服务质量并传送正在进行的会话参与者的相关信息。
notion image

RTCP:实时传输控制协议(Real-time Transport Control Protocol)

RTCP为RTP媒体流提供信道外(out-of-band)控制。RTCP本身并不传输数据,但和RTP一起协作将多媒体数据打包和发送。RTCP定期在流多媒体会话参加者之间传输控制数据。RTCP的主要功能是为RTP所提供的服务质量(Quality of Service)提供反馈。 RTCP收集相关媒体连接的统计信息,例如:传输字节数,传输分组数,丢失分组数,jitter,单向和双向网络延迟等等。网络应用程序可以利用RTCP所提供的信息试图提高服务质量,比如限制信息流量或改用压缩比较小的编解码器。RTCP本身不提供数据加密或身份认证。SRTCP可以用于此类用途。

SRTP & SRTCP

安全实时传输协议(Secure Real-time Transport Protocol或SRTP)是在实时传输协议(Real-time Transport Protocol或RTP)基础上所定义的一个协议,旨在为单播和多播应用程序中的实时传输协议的数据提供加密、消息认证、完整性保证和重放保护。 由于实时传输协议和可以被用来控制实时传输协议的会话的实时传输控制协议(RTP Control Protocol或RTCP)有着紧密的联系,安全实时传输协议同样也有一个伴生协议,它被称为安全实时传输控制协议(Secure RTCP或SRTCP);安全实时传输控制协议为实时传输控制协议提供类似的与安全有关的特性,就像安全实时传输协议为实时传输协议提供的那些一样。

RTSP

RTSP提供了一个可扩展框架,使实时数据,如音频与视频的受控、点播成为可能。数据源包括现场数据与存储在剪辑中的数据。该协议目的在于控制多个数据发送连接,为选择发送通道,如UDP、多播UDP与TCP提供途径,并为选择基于RTP上发送机制提供方法。 RTSP(Real Time Streaming Protocol)是用来控制声音或影像的多媒体串流协议,并允许同时多个串流需求控制,传输时所用的网络通讯协定并不在其定义的范围内,服务器端可以自行选择使用TCP或UDP来传送串流内容,它的语法和运作跟HTTP 1.1类似,但并不特别强调时间同步,所以比较能容忍网络延迟。而前面提到的允许同时多个串流需求控制(Multicast),除了可以降低服务器端的网络用量,更进而支持多方视讯会议(Video Conference)。 因为与HTTP1.1的运作方式相似,所以代理服务器《Proxy》的快取功能《Cache》也同样适用于RTSP,并因RTSP具有重新导向功能,可视实际负载情况来转换提供服务的服务器,以避免过大的负载集中于同一服务器而造成延迟。

RTMP/RTMPS

RTMP(Real Time Messaging Protocol)实时消息传送协议是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输开发的开放协议。
RTMP协议(Real Time Messaging Protocol)是被Flash用于对象,视频,音频的传输.这个协议建立在TCP协议或者轮询HTTP协议之上. RTMP协议就像一个用来装数据包的容器,这些数据既可以是AMF格式的数据,也可以是FLV中的视/音频数据.一个单一的连接可以通过不同的通道传输多路网络流.这些通道中的包都是按照固定大小的包传输的.

HLS

HTTP Live Streaming(HLS)是苹果公司(Apple Inc.)实现的基于HTTP的流媒体传输协议,可实现流媒体的直播和点播,主要应用在iOS系统,为iOS设备(如iPhone、iPad)提供音视频直播和点播方案。HLS点播,基本上就是常见的分段HTTP点播,不同在于,它的分段非常小。 相对于常见的流媒体直播协议,例如RTMP协议、RTSP协议、MMS协议等,HLS直播最大的不同在于,直播客户端获取到的,并不是一个完整的数据流。HLS协议在服务器端将直播数据流存储为连续的、很短时长的媒体文件(MPEG-TS格式),而客户端则不断的下载并播放这些小文件,因为服务器端总是会将最新的直播数据生成新的小文件,这样客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播。由此可见,基本上可以认为,HLS是以点播的技术方式来实现直播。由于数据通过HTTP协议传输,所以完全不用考虑防火墙或者代理的问题,而且分段文件的时长很短,客户端可以很快的选择和切换码率,以适应不同带宽条件下的播放。不过HLS的这种技术特点,决定了它的延迟一般总是会高于普通的流媒体直播协议。

WebRTC

目前国内主流的低延时框架是RTMP。RTMP是基于TCP的标准协议,CDN网络普遍支持,也能做到相对比较低的延迟。在推流端使用RTMP协议,拉流端兼容三种协议:RTMP,HLS。优化后的延时可以控制在2-3秒内,如果配合CDN加速,延时会更低。 WebRTC最初是由Google开发的,它们作为基于浏览器的实时通信的开源解决方案发布。它使用UDP来进行媒体推流,而不需要创建离散的媒体段,这为所有客户端提供了始终如一的低延时。 除了低延时流传输外,WebRTC还提供了一个实时双向数据通道,可用于发送和接收数据流。

比较

http-flv、rtmp和hls HLS:延迟主要来自编码解码时产生延迟、网络延迟、CDN 分发延迟。由于它是切片协议,延迟分两大块,一个是服务端有切片缓冲延迟,另一个是在播放端防抖缓冲会有延迟。切片的大小和数量都会 HLS 影响延迟大小,一般在十秒以上。RTMP/HTTP-FLV: 目前国内大部分厂家在用的 RTMP,它相对于 HLS 在服务端做了优化。RTMP 服务端不再进行切片,而是分别转发每一帧,CDN 分发延迟非常小。RTMP 延迟主要来自播放端防抖缓冲:为提升弱网环境下抖动时直播的流畅度,缓冲延迟一般有五到十秒。
TCP 由于其自身的一些特性,并不适用于低延迟直播场景,主要原因如下:
  • 重传慢:TCP 的 ACK 确认机制,丢包后发送侧超时重传,超时时间一般200ms,会造成接收侧帧抖动。
  • 拥塞判断不准确:基于丢包的拥塞控制算法无法准确判断拥塞,丢包并不等于拥塞;也会造成发送链路 bufferbloat,链路 RTT 增大,延迟增加。
  • 灵活性差:这是最主要原因,TCP 拥塞控制算法在操作系统内核层实现,优化成本较高,移动端只有利用系统已有的优化。
  • 延迟: http-flv:低延迟,内容延迟可以做到2-5秒。 Rtmp:低延迟,内容延迟可以做到2-5秒。 Hls::延迟较高。
  • 三者的易用性: rtmp和http-flv:播放端安装率高。只要浏览器支持FlashPlayer就能非常简易的播放。 hls:最大的优点:HTML5可以直接打开播放;这个意味着可以把一个直播链接通过微信 等转发分享,不需要安装任何独立的APP,有浏览器即可。
  • rtmp和http-flv比较:
#user nobody; worker_processes auto; worker_cpu_affinity auto; worker_rlimit_nofile 1024; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #error_log "pipe:rollback logs/error_log interval=1d baknum=7 maxsize=2G"; #pid logs/nginx.pid; events { use epoll; worker_connections 1024; multi_accept on; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; #access_log "pipe:rollback logs/access_log interval=1d baknum=7 maxsize=1G" main; sendfile on; tcp_nopush on; keepalive_timeout 60; tcp_nodelay on; client_header_buffer_size 4k; open_file_cache max=102400 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 1; client_header_timeout 15; client_body_timeout 15; reset_timedout_connection on; send_timeout 15; server_tokens off; client_max_body_size 1g; gzip on; gzip_min_length 1k; gzip_buffers 4 32k; #gzip_http_version 1.1; gzip_comp_level 6; gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml; gzip_vary on; gzip_proxied any; # ssl ssl_certificate cert.crt; ssl_certificate_key cert.key; ssl_session_cache shared:SSL:10m; ssl_session_timeout 5m; ssl_session_tickets on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers on; #ssl_stapling on; #ssl_stapling_verify on; #resolver 114.114.114.114; upstream pi { server 127.0.0.1:9090; } server { listen 80; listen [::]:80; server_name ijava.me pi.ijava.me static.ijava.me v6.ijava.me static-v6.ijava.me pi-v6.ijava.me; return 301 https://$host$request_uri; } server { listen 80 default; listen [::]:80 default; listen 443 ssl http2 default; listen [::]:443 ssl http2 default; rewrite ^(.*) <https://ijava.me>; } server { listen 443 ssl http2; isten [::]:443 ssl http2; server_name static.ijava.me static-v6.ijava.me; charset utf-8; location / { root /home/pi/data/static; autoindex on; autoindex_exact_size on; autoindex_format html; autoindex_localtime on; set $limit_rate 1M; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ijava.me v6.ijava.me; #ssl_certificate ijava_me.crt; #ssl_certificate_key ijava_me.key; #ssl_session_cache shared:SSL:10m; #ssl_session_timeout 5m; #ssl_session_tickets on; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; #ssl_prefer_server_ciphers on; #ssl_stapling on; #ssl_stapling_verify on; location / { root /home/pi/data/html; index index.html index.htm; charset utf-8; } location /status { stub_status on; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name pi.ijava.me pi-v6.ijava.me; location / { proxy_pass <https://pi>; proxy_set_header X-Forwared-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_redirect off; } location /cockpit/socket { proxy_pass <https://pi>; proxy_redirect off; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; } location ~* \\.(ico|jpe?g|gif|png|bmp|swf|flv)$ { proxy_pass <https://pi>; expires 30d; #log_not_found access_log off; access_log off; } location ~* \\.(js|css)$ { proxy_pass <https://pi>; expires 7d; log_not_found off; access_log off; } } }
net.core.default_qdisc=fq net.ipv4.tcp_congestion_control=bbr # optimiaze fs.file-max = 999999 net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 kernel.sysrq = 0 kernel.core_uses_pid = 1 kernel.msgmnb = 65536 kernel.msgmax = 65536 kernel.shmmax = 68719476736 kernel.shmall = 4294967296 net.ipv4.tcp_max_tw_buckets = 6000 net.ipv4.tcp_sack = 1 net.ipv4.tcp_window_scaling = 1 net.ipv4.ip_local_port_range=1024 65000 net.ipv4.tcp_synack_retries = 1 net.ipv4.tcp_syn_retries = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_keepalive_time = 30 net.ipv4.tcp_syncookies = 1 net.core.somaxconn = 40960 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_timestamps = 0 net.core.netdev_max_backlog = 262144 net.ipv4.tcp_max_syn_backlog = 262144 net.ipv4.tcp_rmem = 10240 87380 12582912 net.ipv4.tcp_wmem = 10240 87380 12582912 net.core.rmem_default = 8388608 net.core.wmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_fin_timeout = 1

安装 tengine

dep:gcc gcc-c++ pcre-devel openssl-devel
  1. 获取安装包
curl -O <https://tengine.taobao.org/download/tengine-2.3.3.tar.gz>
  1. 解压编译安装
tar -xf tengine-2.3.3.tar.gz && cd tengine-2.3.3 ./configure --add-module=nginx-rtmp-module --with-openssl=openssl make && make install
推流:ffmpeg -re -i 1.mp4 -c copy -f flv rtmp://127.0.0.1:1935/hls/1.mp4 拉流:ffmpeg -i rtmp://127.0.0.1/1.mp4 -c copy dump.flv 播放:ffplay rtmp://127.0.0.1/hls/$name
 
 开启调试