内容
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