/// <summary> /// Decodes a PES packet from TS packets. /// </summary> /// <param name="packet">The TS packet to decode a PES packet from.</param> /// <returns>A PES packet if one is decoded, otherwise null.</returns> public Pes?DecodePesFromTsPacket(TsPacket packet) { // If the TS packet is the start of a PES, create a new elementary stream packet if (packet.PayloadUnitStartIndicator) { try { _elementaryStreamPacket = new Pes(packet); } catch { _elementaryStreamPacket = null; } } // If we have an elementary stream packet, add the packet and decode the PES if it is complete if (_elementaryStreamPacket != null) { _elementaryStreamPacket.Add(packet); if (_elementaryStreamPacket.HasAllBytes()) { _elementaryStreamPacket.Decode(); return(_elementaryStreamPacket); } } // Return null if no other response has been returned return(null); }
public void AddPacket(TsPacket newPacket, long timestamp = -1) { try { if (newPacket.Pid != Pid) { throw new InvalidOperationException("Cannot add TS Packet from different pid to a metric!"); } if (newPacket.TransportErrorIndicator) { TeiCount++; _periodTeiCount++; OnTeiDetected(newPacket); } else { CheckCcContinuity(newPacket); CheckPcr(newPacket, timestamp); LastCc = newPacket.ContinuityCounter; } PacketCount++; _periodPacketCount++; } catch (Exception ex) { Debug.WriteLine("Exception generated within AddPacket method: " + ex.Message); } }
public void AddPacket(TsPacket packet) { CheckPid(packet.Pid); // if this isn't a start of payload packet, just return and don't process until we have a start one if (!packet.PayloadUnitStartIndicator) return; InProgressTable = new ProgramAssociationTable { PointerField = packet.Payload[0] }; if (InProgressTable.PointerField > packet.Payload.Length) { Debug.Assert(true, "Program Association Table has packet pointer outside the packet."); } var pos = 1 + InProgressTable.PointerField; InProgressTable.VersionNumber = (byte)((packet.Payload[pos + 5] & 0x3E) >> 1); //if (ProgramAssociationTable != null && ProgramAssociationTable.VersionNumber == InProgressTable.VersionNumber) //{ // InProgressTable = null; // return; //} InProgressTable.TransportStreamId = (short)((packet.Payload[pos + 3] << 8) + packet.Payload[pos + 4]); InProgressTable.TableId = packet.Payload[pos]; InProgressTable.SectionLength = (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); InProgressTable.CurrentNextIndicator = (packet.Payload[pos + 5] & 0x1) != 0; InProgressTable.SectionNumber = packet.Payload[pos + 6]; InProgressTable.LastSectionNumber = packet.Payload[pos + 7]; InProgressTable.ProgramNumbers = new short[((InProgressTable.SectionLength - 9) / 4) + 1]; InProgressTable.Pids = new short[((InProgressTable.SectionLength - 9) / 4) + 1]; var programStart = pos + 8; pos = programStart; for (var i = 0; i < (InProgressTable.SectionLength - 9) / 4; i++) { InProgressTable.ProgramNumbers[i] = (short) ((packet.Payload[programStart + (i * 4)] << 8) + packet.Payload[programStart + 1 + (i * 4)]); InProgressTable.Pids[i] = (short) (((packet.Payload[programStart + 2 + (i * 4)] & 0x1F) << 8) + packet.Payload[programStart + 3 + (i * 4)]); pos += 4; } // InProgressTable.Crc = (uint)(((packet.Payload[pos]) << 24) + (packet.Payload[pos + 1] << 16) + (packet.Payload[pos + 2] << 8) + (packet.Payload[pos + 3])); // if (InProgressTable.Crc == ProgramAssociationTable.Crc) return; ProgramAssociationTable = InProgressTable; OnTableChangeDetected(); }
private void CheckCcContinuity(TsPacket newPacket) { try { if (PacketCount == 0) { //fresh metric, first packet - so no possible error yet... return; } if (newPacket.Pid == 0x1fff) { return; } if (LastCc == newPacket.ContinuityCounter) { //CC should only be expected to increase if data is present //E.g. a pid used for PCR only may never increase CC if (newPacket.ContainsPayload) { CcErrorCount++; _periodCcErrorCount++; } return; } if (LastCc != 15) { if (LastCc + 1 != newPacket.ContinuityCounter) { CcErrorCount++; _periodCcErrorCount++; OnDiscontinuityDetected(newPacket); return; } } if (LastCc != 15 || newPacket.ContinuityCounter == 0) { return; } CcErrorCount++; _periodCcErrorCount++; OnDiscontinuityDetected(newPacket); } catch (Exception ex) { Debug.WriteLine("Exception generated within CheckCcContinuity method: " + ex.Message); } }
private void OnDiscontinuityDetected(TsPacket tsPacket) { //reset reference PCR values used for drift check - set up reference values ResetReferenceTime(0); var handler = DiscontinuityDetected; if (handler == null) { return; } var args = new TransportStreamEventArgs { TsPid = tsPacket.Pid }; handler(this, args); }
private void OnTeiDetected(TsPacket tsPacket) { //reset reference PCR values used for drift check - set up reference values ResetReferenceTime(tsPacket.AdaptationField.Pcr); var handler = TeiDetected; if (handler == null) { return; } var args = new TransportStreamEventArgs { TsPid = tsPacket.Pid }; handler(this, args); }
private void OnTeiDetected(TsPacket tsPacket) { //reset reference PCR values used for drift check - set up reference values _referencePcr = tsPacket.AdaptationField.Pcr; _referenceTime = (ulong)(DateTime.UtcNow.Ticks * 2.7); var handler = TeiDetected; if (handler == null) { return; } var args = new TransportStreamEventArgs { TsPid = tsPacket.Pid }; handler(this, args); }
protected void AddData(TsPacket packet) { CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { Data = new byte[InProgressTable.SectionLength + 3]; _tableBytes = 0; } if (InProgressTable.SectionLength + 3 - _tableBytes > packet.Payload.Length) { Buffer.BlockCopy(packet.Payload, 0, Data, _tableBytes, packet.Payload.Length); _tableBytes += (ushort)packet.Payload.Length; } else { Buffer.BlockCopy(packet.Payload, 0, Data, _tableBytes, InProgressTable.SectionLength + 3 - _tableBytes); _tableBytes += (ushort)(InProgressTable.SectionLength + 3 - _tableBytes); } }
protected void AddData(TsPacket packet) { // CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { //Console.WriteLine("packet.PayloadUnitStartIndicator"); Data = new byte[_InProgress.SectionLength + 3]; _tableBytes = 0; } if (Data == null) { //Console.WriteLine("packet.PayloadUnitStartIndicator"); Data = new byte[_InProgress.SectionLength + 3]; _tableBytes = 0; } if ((_InProgress.SectionLength + 3 - _tableBytes) > packet.Payload.Length) { Buffer.BlockCopy(packet.Payload, 0, Data, _tableBytes, packet.Payload.Length); _tableBytes += (ushort)(packet.Payload.Length); } else { if ((_InProgress.SectionLength + 3 - _tableBytes) > 0) { Buffer.BlockCopy(packet.Payload, 0, Data, _tableBytes, (_InProgress.SectionLength + 3 - _tableBytes)); _tableBytes += (ushort)(_InProgress.SectionLength + 3 - _tableBytes); } else { Console.WriteLine("Error more than 0 bytes needs copying"); } } }
public void AddPacket(TsPacket packet) { CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { InProgressTable = new SpliceInfoTable { Pid = packet.Pid, PointerField = packet.Payload[0] }; if (InProgressTable.PointerField > packet.Payload.Length) { Debug.Assert(true, "Splice Info Table has packet pointer outside the packet."); } var pos = 1 + InProgressTable.PointerField; // InProgressTable.VersionNumber = (byte)(packet.Payload[pos + 5] & 0x3E); InProgressTable.TableId = packet.Payload[pos]; //TODO: Refactor with enum for well-known table IDs, and add option below as filter if (InProgressTable.TableId != 0xFC) { InProgressTable = null; return; } /* if (SpliceInfoTable?.VersionNumber != InProgressTable.VersionNumber) * { * //if the version number of any section jumps, we need to refresh * _sectionsCompleted = new HashSet<int>(); * NetworkInformationItems = new List<NetworkInformationItem>(); * }*/ InProgressTable.SectionLength = (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); } if (InProgressTable == null) { return; } /* if (_sectionsCompleted.Contains(InProgressTable.SectionNumber)) * { * InProgressTable = null; * return; * }*/ AddData(packet); if (!HasAllBytes()) { return; } InProgressTable.ProtocolVersion = packet.Payload[InProgressTable.PointerField + 4]; InProgressTable.EncryptedPacket = (packet.Payload[InProgressTable.PointerField + 5] & 0x80) == 0x80; InProgressTable.EncryptionAlgorithm = (byte)((packet.Payload[InProgressTable.PointerField + 5] >> 1) & 0x3F); InProgressTable.PTSAdjustment = (ulong)(((((ulong)packet.Payload[InProgressTable.PointerField + 5] >> 7) & 0x1) << 32) + ((ulong)packet.Payload[InProgressTable.PointerField + 6] << 24) + ((ulong)packet.Payload[InProgressTable.PointerField + 7] << 16) + ((ulong)packet.Payload[InProgressTable.PointerField + 8] << 8) + ((ulong)packet.Payload[InProgressTable.PointerField + 9])); InProgressTable.CWIndex = packet.Payload[InProgressTable.PointerField + 10]; InProgressTable.Tier = (ushort)((packet.Payload[InProgressTable.PointerField + 11] << 4) + ((packet.Payload[InProgressTable.PointerField + 12] >> 4) & 0xF)); InProgressTable.SpliceCommandLength = (ushort)(((packet.Payload[InProgressTable.PointerField + 12] & 0xF) << 8) + packet.Payload[InProgressTable.PointerField + 13]); InProgressTable.SpliceCommandType = packet.Payload[InProgressTable.PointerField + 14]; var startOfNextField = (ushort)(InProgressTable.PointerField + 15); if (InProgressTable.SpliceCommandType == 5) { var si = new SpliceInsert(); si.SpliceEventId = (ulong)((packet.Payload[startOfNextField] << 24) + (packet.Payload[startOfNextField + 1] << 16) + (packet.Payload[startOfNextField + 2] << 8) + (packet.Payload[startOfNextField + 3])); si.SpliceEventCancelIndicator = (packet.Payload[startOfNextField + 4] & 0x80) == 0x80; if (!si.SpliceEventCancelIndicator) { si.OutOfNetworkIndicator = si.SpliceEventCancelIndicator = (packet.Payload[startOfNextField + 5] & 0x80) == 0x80; si.ProgramSpliceFlag = si.SpliceEventCancelIndicator = (packet.Payload[startOfNextField + 5] & 0x40) == 0x40; si.DurationFlag = si.SpliceEventCancelIndicator = (packet.Payload[startOfNextField + 5] & 0x20) == 0x20; si.SpliceImmediateFlag = si.SpliceEventCancelIndicator = (packet.Payload[startOfNextField + 5] & 0x10) == 0x10; if (si.ProgramSpliceFlag && !si.SpliceImmediateFlag) { var st = new SpliceTime() { TimeSpecifiedFlag = (packet.Payload[startOfNextField + 6] & 0x80) == 0x80 }; if (st.TimeSpecifiedFlag) { st.PTSTime = (ulong)((((((ulong)packet.Payload[startOfNextField + 6] >> 7) & 0x1) << 32)) + ((ulong)packet.Payload[startOfNextField + 7] << 24) + ((ulong)packet.Payload[startOfNextField + 8] << 16) + ((ulong)packet.Payload[startOfNextField + 9] << 8) + ((ulong)packet.Payload[startOfNextField + 10])); } si.SpliceTime = st; } else if (!si.ProgramSpliceFlag) { si.ComponentCount = packet.Payload[startOfNextField + 6]; startOfNextField += 7; List <SpliceInsert.Component> components = new List <SpliceInsert.Component>(); for (int i = 0; i < si.ComponentCount; i++) { SpliceInsert.Component component = new SpliceInsert.Component() { ComponentTag = packet.Payload[startOfNextField] }; if (!si.SpliceImmediateFlag) { var st = new SpliceTime() { TimeSpecifiedFlag = (packet.Payload[startOfNextField + 1] & 0x80) == 0x80 }; if (st.TimeSpecifiedFlag) { st.PTSTime = (ulong)((((((ulong)packet.Payload[startOfNextField + 1] >> 7) & 0x1) << 32)) + ((ulong)packet.Payload[startOfNextField + 2] << 24) + ((ulong)packet.Payload[startOfNextField + 3] << 16) + ((ulong)packet.Payload[startOfNextField + 4] << 8) + ((ulong)packet.Payload[startOfNextField + 5])); startOfNextField += 5; } component.SpliceTime = st; startOfNextField++; } components.Add(component); } si.Components = components; } if (si.DurationFlag) { var br = new BreakDuration() { AutoReturn = (packet.Payload[startOfNextField] & 0x80) == 0x80 }; br.Duration = (ulong)((((((ulong)packet.Payload[startOfNextField] >> 7) & 0x1) << 32)) + ((ulong)packet.Payload[startOfNextField + 1] << 24) + ((ulong)packet.Payload[startOfNextField + 2] << 16) + ((ulong)packet.Payload[startOfNextField + 3] << 8) + ((ulong)packet.Payload[startOfNextField + 4])); startOfNextField += 5; } si.UniqueProgramId = (ushort)((packet.Payload[startOfNextField] << 8) + packet.Payload[startOfNextField + 1]); si.AvailNum = packet.Payload[startOfNextField + 2]; si.AvailsExpected = packet.Payload[startOfNextField + 3]; } InProgressTable.Splice = si; } SpliceInfoTable = InProgressTable; OnTableChangeDetected(); }
public void AddPacket(TsPacket packet) { CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { InProgressTable = new ProgramMapTable { Pid = packet.Pid, PointerField = packet.Payload[0] }; if (InProgressTable.PointerField > packet.Payload.Length) { Debug.Assert(true, "Program Map Table has packet pointer outside the packet."); } var pos = 1 + InProgressTable.PointerField; InProgressTable.VersionNumber = (byte)((packet.Payload[pos + 5] & 0x3E) >> 1); if (ProgramMapTable?.VersionNumber == InProgressTable.VersionNumber) { InProgressTable = null; return; } InProgressTable.TableId = packet.Payload[pos]; InProgressTable.SectionLength = (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); InProgressTable.ProgramNumber = (ushort)((packet.Payload[pos + 3] << 8) + packet.Payload[pos + 4]); InProgressTable.CurrentNextIndicator = (packet.Payload[pos + 5] & 0x1) != 0; InProgressTable.SectionNumber = packet.Payload[pos + 6]; InProgressTable.LastSectionNumber = packet.Payload[pos + 7]; InProgressTable.PcrPid = (ushort)(((packet.Payload[pos + 8] & 0x1f) << 8) + packet.Payload[pos + 9]); InProgressTable.ProgramInfoLength = (ushort)(((packet.Payload[pos + 10] & 0x3) << 8) + packet.Payload[pos + 11]); } if (InProgressTable == null) { return; } AddData(packet); if (!HasAllBytes()) { return; } var startOfNextField = GetDescriptors(InProgressTable.ProgramInfoLength, InProgressTable.PointerField + 13); InProgressTable.EsStreams = ReadEsInfoElements(InProgressTable.SectionLength, startOfNextField); var crcPos = InProgressTable.PointerField + InProgressTable.SectionLength - 1; //+3 for start, -4 for len CRC = -1 InProgressTable.Crc = (uint)((Data[crcPos] << 24) + (Data[crcPos + 1] << 16) + (Data[crcPos + 2] << 8) + Data[crcPos + 3]); ProgramMapTable = InProgressTable; OnTableChangeDetected(); }
public void AddPacket(TsPacket packet) { CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { InProgressTable = new EventInformationTable { Pid = packet.Pid, PointerField = packet.Payload[0] }; if (InProgressTable.PointerField > packet.Payload.Length) { Debug.Assert(true, "Event Information Table has packet pointer outside the packet."); } var pos = 1 + InProgressTable.PointerField; InProgressTable.VersionNumber = (byte)(packet.Payload[pos + 5] & 0x3E); InProgressTable.TableId = packet.Payload[pos]; //TODO: Refactor with enum for well-known table IDs, and add option below as filter if (InProgressTable.TableId != 0x4e) { InProgressTable = null; return; } if (EventInformationTable?.VersionNumber != InProgressTable.VersionNumber) { //if the version number of any section jumps, we need to refresh _sectionsCompleted = new HashSet <int>(); EventInformationItems = new List <EventInformationItem>(); } InProgressTable.SectionLength = (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); InProgressTable.SericeId = (ushort)((packet.Payload[pos + 3] << 8) + packet.Payload[pos + 4]); InProgressTable.CurrentNextIndicator = (packet.Payload[pos + 5] & 0x1) != 0; InProgressTable.SectionNumber = packet.Payload[pos + 6]; InProgressTable.LastSectionNumber = packet.Payload[pos + 7]; InProgressTable.TransportStreamId = (ushort)((packet.Payload[pos + 8] << 8) + packet.Payload[pos + 9]); InProgressTable.OriginalNetworkId = (ushort)((packet.Payload[pos + 10] << 8) + packet.Payload[pos + 11]); } if (InProgressTable == null) { return; } if (_sectionsCompleted.Contains(InProgressTable.SectionNumber)) { InProgressTable = null; return; } AddData(packet); if (!HasAllBytes()) { return; } InProgressTable.SegmentLastSectionNumber = (Data[InProgressTable.PointerField + 13]); InProgressTable.LastTableId = (Data[InProgressTable.PointerField + 14]); var startOfNextField = (ushort)(InProgressTable.PointerField + 15); var transportStreamLoopEnd = (ushort)(InProgressTable.SectionLength - 4); var items = new List <EventInformationItem>(); while (startOfNextField < transportStreamLoopEnd) { var item = new EventInformationItem { EventId = (ushort)((Data[startOfNextField] << 8) + Data[startOfNextField + 1]), StartTime = (ulong)(((ulong)(Data[startOfNextField + 2]) << 32) + ((ulong)(Data[startOfNextField + 3]) << 24) + ((ulong)(Data[startOfNextField + 4]) << 16) + ((ulong)(Data[startOfNextField + 5]) << 8) + ((ulong)(Data[startOfNextField + 6]))), Duration = (uint)((Data[startOfNextField + 7] << 16) + (Data[startOfNextField + 8] << 8) + Data[startOfNextField + 9]), RunningStatus = (byte)((Data[startOfNextField + 10] >> 5) & 0x07), FreeCAMode = (bool)((Data[startOfNextField + 10] & 0x10) == 0x10), DescriptorsLoopLength = (ushort)(((Data[startOfNextField + 10] & 0x3) << 8) + Data[startOfNextField + 11]) }; var descriptors = new List <Descriptor>(); startOfNextField = (ushort)(startOfNextField + 12); var endOfDescriptors = (ushort)(startOfNextField + item.DescriptorsLoopLength); if (endOfDescriptors > Data.Length) { throw new InvalidDataException("Descriptor data in Event Information is marked beyond available data"); } while (startOfNextField < endOfDescriptors) { var des = DescriptorFactory.DescriptorFromData(Data, startOfNextField); descriptors.Add(des); startOfNextField += (ushort)(des.DescriptorLength + 2); } item.Descriptors = descriptors; items.Add(item); } InProgressTable.Items = items; EventInformationItems.AddRange(items); if (InProgressTable.VersionNumber == EventInformationTable?.VersionNumber) { return; } EventInformationTable = InProgressTable; _sectionsCompleted.Add(InProgressTable.SectionNumber); OnTableChangeDetected(); }
public void AddPacket(TsPacket packet) { CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { InProgressTable = new NetworkInformationTable { Pid = packet.Pid, PointerField = packet.Payload[0] }; if (InProgressTable != null) { if (InProgressTable.PointerField > packet.Payload.Length) { Debug.Assert(true, "Network Information Table has packet pointer outside the packet."); } } var pos = 1 + InProgressTable.PointerField; InProgressTable.VersionNumber = (byte)(packet.Payload[pos + 5] & 0x3E); InProgressTable.TableId = packet.Payload[pos]; //TODO: Refactor with enum for well-known table IDs, and add option below as filter if (InProgressTable.TableId != 0x40) { InProgressTable = null; return; } if ((NetworkInformationTable == null) || (NetworkInformationTable.VersionNumber != InProgressTable.VersionNumber)) { //if the version number of any section jumps, we need to refresh _sectionsCompleted = new HashSet <int>(); NetworkInformationItems = new List <NetworkInformationItem>(); } InProgressTable.SectionLength = (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); InProgressTable.TransportStreamId = (ushort)((packet.Payload[pos + 3] << 8) + packet.Payload[pos + 4]); InProgressTable.CurrentNextIndicator = (packet.Payload[pos + 5] & 0x1) != 0; InProgressTable.SectionNumber = packet.Payload[pos + 6]; InProgressTable.LastSectionNumber = packet.Payload[pos + 7]; } if (InProgressTable == null) { return; } if (_sectionsCompleted.Contains(InProgressTable.SectionNumber)) { InProgressTable = null; return; } AddData(packet); if (!HasAllBytes()) { return; } InProgressTable.NetworkDescriptorsLength = (ushort)(((Data[InProgressTable.PointerField + 9] & 0x3) << 8) + Data[InProgressTable.PointerField + 10]); var startOfNextField = (ushort)(InProgressTable.PointerField + 11); List <Descriptor> descriptors = new List <Descriptor>(); var endOfDescriptors = InProgressTable.PointerField + 11 + InProgressTable.NetworkDescriptorsLength; if (endOfDescriptors > Data.Length) { throw new InvalidDataException("Descriptor data in Network Information is marked beyond available data"); } while (startOfNextField < endOfDescriptors) { Descriptor des = DescriptorFactory.DescriptorFromData(Data, startOfNextField); descriptors.Add(des); startOfNextField += (byte)(des.DescriptorLength + 2); } InProgressTable.Descriptors = descriptors; InProgressTable.TransportStreamLoopLength = (ushort)(((Data[startOfNextField] & 0x3) << 8) + Data[startOfNextField + 1]); startOfNextField += 2; var transportStreamLoopEnd = (byte)(startOfNextField + InProgressTable.TransportStreamLoopLength); var items = new List <NetworkInformationItem>(); while (startOfNextField < transportStreamLoopEnd) { var item = new NetworkInformationItem { TransportStreamId = (ushort)((Data[startOfNextField] << 8) + Data[startOfNextField + 1]), OriginalNetworkId = (ushort)((Data[startOfNextField + 2] << 8) + Data[startOfNextField + 3]), ReservedFutureUse = (byte)((Data[startOfNextField + 4] >> 4) & 0x0F), TransportDescriptorsLength = (ushort)(((Data[startOfNextField + 4] & 0x3) << 8) + Data[startOfNextField + 5]) }; descriptors = new List <Descriptor>(); startOfNextField = (byte)(startOfNextField + 6); endOfDescriptors = (byte)(startOfNextField + item.TransportDescriptorsLength); if (endOfDescriptors > Data.Length) { throw new InvalidDataException("Descriptor data in Network Information Item is marked beyond available data"); } while (startOfNextField < endOfDescriptors) { Descriptor des = DescriptorFactory.DescriptorFromData(Data, startOfNextField); descriptors.Add(des); startOfNextField += (byte)(des.DescriptorLength + 2); } item.Descriptors = descriptors; items.Add(item); } InProgressTable.Items = items; NetworkInformationItems.AddRange(items); NetworkInformationTable = InProgressTable; _sectionsCompleted.Add(InProgressTable.SectionNumber); OnTableChangeDetected(); }
private void CheckPcr(TsPacket tsPacket, long timestamp) { if (!tsPacket.AdaptationFieldExists) { return; } if (!tsPacket.AdaptationField.PcrFlag) { return; } if (tsPacket.AdaptationField.FieldSize < 1) { return; } if (tsPacket.AdaptationField.DiscontinuityIndicator) { Debug.WriteLine("Adaptation field discont indicator"); return; } if (_lastPcr != 0) { var latestDelta = tsPacket.AdaptationField.Pcr - _lastPcr; if (latestDelta > _periodLargestPcrDelta) { _periodLargestPcrDelta = latestDelta; } var elapsedPcr = (long)(tsPacket.AdaptationField.Pcr - _referencePcr); //var elapsedClock = (long)((DateTime.UtcNow.Ticks * 2.7) - _referenceTime); //var elapsedClock = (timestamp * 27000) - _referenceTime; var elapsedClock = timestamp * _conversionFactor27Mhz - _referenceTime; var drift = (float)(elapsedClock - elapsedPcr) / 27000; if (drift > _periodLargestPcrDrift) { _periodLargestPcrDrift = drift; } if (drift > PcrDriftLimit) { _largePcrDriftCount++; } drift = (float)(elapsedPcr - elapsedClock) / 27000; if (drift > _periodLowestPcrDrift) { _periodLowestPcrDrift = drift; } if (drift > PcrDriftLimit) { _largePcrDriftCount++; } } else { //first PCR value - set up reference values //wait 10 seconds before sampling datum PCR time - otherwise everything drifts immediately as analyzer finishes launching tasks if (DateTime.UtcNow.Subtract(_startTime) < TimeSpan.FromSeconds(10)) { return; } ResetReferenceTime(tsPacket.AdaptationField.Pcr); } if (_largePcrDriftCount > 5) { //exceeded PCR drift ceiling - reset clocks ResetReferenceTime(tsPacket.AdaptationField.Pcr); } _lastPcr = tsPacket.AdaptationField.Pcr; }
public void AddPacket(TsPacket packet) { //CheckPid(packet.Pid); Console.WriteLine("DsmccFactory packet"); if (packet.PayloadUnitStartIndicator) { Console.WriteLine("####DsmccFactory PayloadUnitStartIndicator"); _InProgress = new Dsmcc { Pid = packet.Pid, PointerField = packet.Payload[0] }; if (_InProgress.PointerField > packet.Payload.Length) { Debug.Assert(true, "Splice Info Table has packet pointer outside the packet."); } var pos = 1 + _InProgress.PointerField; // InProgressTable.VersionNumber = (byte)(packet.Payload[pos + 5] & 0x3E); //_InProgress.TableId = packet.Payload[pos]; //if (_InProgress.TableId != 0x70) //{ // _InProgress = null; // return; //} /* if (SpliceInfoTable?.VersionNumber != InProgressTable.VersionNumber) * { * //if the version number of any section jumps, we need to refresh * _sectionsCompleted = new HashSet<int>(); * NetworkInformationItems = new List<NetworkInformationItem>(); * }*/ //_InProgress.SectionLength = // (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); } if (_InProgress == null) { return; } /* if (_sectionsCompleted.Contains(InProgressTable.SectionNumber)) * { * InProgressTable = null; * return; * }*/ AddData(packet); if (!HasAllBytes()) { return; } //// DATE is 16 bits MJD (modified Julian Date) //ushort mjd = (ushort)((packet.Payload[4] << 8) + (packet.Payload[5])); //DateTime dtMjd = MjdToDateTime(mjd); //// TIME is 24 bits as 6 digits BCD //int Hour = ((byte)(packet.Payload[6] >> 4) * 10) + (byte)(packet.Payload[6] & 0x7); //int Min = ((byte)(packet.Payload[7] >> 4) * 10) + (byte)(packet.Payload[7] & 0x7); //int Sec = ((byte)(packet.Payload[8] >> 4) * 10) + (byte)(packet.Payload[8] & 0x7); //_InProgress.UtcTime = dtMjd.Add(new TimeSpan(Hour, Min, Sec)); Active = _InProgress; //Console.WriteLine("TDT = " + Active.UtcTime.ToString()); OnDsmccChanged(); }
public void AddPacket(TsPacket packet) { CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { InProgressTable = new ServiceDescriptionTable { Pid = packet.Pid, PointerField = packet.Payload[0] }; if (InProgressTable.PointerField > packet.Payload.Length) { Debug.Assert(true, "Service Description Table has packet pointer outside the packet."); } var pos = 1 + InProgressTable.PointerField; InProgressTable.VersionNumber = (byte)(packet.Payload[pos + 5] & 0x3E); InProgressTable.TableId = packet.Payload[pos]; //TODO: Refactor with enum for well-known table IDs, and add option below as filter //if (InProgressTable.TableId != 0x42) //{ // InProgressTable = null; // return; //} if ((ServiceDescriptionTable == null) || (ServiceDescriptionTable.VersionNumber != InProgressTable.VersionNumber)) { //if the version number of any section jumps, we need to refresh _sectionsCompleted = new HashSet <int>(); ServiceDescriptionItems = new List <ServiceDescriptionItem>(); } InProgressTable.SectionLength = (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); InProgressTable.TransportStreamId = (ushort)((packet.Payload[pos + 3] << 8) + packet.Payload[pos + 4]); InProgressTable.CurrentNextIndicator = (packet.Payload[pos + 5] & 0x1) != 0; InProgressTable.SectionNumber = packet.Payload[pos + 6]; InProgressTable.LastSectionNumber = packet.Payload[pos + 7]; InProgressTable.OriginalNetworkId = (ushort)((packet.Payload[pos + 8] << 8) + packet.Payload[pos + 9]); } if (InProgressTable == null) { return; } if (_sectionsCompleted.Contains(InProgressTable.SectionNumber)) { InProgressTable = null; return; } AddData(packet); if (!HasAllBytes()) { return; } var startOfNextField = (ushort)(InProgressTable.PointerField + 12); var transportStreamLoopEnd = (ushort)(InProgressTable.SectionLength - 4); var items = new List <ServiceDescriptionItem>(); while (startOfNextField < transportStreamLoopEnd) { var item = new ServiceDescriptionItem { ServiceId = (ushort)((Data[startOfNextField] << 8) + Data[startOfNextField + 1]), EitScheduleFlag = ((Data[startOfNextField + 2]) & 0x02) == 0x02, EitPresentFollowingFlag = ((Data[startOfNextField + 2]) & 0x01) == 0x01, RunningStatus = (byte)((Data[startOfNextField + 3] >> 5) & 0x07), FreeCaMode = (Data[startOfNextField + 3] & 0x10) == 0x10, DescriptorsLoopLength = (ushort)(((Data[startOfNextField + 3] & 0xf) << 8) + Data[startOfNextField + 4]) }; var descriptors = new List <Descriptor>(); startOfNextField = (ushort)(startOfNextField + 5); var endOfDescriptors = (ushort)(startOfNextField + item.DescriptorsLoopLength); if (endOfDescriptors > Data.Length) { throw new InvalidDataException("Descriptor data in Service Description is marked beyond available data"); } while (startOfNextField < endOfDescriptors) { var des = DescriptorFactory.DescriptorFromData(Data, startOfNextField); descriptors.Add(des); startOfNextField += (ushort)(des.DescriptorLength + 2); } item.Descriptors = descriptors; items.Add(item); } ServiceDescriptionItems.AddRange(items); ServiceDescriptionTable = InProgressTable; ServiceDescriptionTable.Items = ServiceDescriptionItems; _sectionsCompleted.Add(InProgressTable.SectionNumber); InProgressTable.ItemsIncomplete = false; for (var i = 0; i <= InProgressTable.LastSectionNumber; i++) { if (!_sectionsCompleted.Contains(i)) { InProgressTable.ItemsIncomplete = true; } } OnTableChangeDetected(); }
public void AddPacket(TsPacket packet) { CheckPid(packet.Pid); if (packet.PayloadUnitStartIndicator) { InProgressTable = new TdtTable { Pid = packet.Pid, PointerField = packet.Payload[0] }; if (InProgressTable.PointerField > packet.Payload.Length) { Debug.Assert(true, "Splice Info Table has packet pointer outside the packet."); } var pos = 1 + InProgressTable.PointerField; // InProgressTable.VersionNumber = (byte)(packet.Payload[pos + 5] & 0x3E); InProgressTable.TableId = packet.Payload[pos]; if (InProgressTable.TableId != 0x70) { InProgressTable = null; return; } /* if (SpliceInfoTable?.VersionNumber != InProgressTable.VersionNumber) * { * //if the version number of any section jumps, we need to refresh * _sectionsCompleted = new HashSet<int>(); * NetworkInformationItems = new List<NetworkInformationItem>(); * }*/ InProgressTable.SectionLength = (short)(((packet.Payload[pos + 1] & 0x3) << 8) + packet.Payload[pos + 2]); } if (InProgressTable == null) { return; } /* if (_sectionsCompleted.Contains(InProgressTable.SectionNumber)) * { * InProgressTable = null; * return; * }*/ AddData(packet); if (!HasAllBytes()) { return; } /* * https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.03.01_60/en_300468v010301p.pdf * * time_date_section(){ * table_id 8 uimsbf * section_syntax_indicator 1 bslbf * reserved_future_use 1 bslbf * reserved 2 bslbf * section_length 12 uimsbf * UTC_time 40 bslbf * } * * table_id: See table 2. * section_syntax_indicator: This is a one-bit indicator which shall be set to "0". * section_length: This is a 12-bit field, the first two bits of which shall be "00". It specifies the number of bytes of the * section, starting immediately following the section_length field and up to the end of the section. * UTC_time: This 40-bit field contains the current time and date in UTC and MJD (see annex C). This field is coded as * 16 bits giving the 16 LSBs of MJD followed by 24 bits coded as 6 digits in 4-bit BCD. * EXAMPLE: 93/10/13 12:45:00 is coded as "0xC079124500".] * * 40 bits == 5 bytes * * * Console.WriteLine("TDT InProgressTable.TableId = " + InProgressTable.TableId.ToString("X")); * Console.WriteLine("TDT InProgressTable.SectionLength = " + InProgressTable.SectionLength.ToString()); */ // DATE is 16 bits MJD (modified Julian Date) ushort mjd = (ushort)((packet.Payload[4] << 8) + (packet.Payload[5])); DateTime dtMjd = MjdToDateTime(mjd); // TIME is 24 bits as 6 digits BCD int Hour = ((byte)(packet.Payload[6] >> 4) * 10) + (byte)(packet.Payload[6] & 0x7); int Min = ((byte)(packet.Payload[7] >> 4) * 10) + (byte)(packet.Payload[7] & 0x7); int Sec = ((byte)(packet.Payload[8] >> 4) * 10) + (byte)(packet.Payload[8] & 0x7); InProgressTable.UtcTime = dtMjd.Add(new TimeSpan(Hour, Min, Sec)); TdtTable = InProgressTable; Console.WriteLine("TDT = " + TdtTable.UtcTime.ToString()); OnTableChangeDetected(); }
private void MuxPesPacketToTs(PesPacket pp, bool priority) { byte[] data = new byte[Constants.TS_SIZE]; int j = 0; int i = 0; byte[] pes = pp.GetData(); // take care of the first packet data[0] = Constants.SYNC_BYTE; data[1] = 0x40; // payload start data[2] = 0; if (pes.Length < Constants.TS_PAYLOAD_SIZE) { data[3] = 0x30; // adaptation and payload int stufLength = Constants.TS_PAYLOAD_SIZE - pes.Length - 1; data[4] = (byte)stufLength; i = 5; if (stufLength > 0) { data[i] = 0; i++; stufLength--; } for (; i < (6 + stufLength); i++) { data[i] = 0xff; } for (; i < Constants.TS_SIZE; i++) { data[i] = pes[j]; j++; } } else { data[3] = 0x10; // no adaptation, payload only for (i = 4; i < data.Length; i++) { data[i] = pes[j]; j++; } } TsPacket ts = new TsPacket(); ts.SetData(data, 0); ushort pid = pidMappings[pp.PID]; ts.PID = pid; ts.Priority = priority; ts.ContinuityCounter = Continuities[pid]; ts.IncrementContinuityCounter(); Continuities[pid] = ts.ContinuityCounter; buffer.Add(ts); while (j < ((pes.Length / Constants.TS_PAYLOAD_SIZE) * Constants.TS_PAYLOAD_SIZE)) { // take care of the other packets data[0] = Constants.SYNC_BYTE; data[1] = 0x00; // no payload start data[2] = 0; data[3] = 0x10; // no adaptation, payload only for (i = 4; i < data.Length; i++) { data[i] = pes[j]; j++; } ts = new TsPacket(); ts.SetData(data, 0); ts.PID = pid; ts.Priority = priority; ts.ContinuityCounter = Continuities[pid]; ts.IncrementContinuityCounter(); Continuities[pid] = ts.ContinuityCounter; buffer.Add(ts); } // take care of the last packet if (j < pes.Length) { data[0] = Constants.SYNC_BYTE; data[1] = 0x00; // no payload start data[2] = 0; data[3] = 0x30; // adaptation and payload int stufLength = Constants.TS_PAYLOAD_SIZE - (pes.Length - j) - 1; data[4] = (byte)stufLength; i = 5; if (stufLength > 0) { data[i] = 0; i++; stufLength--; for (; i < (6 + stufLength); i++) { data[i] = 0xff; } } for (; i < Constants.TS_SIZE; i++) { data[i] = pes[j]; j++; } ts = new TsPacket(); ts.SetData(data, 0); ts.PID = pid; ts.Priority = priority; ts.ContinuityCounter = Continuities[pid]; ts.IncrementContinuityCounter(); Continuities[pid] = ts.ContinuityCounter; buffer.Add(ts); } }
/// <summary> /// Returns TsPackets for any input data. If data ends with incomplete packet, this is stored and prepended to next call. /// If data stream is restarted, prior buffer will be skipped as sync will not be acknowledged - but any restarts should being with first byte as sync to avoid possible merging with prior data if lengths coincide. /// </summary> /// <remarks> /// This has been copied from the parent TsPacketFactory class, but modified to return packets with error indicators /// if the adaptation field size exceeds the payload and to ignore timestamps (and their errors). /// </remarks> /// <param name="data">Aligned or unaligned data buffer containing TS packets. Aligned is more efficient if possible.</param> /// <param name="dataSize">Optional length parameter to limit amount of data read from referenced array.</param> /// <param name="retainPayload">Optional parameter to trigger any resulting TS payload to be copied into the returned structure</param> /// <param name="preserveSourceData">Optional parameter to trigger complete copy of source data for TS packet to be held in array for quick access</param> /// <returns>Complete TS packets from this data and any prior partial data rolled over.</returns> public new TsPacket[]? GetTsPacketsFromData(byte[] data, int dataSize = 0, bool retainPayload = true, bool preserveSourceData = false) { try { if (dataSize == 0) { dataSize = data.Length; } if (_residualData != null) { byte[] tempArray = new byte[dataSize]; Buffer.BlockCopy(data, 0, tempArray, 0, dataSize); data = new byte[_residualData.Length + tempArray.Length]; Buffer.BlockCopy(_residualData, 0, data, 0, _residualData.Length); Buffer.BlockCopy(tempArray, 0, data, _residualData.Length, tempArray.Length); dataSize = data.Length; } int maxPackets = (dataSize) / TsPacketFixedSize; TsPacket[] tsPackets = new TsPacket[maxPackets]; int packetCounter = 0; int start = FindSync(data, 0, TsPacketFixedSize); while (start >= 0 && ((dataSize - start) >= TsPacketFixedSize)) { TsPacket tsPacket = new TsPacket { SyncByte = data[start], Pid = (short)(((data[start + 1] & 0x1F) << 8) + (data[start + 2])), TransportErrorIndicator = (data[start + 1] & 0x80) != 0, PayloadUnitStartIndicator = (data[start + 1] & 0x40) != 0, TransportPriority = (data[start + 1] & 0x20) != 0, ScramblingControl = (short)(data[start + 3] >> 6), AdaptationFieldExists = (data[start + 3] & 0x20) != 0, ContainsPayload = (data[start + 3] & 0x10) != 0, ContinuityCounter = (short)(data[start + 3] & 0xF), SourceBufferIndex = start }; if (preserveSourceData) { tsPacket.SourceData = new byte[TsPacketFixedSize]; Buffer.BlockCopy(data, start, tsPacket.SourceData, 0, TsPacketFixedSize); } // Skip packets with error indicators or on the null PID if (!tsPacket.TransportErrorIndicator && (tsPacket.Pid != (short)PidType.NullPid)) { var payloadOffs = start + 4; var payloadSize = TsPacketFixedSize - 4; if (tsPacket.AdaptationFieldExists) { tsPacket.AdaptationField = new AdaptationField() { FieldSize = data[start + 4], DiscontinuityIndicator = (data[start + 5] & 0x80) != 0, RandomAccessIndicator = (data[start + 5] & 0x40) != 0, ElementaryStreamPriorityIndicator = (data[start + 5] & 0x20) != 0, PcrFlag = (data[start + 5] & 0x10) != 0, OpcrFlag = (data[start + 5] & 0x8) != 0, SplicingPointFlag = (data[start + 5] & 0x4) != 0, TransportPrivateDataFlag = (data[start + 5] & 0x2) != 0, AdaptationFieldExtensionFlag = (data[start + 5] & 0x1) != 0 }; if (tsPacket.AdaptationField.FieldSize >= payloadSize) { tsPacket.TransportErrorIndicator = true; tsPackets[packetCounter++] = tsPacket; start += TsPacketFixedSize; continue; } if (tsPacket.AdaptationField.PcrFlag && tsPacket.AdaptationField.FieldSize > 0) { //Packet has PCR tsPacket.AdaptationField.Pcr = (((uint)(data[start + 6]) << 24) + ((uint)(data[start + 7] << 16)) + ((uint)(data[start + 8] << 8)) + (data[start + 9])); tsPacket.AdaptationField.Pcr <<= 1; if ((data[start + 10] & 0x80) == 1) { tsPacket.AdaptationField.Pcr |= 1; } tsPacket.AdaptationField.Pcr *= 300; uint iLow = (uint)((data[start + 10] & 1) << 8) + data[start + 11]; tsPacket.AdaptationField.Pcr += iLow; if (_lastPcr == 0) { _lastPcr = tsPacket.AdaptationField.Pcr; } } payloadSize -= tsPacket.AdaptationField.FieldSize; payloadOffs += tsPacket.AdaptationField.FieldSize; } if (tsPacket.ContainsPayload && tsPacket.PayloadUnitStartIndicator) { if (payloadOffs > (dataSize - 2) || data[payloadOffs] != 0 || data[payloadOffs + 1] != 0 || data[payloadOffs + 2] != 1) { // Do nothing } else { tsPacket.PesHeader = new PesHdr { StartCode = (uint)((data[payloadOffs] << 16) + (data[payloadOffs + 1] << 8) + data[payloadOffs + 2]), StreamId = data[payloadOffs + 3], PacketLength = (ushort)((data[payloadOffs + 4] << 8) + data[payloadOffs + 5]), Pts = -1, Dts = -1 }; tsPacket.PesHeader.HeaderLength = (byte)tsPacket.PesHeader.PacketLength; byte stmrId = tsPacket.PesHeader.StreamId; //just copying to small name to make code less huge and slightly faster... if ((stmrId != (uint)PesStreamTypes.ProgramStreamMap) && (stmrId != (uint)PesStreamTypes.PaddingStream) && (stmrId != (uint)PesStreamTypes.PrivateStream2) && (stmrId != (uint)PesStreamTypes.ECMStream) && (stmrId != (uint)PesStreamTypes.EMMStream) && (stmrId != (uint)PesStreamTypes.ProgramStreamDirectory) && (stmrId != (uint)PesStreamTypes.DSMCCStream) && (stmrId != (uint)PesStreamTypes.H2221TypeEStream)) { tsPacket.PesHeader.HeaderLength = (byte)(9 + data[payloadOffs + 8]); } tsPacket.PesHeader.Payload = new byte[tsPacket.PesHeader.HeaderLength]; Buffer.BlockCopy(data, payloadOffs, tsPacket.PesHeader.Payload, 0, tsPacket.PesHeader.HeaderLength); payloadOffs += tsPacket.PesHeader.HeaderLength; payloadSize -= tsPacket.PesHeader.HeaderLength; } } if (payloadSize > 1 && retainPayload) { tsPacket.Payload = new byte[payloadSize]; Buffer.BlockCopy(data, payloadOffs, tsPacket.Payload, 0, payloadSize); } } tsPackets[packetCounter++] = tsPacket; start += TsPacketFixedSize; if (start >= dataSize) { break; } if (data[start] != SyncByte) { break; // but this is strange! } } if ((start + TsPacketFixedSize) != dataSize) { //we have 'residual' data to carry over to next call _residualData = new byte[dataSize - start]; Buffer.BlockCopy(data, start, _residualData, 0, dataSize - start); } return(tsPackets); } catch { return(null); } }