internal VideoParams264(TrackEntry videoTrack) { // File.WriteAllBytes( @"C:\Temp\2remove\mkv\videoPrivateData.bin", videoTrack.codecPrivate ); ReadOnlySpan <byte> codecPrivate = videoTrack.codecPrivate.AsSpan(); int cbHeader = Marshal.SizeOf <NativeStruct>(); NativeStruct ns = codecPrivate.Slice(0, cbHeader).cast <NativeStruct>()[0]; profile = ns.profileCode; profileCompatibility = ns.profileCompatibility; levelCode = ns.levelCode; int offset = cbHeader; sps = ContainerUtils.copyBlobs(ns.numOfSequenceParameterSets, codecPrivate, ref offset); // File.WriteAllBytes( @"C:\Temp\2remove\mkv\sps.bin", sps[ 0 ] ); int ppsCount = codecPrivate[offset++]; pps = ContainerUtils.copyBlobs(ppsCount, codecPrivate, ref offset); ReadOnlySpan <byte> spsBlob = sps[0].AsSpan(); if (MiscUtils.getNaluType(spsBlob[0]) != eNaluType.SPS) { throw new ApplicationException("The SPS is invalid, wrong NALU type"); } spsBlob = spsBlob.Slice(1); BitReader spsReader = new BitReader(spsBlob); parsedSps = new SequenceParameterSet(ref spsReader); chromaFormat = parsedSps.chromaFormat; bitDepthLuma = parsedSps.bitDepthLuma; bitDepthChroma = parsedSps.bitDepthChroma; m_decodedSize = new sDecodedVideoSize(parsedSps.decodedSize, parsedSps.cropRectangle, chromaFormat); }
public AVC1SampleEntry(Mp4Reader reader, int bytesLeft) : base(reader, ref bytesLeft) { var avcc = reader.readStructure <Structures.AVCDecoderConfigurationRecord>(); if (avcc.boxType != eAVC1BoxType.avcC) { throw new NotImplementedException(); } bytesLeft -= decoderConfigSizeof; profile = avcc.profileCode; profileCompatibility = avcc.profileCompatibility; levelCode = avcc.levelCode; naluLengthSize = checked ((byte)(avcc.lengthSizeMinusOne + 1)); Span <byte> remainingStuff = stackalloc byte[bytesLeft]; reader.read(remainingStuff); int readOffset = 0; sps = ContainerUtils.copyBlobs(avcc.numOfSequenceParameterSets, remainingStuff, ref readOffset); if (null == sps) { throw new ArgumentException("The file doesn't have an SPS"); } // SpsData spsData = new SpsData( sps[ 0 ] ); // File.WriteAllBytes( @"C:\Temp\2remove\h264\sps.bin", sps[ 0 ] ); int ppsCount = remainingStuff[readOffset++]; pps = ContainerUtils.copyBlobs(ppsCount, remainingStuff, ref readOffset); if (null == sps || null == pps) { throw new NotImplementedException("Vrmac Video only supports mp4 files with out-of-band SPS and PPS blobs, in the `avcC` atom of the file."); } if (sps.Length > 1 || pps.Length > 1) { throw new NotImplementedException("Vrmac Video only supports mp4 files with a single out-of-band SPS and PPS for the complete video."); // The video payload may include other PPS-es, these are fine. } if (readOffset >= remainingStuff.Length) { return; } remainingStuff = remainingStuff.Slice(readOffset); if (readOffset + decoderConfigSizeof < avcc.length) { // The spec I have says the files with profile IDs 100, 110, 122, 144 have this. // The mp4 file I use to test this code has 100, but misses this data. chromaFormat = (eChromaFormat)(remainingStuff[0] & 3); bitDepthLuma = (byte)((remainingStuff[1] & 7) + 8); bitDepthChroma = (byte)((remainingStuff[2] & 7) + 8); int numPpsEx = remainingStuff[3]; readOffset = 4; // Resetting because sliced the span ppsExt = ContainerUtils.copyBlobs(numPpsEx, remainingStuff, ref readOffset); remainingStuff = remainingStuff.Slice(readOffset); } else { // https://en.wikipedia.org/wiki/Advanced_Video_Coding#Feature_support_in_particular_profiles chromaFormat = eChromaFormat.c420; bitDepthLuma = 8; bitDepthChroma = 8; } while (!remainingStuff.IsEmpty) { int size = BitConverter.ToInt32(remainingStuff).endian(); eAVC1BoxType code = (eAVC1BoxType)BitConverter.ToUInt32(remainingStuff.Slice(4)); switch (code) { case eAVC1BoxType.btrt: bitRate = new MPEG4BitRateBox(remainingStuff); m_maxBytesInFrame = bitRate.decodingBufferSize; break; } remainingStuff = remainingStuff.Slice(size); } }
internal SequenceParameterSet(ref BitReader reader) { // Ported from C++ in Chromium, \chromium-master\media\video\h264_parser.cc profile = (eAvcProfile)reader.readInt(8); reader.skipBits(8); // constraint_set0_flag x6 + 2 reserved bits levelIndex = (byte)reader.readInt(8); parameterSetId = checked ((byte)reader.unsignedGolomb()); // seq_parameter_set_id if (parameterSetId >= 32) { throw new ArgumentException(); } if (extraBs.Contains((byte)profile)) { uint chromaFormatIndex = reader.unsignedGolomb(); // uint chroma_format_idc if (chromaFormatIndex >= 4) { throw new ArgumentException(); } chromaFormat = (eChromaFormat)chromaFormatIndex; if (3 == chromaFormatIndex) { separateColourPlaneFlag = reader.readBit(); } else { separateColourPlaneFlag = false; } uint bit_depth_luma_minus8 = reader.unsignedGolomb(); if (bit_depth_luma_minus8 >= 7) { throw new ArgumentException(); } bitDepthLuma = (byte)(bit_depth_luma_minus8 + 8); uint bit_depth_chroma_minus8 = reader.unsignedGolomb(); if (bit_depth_chroma_minus8 >= 7) { throw new ArgumentException(); } bitDepthChroma = (byte)(bit_depth_chroma_minus8 + 8); bool qpprime_y_zero_transform_bypass_flag = reader.readBit(); bool seq_scaling_matrix_present_flag = reader.readBit(); if (seq_scaling_matrix_present_flag) { throw new NotImplementedException(); } } else { chromaFormat = eChromaFormat.c420; separateColourPlaneFlag = false; bitDepthLuma = bitDepthChroma = 8; } uint log2_max_frame_num_minus4 = reader.unsignedGolomb(); frameIndexBits = checked ((byte)(log2_max_frame_num_minus4 + 4)); uint pic_order_cnt_type = reader.unsignedGolomb(); switch (pic_order_cnt_type) { case 0: reader.skipGolomb(); // log2_max_pic_order_cnt_lsb_minus4 break; case 1: reader.skipBits(1); // delta_pic_order_always_zero_flag reader.skipGolomb(); // int offset_for_non_ref_pic reader.skipGolomb(); // int offset_for_top_to_bottom_field uint num_ref_frames_in_pic_order_cnt_cycle = reader.unsignedGolomb(); if (num_ref_frames_in_pic_order_cnt_cycle >= 0xFF) { throw new ArgumentException(); } for (int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { reader.skipGolomb(); // int offset_for_ref_frame } break; } reader.skipGolomb(); // uint max_num_ref_frames reader.skipBits(1); // bool gaps_in_frame_num_value_allowed_flag // Decoded size int pic_width_in_mbs_minus1 = (int)reader.unsignedGolomb(); int pic_height_in_map_units_minus1 = (int)reader.unsignedGolomb(); bool frame_mbs_only_flag = reader.readBit(); decodedSize = default; decodedSize.cx = (pic_width_in_mbs_minus1 + 1) * 16; if (false == frame_mbs_only_flag) { reader.skipBits(1); // bool mb_adaptive_frame_field_flag } int map_unit = frame_mbs_only_flag ? 16 : 32; decodedSize.cy = map_unit * (pic_height_in_map_units_minus1 + 1); reader.skipBits(1); // direct_8x8_inference_flag // Frame cropping bool frame_cropping_flag = reader.readBit(); if (frame_cropping_flag) { int frame_crop_left_offset = (int)reader.unsignedGolomb(); int frame_crop_right_offset = (int)reader.unsignedGolomb(); int frame_crop_top_offset = (int)reader.unsignedGolomb(); int frame_crop_bottom_offset = (int)reader.unsignedGolomb(); CSize cropUnit; switch (chromaFormat) { case eChromaFormat.c420: cropUnit = new CSize(2, 2); break; case eChromaFormat.c422: cropUnit = new CSize(2, 1); break; case eChromaFormat.c444: cropUnit = new CSize(1, 1); break; default: throw new ArgumentException(); } cropRectangle = default; cropRectangle.left = frame_crop_left_offset * cropUnit.cx; cropRectangle.top = frame_crop_top_offset * cropUnit.cy; cropRectangle.right = decodedSize.cx - frame_crop_right_offset * cropUnit.cx; cropRectangle.bottom = decodedSize.cy - frame_crop_bottom_offset * cropUnit.cy; } else { cropRectangle = new CRect(default, decodedSize);