/// <summary> /// Process provided <paramref name="baseBandFrame"/>. Decapsulate frames from <see cref="GsePacket"/>s in <paramref name="baseBandFrame"/>. In case of GSE fragmentation, /// add <see cref="GsePacket"/>s to corresponding <see cref="GseReassemblyBuffer"/>s and perform one step of thr GSE reassembly procedure. Successfully reassembled GSE is /// then decapsulated, too. /// </summary> /// <param name="baseBandFrame">Base-Band frame from which the <see cref="GsePacket"/>s (<see cref="BaseBandFrame.UserPackets"/>) should be decapsulated.</param> /// <param name="encapsulatingFrames">Frames carrying provided <paramref name="baseBandFrame"/>.</param> /// <returns>Decapsualted frames, which were eventually reassembled if it was required.</returns> public IEnumerable <PmFrameBase> Process([Required] BaseBandFrame baseBandFrame, [Required] ICollection <PmFrameBase> encapsulatingFrames) { var decapsulatedFrames = new List <PmFrameBase>(); // key for identification of a stream used in this._reassemblyBuffers var si = baseBandFrame.BaseBandHeader.InputStreamIdentifier ?? SingleInputStreamReservedIdentifier; // lazy instantiation of reassembling structures for given stream (si) if (!this._reassemblyBuffers.ContainsKey(si)) { this._reassemblyBuffers.Add(si, new Dictionary <byte, GseReassemblyBuffer>()); } var stop = false; foreach (var gsePacket in baseBandFrame.UserPackets) { if (stop) { break; } switch (gsePacket.Type) { case PacketType.Complete: // complete packet is here considered one fragment itself var completeFrame = PmFrameEncapsulated.Create(new List <IFragment>(1) { gsePacket }); completeFrame.DecapsulatedFromFrames.AddRange(encapsulatingFrames); foreach (var encapsulatingFrame in encapsulatingFrames) { encapsulatingFrame.EncapsulatedFrames.Add(completeFrame); } decapsulatedFrames.Add(completeFrame); continue; case PacketType.Padding: // "(...) padding is detected and the Receiver shall discard all the following bytes until the end of the Base // Band frame." http://www.etsi.org/deliver/etsi_ts/102600_102699/10260601/01.02.01_60/ts_10260601v010201p.pdf#page=22 stop = true; break; case PacketType.Start: if (!gsePacket.Header.FragmentID.HasValue) { throw new ArgumentException("Fragmented packet does not contain FragmentID."); } // lazy instantiation of GseReassemblyBuffer for given FragmentID if (!this._reassemblyBuffers[si].ContainsKey(gsePacket.Header.FragmentID.Value)) { this._reassemblyBuffers[si].Add(gsePacket.Header.FragmentID.Value, new GseReassemblyBuffer()); } if (this._reassemblyBuffers[si][gsePacket.Header.FragmentID.Value].Count > 0) { // "(...) the receiver has fragments for that Frag ID in a reassembly buffer. If the Frag ID is already in use, // the receiver shall first discard the already buffered fragments corresponding to this Frag ID;" // http://www.etsi.org/deliver/etsi_ts/102600_102699/10260601/01.02.01_60/ts_10260601v010201p.pdf#page=21 this._reassemblyBuffers[si][gsePacket.Header.FragmentID.Value].Clear(); // Buffered GSE fragments were discarded. } this._reassemblyBuffers[si][gsePacket.Header.FragmentID.Value].Add(gsePacket, encapsulatingFrames); break; case PacketType.Intermediate: if (!gsePacket.Header.FragmentID.HasValue) { throw new ArgumentException("Fragmented packet does not contain FragmentID."); } if (this._reassemblyBuffers[si].ContainsKey(gsePacket.Header.FragmentID.Value)) { this._reassemblyBuffers[si][gsePacket.Header.FragmentID.Value].Add(gsePacket, encapsulatingFrames); } // else: Intermediate GSE fragment was discarded because there was not Start GSE fragment before. break; case PacketType.End: if (!gsePacket.Header.FragmentID.HasValue) { throw new ArgumentException("Fragmented packet does not contain FragmentID."); } if (this._reassemblyBuffers[si].ContainsKey(gsePacket.Header.FragmentID.Value)) { this._reassemblyBuffers[si][gsePacket.Header.FragmentID.Value].Add(gsePacket, encapsulatingFrames); var decapsulated = this._reassemblyBuffers[si][gsePacket.Header.FragmentID.Value].Retrieve(); decapsulatedFrames.Add(decapsulated); } // else: End GSE fragment was discarded because there was not Start GSE fragment before. break; default: throw new ArgumentOutOfRangeException($"Unexpected PacketType ({gsePacket.Type})."); } } foreach (var buffer in this._reassemblyBuffers[si]) { buffer.Value.BaseBandProcessed(); } return(decapsulatedFrames); }
public L7DvbS2GseDecapsulatorBlock() { this._outputBuffer = new BufferBlock <PmFrameBase>(); this._decapsulator = new ActionBlock <L7Conversation>(async l7Conversation => { var stream = new PDUStreamBasedProvider(l7Conversation, EfcPDUProviderType.ContinueInterlay); var reader = new PDUStreamReader(stream, Encoding.GetEncoding(437), true) { ReadBigEndian = true }; var gseReassembler = new GseReassemblingDecapsulator(); while (!reader.EndOfStream) { try { var bb = BaseBandFrame.Parse(reader); // It's important to get `reader.PDUStreamBasedProvider.GetCurrentPDU()` after parsing Base-Band frame. // During reading, its value is set to the last read PDU. If we would call it before Base-Band parsing, // retrieved value would not be current PDU, but the previous one. // frames encapsulating parsed Base-Band var pdu = reader.PDUStreamBasedProvider.GetCurrentPDU(); if (pdu == null) { break; } var frames = ImmutableList.CreateRange(pdu.FrameList); // Reassemble any fragmented GSE packets and obtain decapsulated frames, if any of them have been just finished. var decapsulatedFrames = gseReassembler.Process(bb, frames); foreach (var f in decapsulatedFrames) { this._outputBuffer.Post(f); } } catch (InvalidPacketFormatException) { // Current PDU of l7Conversation does not contain DVB-S2 Base Band Frame. } catch (NotImplementedException) { // NOTE: Only Generic Continuous Stream Input is supported at this moment. } // move to the next message if (!reader.NewMessage()) { break; } } }); this._decapsulator.Completion.ContinueWith(t => { if (t.IsFaulted) { ((IDataflowBlock)this._outputBuffer).Fault(t.Exception); } else { this._outputBuffer.Complete(); } }); }