H264 码流分析

内容
1、 H264 码流分析: 一帧可能由多个NALU组成,由Slice片区类型不同定义成不同的NALU 单元,每个片区之间用0001 隔离,H264纯数据不允许出现连续三个字节为0,在第二个后面插入03,00 00 30 00 表示00 00 00
2、 片区类型的定义为:首位 & 0X1F(取低5位)
1:表示 非I帧,具体是P还是B帧不知道
5:表示 I帧
6:表示 SEI
7:表示 SPS
8:表示 PPS
例如:
39 -> 7 -> SPS
40 -> 8 -> PPS
6 -> 6 -> SEI
37 -> 5 -> I 帧


3、 NALU = NALU Header + NALU Body(通常是RBSP(Raw Byte Sequence Payload 原始字节序列载荷)数据)
NALU Header = SPS + PPS + I Slice
NALU Body = P/B Slice

4、 1 SPS + 1PPS + 1 SEI + 1 I帧 + N(P/B)帧
SPS(Sequence Parameter Set):SPS包含了视频序列的参数和配置信息,如编码标准、图像分辨率、帧率、色彩空间等。SPS对于整个视频序列都是通用的,一般只需要发送一次,在视频流的开始或关键帧附近。解码器需要使用SPS来正确解码视频数据。 主要记录了编码视频的Profile、level、图像宽高等
PPS(Picture Parameter Set):PPS包含了图像特定的参数和配置信息,如量化矩阵、图像类型等。与SPS不同,PPS可能会针对每一帧图像都发送一次,它提供了用于该帧的特定设置,允许视频序列中不同的图像有不同的参数配置。主要记录编码数据所依赖的参数,如参考帧,图像序列计算方法等。
SEI(Supplemental Enhancement Information):SEI用于向视频码流中插入一些额外的、与视频内容相关的信息,比如图像标签、时间戳、颜色参数等。SEI数据是一些元数据信息,通常用于指示对视频进行特殊处理或详细描述视频内容。
SPS包含了视频序列的基本参数,PPS包含了图像特定的参数,而SEI用于插入额外的元数据信息。这三者一起构成了视频编码过程中必要的参数和附加信息,以便在解码和处理视频数据时能够正确地还原和显示视频内容

解析码流数据:

真正 Sps 数据sequence: 从39 100 到...129 定义为 。在解析的时候跳过第一位 39 ,因为39 是sps 定义sps类型字节 

var sequenceParameterSet = sequence.Skip(1).ToArray();

 /// <summary>
 ///比特流读取状态
 /// </summary>
 internal unsafe struct BitStreamReader
 {
     /// <summary>
     /// 比特流的起始位置
     /// </summary>
     public byte* Start;
     /// <summary>
     /// 当前的读取位置
     /// </summary>
     public byte* CurrentPosition;
     /// <summary>
     /// 比特流的结束位置
     /// </summary>
     public byte* End;
     /// <summary>
     /// 当前位置后剩余的比特数
     /// </summary>
     public int BitsRemaining;
 }
  internal  unsafe struct Sps
  {
      public Sps() { }
      public int ProfileIdc { get; set; }
      public int ConstraintSet0Flag { get; set; }
      public int ConstraintSet1Flag { get; set; }
      public int ConstraintSet2Flag { get; set; }
      public int ConstraintSet3Flag { get; set; }
      public int ConstraintSet4Flag { get; set; }
      public int ConstraintSet5Flag { get; set; }
      public int ReservedZero2Bits { get; set; }
      public int LevelIdc { get; set; }
      public int SeqParameterSetId { get; set; }
      public int ChromaFormatIdc { get; set; }
      public int ResidualColourTransformFlag { get; set; }
      public int BitDepthLumaMinus8 { get; set; }
      public int BitDepthChromaMinus8 { get; set; }
      public int QpprimeYZeroTransformBypassFlag { get; set; }
      public int SeqScalingMatrixPresentFlag { get; set; }
      public int[] SeqScalingListPresentFlag{ get; set; } = new int[8];
      public int[][] ScalingList4X4 { get; set; } = new int[6][];
      public int[] UseDefaultScalingMatrix4X4Flag { get; set; } = new int[6];
      public int[][] ScalingList8X8 { get; set; } = new int[6][];
      public int[] UseDefaultScalingMatrix8x8Flag{ get; set; } = new int[3];
      public int Log2MaxFrameNumMinus4 { get; set; }
      public int PicOrderCntType { get; set; }
      public int Log2MaxPicOrderCntLsbMinus4 { get; set; }
      public int DeltaPicOrderAlwaysZeroFlag { get; set; }
      public int OffsetForNonRefPic { get; set; }
      public int OffsetForTopToBottomField { get; set; }
      public int NumRefFramesInPicOrderCntCycle { get; set; }
      public int[] OffsetForRefFrame { get; set; } = new int[256];
      public int NumRefFrames { get; set; }
      public int GapsInFrameNumValueAllowedFlag { get; set; }
      public int PicWidthInMbsMinus1 { get; set; }
      public int PicHeightInMapUnitsMinus1 { get; set; }
      public int FrameMbsOnlyFlag { get; set; }
      public int MbAdaptiveFrameFieldFlag { get; set; }
      public int Direct8X8InferenceFlag { get; set; }
      public int FrameCroppingFlag { get; set; }
      public int FrameCropLeftOffset { get; set; }
      public int FrameCropRightOffset { get; set; }
      public int FrameCropTopOffset { get; set; }
      public int FrameCropBottomOffset { get; set; }
      public int VuiParametersPresentFlag { get; set; }
      public Vui Vui { get; set; } = new Vui();
      public Hrd Hrd { get; set; } = new Hrd();
  }

  internal struct Vui
  {
      public int AspectRatioInfoPresentFlag { get; set; }
      public int AspectRatioIdc { get; set; }
      public int SarWidth { get; set; }
      public int SarHeight { get; set; }
      public int OverscanInfoPresentFlag { get; set; }
      public int OverscanAppropriateFlag { get; set; }
      public int VideoSignalTypePresentFlag { get; set; }
      public int VideoFormat { get; set; }
      public int VideoFullRangeFlag { get; set; }
      public int ColourDescriptionPresentFlag { get; set; }
      public int ColourPrimaries { get; set; }
      public int TransferCharacteristics { get; set; }
      public int MatrixCoefficients { get; set; }
      public int ChromaLocInfoPresentFlag { get; set; }
      public int ChromaSampleLocTypeTopField { get; set; }
      public int ChromaSampleLocTypeBottomField { get; set; }
      public int TimingInfoPresentFlag { get; set; }
      public int NumUnitsInTick { get; set; }
      public int TimeScale { get; set; }
      public int FixedFrameRateFlag { get; set; }
      public int NalHrdParametersPresentFlag { get; set; }
      public int VclHrdParametersPresentFlag { get; set; }
      public int LowDelayHrdFlag { get; set; }
      public int PicStructPresentFlag { get; set; }
      public int BitStreamRestrictionFlag { get; set; }
      public int MotionVectorsOverPicBoundariesFlag { get; set; }
      public int MaxBytesPerPicDenom { get; set; }
      public int MaxBitsPerMbDenom { get; set; }
      public int Log2MaxMvLengthHorizontal { get; set; }
      public int Log2MaxMvLengthVertical { get; set; }
      public int NumReorderFrames { get; set; }
      public int MaxDecFrameBuffering { get; set; }
  }

  internal struct Hrd
  {
      public Hrd() { }
      public int CpbCntMinus1 { get; set; }
      public int BitRateScale { get; set; }
      public int CpbSizeScale { get; set; }
      public int[] BitRateValueMinus1 { get; set; } = new int[32];
      public int[] CpbSizeValueMinus1 { get; set; } = new int[32];
      public int[] CbrFlag { get; set; } = new int[32];
      public int InitialCpbRemovalDelayLengthMinus1 { get; set; }
      public int CpbRemovalDelayLengthMinus1 { get; set; }
      public int DpbOutputDelayLengthMinus1 { get; set; }
      public int TimeOffsetLength { get; set; }
  }
  internal unsafe class BitStreamAnalyze
  {
      /// <summary>
      /// 初始化比特流
      /// </summary>
      /// <param name="buf">比特流指针</param>
      /// <param name="size">字节长度</param>
      public static BitStreamReader BitStreamInit(byte* buf, int size)
      {
          BitStreamReader b = new BitStreamReader
          {
              Start = buf,
              CurrentPosition = buf,
              End = buf + size,
              BitsRemaining = 8
          };
          return b;
      }

      /// <summary>
      /// 读起Sps
      /// </summary>
      public static Sps ReadSeqParameterSetRbsp(BitStreamReader* b)
      {
          int i;
          var sps = new Sps
          {
              ChromaFormatIdc = 1,
              ProfileIdc = BsReadU8(b),
              ConstraintSet0Flag = BsReadU1(b),
              ConstraintSet1Flag = BsReadU1(b),
              ConstraintSet2Flag = BsReadU1(b),
              ConstraintSet3Flag = BsReadU1(b),
              ConstraintSet4Flag = BsReadU1(b),
              ConstraintSet5Flag = BsReadU1(b),
              ReservedZero2Bits = BsReadU(b, 2),
              LevelIdc = BsReadU8(b),
              SeqParameterSetId = BsReadUe(b)
          };
          if (sps.ProfileIdc == 100 || sps.ProfileIdc == 110 || sps.ProfileIdc == 122 || sps.ProfileIdc == 144)
          {
              sps.ChromaFormatIdc = BsReadUe(b);
              if (sps.ChromaFormatIdc == 3)
              {
                  sps.ResidualColourTransformFlag = BsReadU1(b);
              }
              sps.BitDepthLumaMinus8 = BsReadUe(b);
              sps.BitDepthChromaMinus8 = BsReadUe(b);
              sps.QpprimeYZeroTransformBypassFlag = BsReadU1(b);
              sps.SeqScalingMatrixPresentFlag = BsReadU1(b);
              if (sps.SeqScalingMatrixPresentFlag !=0 )
              {
                  for (i = 0; i < 8; i++)
                  {
                      sps.SeqScalingListPresentFlag[i] = BsReadU1(b);
                      if (sps.SeqScalingListPresentFlag[i] == 0) continue;
                      if (i < 6)
                          ReadScalingList(b, sps.ScalingList4X4[i], 16, sps.UseDefaultScalingMatrix4X4Flag[i]);
                      else
                          ReadScalingList(b, sps.ScalingList8X8[i - 6], 64, sps.UseDefaultScalingMatrix8x8Flag[i - 6]);
                  }
              }
          }
          sps.Log2MaxFrameNumMinus4 = BsReadUe(b);
          sps.PicOrderCntType = BsReadUe(b);
          if (sps.PicOrderCntType == 0)
          {
              sps.Log2MaxPicOrderCntLsbMinus4 = BsReadUe(b);
          }
          else if (sps.PicOrderCntType == 1)
          {
              sps.DeltaPicOrderAlwaysZeroFlag = BsReadU1(b);
              sps.OffsetForNonRefPic = BsReadSe(b);
              sps.OffsetForTopToBottomField = BsReadSe(b);
              sps.NumRefFramesInPicOrderCntCycle = BsReadUe(b);
              for (i = 0; i < sps.NumRefFramesInPicOrderCntCycle; i++)
              {
                  sps.OffsetForRefFrame[i] = BsReadSe(b);
              }
          }
          sps.NumRefFrames = BsReadUe(b);
          sps.GapsInFrameNumValueAllowedFlag = BsReadU1(b);
          sps.PicWidthInMbsMinus1 = BsReadUe(b);
          sps.PicHeightInMapUnitsMinus1 = BsReadUe(b);
          sps.FrameMbsOnlyFlag = BsReadU1(b);
          if (sps.FrameMbsOnlyFlag == 0)
          {
              sps.MbAdaptiveFrameFieldFlag = BsReadU1(b);
          }
          sps.Direct8X8InferenceFlag = BsReadU1(b);
          sps.FrameCroppingFlag = BsReadU1(b);
          if (sps.FrameCroppingFlag != 0)
          {
              sps.FrameCropLeftOffset = BsReadUe(b);
              sps.FrameCropRightOffset = BsReadUe(b);
              sps.FrameCropTopOffset = BsReadUe(b);
              sps.FrameCropBottomOffset = BsReadUe(b);
          }
          sps.VuiParametersPresentFlag = BsReadU1(b);
          if (sps.VuiParametersPresentFlag != 0)
          {
              ReadVuiParameters(sps,b);
          }
          ReadRbspTrailingBits(sps,b);
          return sps;
      }

      #region 私有方法
      private static  int BsReadU8(BitStreamReader* b)
      {
          if (b->BitsRemaining == 8 && !BsEof(b)) 
          {
              int r = b->CurrentPosition[0];
              b->CurrentPosition++;
              return r;
          }
          return BsReadU(b, 8);
      }

      private static bool BsEof(BitStreamReader* b) => b->CurrentPosition >= b->End;

      private static  int BsReadU(BitStreamReader* b, int n)
      {
          int r = 0;
          int i;
          for (i = 0; i < n; i++)
          {
              r |= (BsReadU1(b) << (n - i - 1));
          }
          return r;
      }

      private static  int  BsReadU1(BitStreamReader* b)
      {
          var r = 0;
          b->BitsRemaining--;
          if (!BsEof(b))
          {
              r = ((*(b->CurrentPosition)) >> b->BitsRemaining) & 0x01;
          }

          if (b->BitsRemaining == 0)
          {
              b->CurrentPosition++;
              b->BitsRemaining = 8;
          }
          return r;
      }

      private static  int BsReadUe(BitStreamReader* b)
      {
          int r = 0;
          int i = 0;
          while ((BsReadU1(b) == 0) && (i < 32) && (!BsEof(b)))
          {
              i++;
          }
          r = BsReadU(b, i);
          r += (1 << i) - 1;
          return r;
      }

      private static void ReadScalingList(BitStreamReader* b, int[] scalingList, int sizeOfScalingList,int useDefaultScalingMatrixFlag)
      {
          if (scalingList == null) return;
          int j;
          int lastScale = 8;
          int nextScale = 8;
          for (j = 0; j < sizeOfScalingList; j++)
          {
              if (nextScale != 0)
              {
                  int deltaScale = BsReadSe(b);
                  nextScale = (lastScale + deltaScale + 256) % 256;
                  var b1 = j == 0 && nextScale == 0;
                  useDefaultScalingMatrixFlag = b1 ? 1 : 0;
              }
              scalingList[j] = (nextScale == 0) ? lastScale : nextScale;
              lastScale = scalingList[j];
          }
      }

      private static  int BsReadSe(BitStreamReader* b)
      {
          int  r = BsReadUe(b);
          if ((r & 0x01 ) !=0)
              r = (r + 1) / 2;
          else
              r = -(r / 2);
          return r;
      }

      private static void ReadVuiParameters(Sps sps, BitStreamReader* b)
      {
          var spsVui = sps.Vui;
          spsVui.AspectRatioInfoPresentFlag = BsReadU1(b);
          if (spsVui.AspectRatioInfoPresentFlag !=0)
          {
              spsVui.AspectRatioIdc = BsReadU8(b);
              int sarExtended = 255; 
              if (spsVui.AspectRatioIdc == sarExtended)
              {
                  spsVui.SarWidth = BsReadU(b, 16);
                  spsVui.SarHeight = BsReadU(b, 16);
              }
          }
          spsVui.OverscanInfoPresentFlag = BsReadU1(b);
          if (spsVui.OverscanInfoPresentFlag !=0)
          {
              spsVui.OverscanAppropriateFlag = BsReadU1(b);
          }
          spsVui.VideoSignalTypePresentFlag = BsReadU1(b);
          if (spsVui.VideoSignalTypePresentFlag != 0)
          {
              spsVui.VideoFormat = BsReadU(b, 3);
              spsVui.VideoFullRangeFlag = BsReadU1(b);
              spsVui.ColourDescriptionPresentFlag = BsReadU1(b);
              if (spsVui.ColourDescriptionPresentFlag != 0)
              {
                  spsVui.ColourPrimaries = BsReadU8(b);
                  spsVui.TransferCharacteristics = BsReadU8(b);
                  spsVui.MatrixCoefficients = BsReadU8(b);
              }
          }
          spsVui.ChromaLocInfoPresentFlag = BsReadU1(b);
          if (spsVui.ChromaLocInfoPresentFlag !=0)
          {
              spsVui.ChromaSampleLocTypeTopField = BsReadUe(b);
              spsVui.ChromaSampleLocTypeBottomField = BsReadUe(b);
          }
          spsVui.TimingInfoPresentFlag = BsReadU1(b);
          if (spsVui.TimingInfoPresentFlag != 0)
          {
              spsVui.NumUnitsInTick = BsReadU(b, 32);
              spsVui.TimeScale = BsReadU(b, 32);
              spsVui.FixedFrameRateFlag = BsReadU1(b);
          }
          spsVui.NalHrdParametersPresentFlag = BsReadU1(b);
          if (spsVui.NalHrdParametersPresentFlag != 0)
          {
              ReadHrdParameters(sps, b);
          }
          spsVui.VclHrdParametersPresentFlag = BsReadU1(b);
          if (spsVui.VclHrdParametersPresentFlag != 0)
          {
              ReadHrdParameters(sps, b);
          }
          if (spsVui.NalHrdParametersPresentFlag  != 0 || spsVui.VclHrdParametersPresentFlag != 0)
          {
              spsVui.LowDelayHrdFlag = BsReadU1(b);
          }
          spsVui.PicStructPresentFlag = BsReadU1(b);
          spsVui.BitStreamRestrictionFlag = BsReadU1(b);
          if (spsVui.BitStreamRestrictionFlag != 0)
          {
              spsVui.MotionVectorsOverPicBoundariesFlag = BsReadU1(b);
              spsVui.MaxBytesPerPicDenom = BsReadUe(b);
              spsVui.MaxBitsPerMbDenom = BsReadUe(b);
              spsVui.Log2MaxMvLengthHorizontal = BsReadUe(b);
              spsVui.Log2MaxMvLengthVertical = BsReadUe(b);
              spsVui.NumReorderFrames = BsReadUe(b);
              spsVui.MaxDecFrameBuffering = BsReadUe(b);
          }
      }

      private static void ReadHrdParameters(Sps sps, BitStreamReader* b)
      {
          var spsHrd = sps.Hrd;
          spsHrd.CpbCntMinus1 = BsReadUe(b);
          spsHrd.BitRateScale = BsReadU(b, 4);
          spsHrd.CpbSizeScale = BsReadU(b, 4);
          for (var schedSelIdx = 0; schedSelIdx <= spsHrd.CpbCntMinus1; schedSelIdx++)
          {
              spsHrd.BitRateValueMinus1[schedSelIdx] = BsReadUe(b);
              spsHrd.CpbSizeValueMinus1[schedSelIdx] = BsReadUe(b);
              spsHrd.CbrFlag[schedSelIdx] = BsReadU1(b);
          }
          spsHrd.InitialCpbRemovalDelayLengthMinus1 = BsReadU(b, 5);
          spsHrd.CpbRemovalDelayLengthMinus1 = BsReadU(b, 5);
          spsHrd.DpbOutputDelayLengthMinus1 = BsReadU(b, 5);
          spsHrd.TimeOffsetLength = BsReadU(b, 5);
      }

      private static  void ReadRbspTrailingBits(Sps sps, BitStreamReader* b)
      {
          int rbspStopOneBit = BsReadU1(b); // equal to 1
          while (!BsByteAligned(b))
          {
              int rbspAlignmentZeroBit = BsReadU1(b); // equal to 0
          }
      }

      private static bool BsByteAligned(BitStreamReader* b) => b->BitsRemaining == 8;
      #endregion
  }

调用解析代码:

 unsafe
 {
     var sequenceParameterSet = sequence.Skip(1).ToArray();
     fixed (byte* p = sequenceParameterSet)
     {
         var bsT = BitStreamAnalyze.BitStreamInit(p, h264.LengthOfSps-1);
         var sps = BitStreamAnalyze.ReadSeqParameterSetRbsp(&bsT);
         widthSource = (sps.PicWidthInMbsMinus1 + 1) * 16;
         heightSource = (sps.PicHeightInMapUnitsMinus1 + 1) * 16;
     }
 }

 

参考资料:
1 https://blog.csdn.net/weixin_53762042/article/details/131267410
2 https://blog.csdn.net/oqqHuTu12345678/article/details/128221209
3 http://blog.csdn.net/leixiaohua1020

h264 码流解析项目
https://gitee.com/xgdu/H264_Analysis.git

posted on 2024-03-30 21:13  TanZhiWei  阅读(85)  评论(0编辑  收藏  举报