Enhance rtmp-flv协议开始支持多码流

Enhance rtmp-flv协议开始支持多码流

小编之前在LiveVideoStack写过博文详解Enhanced-RTMP支持H.265,详解了Enhance-Rtmp如何支持H265编码。

后面也写过ffmpeg7.0 flv支持hdr,Enhanced-Rtmp支持Hdr。

最近Enhance-Rtmp协议再次更新到v2版本,开始支持多码流方式,也就是我们常说的推送多份不同分辨率的视频,方便观众根据需要来观看。

协议原文github地址:

复制代码
https://github.com/veovera/enhanced-rtmp/blob/main/docs/enhanced/enhanced-rtmp-v2.md

本文主要内容:

  • 介绍应用场景:Enhance Rtmp-Flv V2协议支持哪几种多码率解决方案

  • 详解协议内容:Enhance Rtmp-Flv V2协议如何支持多码率

文章有点长,可以先关注或收藏,有空再看。

1 Enhance Rtmp-Flv V2支持多码率

该V2协议支持多种应用的场景,如下:

新rtmp-flv支持多种码流了,4种不同类型的应用方式:

  • 自适应比特率流式传输:多轨支持允许客户端发送自适应比特率 (ABR) 阶梯,从而避免服务器端转码的需要并减少质量损失。这也有助于发送具有多种编解码器(如 AV1、HEVC 和 VP9)的内容。

  • 设备特定流式传输:该功能允许流式传输不同的宽高比,针对各种设备配置文件进行量身定制,从而实现更动态、更灵活的演示。

  • 帧级同步:例如,您可以在音乐会中同步多个摄像机视图。

  • 多语言支持:现在支持单个 [FLV] 文件中的多个音轨,无需多个文件版本。

1.1 自适应比特率流式传输

在新兴的视频编码,如AV1、HEVC 和 VP9,是支持SVC,多空间层和多时间层的,也就是有多个Spatial Layers(空间层), 多个Temporal Layers(时间层)。

常规H264(不支持SVC),I帧,P帧和B帧都是前后依赖:

P帧和B帧少了前面I帧或P帧或B帧后,都会解码失败。

而SVC克服这个问题,SVC编码帧的依赖:

T0的帧之间前后依赖,T1层的帧依赖T0的帧,T2的帧依赖T1和T0。举例,如果整视频为30帧/s:

  • T2层的帧就为30帧/s,连续性最好,但是需要解码全部帧:T0,T1,T2;

  • T1层只有15帧/s,只需要解码T0, T1;

  • T0层只有7.5帧/s,只需要解码T0帧,虽然连续性最差,但是基本视频可用;

这样SVC保证接收端在网络不好的情况下,如果只接收到T0帧,也能基本可用。

Enhance-Rtmp2.0支持对SVC多层的多码率特性,实现:

推流SVC多层,拉流可以根据情况生成多种码率的拉流。

1.2 设备特定流式传输

该功能允许流式传输不同的宽高比,针对各种设备配置文件进行量身定制,从而实现更动态、更灵活的演示

常规的H264/H265(不支持SVC的),可以推流多个分辨率的流,放在一个rtmp推流中,通过flv的扩展头来区分不同的分辨率。

1.3 帧级同步

多摄像头,多视频源的同步推送,可以把不同视频源放在一个rtmp流中进行推送,通过flv的扩展头来区分不同的摄像头。

举例一些应用:

  • 大型会议不同摄像头的拍摄,如音乐会等

  • 安放鱼眼摄像头(多个摄像头的拼接)

  • VR应用(两个重叠的左右眼视频推送)

1.4 多语言支持

现在支持单个 [FLV] 文件中的多个音轨,无需多个文件版本。也就是支持多个audio track,支持多语言的音频,实现单文件多语音语言的存储。

方便用户选择不同的语言进行听取。

2 Enhande Rtmp-Flv具体细节

在博文详解Enhanced-RTMP支持H.265中,详细介绍了Enhance Rtmp如何支持H265编码,本章介绍Enhanced rtmp如何支持多码流。

在Rtmp流中承载video和audio,首先是Rtmp Message Header:

复制代码
 0                   1                   2                   3+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|MessageType ID |                Payload length                 ||    (1 byte)   |                   (3 bytes)                   |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                           Timestamp                           ||                           (4 bytes)                           |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                 Stream ID                     ||                 (3 bytes)                     |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

如上Rtmp Message Header,关键信息:MessageType ID: 8是音频,9是视频。

在这个头后面就是Flv的video tag或audio tag数据。

每个flv video tag或audio tag数据,在Enhance Rtmp中都有extesion的定义,用来支持更多的内容:

  • 更多的编码器:h265/av1/opus等

  • 更多的能力:多码率等

2.1 Extended AudioTagHeader

这里介绍如何支持多audio track,用协议伪码进行详解,下图中UB单位为bit,UI8为一个字节。

复制代码
soundFormat = UB[4] as SoundFormat/*soundFormat为4bits,常规的值:enum SoundFormat {                                                                  ¦  LPcmPlatformEndian  = 0,                                                          ¦  AdPcm               = 1,                                                          ¦  Mp3                 = 2,                                                          ¦  LPcmLittleEndian    = 3,                                                          ¦  Nellymoser16KMono   = 4,                                                          ¦  Nellymoser8KMono    = 5,                                                          ¦  Nellymoser          = 6,                                                          ¦  G711ALaw            = 7,                                                          ¦  G711MuLaw           = 8,                                                          ¦  ExHeader            = 9,    // new, used to signal FOURCC mode                    ¦  Aac                 = 10,                                                         ¦  Speex               = 11,                                                         ¦                                                                 ¦  Mp3_8K              = 14,                                                         ¦  Native              = 15,   // Device specific sound                              ¦}原来常规flv协议规定audio codec有:Aac(10), Mp3(15)。但是9元来是reverve保留的,在Enhance RtmpFlv中变成ExHeader标志。*/
//如果soundFormat的值不是ExHeader(9),//就是传统的Rtmp audio tag信息if (soundFormat != SoundFormat.ExHeader) {                                         //flv audioi tag传统信息                                                             soundRate = UB[2]                                                                soundSize = UB[1]                                                                soundType = UB[1]                                                                                                                            ¦}
processAudioBody = false//如果soundFormat的值是ExHeader(9),//就是Rtmp audio ExHeader信息                                                         if (soundFormat == SoundFormat.ExHeader) {                                            processAudioBody = true                                                                                                                                                 /*  audioPacketType含义:  enum AudioPacketType {                                                       SequenceStart       = 0,                                                   CodedFrames         = 1,  SequenceEnd         = 2,  MultichannelConfig  = 4,  Multitrack          = 5,  ModEx               = 7,  }  这里关注Multitrack,就是多audio track标志位  */  audioPacketType = UB[4] as AudioPacketType                                                                                      // Process each ModEx data packet                                                   while (audioPacketType == AudioPacketType.ModEx) {                                    //省略                                                                             }                                                                                   //如果是Multitrack,就是多audio track标志位  if (audioPacketType == AudioPacketType.Multitrack) {                                  isAudioMultitrack = true;    /*       enum AvMultitrackType {          OneTrack              = 0,                   ManyTracks            = 1,                   ManyTracksManyCodecs  = 2,                                   }     */    //如果是1,就是多audio track;    //如果是2,就是多audio track,且每个track的codec不同    audioMultitrackType = UB[4] as AvMultitrackType                                                                                                                         audioPacketType = UB[4] as AudioPacketType
    //如果audioMultitrackType不是多audio track多codec模式                                                                                      if (audioMultitrackType != AvMultitrackType.ManyTracksManyCodecs) {                   //得到该audio的编码FourCC信息,如Mp4a, Opus等      audioFourCc = FOURCC as AudioFourCc    }                                                                                 } else {    //得到该audio的编码FourCC信息,如Mp4a, Opus等    audioFourCc = FOURCC as AudioFourCc  }                                                                                 }
//如果扩展audio使能while (processAudioBody) {  //如果是音频多track模式  if (isAudioMultitrack) {    //如果audioMultitrackType是多track多codec模式    if (audioMultitrackType == AvMultitrackType.ManyTracksManyCodecs) {                   //因为是多track,每个track有独立的codec;      //得到当前的audio codec FourCc信息      audioFourCc = FOURCC as AudioFourCc                                               }                                                                                                                                                                       //当前的audio trackId                                                     audioTrackId = UI8                                                                                                                                                      if (audioMultitrackType != AvMultitrackType.OneTrack) {                               //如果不是单track模式,sizeOfAudioTrack用来表示本track的大小,      //在while循环结束前,判断是否继续下一个track的检测      sizeOfAudioTrack = UI24                                                           }                                                                                 }                                                                                                                                                                       if (audioPacketType == AudioPacketType.MultichannelConfig) {                          //多声道配置,略                                                         audioChannelOrder = UI8 as AudioChannelOrder                                                                                                                                                            channelCount = UI8                                                                  if (audioChannelOrder == AudioChannelOrder.Custom) {                                       audioChannelMapping = UI8[channelCount] as AudioChannel                           }                                                                                   if (audioChannelOrder == AudioChannelOrder.Native) {                                                                           audioChannelFlags = UI32                                                          }                                                                                 }                                                                                                                                                                       if (audioPacketType == AudioPacketType.SequenceEnd) {                                 // signals end of sequence                                                        }                                                                                 
  /** 如果是SequenceStart,表示内容为audio sequence header **/  /** 一般第一个音频报文为audio sequence header **/  if (audioPacketType == AudioPacketType.SequenceStart) {                               if (audioFourCc == AudioFourCc.Aac) {                                                 // Aac的Sequence header      aacHeader = [AacSequenceHeader]                                                   }                                                                                                                                                                       if (audioFourCc == AudioFourCc.Flac) {                                                // Flac的Sequence header      flacHeader = [FlacSequenceHeader]                                                 }                                                                                                                                                                       if (audioFourCc == AudioFourCc.Opus) {      // Opus的Sequence header      opusHeader = [OpusSequenceHeader]                                                 }                                                                                 }                                                                                                                                                                       if (audioPacketType == AudioPacketType.CodedFrames) {                                 if (audioFourCc == AudioFourCc.Ac3 || audioFourCc == AudioFourCc.Eac3) {              //ac3音频编码数据              ac3Data = [Ac3CodedData]                                                          }                                                                                                                                                                       if (audioFourCc == AudioFourCc.Opus) {                                                //opus编码数据                                          opusData = [OpusCodedData]                                                        }                                                                                                                                                                       if (audioFourCc == AudioFourCc.Mp3) {                                                 //mp3编码数据                                                          mp3Data = [Mp3CodedData]                                                          }                                                                                                                                                                       if (audioFourCc == AudioFourCc.Aac) {                                                 //aac编码数据      aacData = [AacCodedData]                                                          }                                                                                                                                                                       if (audioFourCc == AudioFourCc.Flac) {                                                //Flac音频数据                              flacData = [FlacCodedData]                                                        }                                                                                 }                                                                                   //如果isAudioMultitrack使能,且是多track模式,  //且sizeOfAudioTrack没有完结,  //本报文contiue去寻找下一个audio track的信息。  //否则结束循环  if (    isAudioMultitrack &&                                                                audioMultitrackType != AvMultitrackType.OneTrack &&                                 positionDataPtrToNextAudioTrack(sizeOfAudioTrack)                                 ) {                                                                                   // TODO: need to implement positionDataPtrToNextVideoTrack()                        continue                                                                          }                                                                                                                                                                         // 否则,结束while循环                                                    break                                                                             }

如上所示,通过audio type是否为9,判断后面是否是ext audio header,再判断audioPacketType是否为Multitrack,得知是否为多audio track模式;

如果是多audio track模式,最后通过while循环,分析各个track的信息,得到对应各个track的codec类型,codec数据。

2.2 Extended VideoTagHeader

传统的rtmp flv video tag header:

  • 如果视频数据是H264的sequence header(也就是包含sps/pps的Avcc Header),就应该是0x17 00;
  • 如果视频数据是H264的Iframe,就应该是0x17 01;

  • 如果视频数据是H264的非Iframe,就应该是0x27 01

在Extended video tag header中,第一个字节的第一个bit表示ExVideoHeader使能位,如果该位使能,带有更多的信息,如更多的codec类型,或多video track信息。

具体如下:

复制代码
//第一个bit表示ExVideoHeader,为1表示使能扩展信息isExVideoHeader = UB[1]//后3个bits为传统的video codec类型,如7为h264videoFrameType = UB[3] as VideoFrameType
//如果没有使能isExVideoHeader,就是传统的flv video tagif (isExVideoHeader == 0) {                                                                     videoCodecId = UB[4] as VideoCodecId                                     if (videoFrameType == VideoFrameType.Command) {                            videoCommand = UI8 as VideoCommand                                     }                                                                      }
processVideoBody = false//如果使能isExVideoHeader,//开始分析Ext Video Headerif (isExVideoHeader == 1) {                                                           processVideoBody = true                                                           
/*enum VideoPacketType {  SequenceStart         = 0,  CodedFrames           = 1,  SequenceEnd           = 2,  CodedFramesX          = 3,  Metadata              = 4,  MPEG2TSSequenceStart  = 5,  Multitrack            = 6,//video多track使能  ModEx                 = 7,}*/  //这里关注videoPacketType是否为VideoPacketType.Multitrack  videoPacketType = UB[4] as VideoPacketType
  // Process each ModEx data packet                                                   while (videoPacketType == VideoPacketType.ModEx) {                                    //ModEx类型,分析:略    modExDataSize = UI8 + 1    if (modExDataSize == 256) {                                                           modExDataSize = UI16 + 1;                                                         }                                                                                   modExData = UI8[modExDataSize]    videoPacketModExType = UB[4] as VideoPacketModExType                                                                                     videoPacketType = UB[4] as VideoPacketType  // at byte boundary after this read     if (videoPacketModExType == VideoPacketModExType.TimestampOffsetNano) {                    videoTimestampNanoOffset = bytesToUI24(modExData)                                                                }                                                                                 }                                                                                 
  if (                                                                                  videoPacketType != VideoPacketType.Metadata &&                                      videoFrameType == VideoFrameType.Command                                          ) {                                                                                   videoCommand = UI8 as VideoCommand                                                                             processVideoBody = false                                                          }  /* 关注videoPacketType == VideoPacketType.Multitrack */   else if (videoPacketType == VideoPacketType.Multitrack) {                           isVideoMultitrack = true;        /*      enum AvMultitrackType {        OneTrack              = 0,                                                          ManyTracks            = 1,                                                          ManyTracksManyCodecs  = 2,                                                        }      track类型为单track,多track,或多track多codec类型    */    videoMultitrackType = UB[4] as AvMultitrackType                                                      videoPacketType = UB[4] as VideoPacketType                                      
    /** 如果不是多track多codec类型,得到codecFourCc信息**/    if (videoMultitrackType != AvMultitrackType.ManyTracksManyCodecs) {                   //得到video codec FourCc信息,如"vp08", "avc1", "hvc1"      videoFourCc = FOURCC as VideoFourCc                                               }                                                                                 } else {    //如果不是多video track,得到video codec FourCc信息    videoFourCc = FOURCC as VideoFourCc                                               }                                                                                 }
/** 如果扩展video tag header使能 **//** 进入分析循环体 **/while (processVideoBody) {  //如果video多track使能  if (isVideoMultitrack) {                                                              if (videoMultitrackType == AvMultitrackType.ManyTracksManyCodecs) {                   //如果是多track多codec类型      //得到本track的codec FourCc信息,如"vp08", "avc1", "hvc1"      videoFourCc = FOURCC as VideoFourCc                                               }                                                                                   //得到但字节的video trackId                                                   videoTrackId = UI8                                                              
    if (videoMultitrackType != AvMultitrackType.OneTrack) {                               //如果不是单track类型,得到本track的size大小      sizeOfVideoTrack = UI24                                                           }                                                                                 }                                                                                 
  if (videoPacketType == VideoPacketType.Metadata) {                                                 videoMetadata = [VideoMetadata]                                                   }                                                                                 
  if (videoPacketType == VideoPacketType.SequenceEnd) {                                 // signals end of sequence                                                        }                                                                                 
  if (videoPacketType == VideoPacketType.SequenceStart) {                               if (videoFourCc == VideoFourCc.Vp8) {                                                 //vp8的codec sequece信息      vp8Header = [VPCodecConfigurationRecord]                                          }                                                                               
    if (videoFourCc == VideoFourCc.Vp9) {                                                 // vp9的codec sequece信息      vp9Header = [VPCodecConfigurationRecord]                                          }                                                                               
    if (videoFourCc == VideoFourCc.Av1) {                                                 //av1的codec sequece信息      av1Header = [AV1CodecConfigurationRecord]                                         }                                                                               
    if (videoFourCc == VideoFourCc.Avc) {                                                 //h264的codec sequece信息                                               avcHeader = [AVCDecoderConfigurationRecord]                                       }                                                                               
    if (videoFourCc == VideoFourCc.Hevc) {                                                //h265的codec sequece信息                                              hevcHeader = [HEVCDecoderConfigurationRecord]                                     }                                                                                 }                                                                                 
  //mpegts的sequence信息  if (videoPacketType == VideoPacketType.MPEG2TSSequenceStart) {                        if (videoFourCc == VideoFourCc.Av1) {                                                 // body contains a video descriptor to start the sequence                           av1Header = [AV1VideoDescriptor]                                                  }                                                                                 }                                                                                 
  //videoPacketType为CodedFrames  //表示内容为codec的编码数据  if (videoPacketType == VideoPacketType.CodedFrames) {                                 if (videoFourCc == VideoFourCc.Vp8) {                                                 //vp8的编码数据      vp8CodedData = [Vp8CodedData]                                                     }                                                                               
    if (videoFourCc == VideoFourCc.Vp9) {                                                 //vp9的编码数据      vp9CodedData = [Vp9CodedData]                                                     }                                                                               
    if (videoFourCc == VideoFourCc.Av1) {                                                 //av1的编码数据      av1CodedData = [Av1CodedData]                                                     }                                                                               
    if (videoFourCc == VideoFourCc.Avc) {      //3字节的时间戳偏移(为由dts计算pts时间)      compositionTimeOffset = SI24                                                        //h264的编码数据      avcCodedData = [AvcCodedData]                                                     }                                                                               
    if (videoFourCc == VideoFourCc.Hevc) {      //3字节的时间戳偏移(为由dts计算pts时间)      compositionTimeOffset = SI24                                                        //h265的编码数据      hevcData = [HevcCodedData]                                                        }                                                                                 }                                                                                 
  //若videoPacketType为CodedFramesX  //没有偏移时间的字段,节省3个字节  if (videoPacketType == VideoPacketType.CodedFramesX) {                                if (videoFourCc == VideoFourCc.Avc) {                                                 //h264的编码数据,没有偏移时间的字段,节省3个字节      avcCodedData = [AvcCodedData]                                                     }                                                                               
    if (videoFourCc == VideoFourCc.Hevc) {      //h265的编码数据,没有偏移时间的字段,节省3个字节      hevcData = [HevcCodedData]                                                        }                                                                                 }                                                                                 
  if (                                                                                  isVideoMultitrack &&                                                                videoMultitrackType != AvMultitrackType.OneTrack &&                                 positionDataPtrToNextVideoTrack(sizeOfVideoTrack)                                 ) {                                                                                   //如果是video多track模式下,且sizeOfVideoTrack后还有数据,    //则继续分析后续的数据    continue                                                                          }                                                                                 
  //否则,结束while循环的数据分析  break                                                                             } 

3 总结

Enhance-Rtmp协议再次更新到v2版本,开始支持多码流方式。

本文主要内容:

  1. 应用场景:Enhance Rtmp-Flv V2协议支持哪几种多码率解决方案;
  • 自适应比特率流式传输:多轨支持允许客户端发送自适应比特率 (ABR) 阶梯,从而避免服务器端转码的需要并减少质量损失。这也有助于发送具有多种编解码器(如 AV1、HEVC 和 VP9)的内容。
  • 设备特定流式传输:该功能允许流式传输不同的宽高比,针对各种设备配置文件进行量身定制,从而实现更动态、更灵活的演示。

  • 帧级同步:例如,您可以在音乐会中同步多个摄像机视图。

  • 多语言支持:现在支持单个 [FLV] 文件中的多个音轨,无需多个文件版本。

  1. 协议内容:Enhance Rtmp-Flv V2协议如何支持多码率

扩展flv tag header如何定义多码率方案。

相关推荐
Sleepless_斑马24 天前
RTMP/RTSP流媒体服务器搭建、ffmpeg推流桌面、vlc拉流
ffmpeg·rtmp·rtsp
REDcker1 个月前
web 端 H265 软解码实现原理与使用说明
前端·音视频·播放器·h265·解码·软解码
小曾同学.com2 个月前
为什么过滤 rtmpt 而不是 rtmp?
wireshark·实时音视频·抓包·rtmp·rtmpt
Industio_触觉智能2 个月前
RK3576轻松搭建RTMP视频推流,基于FFmpeg+Nginx协同
nginx·ffmpeg·实时音视频·rtmp·瑞芯微·视频推流·rk3576
广东数字化转型3 个月前
JT808,JT1078 —— AAC编码 —— 部标机语音对讲Java实现
aac·h264·h265·g711a·部标机
Sam Xiao3 个月前
JT808,JT1078 —— AAC编码 —— 部标机语音对讲Java实现
aac·h264·h265·g711a·metro·部标机
DogDaoDao3 个月前
OpenCV音视频编解码器详解
人工智能·opencv·音视频·视频编解码·h264·h265·音视频编解码
却道天凉_好个秋5 个月前
音视频学习(六十三):AVCC和HVCC
音视频·h264·h265·avcc·hvcc
CHN悠远5 个月前
FFMPEG 10BIT下 Intel b570 qsv 硬解AV1,H265视频编码测试
ffmpeg·av1·h265