/// <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);
        }