//============================================================================ // 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; using MediaBrowser.Model.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 PMTProgramDescriptors = new List(); public ushort PMTPID = 0xFFFF; public Dictionary PMT = new Dictionary(); 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 FileSystemMetadata FileInfo = null; public string Name = null; public long Size = 0; public double Length = 0; public TSInterleavedFile InterleavedFile = null; private Dictionary StreamStates = new Dictionary(); public Dictionary Streams = new Dictionary(); public Dictionary> StreamDiagnostics = new Dictionary>(); private List Playlists = null; private readonly IFileSystem _fileSystem; public TSStreamFile(FileSystemMetadata fileInfo, IFileSystem fileSystem) { FileInfo = fileInfo; _fileSystem = fileSystem; 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 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 playlists, bool isFullScan) { if (playlists == null || playlists.Count == 0) { return; } Playlists = playlists; int dataSize = 16384; Stream fileStream = null; try { string fileName; if (BDInfoSettings.EnableSSIF && InterleavedFile != null) { fileName = InterleavedFile.FileInfo.FullName; } else { fileName = FileInfo.FullName; } fileStream = _fileSystem.GetFileStream( fileName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, 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 streamDescriptors = new List(); /* * 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 { // 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 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(); } return stream; } } }