/// <summary> /// Retrieve content of this buffer as a decapsulated frame and clear this buffer. /// </summary> /// <exception cref="InvalidOperationException">If buffer does not contain completed sequence of GSE fragments.</exception> /// <returns>Reassembled and decapsulated frame.</returns> public PmFrameBase Retrieve() { if (!this.IsCompleted) { throw new InvalidOperationException("Buffer does not contain completed sequence of GSE fragments."); } var decapsulatedFrame = PmFrameEncapsulated.Create(this._buffer); foreach (var encapsulatingFrame in this._encapsulatingFrames) { decapsulatedFrame.DecapsulatedFromFrames.Add(encapsulatingFrame); encapsulatingFrame.EncapsulatedFrames.Add(decapsulatedFrame); } this.Clear(); return(decapsulatedFrame); }
/// <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); }