/// <summary> /// Parses a <see cref="BlockType.StreamInfo"/> <see cref="Media.Container.Node"/> /// </summary> /// <param name=nameof(node)>The <see cref="Media.Container.Node"/> to parse</param> internal protected void ParseStreamInfo(Container.Node node = null) { node = node ?? Root; VerifyBlockType(node, BlockType.StreamInfo); //METADATA_BLOCK_STREAMINFO using (System.IO.BinaryReader bi = new System.IO.BinaryReader(node.DataStream)) { using (BitReader br = new BitReader(bi.BaseStream, Binary.BitOrder.MostSignificant, IdentifierSize * 2, true)) { m_MinBlockSize = Common.Binary.ReadU16(node.Data, 0, BitConverter.IsLittleEndian); //br.Read16(); m_MaxBlockSize = Common.Binary.ReadU16(node.Data, 2, BitConverter.IsLittleEndian); m_MinFrameSize = (int)Common.Binary.ReadU24(node.Data, 4, BitConverter.IsLittleEndian); m_MaxFrameSize = (int)Common.Binary.ReadU24(node.Data, 7, BitConverter.IsLittleEndian); m_SampleRate = (int)Common.Binary.ReadUInt64MSB(node.Data, 10, 20, 0); m_Channels = 1 + (int)Common.Binary.ReadUInt64MSB(node.Data, 12, 3, 4); m_BitsPerSample = 1 + (int)Common.Binary.ReadUInt64MSB(node.Data, 12, 5, 7); m_TotalSamples = Common.Binary.ReadUInt64MSB(node.Data, 13, 36, 4); m_Md5 = new string(Encoding.ASCII.GetChars(node.Data, 8, 16)); } } }
public override IEnumerator <Container.Node> GetEnumerator() { while (Remaining >= MinimumSize) { Container.Node next = ReadNext(); if (next == null) { yield break; } yield return(next); //Determine if the node holds data required. switch (next.Identifier[3]) { case Mpeg.StreamTypes.ProgramStreamMap: { ParseProgramStreamMap(next); break; } } Skip(next.DataSize); } }
public override string ToTextualConvention(Container.Node node) { if (node.Master.Equals(this)) { return(ProgramStreamReader.ToTextualConvention(node.Identifier)); } return(base.ToTextualConvention(node)); }
public override string ToTextualConvention(Container.Node node) { if (node.Master.Equals(this)) { return(RiffReader.ToFourCharacterCode(node.Identifier)); } return(base.ToTextualConvention(node)); }
/// <summary> /// <see cref="Mpeg.StartCodes"/> for names. /// </summary> /// <param name="name"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <returns></returns> public Container.Node FindNode(byte name, long offset, long count) { long position = Position; Container.Node result = FindNodes(offset, count, name).FirstOrDefault(); Position = position; return(result); }
protected virtual void ParseProgramStreamMap(Container.Node node) { if (node.Identifier[3] != Mpeg.StreamTypes.ProgramStreamMap) { return; } int dataLength = node.Data.Length; if (dataLength < 4) { return; } byte mapId = node.Data[0]; byte reserved = node.Data[1]; ushort infoLength = Common.Binary.ReadU16(node.Data, 2, Common.Binary.IsLittleEndian); ushort mapLength = Common.Binary.ReadU16(node.Data, 4 + infoLength, Common.Binary.IsLittleEndian); int offset = 4 + infoLength + 2; int crcOffset = dataLength - 4; //While data remains in the map while (offset < crcOffset) { //Find out the type of item it is byte esType = node.Data[offset++]; //Find out the elementary stream id byte esId = node.Data[offset++]; //find out how long the info is ushort esInfoLength = Common.Binary.ReadU16(node.Data, offset, Common.Binary.IsLittleEndian); //Get a array containing the info Common.MemorySegment esData = esInfoLength == 0 ? Media.Common.MemorySegment.Empty : new Common.MemorySegment(node.Data, offset, esInfoLength); //node.Data.Skip(offset).Take(esInfoLength).ToArray(); //Create the entry var entry = new Tuple <byte, Common.MemorySegment>(esType, esData); //should keep entries until crc is updated if present and then insert. //Add it to the ProgramStreams m_ProgramStreams.AddOrUpdate(esId, entry, (id, old) => entry); //Move the offset offset += 2 + esInfoLength; } //Map crc }
public override IEnumerator <Container.Node> GetEnumerator() { while (Remaining >= PacketizedElementaryStreamReader.IdentifierSize) { Container.Node next = ReadNext(); if (next == null) { yield break; } yield return(next); //Determine if the node holds data required. switch (next.Identifier[3]) { case Mpeg.StreamTypes.PackHeader: { //Decoder the SCR double scr; ParsePackHeader(next, out scr); //Set the SystemClockRate m_SystemClockRate = scr; break; } case Mpeg.StreamTypes.SystemHeader: { //Parse the system Header ParseSystemsHeader(next); break; } case Mpeg.StreamTypes.ProgramStreamMap: { ParseProgramStreamMap(next); break; } } Skip(next.DataSize); } }
internal protected void ParseData(Container.Node node) { /* * The "data" subchunk contains the size of the data and the actual sound: * * 36 4 Subchunk2ID Contains the letters "data" * (0x64617461 big-endian form). * 40 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 * This is the number of bytes in the data. * You can also think of this as the size * of the read of the subchunk following this * number. * 44 * Data The actual sound data. */ //using (var chunk = ReadChunk(FourCharacterCode.data, Root.Offset)) //{ //} }
internal protected virtual void ParseDescriptionTable(Container.Node node) { int offset = ReadPointerField(node); //Get the table id TransportStreamUnit.TableIdentifier tableId = (TransportStreamUnit.TableIdentifier)node.Data[offset++]; if (tableId != TransportStreamUnit.TableIdentifier.ProgramAssociation) { return; } TransportStreamUnit.PacketIdentifier pcrPid = (TransportStreamUnit.PacketIdentifier)(Common.Binary.ReadU16(node.Data, offset, Common.Binary.IsLittleEndian) & TransportStreamUnit.PacketIdentifierMask); int infoLength = Common.Binary.ReadU16(node.Data, ref offset, Common.Binary.IsLittleEndian) & 0x0FFF; //Determine where to end (don't count the crc) int end = (infoLength + 3) - 4; while (offset < end) { byte esType = node.Data[offset++]; //Could store two bytes and have methods to extract with masks. TransportStreamUnit.PacketIdentifier esPid = (TransportStreamUnit.PacketIdentifier)(Common.Binary.ReadU16(node.Data, offset, Common.Binary.IsLittleEndian) & TransportStreamUnit.PacketIdentifierMask); ushort descLength = (ushort)(Common.Binary.ReadU16(node.Data, offset, Common.Binary.IsLittleEndian) & 0x0FFF); var entry = new Tuple <TransportStreamUnit.PacketIdentifier, ushort>(esPid, descLength); m_ProgramDescriptions.AddOrUpdate(esType, entry, (a, old) => entry); offset += 2; } //CRC }
protected virtual void ParseSystemsHeader(Container.Node node) { if (node.Identifier[3] != Mpeg.StreamTypes.SystemHeader) { return; } //These values or the entire node should probably also be stored. //22-bit unsigned integer. Must be greater than or equal to (>=) the maximum value of the program_mux_rate coded in any pack of the program stream. //For DVD-Video this value should be 25200 decimal. uint rateBound = Common.Binary.ReadU24(node.Data, 0, Common.Binary.IsLittleEndian); rateBound &= 0x80000001; //2147483649 //Make a 'checked' read byte temp = node.Data[3]; //6-bit unsigned integer, ranging from 0 to 32 inclusive. Must be greater than or equal to (>=) the maximum number of audio streams in the program stream. //ISO 13818-1 states this should be the MPEG audio streams, but DVD-Video counts all audio streams. //For DVD-Video this should be the number of audio streams of any type, from 0 to 8 inclusive. byte audioBound = (byte)(temp & 0xFC); //252 //1-bit boolean. If TRUE (1) the program stream is multiplexed at a fixed bitrate. //For DVD-Video this flag should be FALSE (0). bool fixedBitrate = (temp & 2) > 0; //1-bit boolean. If TRUE (1) the program stream meets the requirements of a "Constrained System parameter Program Stream". //For DVD-Video this flag must be FALSE (0). bool csps = (temp & 1) > 0; //1-bit boolean. TRUE (1) indicates that there is a specified constant rational relationship between the audio sampling rate and the system_clock_frequency (27MHz). //For DVD-Video this flag should be TRUE (1). //Make a 'checked' read temp = node.Data[4]; bool system_audio_lock = (temp & 128) > 0; //1-bit boolean. TRUE (1) indicates that there is a specified constant rational relationship between the video picture rate and the system_clock_frequency (27MHz). //For DVD-Video this flag should be TRUE (1). //The PAL/SECAM ratio is 1080000 system clocks (3600 90KHz clocks) per displayed picture. //The NTSC ratio is 900900 system clocks (3003 90KHz clocks) per displayed picture. This rate differs slightly from the nominal rate for NTSC, but is fixed, and consistent with ITU-601. bool system_video_lock = (temp & 64) > 0; //5-bit unsigned integer, ranging from 0 to 16 inclusive. Must be greater than or equal to (>=) the maximum number of video streams in the program stream. //For DVD-Video this value will always be 1. byte video_boud = (byte)(temp & 0xE0); //1-bit boolean. If CSPS_flag is TRUE (1) this specifies which restraint is applicable to the packet rate, otherwise the flag has no meaning. //For DVD-Video this flag must be FALSE (0). //Make a 'checked' read temp = node.Data[5]; bool packRateRestriction = (temp & 128) > 0; //7-bit reserved. Should always equal 111 1111. if ((temp & 0x7F) != 0x7F) { throw new InvalidOperationException("Systems Header Reserved Bits are not Reserved"); } //Stream bound entries start at byte 6 int offset = 6; //Note: While the System header is flexible, for DVD-Video the length and content are fixed. There must be 4 stream_bound entries: while (offset < node.DataSize) { /* * 8-bit unsigned integer. Indicates to which stream the following buffer bound applies. * 1011 1000 (0xB8) indicates all audio streams. * 1011 1001 (0xB9) indicates all video streams. * Any other value must be greater than or equal to 1011 1100 (0xBC) and refers to the stream as defined for PES stream ID */ byte streamId = node.Data[offset++]; //Use constants from StreamType and see if a switch could be better. if (streamId < 0xBC && streamId != 0xB8 && streamId != 0xB9) { throw new InvalidOperationException("All Entries in the System Header must apply to a stream with an id >= 0xBC"); } /* * 1-bit boolean. False (0) indicates the multiplier is 128, TRUE (1) indicates the multiplier is 1024. * Must be 0 for audio streams, 1 for video streams. May be 0 or 1 for other types. */ //2 reserved bits (32 = 00100000) bool bufferBoundScale = (node.Data[offset] & 32) > 0; /* * 13-bit unsigned integer. When multiplied by either 128 or 1024, as indicated by P-STD_buffer_bound_scale, * defines a value that is greater than or equal to the maximum P-STD buffer size for all packets of the designated stream in the entire program stream. */ //3 bits masked out (0x1FFF = 8191 = 0001111111111111) ushort stdBufferSizeBound = (ushort)(Common.Binary.ReadU16(node.Data, ref offset, Common.Binary.IsLittleEndian) & 0x1FFF); //Create the the StreamBound Entry var entry = new Tuple <bool, ushort, Container.Node>(bufferBoundScale, stdBufferSizeBound, node); //Add or Update given the streamId m_StreamBoundEntries.AddOrUpdate(streamId, entry, (a, old) => entry); } }
//probably not be needed.. //int? m_PackHeaderVersion; ///// <summary> ///// Decodes the Mpeg Version from the Pack Header found in the Root node. ///// (Adds 1 to the value found because MPEG 1 used 0 and MPEG 2 used 1). ///// </summary> //public int PackHeaderVersion //{ // get // { // if (!m_PackHeaderVersion.HasValue) // { // using (var root = Root) m_PackHeaderVersion = 1 + root.Identifier[4] >> 6; // } // return m_PackHeaderVersion.Value; // } //} protected virtual void ParsePackHeader(Container.Node node, out double scr) { //Indicate no value found scr = double.NaN; //If the identifier is not the PackHeader then do nothing if (node.Identifier[3] != Mpeg.StreamTypes.PackHeader) { return; } //Declare the high and low parts for decoding double high = 0; uint low = 0; //m_PackHeaderVersion could be ser here.. //Determine which version of the Pack Header this is switch (node.Identifier[4] >> 6) { case 0: //MPEG 1 { //Decode the SCR //Todo, use ReadBigEndianInteger or BitStream high = (double)((node.Identifier[5] >> 3) & 0x01); low = ((uint)((node.Identifier[5] >> 1) & 0x03) << 30) | (uint)(node.Identifier[6] << 22) | (uint)((node.Identifier[7] >> 1) << 15) | (uint)(node.Identifier[8] << 7) | (uint)(node.Identifier[9] << 1); break; } default: //MPEG 2 { //Decode the SCR //Todo, use ReadBigEndianInteger or BitStream high = (double)((node.Identifier[5] & 0x20) >> 5); low = ((uint)((node.Identifier[5] & 0x18) >> 3) << 30) | (uint)((node.Identifier[5] & 0x03) << 28) | (uint)(node.Identifier[6] << 20) | (uint)((node.Identifier[7] & 0xF8) << 12) | (uint)((node.Identifier[7] & 0x03) << 13) | (uint)(node.Identifier[8] << 5) | (uint)(node.Identifier[9] >> 3); //Determine if this should be also decoded here. //Program Mux Rate - This is a 22 bit integer specifying the rate at which the program stream target decoder receives the Program Stream during the pack in which it is included. The value of program_mux_rate is measured in units of 50 bytes/second. The value 0 is forbidden. break; } } //SCR and SCR_ext together are the System Clock Reference, a counter driven at 27MHz, used as a reference to synchronize streams. The clock is divided by 300 (to match the 90KHz clocks such as PTS/DTS), the quotient is SCR (33 bits), the remainder is SCR_ext (9 bits) scr = (((high * 0x10000) * 0x10000) + low) / 90000.0; }
public static bool HasPayload(TransportStreamReader reader, Container.Node tsUnit) { return(GetAdaptationFieldControl(reader, tsUnit).HasFlag(TransportStreamUnit.AdaptationFieldControl.AdaptationFieldAndPayload)); }
public static TransportStreamUnit.ScramblingControl GetScramblingControl(TransportStreamReader reader, Container.Node tsUnit) { return(TransportStreamUnit.GetScramblingControl(tsUnit.Identifier, reader.UnitOverhead)); }
string Media.Container.IMediaContainer.ToTextualConvention(Container.Node n) { return("RtpDump-Node"); //Create a packet from the node, 3 byte identifer is RTP //return RtpSend.ToTextualConvention(packet); }
public static bool HasAdaptationField(TransportStreamReader reader, Container.Node tsUnit) { return(TransportStreamUnit.GetAdaptationFieldControl(tsUnit.Identifier, reader.UnitOverhead) > TransportStreamUnit.AdaptationFieldControl.None); }
//Static and take reader? //Maps from Program to PacketIdentifer? internal protected virtual void ParseProgramAssociationTable(Container.Node node) { int offset = ReadPointerField(node); //Get the table id TransportStreamUnit.TableIdentifier tableId = (TransportStreamUnit.TableIdentifier)node.Data[offset++]; if (tableId != TransportStreamUnit.TableIdentifier.ProgramAssociation) { return; } /* * Program Association Table (PAT) section syntax * syntax bit index # of bits mnemonic * table_id 0 8 uimsbf * section_syntax_indicator 8 1 bslbf * '0' 9 1 bslbf * reserved 10 2 bslbf * section_length 12 12 uimsbf * transport_stream_id 24 16 uimsbf * reserved 40 2 bslbf * version_number 42 5 uimsbf * current_next_indicator 47 1 bslbf * section_number 48 8 bslbf * last_section_number 56 8 bslbf * for i = 0 to N * program_number 56 + (i * 4) 16 uimsbf * reserved 72 + (i * 4) 3 bslbf * if program_number = 0 * network_PID 75 + (i * 4) 13 uimsbf * else * program_map_pid 75 + (i * 4) 13 uimsbf * end if * next * CRC_32 88 + (i * 4) 32 rpchof * Table section legend */ //section syntax indicator, 0 bit, and 2 reserved bits. //Section Length The number of bytes that follow for the syntax section (with CRC value) and/or table data. These bytes must not exceed a value of 1021. // section_length field is a 12-bit field that gives the length of the table section beyond this field //Since it is carried starting at bit index 12 in the section (the second and third bytes), the actual size of the table section is section_length + 3. ushort sectionLength = (ushort)(Common.Binary.ReadU16(node.Data, ref offset, Common.Binary.IsLittleEndian) & 0x0FFF); //transport_stream_id 24 16 uimsbf ushort transportStreamId = (ushort)(Common.Binary.ReadU16(node.Data, ref offset, Common.Binary.IsLittleEndian) & 0x0FFF); //Skip reserved, version number and current/next indicator. ++offset; //Skip section number ++offset; //Skip last section number ++offset; //Determine where to end (don't count the crc) int end = (sectionLength + 3) - 4; //4 bytes per ProgramInfo in node.Data while (offset < end) { //2 Bytes ProgramNumber ushort programNumber = Common.Binary.ReadU16(node.Data, offset, Common.Binary.IsLittleEndian); //3 bits reserved //2 Bytes ProgramID TransportStreamUnit.PacketIdentifier pid = TransportStreamUnit.GetPacketIdentifier(node.Data, offset + 2); //Add or update the entry m_ProgramAssociations.AddOrUpdate(programNumber, pid, (id, old) => pid); //Move the offset offset += 4; } //CRC }
static internal int ReadPointerField(Container.Node node, int offset = 0) { int pointer = node.Data[offset++]; return(pointer > 0 ? offset += pointer * Common.Binary.BitsPerByte : offset); }
/// <summary> /// Enumerates each unit found in the TransportStream. /// When a unit is found it is either added to the Streams or it is parsed to obtain information. /// </summary> /// <returns></returns> public override IEnumerator <Container.Node> GetEnumerator() { //Ensure a Unit remains while (Remaining >= TransportUnitSize) { //Read a unit Container.Node next = ReadNext(); //No more units stops the enumeration if (next == null) { yield break; } //Return the unit for external handling yield return(next); //Get the type of packet this is TransportStreamUnit.PacketIdentifier pid = GetPacketIdentifier(this, next.Identifier); //Need to parse the packet to ensure that GetTracks can always be updated. switch (pid) { case TransportStreamUnit.PacketIdentifier.ProgramAssociationTable: { /* * * The Program Association Table (PAT) is the entry point for the Program Specific Information (PSI) tables. * * It is always carried in packets with PID (packet ID) = 0. For each assigned program number, the PAT lists the PID for packets containing that program's PMT. * * The PMT lists all the PIDs for packets containing elements of a particular program (audio, video, aux data, and Program Clock Reference (PCR)). * * The PAT also contains the PIDs for the NIT(s). * * The NIT is an optional table that maps channel frequencies, transponder numbers, and other guide information for programs. * */ ParseProgramAssociationTable(next); goto default; } case TransportStreamUnit.PacketIdentifier.DescriptionTable: // TableIdentifier.ProgramMap { //Parse the table ParseDescriptionTable(next); goto default; } //case PacketIdentifier.Program case TransportStreamUnit.PacketIdentifier.ConditionalAccessTable: case TransportStreamUnit.PacketIdentifier.SelectionInformationTable: case TransportStreamUnit.PacketIdentifier.NetworkInformationTable: { //Todo goto default; } default: { //Include the PId with the StreamId m_ProgramIds.Add(pid); break; } } //Done with the unit Skip(next.DataSize); } }
public static byte[] GetAdaptationFieldData(TransportStreamReader reader, Container.Node tsUnit) { return(TransportStreamUnit.AdaptationField.GetAdaptationFieldData(tsUnit.Identifier, reader.UnitOverhead, tsUnit.Data, 0)); }
public static int GetContinuityCounter(TransportStreamReader reader, Container.Node tsUnit) { return(TransportStreamUnit.GetContinuityCounter(tsUnit.Identifier, reader.UnitOverhead)); }
public static bool HasPayloadUnitStartIndicator(TransportStreamReader reader, Container.Node tsUnit) { return(TransportStreamUnit.HasPayloadUnitStartIndicator(tsUnit.Identifier, reader.UnitOverhead)); }
public static bool HasTransportPriority(TransportStreamReader reader, Container.Node tsUnit) { return(TransportStreamUnit.HasTransportPriority(tsUnit.Identifier, reader.UnitOverhead)); }