# Source: segment-inspector.js

    /**
     * @file segment-inspector.js
     */

    import {TransportPacketStream, TransportParseStream, ElementaryStream, TimestampRolloverStream} from 'mux.js/lib/m2ts';
    import StreamTypes from 'mux.js/lib/m2ts/stream-types.js';

    const PES_TIMESCALE = 90000;

    /**
     * An object that inspects ts segment data to extract timing information for
     * more precise calculations
     *
     * @class SegmentInspector
     */
    export default class SegmentInspector {
      constructor() {
        this.transportPacketStream = new TransportPacketStream();
        this.transportParseStream = new TransportParseStream();
        this.elementaryStream = new ElementaryStream();
        this.audioTimestampRolloverStream = new TimestampRolloverStream('audio');
        this.videoTimestampRolloverStream = new TimestampRolloverStream('video');
      }

      /**
       * Inspects the given segment byte array and returns an object with timing
       * information for the first and last packet.
       */
      inspect(segment) {
        var tsPackets = [];
        var segmentInfo;

        this.transportPacketStream
          .pipe(this.transportParseStream);

        this.transportParseStream.on('data', function(event) {
          tsPackets.push(event);
        });

        this.transportParseStream.on('done', () => {
          segmentInfo = this.parsePackets_(tsPackets);
        });

        this.transportPacketStream.push(segment);
        this.transportPacketStream.flush();

        this.adjustTimestamp_(segmentInfo);

        this.dispose();
        return segmentInfo;
      }

      /**
       * Parse the given pes packets to gain information from the first and last complete packet
       */
      parsePackets_(packets) {
        var segmentInfo = {
          video: [],
          audio: []
        };

        var processData = true;
        var first = true;

        this.elementaryStream.on('data', function(data) {
          if (processData) {
            if (data.type === 'audio') {
              if ((first && segmentInfo.audio.length === 0) ||
                  (!first && segmentInfo.audio.length === 1)) {
                segmentInfo.audio.push(data);
              }
            }
            if (data.type === 'video') {
              if ((first && segmentInfo.video.length === 0) ||
                  (!first && segmentInfo.video.length === 1)) {
                segmentInfo.video.push(data);
              }
            }
            if (first &&
                segmentInfo.audio.length === 1 &&
                segmentInfo.video.length === 1) {
              processData = false;
            } else if (!first &&
                        segmentInfo.audio.length === 2 &&
                        segmentInfo.video.length === 2) {
              processData = false;
            }
          }
        });

        let i = 0;
        let packet;

        while(processData && i < packets.length) {
          packet = packets[i];
          this.elementaryStream.push(packet);
          i++;
        }

        this.elementaryStream.flush();

        processData = true;
        first = false;

        i = packets.length - 1;

        let lastPes = {
          audio: {
            done: false,
            data: []
          },
          video: {
            done: false,
            data: []
          }
        };

        // Walk back from the end to find the last video and audio pes packets
        while(i > -1) {
          packet = packets[i];
          let streamType;

          switch (packet.streamType) {
            case StreamTypes.H264_STREAM_TYPE:
              streamType = 'video';
              break;
            case StreamTypes.ADTS_STREAM_TYPE:
              streamType = 'audio';
              break;
            default:
              i--;
              continue;
          }
          if (!lastPes[streamType].done) {
            lastPes[streamType].data.unshift(packet);

            if (packet.payloadUnitStartIndicator) {
              lastPes[streamType].done = true;
            }
          }

          if (lastPes.audio.done && lastPes.video.done) {
            break;
          }

          i--;
        }

        lastPes.audio.data.forEach((packet) => {
          this.elementaryStream.push(packet);
        });
        lastPes.video.data.forEach((packet) => {
          this.elementaryStream.push(packet);
        });
        this.elementaryStream.flush();

        return segmentInfo;
      }

      /**
       * Adjusts the timestamp information for the segment to account for
       * rollover and convert to seconds based on pes packet timescale (90khz clock)
       */
      adjustTimestamp_(segmentInfo) {
        var i = 0;

        this.audioTimestampRolloverStream.on('data', function(data) {
          segmentInfo.audio[i].pts = data.pts / PES_TIMESCALE;
          segmentInfo.audio[i].dts = data.dts / PES_TIMESCALE;
        });

        this.videoTimestampRolloverStream.on('data', function(data) {
          segmentInfo.video[i].pts = data.pts / PES_TIMESCALE;
          segmentInfo.video[i].dts = data.dts / PES_TIMESCALE;
        });

        this.audioTimestampRolloverStream.push(segmentInfo.audio[i]);
        this.videoTimestampRolloverStream.push(segmentInfo.video[i]);

        i = 1;

        this.audioTimestampRolloverStream.push(segmentInfo.audio[i]);
        this.videoTimestampRolloverStream.push(segmentInfo.video[i]);

        this.audioTimestampRolloverStream.flush();
        this.videoTimestampRolloverStream.flush();
      }

      dispose() {
        this.transportPacketStream.dispose();
        this.transportParseStream.dispose();
        this.elementaryStream.dispose();
        this.audioTimestampRolloverStream.dispose();
        this.videoTimestampRolloverStream.dispose();
      }
    }