internal DecoderConfiguration(ref Reader reader) { var dcd = reader.readStructure <DecoderConfigDescriptor>(); objectType = (eObjectType)dcd.objectTypeIndication; streamType = (eStreamType)dcd.streamType; upStream = dcd.upStream; decodingBufferSize = dcd.bufferSizeDB; maximumBitrate = dcd.maxBitrate; averageBitrate = dcd.avgBitrate; // The reader gotta be positioned at the start of AudioSpecificConfig tag. // See ISO/IEC 14496-1 annex "E" for more info on tags and sizes. eDescriptorTag tag = (eDescriptorTag)reader.readByte(); if (tag != eDescriptorTag.DecoderSpecificInfo) { throw new ArgumentException($"Expected eDescriptorTag.DecoderSpecificInfo, got { tag } instead"); } int cb = reader.readSize(); if (reader.bytesLeft < cb) { throw new EndOfStreamException($"DecoderSpecificInfo has size { cb } in the header, yet the stream only has { reader.bytesLeft } unread bytes left"); } audioSpecificConfig = new byte[cb]; reader.readBytes(audioSpecificConfig.AsSpan()); }
public MP4AudioSampleEntry(Mp4Reader mp4, int bytesLeft) : base(mp4, ref bytesLeft) { Span <byte> bytes = stackalloc byte[bytesLeft]; mp4.read(bytes); int atomSize = BitConverter.ToInt32(bytes).endian(); uint atomTag = BitConverter.ToUInt32(bytes.Slice(4)); switch (atomTag) { case (uint)eAudioBoxType.esds: break; default: throw new NotImplementedException(); } // Skip header, version and flags bytes = bytes.Slice(12); Reader streamReader = new Reader(bytes); if (streamReader.EOF) { throw new ArgumentException($"The `esds` atom is empty"); } // Read tag and size eDescriptorTag tag = streamReader.readTag(); if (eDescriptorTag.ElementaryStream != tag) { throw new ArgumentException($"The `esds` atom is expected to contain an elementary stream descriptor, got { tag } instead"); } int size = streamReader.readSize(); // Create a reader for the content Reader esdReader = streamReader.readSubStream(size); // Read ElementaryStreamDescriptor ElementaryStreamDescriptor esd = esdReader.readStructure <ElementaryStreamDescriptor>(); id = esd.id; flags = esd.flags; priority = esd.priority; if (esd.flags.HasFlag(eDescriptorFlags.DependentStream)) { dependsOn = esdReader.readStructure <ushort>().endian(); } if (esd.flags.HasFlag(eDescriptorFlags.URL)) { byte urlLength = esdReader.readByte(); Span <byte> urlBuffer = stackalloc byte[urlLength]; esdReader.readBytes(urlBuffer); unsafe { fixed(byte *ptr = urlBuffer) url = StringMarshal.copy(ptr, urlLength); } } if (esd.flags.HasFlag(eDescriptorFlags.OCRstream)) { esId = esdReader.readStructure <ushort>().endian(); } while (!esdReader.EOF) { tag = esdReader.readTag(); size = esdReader.readSize(); Reader ss = esdReader.readSubStream(size); switch (tag) { case eDescriptorTag.DecoderConfiguration: decoderConfig = new DecoderConfiguration(ref ss); break; case eDescriptorTag.SyncLayerConfiguration: syncLayerConfig = new SyncLayerConfiguration(ref ss); break; case eDescriptorTag.ProfileLevelIndicationIndex: profileLevelIndicationindex = ss.readByte(); break; case eDescriptorTag.IPIdentificationPointer: case eDescriptorTag.IPMPPointer: case eDescriptorTag.Language: case eDescriptorTag.QoS: case eDescriptorTag.Registration: // Because of the way we implemented readSubStream, this break will skip them gracefully. // TODO: support at least language, likely to be seen in the wild break; default: throw new NotSupportedException(); } } // TODO: the esdReader has EOF, but the outer one, streamReader, might have not. It might contain moar stuff. // You can test for `if( !streamReader.EOF )` here, and parse even moar garbage carefully designed by these ISO/IEC committees and documented in many thousands of pages of these PDFs they sell. }