diff options
Diffstat (limited to 'BDInfo/TSStreamFile.cs')
| -rw-r--r-- | BDInfo/TSStreamFile.cs | 1549 |
1 files changed, 1549 insertions, 0 deletions
diff --git a/BDInfo/TSStreamFile.cs b/BDInfo/TSStreamFile.cs new file mode 100644 index 000000000..86cefe826 --- /dev/null +++ b/BDInfo/TSStreamFile.cs @@ -0,0 +1,1549 @@ +//============================================================================ +// BDInfo - Blu-ray Video and Audio Analysis Tool +// Copyright © 2010 Cinema Squid +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//============================================================================= + +#undef DEBUG +using System; +using System.Collections.Generic; +using System.IO; + +namespace BDInfo +{ + public class TSStreamState + { + public ulong TransferCount = 0; + + public string StreamTag = null; + + public ulong TotalPackets = 0; + public ulong WindowPackets = 0; + + public ulong TotalBytes = 0; + public ulong WindowBytes = 0; + + public long PeakTransferLength = 0; + public long PeakTransferRate = 0; + + public double TransferMarker = 0; + public double TransferInterval = 0; + + public TSStreamBuffer StreamBuffer = new TSStreamBuffer(); + + public uint Parse = 0; + public bool TransferState = false; + public int TransferLength = 0; + public int PacketLength = 0; + public byte PacketLengthParse = 0; + public byte PacketParse = 0; + + public byte PTSParse = 0; + public ulong PTS = 0; + public ulong PTSTemp = 0; + public ulong PTSLast = 0; + public ulong PTSPrev = 0; + public ulong PTSDiff = 0; + public ulong PTSCount = 0; + public ulong PTSTransfer = 0; + + public byte DTSParse = 0; + public ulong DTSTemp = 0; + public ulong DTSPrev = 0; + + public byte PESHeaderLength = 0; + public byte PESHeaderFlags = 0; +#if DEBUG + public byte PESHeaderIndex = 0; + public byte[] PESHeader = new byte[256 + 9]; +#endif + } + + public class TSPacketParser + { + public bool SyncState = false; + public byte TimeCodeParse = 4; + public byte PacketLength = 0; + public byte HeaderParse = 0; + + public uint TimeCode; + public byte TransportErrorIndicator; + public byte PayloadUnitStartIndicator; + public byte TransportPriority; + public ushort PID; + public byte TransportScramblingControl; + public byte AdaptionFieldControl; + + public bool AdaptionFieldState = false; + public byte AdaptionFieldParse = 0; + public byte AdaptionFieldLength = 0; + + public ushort PCRPID = 0xFFFF; + public byte PCRParse = 0; + public ulong PreviousPCR = 0; + public ulong PCR = 0; + public ulong PCRCount = 0; + public ulong PTSFirst = ulong.MaxValue; + public ulong PTSLast = ulong.MinValue; + public ulong PTSDiff = 0; + + public byte[] PAT = new byte[1024]; + public bool PATSectionStart = false; + public byte PATPointerField = 0; + public uint PATOffset = 0; + public byte PATSectionLengthParse = 0; + public ushort PATSectionLength = 0; + public uint PATSectionParse = 0; + public bool PATTransferState = false; + public byte PATSectionNumber = 0; + public byte PATLastSectionNumber = 0; + + public ushort TransportStreamId = 0xFFFF; + + public List<TSDescriptor> PMTProgramDescriptors = new List<TSDescriptor>(); + public ushort PMTPID = 0xFFFF; + public Dictionary<ushort, byte[]> PMT = new Dictionary<ushort, byte[]>(); + public bool PMTSectionStart = false; + public ushort PMTProgramInfoLength = 0; + public byte PMTProgramDescriptor = 0; + public byte PMTProgramDescriptorLengthParse = 0; + public byte PMTProgramDescriptorLength = 0; + public ushort PMTStreamInfoLength = 0; + public uint PMTStreamDescriptorLengthParse = 0; + public uint PMTStreamDescriptorLength = 0; + public byte PMTPointerField = 0; + public uint PMTOffset = 0; + public uint PMTSectionLengthParse = 0; + public ushort PMTSectionLength = 0; + public uint PMTSectionParse = 0; + public bool PMTTransferState = false; + public byte PMTSectionNumber = 0; + public byte PMTLastSectionNumber = 0; + + public byte PMTTemp = 0; + + public TSStream Stream = null; + public TSStreamState StreamState = null; + + public ulong TotalPackets = 0; + } + + public class TSStreamDiagnostics + { + public ulong Bytes = 0; + public ulong Packets = 0; + public double Marker = 0; + public double Interval = 0; + public string Tag = null; + } + + public class TSStreamFile + { + public FileInfo FileInfo = null; + public string Name = null; + public long Size = 0; + public double Length = 0; + + public TSInterleavedFile InterleavedFile = null; + + private Dictionary<ushort, TSStreamState> StreamStates = + new Dictionary<ushort, TSStreamState>(); + + public Dictionary<ushort, TSStream> Streams = + new Dictionary<ushort, TSStream>(); + + public Dictionary<ushort, List<TSStreamDiagnostics>> StreamDiagnostics = + new Dictionary<ushort, List<TSStreamDiagnostics>>(); + + private List<TSPlaylistFile> Playlists = null; + + public TSStreamFile(FileInfo fileInfo) + { + FileInfo = fileInfo; + Name = fileInfo.Name.ToUpper(); + } + + public string DisplayName + { + get + { + if (BDInfoSettings.EnableSSIF && + InterleavedFile != null) + { + return InterleavedFile.Name; + } + return Name; + } + } + + private bool ScanStream( + TSStream stream, + TSStreamState streamState, + TSStreamBuffer buffer) + { + streamState.StreamTag = null; + + long bitrate = 0; + if (stream.IsAudioStream && + streamState.PTSTransfer > 0) + { + bitrate = (long)Math.Round( + (buffer.TransferLength * 8.0) / + ((double)streamState.PTSTransfer / 90000)); + + if (bitrate > streamState.PeakTransferRate) + { + streamState.PeakTransferRate = bitrate; + } + } + if (buffer.TransferLength > streamState.PeakTransferLength) + { + streamState.PeakTransferLength = buffer.TransferLength; + } + + buffer.BeginRead(); + switch (stream.StreamType) + { + case TSStreamType.MPEG2_VIDEO: + TSCodecMPEG2.Scan( + (TSVideoStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.AVC_VIDEO: + TSCodecAVC.Scan( + (TSVideoStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.MVC_VIDEO: + TSCodecMVC.Scan( + (TSVideoStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.VC1_VIDEO: + TSCodecVC1.Scan( + (TSVideoStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.AC3_AUDIO: + TSCodecAC3.Scan( + (TSAudioStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.AC3_PLUS_AUDIO: + case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: + TSCodecAC3.Scan( + (TSAudioStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.AC3_TRUE_HD_AUDIO: + TSCodecTrueHD.Scan( + (TSAudioStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.LPCM_AUDIO: + TSCodecLPCM.Scan( + (TSAudioStream)stream, buffer, ref streamState.StreamTag); + break; + + case TSStreamType.DTS_AUDIO: + TSCodecDTS.Scan( + (TSAudioStream)stream, buffer, bitrate, ref streamState.StreamTag); + break; + + case TSStreamType.DTS_HD_AUDIO: + case TSStreamType.DTS_HD_MASTER_AUDIO: + case TSStreamType.DTS_HD_SECONDARY_AUDIO: + TSCodecDTSHD.Scan( + (TSAudioStream)stream, buffer, bitrate, ref streamState.StreamTag); + break; + + default: + stream.IsInitialized = true; + break; + } + buffer.EndRead(); + streamState.StreamBuffer.Reset(); + + bool isAVC = false; + bool isMVC = false; + foreach (TSStream finishedStream in Streams.Values) + { + if (!finishedStream.IsInitialized) + { + return false; + } + if (finishedStream.StreamType == TSStreamType.AVC_VIDEO) + { + isAVC = true; + } + if (finishedStream.StreamType == TSStreamType.MVC_VIDEO) + { + isMVC = true; + } + } + if (isMVC && !isAVC) + { + return false; + } + return true; + } + + private void UpdateStreamBitrates( + ushort PTSPID, + ulong PTS, + ulong PTSDiff) + { + if (Playlists == null) return; + + foreach (ushort PID in StreamStates.Keys) + { + if (Streams.ContainsKey(PID) && + Streams[PID].IsVideoStream && + PID != PTSPID) + { + continue; + } + if (StreamStates[PID].WindowPackets == 0) + { + continue; + } + UpdateStreamBitrate(PID, PTSPID, PTS, PTSDiff); + } + + foreach (TSPlaylistFile playlist in Playlists) + { + double packetSeconds = 0; + foreach (TSStreamClip clip in playlist.StreamClips) + { + if (clip.AngleIndex == 0) + { + packetSeconds += clip.PacketSeconds; + } + } + if (packetSeconds > 0) + { + foreach (TSStream playlistStream in playlist.SortedStreams) + { + if (playlistStream.IsVBR) + { + playlistStream.BitRate = (long)Math.Round( + ((playlistStream.PayloadBytes * 8.0) / packetSeconds)); + + if (playlistStream.StreamType == TSStreamType.AC3_TRUE_HD_AUDIO && + ((TSAudioStream)playlistStream).CoreStream != null) + { + playlistStream.BitRate -= + ((TSAudioStream)playlistStream).CoreStream.BitRate; + } + } + } + } + } + } + + private void UpdateStreamBitrate( + ushort PID, + ushort PTSPID, + ulong PTS, + ulong PTSDiff) + { + if (Playlists == null) return; + + TSStreamState streamState = StreamStates[PID]; + double streamTime = (double)PTS / 90000; + double streamInterval = (double)PTSDiff / 90000; + double streamOffset = streamTime + streamInterval; + + foreach (TSPlaylistFile playlist in Playlists) + { + foreach (TSStreamClip clip in playlist.StreamClips) + { + if (clip.Name != this.Name) continue; + + if (streamTime == 0 || + (streamTime >= clip.TimeIn && + streamTime <= clip.TimeOut)) + { + clip.PayloadBytes += streamState.WindowBytes; + clip.PacketCount += streamState.WindowPackets; + + if (streamOffset > clip.TimeIn && + streamOffset - clip.TimeIn > clip.PacketSeconds) + { + clip.PacketSeconds = streamOffset - clip.TimeIn; + } + + Dictionary<ushort, TSStream> playlistStreams = playlist.Streams; + if (clip.AngleIndex > 0 && + clip.AngleIndex < playlist.AngleStreams.Count + 1) + { + playlistStreams = playlist.AngleStreams[clip.AngleIndex - 1]; + } + if (playlistStreams.ContainsKey(PID)) + { + TSStream stream = playlistStreams[PID]; + + stream.PayloadBytes += streamState.WindowBytes; + stream.PacketCount += streamState.WindowPackets; + + if (stream.IsVideoStream) + { + stream.PacketSeconds += streamInterval; + + stream.ActiveBitRate = (long)Math.Round( + ((stream.PayloadBytes * 8.0) / + stream.PacketSeconds)); + } + + if (stream.StreamType == TSStreamType.AC3_TRUE_HD_AUDIO && + ((TSAudioStream)stream).CoreStream != null) + { + stream.ActiveBitRate -= + ((TSAudioStream)stream).CoreStream.BitRate; + } + } + } + } + } + + if (Streams.ContainsKey(PID)) + { + TSStream stream = Streams[PID]; + stream.PayloadBytes += streamState.WindowBytes; + stream.PacketCount += streamState.WindowPackets; + + if (stream.IsVideoStream) + { + TSStreamDiagnostics diag = new TSStreamDiagnostics(); + diag.Marker = (double)PTS / 90000; + diag.Interval = (double)PTSDiff / 90000; + diag.Bytes = streamState.WindowBytes; + diag.Packets = streamState.WindowPackets; + diag.Tag = streamState.StreamTag; + StreamDiagnostics[PID].Add(diag); + + stream.PacketSeconds += streamInterval; + } + } + streamState.WindowPackets = 0; + streamState.WindowBytes = 0; + } + + public void Scan(List<TSPlaylistFile> playlists, bool isFullScan) + { + if (playlists == null || playlists.Count == 0) + { + return; + } + + Playlists = playlists; + int dataSize = 16384; + FileStream fileStream = null; + try + { + string fileName; + if (BDInfoSettings.EnableSSIF && + InterleavedFile != null) + { + fileName = InterleavedFile.FileInfo.FullName; + } + else + { + fileName = FileInfo.FullName; + } + fileStream = new FileStream( + fileName, + FileMode.Open, + FileAccess.Read, + FileShare.Read, + dataSize, false); + + Size = 0; + Length = 0; + + Streams.Clear(); + StreamStates.Clear(); + StreamDiagnostics.Clear(); + + TSPacketParser parser = + new TSPacketParser(); + + long fileLength = (uint)fileStream.Length; + byte[] buffer = new byte[dataSize]; + int bufferLength = 0; + while ((bufferLength = + fileStream.Read(buffer, 0, buffer.Length)) > 0) + { + int offset = 0; + for (int i = 0; i < bufferLength; i++) + { + if (parser.SyncState == false) + { + if (parser.TimeCodeParse > 0) + { + parser.TimeCodeParse--; + switch (parser.TimeCodeParse) + { + case 3: + parser.TimeCode = 0; + parser.TimeCode |= + ((uint)buffer[i] & 0x3F) << 24; + break; + case 2: + parser.TimeCode |= + ((uint)buffer[i] & 0xFF) << 16; + break; + case 1: + parser.TimeCode |= + ((uint)buffer[i] & 0xFF) << 8; + break; + case 0: + parser.TimeCode |= + ((uint)buffer[i] & 0xFF); + break; + } + } + else if (buffer[i] == 0x47) + { + parser.SyncState = true; + parser.PacketLength = 187; + parser.TimeCodeParse = 4; + parser.HeaderParse = 3; + } + } + else if (parser.HeaderParse > 0) + { + parser.PacketLength--; + parser.HeaderParse--; + + switch (parser.HeaderParse) + { + case 2: + { + parser.TransportErrorIndicator = + (byte)((buffer[i] >> 7) & 0x1); + parser.PayloadUnitStartIndicator = + (byte)((buffer[i] >> 6) & 0x1); + parser.TransportPriority = + (byte)((buffer[i] >> 5) & 0x1); + parser.PID = + (ushort)((buffer[i] & 0x1f) << 8); + } + break; + + case 1: + { + parser.PID |= (ushort)buffer[i]; + if (Streams.ContainsKey(parser.PID)) + { + parser.Stream = Streams[parser.PID]; + } + else + { + parser.Stream = null; + } + if (!StreamStates.ContainsKey(parser.PID)) + { + StreamStates[parser.PID] = new TSStreamState(); + } + parser.StreamState = StreamStates[parser.PID]; + parser.StreamState.TotalPackets++; + parser.StreamState.WindowPackets++; + parser.TotalPackets++; + } + break; + + case 0: + { + parser.TransportScramblingControl = + (byte)((buffer[i] >> 6) & 0x3); + parser.AdaptionFieldControl = + (byte)((buffer[i] >> 4) & 0x3); + + if ((parser.AdaptionFieldControl & 0x2) == 0x2) + { + parser.AdaptionFieldState = true; + } + if (parser.PayloadUnitStartIndicator == 1) + { + if (parser.PID == 0) + { + parser.PATSectionStart = true; + } + else if (parser.PID == parser.PMTPID) + { + parser.PMTSectionStart = true; + } + else if (parser.StreamState != null && + parser.StreamState.TransferState) + { + parser.StreamState.TransferState = false; + parser.StreamState.TransferCount++; + + bool isFinished = ScanStream( + parser.Stream, + parser.StreamState, + parser.StreamState.StreamBuffer); + + if (!isFullScan && isFinished) + { + return; + } + } + } + } + break; + } + } + else if (parser.AdaptionFieldState) + { + parser.PacketLength--; + parser.AdaptionFieldParse = buffer[i]; + parser.AdaptionFieldLength = buffer[i]; + parser.AdaptionFieldState = false; + } + else if (parser.AdaptionFieldParse > 0) + { + parser.PacketLength--; + parser.AdaptionFieldParse--; + if ((parser.AdaptionFieldLength - parser.AdaptionFieldParse) == 1) + { + if ((buffer[i] & 0x10) == 0x10) + { + parser.PCRParse = 6; + parser.PCR = 0; + } + } + else if (parser.PCRParse > 0) + { + parser.PCRParse--; + parser.PCR = (parser.PCR << 8) + (ulong)buffer[i]; + if (parser.PCRParse == 0) + { + parser.PreviousPCR = parser.PCR; + parser.PCR = (parser.PCR & 0x1FF) + + ((parser.PCR >> 15) * 300); + } + parser.PCRCount++; + } + if (parser.PacketLength == 0) + { + parser.SyncState = false; + } + } + else if (parser.PID == 0) + { + if (parser.PATTransferState) + { + if ((bufferLength - i) > parser.PATSectionLength) + { + offset = parser.PATSectionLength; + } + else + { + offset = (bufferLength - i); + } + if (parser.PacketLength <= offset) + { + offset = parser.PacketLength; + } + + for (int k = 0; k < offset; k++) + { + parser.PAT[parser.PATOffset++] = buffer[i++]; + parser.PATSectionLength--; + parser.PacketLength--; + } --i; + + if (parser.PATSectionLength == 0) + { + parser.PATTransferState = false; + if (parser.PATSectionNumber == parser.PATLastSectionNumber) + { + for (int k = 0; k < (parser.PATOffset - 4); k += 4) + { + uint programNumber = (uint) + ((parser.PAT[k] << 8) + + parser.PAT[k + 1]); + + ushort programPID = (ushort) + (((parser.PAT[k + 2] & 0x1F) << 8) + + parser.PAT[k + 3]); + + if (programNumber == 1) + { + parser.PMTPID = programPID; + } + } + } + } + } + else + { + --parser.PacketLength; + if (parser.PATSectionStart) + { + parser.PATPointerField = buffer[i]; + if (parser.PATPointerField == 0) + { + parser.PATSectionLengthParse = 3; + } + parser.PATSectionStart = false; + } + else if (parser.PATPointerField > 0) + { + --parser.PATPointerField; + if (parser.PATPointerField == 0) + { + parser.PATSectionLengthParse = 3; + } + } + else if (parser.PATSectionLengthParse > 0) + { + --parser.PATSectionLengthParse; + switch (parser.PATSectionLengthParse) + { + case 2: + break; + case 1: + parser.PATSectionLength = (ushort) + ((buffer[i] & 0xF) << 8); + break; + case 0: + parser.PATSectionLength |= buffer[i]; + if (parser.PATSectionLength > 1021) + { + parser.PATSectionLength = 0; + } + else + { + parser.PATSectionParse = 5; + } + break; + } + } + else if (parser.PATSectionParse > 0) + { + --parser.PATSectionLength; + --parser.PATSectionParse; + + switch (parser.PATSectionParse) + { + case 4: + parser.TransportStreamId = (ushort) + (buffer[i] << 8); + break; + case 3: + parser.TransportStreamId |= buffer[i]; + break; + case 2: + break; + case 1: + parser.PATSectionNumber = buffer[i]; + if (parser.PATSectionNumber == 0) + { + parser.PATOffset = 0; + } + break; + case 0: + parser.PATLastSectionNumber = buffer[i]; + parser.PATTransferState = true; + break; + } + } + } + if (parser.PacketLength == 0) + { + parser.SyncState = false; + } + } + else if (parser.PID == parser.PMTPID) + { + if (parser.PMTTransferState) + { + if ((bufferLength - i) >= parser.PMTSectionLength) + { + offset = parser.PMTSectionLength; + } + else + { + offset = (bufferLength - i); + } + if (parser.PacketLength <= offset) + { + offset = parser.PacketLength; + } + if (!parser.PMT.ContainsKey(parser.PID)) + { + parser.PMT[parser.PID] = new byte[1024]; + } + + byte[] PMT = parser.PMT[parser.PID]; + for (int k = 0; k < offset; k++) + { + PMT[parser.PMTOffset++] = buffer[i++]; + --parser.PMTSectionLength; + --parser.PacketLength; + } --i; + + if (parser.PMTSectionLength == 0) + { + parser.PMTTransferState = false; + if (parser.PMTSectionNumber == parser.PMTLastSectionNumber) + { + //Console.WriteLine("PMT Start: " + parser.PMTTemp); + try + { + for (int k = 0; k < (parser.PMTOffset - 4); k += 5) + { + byte streamType = PMT[k]; + + ushort streamPID = (ushort) + (((PMT[k + 1] & 0x1F) << 8) + + PMT[k + 2]); + + ushort streamInfoLength = (ushort) + (((PMT[k + 3] & 0xF) << 8) + + PMT[k + 4]); + + /* + if (streamInfoLength == 2) + { + // TODO: Cleanup + //streamInfoLength = 0; + } + + Console.WriteLine(string.Format( + "Type: {0} PID: {1} Length: {2}", + streamType, streamPID, streamInfoLength)); + */ + + if (!Streams.ContainsKey(streamPID)) + { + List<TSDescriptor> streamDescriptors = + new List<TSDescriptor>(); + + /* + * TODO: Getting bad streamInfoLength + if (streamInfoLength > 0) + { + for (int d = 0; d < streamInfoLength; d++) + { + byte name = PMT[k + d + 5]; + byte length = PMT[k + d + 6]; + TSDescriptor descriptor = + new TSDescriptor(name, length); + for (int v = 0; v < length; v++) + { + descriptor.Value[v] = + PMT[k + d + v + 7]; + } + streamDescriptors.Add(descriptor); + d += (length + 1); + } + } + */ + CreateStream(streamPID, streamType, streamDescriptors); + } + k += streamInfoLength; + } + } + catch (Exception ex) + { + // TODO + //Console.WriteLine(ex.Message); + } + } + } + } + else + { + --parser.PacketLength; + if (parser.PMTSectionStart) + { + parser.PMTPointerField = buffer[i]; + if (parser.PMTPointerField == 0) + { + parser.PMTSectionLengthParse = 3; + } + parser.PMTSectionStart = false; + } + else if (parser.PMTPointerField > 0) + { + --parser.PMTPointerField; + if (parser.PMTPointerField == 0) + { + parser.PMTSectionLengthParse = 3; + } + } + else if (parser.PMTSectionLengthParse > 0) + { + --parser.PMTSectionLengthParse; + switch (parser.PMTSectionLengthParse) + { + case 2: + if (buffer[i] != 0x2) + { + parser.PMTSectionLengthParse = 0; + } + break; + case 1: + parser.PMTSectionLength = (ushort) + ((buffer[i] & 0xF) << 8); + break; + case 0: + parser.PMTSectionLength |= buffer[i]; + if (parser.PMTSectionLength > 1021) + { + parser.PMTSectionLength = 0; + } + else + { + parser.PMTSectionParse = 9; + } + break; + } + } + else if (parser.PMTSectionParse > 0) + { + --parser.PMTSectionLength; + --parser.PMTSectionParse; + + switch (parser.PMTSectionParse) + { + case 8: + case 7: + break; + case 6: + parser.PMTTemp = buffer[i]; + break; + case 5: + parser.PMTSectionNumber = buffer[i]; + if (parser.PMTSectionNumber == 0) + { + parser.PMTOffset = 0; + } + break; + case 4: + parser.PMTLastSectionNumber = buffer[i]; + break; + case 3: + parser.PCRPID = (ushort) + ((buffer[i] & 0x1F) << 8); + break; + case 2: + parser.PCRPID |= buffer[i]; + break; + case 1: + parser.PMTProgramInfoLength = (ushort) + ((buffer[i] & 0xF) << 8); + break; + case 0: + parser.PMTProgramInfoLength |= buffer[i]; + if (parser.PMTProgramInfoLength == 0) + { + parser.PMTTransferState = true; + } + else + { + parser.PMTProgramDescriptorLengthParse = 2; + } + break; + } + } + else if (parser.PMTProgramInfoLength > 0) + { + --parser.PMTSectionLength; + --parser.PMTProgramInfoLength; + + if (parser.PMTProgramDescriptorLengthParse > 0) + { + --parser.PMTProgramDescriptorLengthParse; + switch (parser.PMTProgramDescriptorLengthParse) + { + case 1: + parser.PMTProgramDescriptor = buffer[i]; + break; + case 0: + parser.PMTProgramDescriptorLength = buffer[i]; + parser.PMTProgramDescriptors.Add( + new TSDescriptor( + parser.PMTProgramDescriptor, + parser.PMTProgramDescriptorLength)); + break; + } + } + else if (parser.PMTProgramDescriptorLength > 0) + { + --parser.PMTProgramDescriptorLength; + + TSDescriptor descriptor = parser.PMTProgramDescriptors[ + parser.PMTProgramDescriptors.Count - 1]; + + int valueIndex = + descriptor.Value.Length - + parser.PMTProgramDescriptorLength - 1; + + descriptor.Value[valueIndex] = buffer[i]; + + if (parser.PMTProgramDescriptorLength == 0 && + parser.PMTProgramInfoLength > 0) + { + parser.PMTProgramDescriptorLengthParse = 2; + } + } + if (parser.PMTProgramInfoLength == 0) + { + parser.PMTTransferState = true; + } + } + } + if (parser.PacketLength == 0) + { + parser.SyncState = false; + } + } + else if (parser.Stream != null && + parser.StreamState != null && + parser.TransportScramblingControl == 0) + { + TSStream stream = parser.Stream; + TSStreamState streamState = parser.StreamState; + + streamState.Parse = + (streamState.Parse << 8) + buffer[i]; + + if (streamState.TransferState) + { + if ((bufferLength - i) >= streamState.PacketLength && + streamState.PacketLength > 0) + { + offset = streamState.PacketLength; + } + else + { + offset = (bufferLength - i); + } + if (parser.PacketLength <= offset) + { + offset = parser.PacketLength; + } + streamState.TransferLength = offset; + + if (!stream.IsInitialized || + stream.IsVideoStream) + { + streamState.StreamBuffer.Add( + buffer, i, offset); + } + else + { + streamState.StreamBuffer.TransferLength += offset; + } + + i += (int)(streamState.TransferLength - 1); + streamState.PacketLength -= streamState.TransferLength; + parser.PacketLength -= (byte)streamState.TransferLength; + + streamState.TotalBytes += (ulong)streamState.TransferLength; + streamState.WindowBytes += (ulong)streamState.TransferLength; + + if (streamState.PacketLength == 0) + { + streamState.TransferState = false; + streamState.TransferCount++; + bool isFinished = ScanStream( + stream, + streamState, + streamState.StreamBuffer); + + if (!isFullScan && isFinished) + { + return; + } + } + } + else + { + --parser.PacketLength; + + bool headerFound = false; + if (stream.IsVideoStream && + streamState.Parse == 0x000001FD) + { + headerFound = true; + } + if (stream.IsVideoStream && + streamState.Parse >= 0x000001E0 && + streamState.Parse <= 0x000001EF) + { + headerFound = true; + } + if (stream.IsAudioStream && + streamState.Parse == 0x000001BD) + { + headerFound = true; + } + if (stream.IsAudioStream && + (streamState.Parse == 0x000001FA || + streamState.Parse == 0x000001FD)) + { + headerFound = true; + } + + if (!stream.IsVideoStream && + !stream.IsAudioStream && + (streamState.Parse == 0x000001FA || + streamState.Parse == 0x000001FD || + streamState.Parse == 0x000001BD || + (streamState.Parse >= 0x000001E0 && + streamState.Parse <= 0x000001EF))) + { + headerFound = true; + } + + if (headerFound) + { + streamState.PacketLengthParse = 2; +#if DEBUG + streamState.PESHeaderIndex = 0; + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)((streamState.Parse >> 24) & 0xFF); + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)((streamState.Parse >> 16) & 0xFF); + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)((streamState.Parse >> 8) & 0xFF); + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + } + else if (streamState.PacketLengthParse > 0) + { + --streamState.PacketLengthParse; + switch (streamState.PacketLengthParse) + { + case 1: +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 0: + streamState.PacketLength = + (int)(streamState.Parse & 0xFFFF); + streamState.PacketParse = 3; +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + } + } + else if (streamState.PacketParse > 0) + { + --streamState.PacketLength; + --streamState.PacketParse; + + switch (streamState.PacketParse) + { + case 2: +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 1: + streamState.PESHeaderFlags = + (byte)(streamState.Parse & 0xFF); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 0: + streamState.PESHeaderLength = + (byte)(streamState.Parse & 0xFF); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + if ((streamState.PESHeaderFlags & 0xC0) == 0x80) + { + streamState.PTSParse = 5; + } + else if ((streamState.PESHeaderFlags & 0xC0) == 0xC0) + { + streamState.DTSParse = 10; + } + if (streamState.PESHeaderLength == 0) + { + streamState.TransferState = true; + } + break; + } + } + else if (streamState.PTSParse > 0) + { + --streamState.PacketLength; + --streamState.PESHeaderLength; + --streamState.PTSParse; + + switch (streamState.PTSParse) + { + case 4: + streamState.PTSTemp = + ((streamState.Parse & 0xE) << 29); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + break; + + case 3: + streamState.PTSTemp |= + ((streamState.Parse & 0xFF) << 22); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 2: + streamState.PTSTemp |= + ((streamState.Parse & 0xFE) << 14); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 1: + streamState.PTSTemp |= + ((streamState.Parse & 0xFF) << 7); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 0: + streamState.PTSTemp |= + ((streamState.Parse & 0xFE) >> 1); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + streamState.PTS = streamState.PTSTemp; + + if (streamState.PTS > streamState.PTSLast) + { + if (streamState.PTSLast > 0) + { + streamState.PTSTransfer = (streamState.PTS - streamState.PTSLast); + } + streamState.PTSLast = streamState.PTS; + } + + streamState.PTSDiff = streamState.PTS - streamState.DTSPrev; + + if (streamState.PTSCount > 0 && + stream.IsVideoStream) + { + UpdateStreamBitrates(stream.PID, streamState.PTS, streamState.PTSDiff); + if (streamState.DTSTemp < parser.PTSFirst) + { + parser.PTSFirst = streamState.DTSTemp; + } + if (streamState.DTSTemp > parser.PTSLast) + { + parser.PTSLast = streamState.DTSTemp; + } + Length = (double)(parser.PTSLast - parser.PTSFirst) / 90000; + } + + streamState.DTSPrev = streamState.PTS; + streamState.PTSCount++; + if (streamState.PESHeaderLength == 0) + { + streamState.TransferState = true; + } + break; + } + } + else if (streamState.DTSParse > 0) + { + --streamState.PacketLength; + --streamState.PESHeaderLength; + --streamState.DTSParse; + + switch (streamState.DTSParse) + { + case 9: + streamState.PTSTemp = + ((streamState.Parse & 0xE) << 29); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 8: + streamState.PTSTemp |= + ((streamState.Parse & 0xFF) << 22); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 7: + streamState.PTSTemp |= + ((streamState.Parse & 0xFE) << 14); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + break; + + case 6: + streamState.PTSTemp |= + ((streamState.Parse & 0xFF) << 7); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 5: + streamState.PTSTemp |= + ((streamState.Parse & 0xFE) >> 1); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + streamState.PTS = streamState.PTSTemp; + if (streamState.PTS > streamState.PTSLast) + { + streamState.PTSLast = streamState.PTS; + } + break; + + case 4: + streamState.DTSTemp = + ((streamState.Parse & 0xE) << 29); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + break; + + case 3: + streamState.DTSTemp |= + ((streamState.Parse & 0xFF) << 22); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + break; + + case 2: + streamState.DTSTemp |= + ((streamState.Parse & 0xFE) << 14); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + break; + + case 1: + streamState.DTSTemp |= + ((streamState.Parse & 0xFF) << 7); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + break; + + case 0: + streamState.DTSTemp |= + ((streamState.Parse & 0xFE) >> 1); +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xff); +#endif + streamState.PTSDiff = streamState.DTSTemp - streamState.DTSPrev; + + if (streamState.PTSCount > 0 && + stream.IsVideoStream) + { + UpdateStreamBitrates(stream.PID, streamState.DTSTemp, streamState.PTSDiff); + if (streamState.DTSTemp < parser.PTSFirst) + { + parser.PTSFirst = streamState.DTSTemp; + } + if (streamState.DTSTemp > parser.PTSLast) + { + parser.PTSLast = streamState.DTSTemp; + } + Length = (double)(parser.PTSLast - parser.PTSFirst) / 90000; + } + streamState.DTSPrev = streamState.DTSTemp; + streamState.PTSCount++; + if (streamState.PESHeaderLength == 0) + { + streamState.TransferState = true; + } + break; + } + } + else if (streamState.PESHeaderLength > 0) + { + --streamState.PacketLength; + --streamState.PESHeaderLength; +#if DEBUG + streamState.PESHeader[streamState.PESHeaderIndex++] = + (byte)(streamState.Parse & 0xFF); +#endif + if (streamState.PESHeaderLength == 0) + { + streamState.TransferState = true; + } + } + } + if (parser.PacketLength == 0) + { + parser.SyncState = false; + } + } + else + { + parser.PacketLength--; + if ((bufferLength - i) >= parser.PacketLength) + { + i = i + parser.PacketLength; + parser.PacketLength = 0; + } + else + { + parser.PacketLength -= (byte)((bufferLength - i) + 1); + i = bufferLength; + } + if (parser.PacketLength == 0) + { + parser.SyncState = false; + } + } + } + Size += bufferLength; + } + + ulong PTSLast = 0; + ulong PTSDiff = 0; + foreach (TSStream stream in Streams.Values) + { + if (!stream.IsVideoStream) continue; + + if (StreamStates.ContainsKey(stream.PID) && + StreamStates[stream.PID].PTSLast > PTSLast) + { + PTSLast = StreamStates[stream.PID].PTSLast; + PTSDiff = PTSLast - StreamStates[stream.PID].DTSPrev; + } + UpdateStreamBitrates(stream.PID, PTSLast, PTSDiff); + } + } + finally + { + if (fileStream != null) + { + fileStream.Dispose(); + } + } + } + + private TSStream CreateStream( + ushort streamPID, + byte streamType, + List<TSDescriptor> streamDescriptors) + { + TSStream stream = null; + + switch ((TSStreamType)streamType) + { + case TSStreamType.MVC_VIDEO: + case TSStreamType.AVC_VIDEO: + case TSStreamType.MPEG1_VIDEO: + case TSStreamType.MPEG2_VIDEO: + case TSStreamType.VC1_VIDEO: + { + stream = new TSVideoStream(); + } + break; + + case TSStreamType.AC3_AUDIO: + case TSStreamType.AC3_PLUS_AUDIO: + case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: + case TSStreamType.AC3_TRUE_HD_AUDIO: + case TSStreamType.DTS_AUDIO: + case TSStreamType.DTS_HD_AUDIO: + case TSStreamType.DTS_HD_MASTER_AUDIO: + case TSStreamType.DTS_HD_SECONDARY_AUDIO: + case TSStreamType.LPCM_AUDIO: + case TSStreamType.MPEG1_AUDIO: + case TSStreamType.MPEG2_AUDIO: + { + stream = new TSAudioStream(); + } + break; + + case TSStreamType.INTERACTIVE_GRAPHICS: + case TSStreamType.PRESENTATION_GRAPHICS: + { + stream = new TSGraphicsStream(); + } + break; + + case TSStreamType.SUBTITLE: + { + stream = new TSTextStream(); + } + break; + + default: + break; + } + + if (stream != null && + !Streams.ContainsKey(streamPID)) + { + stream.PID = streamPID; + stream.StreamType = (TSStreamType)streamType; + stream.Descriptors = streamDescriptors; + Streams[stream.PID] = stream; + } + if (!StreamDiagnostics.ContainsKey(streamPID)) + { + StreamDiagnostics[streamPID] = + new List<TSStreamDiagnostics>(); + } + + return stream; + } + } +} |
