/// <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);
        }
Exemplo n.º 2
0
        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();
                }
            });
        }