private IList <CtfStreamPlayback> GeneratePlaybackStreams(ICtfInput source, CtfPlaybackOptions playbackOptions)
        {
            var playbackStreams = new List <CtfStreamPlayback>();

            // Initialize packets from all streams, and sort by times
            foreach (var trace in source.Traces)
            {
                var          metadataParser = customization.CreateMetadataParser(trace);
                ICtfMetadata metadata       = metadataParser.Parse(trace.MetadataStream.Stream);

                for (int streamIndex = 0; streamIndex < trace.EventStreams.Count; streamIndex++)
                {
                    var stream         = trace.EventStreams[streamIndex];
                    var eventStream    = new CtfEventStream(stream, metadata, customization);
                    var streamPlayback = new CtfStreamPlayback(eventStream, playbackOptions, cancellationToken);
                    if (eventStream.ByteCount > 0 && streamPlayback.MoveToNextEvent())
                    {
                        Debug.Assert(streamPlayback.CurrentEvent != null);
                        playbackStreams.Add(streamPlayback);
                        this.streamToTrace.Add(streamPlayback, trace);
                        this.totalBytesToProcess += stream.ByteCount;
                    }
                    else
                    {
                        Debug.Assert(false, eventStream.StreamSource + " appears to have no data.\n\n Ignoring the error will cause the trace to be partially loaded.");
                    }
                }
            }

            return(playbackStreams);
        }
        /// <summary>
        /// Playback the events in time order. Event callbacks happen through ICtfPlaybackCustomization.ProcessEvent,
        /// allowing for customized event delivery.
        /// </summary>
        /// <param name="source">CTF trace input source</param>
        /// <param name="playbackOptions">Playback options</param>
        /// <param name="progress">Progress meter</param>
        public void Playback(
            ICtfInput source,
            CtfPlaybackOptions playbackOptions,
            IProgress <byte> progress)
        {
            Guard.NotNull(source, nameof(source));
            Guard.NotNull(progress, nameof(progress));

            var playbackStreams = GeneratePlaybackStreams(source, playbackOptions);

            while (playbackStreams.Any())
            {
                // Find the event stream with the earlieste available event.

                CtfStreamPlayback streamPlayback = playbackStreams[0];
                for (int x = 1; x < playbackStreams.Count; x++)
                {
                    if (playbackStreams[x].CurrentEvent.Timestamp.NanosecondsFromPosixEpoch < streamPlayback.CurrentEvent.Timestamp.NanosecondsFromPosixEpoch)
                    {
                        streamPlayback = playbackStreams[x];
                    }
                }

                if (streamPlayback.CurrentEvent.Timestamp.NanosecondsFromPosixEpoch < this.lastEventTimestamp)
                {
                    Debug.Assert(false, "time inversion?");
                    Console.Error.WriteLine("Time inversion discovered in LTTng trace.");
                }

                this.customization.ProcessEvent(
                    streamPlayback.CurrentEvent,
                    streamPlayback.CurrentPacket,
                    this.streamToTrace[streamPlayback],
                    streamPlayback.EventStream,
                    streamPlayback.Metadata);

                this.eventCount++;

                this.lastEventTimestamp = streamPlayback.CurrentEvent.Timestamp.NanosecondsFromPosixEpoch;

                if (!streamPlayback.MoveToNextEvent())
                {
                    playbackStreams.Remove(streamPlayback);
                    this.bytesProcessedFromCompletedStreams += streamPlayback.EventStream.ByteCount;
                }

                if (this.eventCount - this.lastProgressUpdateTimeEventNumber >= 5000)
                {
                    this.UpdateProgress(playbackStreams, progress);
                }

                this.cancellationToken.ThrowIfCancellationRequested();
            }

            this.UpdateProgress(playbackStreams, progress);
        }