//============================================================================ // 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 //============================================================================= using System; using System.Collections.Generic; namespace BDInfo { public enum TSStreamType : byte { Unknown = 0, MPEG1_VIDEO = 0x01, MPEG2_VIDEO = 0x02, AVC_VIDEO = 0x1b, MVC_VIDEO = 0x20, VC1_VIDEO = 0xea, MPEG1_AUDIO = 0x03, MPEG2_AUDIO = 0x04, LPCM_AUDIO = 0x80, AC3_AUDIO = 0x81, AC3_PLUS_AUDIO = 0x84, AC3_PLUS_SECONDARY_AUDIO = 0xA1, AC3_TRUE_HD_AUDIO = 0x83, DTS_AUDIO = 0x82, DTS_HD_AUDIO = 0x85, DTS_HD_SECONDARY_AUDIO = 0xA2, DTS_HD_MASTER_AUDIO = 0x86, PRESENTATION_GRAPHICS = 0x90, INTERACTIVE_GRAPHICS = 0x91, SUBTITLE = 0x92 } public enum TSVideoFormat : byte { Unknown = 0, VIDEOFORMAT_480i = 1, VIDEOFORMAT_576i = 2, VIDEOFORMAT_480p = 3, VIDEOFORMAT_1080i = 4, VIDEOFORMAT_720p = 5, VIDEOFORMAT_1080p = 6, VIDEOFORMAT_576p = 7, } public enum TSFrameRate : byte { Unknown = 0, FRAMERATE_23_976 = 1, FRAMERATE_24 = 2, FRAMERATE_25 = 3, FRAMERATE_29_97 = 4, FRAMERATE_50 = 6, FRAMERATE_59_94 = 7 } public enum TSChannelLayout : byte { Unknown = 0, CHANNELLAYOUT_MONO = 1, CHANNELLAYOUT_STEREO = 3, CHANNELLAYOUT_MULTI = 6, CHANNELLAYOUT_COMBO = 12 } public enum TSSampleRate : byte { Unknown = 0, SAMPLERATE_48 = 1, SAMPLERATE_96 = 4, SAMPLERATE_192 = 5, SAMPLERATE_48_192 = 12, SAMPLERATE_48_96 = 14 } public enum TSAspectRatio { Unknown = 0, ASPECT_4_3 = 2, ASPECT_16_9 = 3, ASPECT_2_21 = 4 } public class TSDescriptor { public byte Name; public byte[] Value; public TSDescriptor(byte name, byte length) { Name = name; Value = new byte[length]; } public TSDescriptor Clone() { var descriptor = new TSDescriptor(Name, (byte)Value.Length); Value.CopyTo(descriptor.Value, 0); return descriptor; } } public abstract class TSStream { public TSStream() { } public override string ToString() { return string.Format("{0} ({1})", CodecShortName, PID); } public ushort PID; public TSStreamType StreamType; public List Descriptors = null; public long BitRate = 0; public long ActiveBitRate = 0; public bool IsVBR = false; public bool IsInitialized = false; public string LanguageName; public bool IsHidden = false; public ulong PayloadBytes = 0; public ulong PacketCount = 0; public double PacketSeconds = 0; public int AngleIndex = 0; public ulong PacketSize => PacketCount * 192; private string _LanguageCode; public string LanguageCode { get => _LanguageCode; set { _LanguageCode = value; LanguageName = LanguageCodes.GetName(value); } } public bool IsVideoStream { get { switch (StreamType) { case TSStreamType.MPEG1_VIDEO: case TSStreamType.MPEG2_VIDEO: case TSStreamType.AVC_VIDEO: case TSStreamType.MVC_VIDEO: case TSStreamType.VC1_VIDEO: return true; default: return false; } } } public bool IsAudioStream { get { switch (StreamType) { case TSStreamType.MPEG1_AUDIO: case TSStreamType.MPEG2_AUDIO: case TSStreamType.LPCM_AUDIO: 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_SECONDARY_AUDIO: case TSStreamType.DTS_HD_MASTER_AUDIO: return true; default: return false; } } } public bool IsGraphicsStream { get { switch (StreamType) { case TSStreamType.PRESENTATION_GRAPHICS: case TSStreamType.INTERACTIVE_GRAPHICS: return true; default: return false; } } } public bool IsTextStream { get { switch (StreamType) { case TSStreamType.SUBTITLE: return true; default: return false; } } } public string CodecName { get { switch (StreamType) { case TSStreamType.MPEG1_VIDEO: return "MPEG-1 Video"; case TSStreamType.MPEG2_VIDEO: return "MPEG-2 Video"; case TSStreamType.AVC_VIDEO: return "MPEG-4 AVC Video"; case TSStreamType.MVC_VIDEO: return "MPEG-4 MVC Video"; case TSStreamType.VC1_VIDEO: return "VC-1 Video"; case TSStreamType.MPEG1_AUDIO: return "MP1 Audio"; case TSStreamType.MPEG2_AUDIO: return "MP2 Audio"; case TSStreamType.LPCM_AUDIO: return "LPCM Audio"; case TSStreamType.AC3_AUDIO: if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) return "Dolby Digital EX Audio"; else return "Dolby Digital Audio"; case TSStreamType.AC3_PLUS_AUDIO: case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: return "Dolby Digital Plus Audio"; case TSStreamType.AC3_TRUE_HD_AUDIO: return "Dolby TrueHD Audio"; case TSStreamType.DTS_AUDIO: if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) return "DTS-ES Audio"; else return "DTS Audio"; case TSStreamType.DTS_HD_AUDIO: return "DTS-HD High-Res Audio"; case TSStreamType.DTS_HD_SECONDARY_AUDIO: return "DTS Express"; case TSStreamType.DTS_HD_MASTER_AUDIO: return "DTS-HD Master Audio"; case TSStreamType.PRESENTATION_GRAPHICS: return "Presentation Graphics"; case TSStreamType.INTERACTIVE_GRAPHICS: return "Interactive Graphics"; case TSStreamType.SUBTITLE: return "Subtitle"; default: return "UNKNOWN"; } } } public string CodecAltName { get { switch (StreamType) { case TSStreamType.MPEG1_VIDEO: return "MPEG-1"; case TSStreamType.MPEG2_VIDEO: return "MPEG-2"; case TSStreamType.AVC_VIDEO: return "AVC"; case TSStreamType.MVC_VIDEO: return "MVC"; case TSStreamType.VC1_VIDEO: return "VC-1"; case TSStreamType.MPEG1_AUDIO: return "MP1"; case TSStreamType.MPEG2_AUDIO: return "MP2"; case TSStreamType.LPCM_AUDIO: return "LPCM"; case TSStreamType.AC3_AUDIO: return "DD AC3"; case TSStreamType.AC3_PLUS_AUDIO: case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: return "DD AC3+"; case TSStreamType.AC3_TRUE_HD_AUDIO: return "Dolby TrueHD"; case TSStreamType.DTS_AUDIO: return "DTS"; case TSStreamType.DTS_HD_AUDIO: return "DTS-HD Hi-Res"; case TSStreamType.DTS_HD_SECONDARY_AUDIO: return "DTS Express"; case TSStreamType.DTS_HD_MASTER_AUDIO: return "DTS-HD Master"; case TSStreamType.PRESENTATION_GRAPHICS: return "PGS"; case TSStreamType.INTERACTIVE_GRAPHICS: return "IGS"; case TSStreamType.SUBTITLE: return "SUB"; default: return "UNKNOWN"; } } } public string CodecShortName { get { switch (StreamType) { case TSStreamType.MPEG1_VIDEO: return "MPEG-1"; case TSStreamType.MPEG2_VIDEO: return "MPEG-2"; case TSStreamType.AVC_VIDEO: return "AVC"; case TSStreamType.MVC_VIDEO: return "MVC"; case TSStreamType.VC1_VIDEO: return "VC-1"; case TSStreamType.MPEG1_AUDIO: return "MP1"; case TSStreamType.MPEG2_AUDIO: return "MP2"; case TSStreamType.LPCM_AUDIO: return "LPCM"; case TSStreamType.AC3_AUDIO: if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) return "AC3-EX"; else return "AC3"; case TSStreamType.AC3_PLUS_AUDIO: case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: return "AC3+"; case TSStreamType.AC3_TRUE_HD_AUDIO: return "TrueHD"; case TSStreamType.DTS_AUDIO: if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) return "DTS-ES"; else return "DTS"; case TSStreamType.DTS_HD_AUDIO: return "DTS-HD HR"; case TSStreamType.DTS_HD_SECONDARY_AUDIO: return "DTS Express"; case TSStreamType.DTS_HD_MASTER_AUDIO: return "DTS-HD MA"; case TSStreamType.PRESENTATION_GRAPHICS: return "PGS"; case TSStreamType.INTERACTIVE_GRAPHICS: return "IGS"; case TSStreamType.SUBTITLE: return "SUB"; default: return "UNKNOWN"; } } } public virtual string Description => ""; public abstract TSStream Clone(); protected void CopyTo(TSStream stream) { stream.PID = PID; stream.StreamType = StreamType; stream.IsVBR = IsVBR; stream.BitRate = BitRate; stream.IsInitialized = IsInitialized; stream.LanguageCode = _LanguageCode; if (Descriptors != null) { stream.Descriptors = new List(); foreach (var descriptor in Descriptors) { stream.Descriptors.Add(descriptor.Clone()); } } } } public class TSVideoStream : TSStream { public TSVideoStream() { } public int Width; public int Height; public bool IsInterlaced; public int FrameRateEnumerator; public int FrameRateDenominator; public TSAspectRatio AspectRatio; public string EncodingProfile; private TSVideoFormat _VideoFormat; public TSVideoFormat VideoFormat { get => _VideoFormat; set { _VideoFormat = value; switch (value) { case TSVideoFormat.VIDEOFORMAT_480i: Height = 480; IsInterlaced = true; break; case TSVideoFormat.VIDEOFORMAT_480p: Height = 480; IsInterlaced = false; break; case TSVideoFormat.VIDEOFORMAT_576i: Height = 576; IsInterlaced = true; break; case TSVideoFormat.VIDEOFORMAT_576p: Height = 576; IsInterlaced = false; break; case TSVideoFormat.VIDEOFORMAT_720p: Height = 720; IsInterlaced = false; break; case TSVideoFormat.VIDEOFORMAT_1080i: Height = 1080; IsInterlaced = true; break; case TSVideoFormat.VIDEOFORMAT_1080p: Height = 1080; IsInterlaced = false; break; } } } private TSFrameRate _FrameRate; public TSFrameRate FrameRate { get => _FrameRate; set { _FrameRate = value; switch (value) { case TSFrameRate.FRAMERATE_23_976: FrameRateEnumerator = 24000; FrameRateDenominator = 1001; break; case TSFrameRate.FRAMERATE_24: FrameRateEnumerator = 24000; FrameRateDenominator = 1000; break; case TSFrameRate.FRAMERATE_25: FrameRateEnumerator = 25000; FrameRateDenominator = 1000; break; case TSFrameRate.FRAMERATE_29_97: FrameRateEnumerator = 30000; FrameRateDenominator = 1001; break; case TSFrameRate.FRAMERATE_50: FrameRateEnumerator = 50000; FrameRateDenominator = 1000; break; case TSFrameRate.FRAMERATE_59_94: FrameRateEnumerator = 60000; FrameRateDenominator = 1001; break; } } } public override string Description { get { string description = ""; if (Height > 0) { description += string.Format("{0:D}{1} / ", Height, IsInterlaced ? "i" : "p"); } if (FrameRateEnumerator > 0 && FrameRateDenominator > 0) { if (FrameRateEnumerator % FrameRateDenominator == 0) { description += string.Format("{0:D} fps / ", FrameRateEnumerator / FrameRateDenominator); } else { description += string.Format("{0:F3} fps / ", (double)FrameRateEnumerator / FrameRateDenominator); } } if (AspectRatio == TSAspectRatio.ASPECT_4_3) { description += "4:3 / "; } else if (AspectRatio == TSAspectRatio.ASPECT_16_9) { description += "16:9 / "; } if (EncodingProfile != null) { description += EncodingProfile + " / "; } if (description.EndsWith(" / ")) { description = description.Substring(0, description.Length - 3); } return description; } } public override TSStream Clone() { var stream = new TSVideoStream(); CopyTo(stream); stream.VideoFormat = _VideoFormat; stream.FrameRate = _FrameRate; stream.Width = Width; stream.Height = Height; stream.IsInterlaced = IsInterlaced; stream.FrameRateEnumerator = FrameRateEnumerator; stream.FrameRateDenominator = FrameRateDenominator; stream.AspectRatio = AspectRatio; stream.EncodingProfile = EncodingProfile; return stream; } } public enum TSAudioMode { Unknown, DualMono, Stereo, Surround, Extended } public class TSAudioStream : TSStream { public TSAudioStream() { } public int SampleRate; public int ChannelCount; public int BitDepth; public int LFE; public int DialNorm; public TSAudioMode AudioMode; public TSAudioStream CoreStream; public TSChannelLayout ChannelLayout; public static int ConvertSampleRate( TSSampleRate sampleRate) { switch (sampleRate) { case TSSampleRate.SAMPLERATE_48: return 48000; case TSSampleRate.SAMPLERATE_96: case TSSampleRate.SAMPLERATE_48_96: return 96000; case TSSampleRate.SAMPLERATE_192: case TSSampleRate.SAMPLERATE_48_192: return 192000; } return 0; } public string ChannelDescription { get { if (ChannelLayout == TSChannelLayout.CHANNELLAYOUT_MONO && ChannelCount == 2) { } string description = ""; if (ChannelCount > 0) { description += string.Format( "{0:D}.{1:D}", ChannelCount, LFE); } else { switch (ChannelLayout) { case TSChannelLayout.CHANNELLAYOUT_MONO: description += "1.0"; break; case TSChannelLayout.CHANNELLAYOUT_STEREO: description += "2.0"; break; case TSChannelLayout.CHANNELLAYOUT_MULTI: description += "5.1"; break; } } if (AudioMode == TSAudioMode.Extended) { if (StreamType == TSStreamType.AC3_AUDIO) { description += "-EX"; } if (StreamType == TSStreamType.DTS_AUDIO || StreamType == TSStreamType.DTS_HD_AUDIO || StreamType == TSStreamType.DTS_HD_MASTER_AUDIO) { description += "-ES"; } } return description; } } public override string Description { get { string description = ChannelDescription; if (SampleRate > 0) { description += string.Format( " / {0:D} kHz", SampleRate / 1000); } if (BitRate > 0) { description += string.Format( " / {0:D} kbps", (uint)Math.Round((double)BitRate / 1000)); } if (BitDepth > 0) { description += string.Format( " / {0:D}-bit", BitDepth); } if (DialNorm != 0) { description += string.Format( " / DN {0}dB", DialNorm); } if (ChannelCount == 2) { switch (AudioMode) { case TSAudioMode.DualMono: description += " / Dual Mono"; break; case TSAudioMode.Surround: description += " / Dolby Surround"; break; } } if (description.EndsWith(" / ")) { description = description.Substring(0, description.Length - 3); } if (CoreStream != null) { string codec = ""; switch (CoreStream.StreamType) { case TSStreamType.AC3_AUDIO: codec = "AC3 Embedded"; break; case TSStreamType.DTS_AUDIO: codec = "DTS Core"; break; } description += string.Format( " ({0}: {1})", codec, CoreStream.Description); } return description; } } public override TSStream Clone() { var stream = new TSAudioStream(); CopyTo(stream); stream.SampleRate = SampleRate; stream.ChannelLayout = ChannelLayout; stream.ChannelCount = ChannelCount; stream.BitDepth = BitDepth; stream.LFE = LFE; stream.DialNorm = DialNorm; stream.AudioMode = AudioMode; if (CoreStream != null) { stream.CoreStream = (TSAudioStream)CoreStream.Clone(); } return stream; } } public class TSGraphicsStream : TSStream { public TSGraphicsStream() { IsVBR = true; IsInitialized = true; } public override TSStream Clone() { var stream = new TSGraphicsStream(); CopyTo(stream); return stream; } } public class TSTextStream : TSStream { public TSTextStream() { IsVBR = true; IsInitialized = true; } public override TSStream Clone() { var stream = new TSTextStream(); CopyTo(stream); return stream; } } }