diff options
Diffstat (limited to 'BDInfo')
| -rw-r--r-- | BDInfo/BDInfo.csproj | 77 | ||||
| -rw-r--r-- | BDInfo/BDInfo.nuget.targets | 6 | ||||
| -rw-r--r-- | BDInfo/BDInfoSettings.cs | 105 | ||||
| -rw-r--r-- | BDInfo/BDROM.cs | 437 | ||||
| -rw-r--r-- | BDInfo/BitVector32.cs | 308 | ||||
| -rw-r--r-- | BDInfo/LanguageCodes.cs | 493 | ||||
| -rw-r--r-- | BDInfo/Properties/AssemblyInfo.cs | 29 | ||||
| -rw-r--r-- | BDInfo/ReadMe.txt | 5 | ||||
| -rw-r--r-- | BDInfo/TSCodecAC3.cs | 309 | ||||
| -rw-r--r-- | BDInfo/TSCodecAVC.cs | 148 | ||||
| -rw-r--r-- | BDInfo/TSCodecDTS.cs | 159 | ||||
| -rw-r--r-- | BDInfo/TSCodecDTSHD.cs | 246 | ||||
| -rw-r--r-- | BDInfo/TSCodecLPCM.cs | 123 | ||||
| -rw-r--r-- | BDInfo/TSCodecMPEG2.cs | 208 | ||||
| -rw-r--r-- | BDInfo/TSCodecMVC.cs | 36 | ||||
| -rw-r--r-- | BDInfo/TSCodecTrueHD.cs | 186 | ||||
| -rw-r--r-- | BDInfo/TSCodecVC1.cs | 131 | ||||
| -rw-r--r-- | BDInfo/TSInterleavedFile.cs | 38 | ||||
| -rw-r--r-- | BDInfo/TSPlaylistFile.cs | 1292 | ||||
| -rw-r--r-- | BDInfo/TSStream.cs | 801 | ||||
| -rw-r--r-- | BDInfo/TSStreamBuffer.cs | 142 | ||||
| -rw-r--r-- | BDInfo/TSStreamClip.cs | 113 | ||||
| -rw-r--r-- | BDInfo/TSStreamClipFile.cs | 253 | ||||
| -rw-r--r-- | BDInfo/TSStreamFile.cs | 1553 | ||||
| -rw-r--r-- | BDInfo/project.json | 17 |
25 files changed, 7215 insertions, 0 deletions
diff --git a/BDInfo/BDInfo.csproj b/BDInfo/BDInfo.csproj new file mode 100644 index 000000000..e7013f341 --- /dev/null +++ b/BDInfo/BDInfo.csproj @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{88AE38DF-19D7-406F-A6A9-09527719A21E}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>BDInfo</RootNamespace> + <AssemblyName>BDInfo</AssemblyName> + <DefaultLanguage>en-US</DefaultLanguage> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <TargetFrameworkProfile>Profile7</TargetFrameworkProfile> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <None Include="project.json" /> + <!-- A reference to the entire .NET Framework is automatically included --> + </ItemGroup> + <ItemGroup> + <Compile Include="BDInfoSettings.cs" /> + <Compile Include="BDROM.cs" /> + <Compile Include="BitVector32.cs" /> + <Compile Include="LanguageCodes.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="TSCodecAC3.cs" /> + <Compile Include="TSCodecAVC.cs" /> + <Compile Include="TSCodecDTS.cs" /> + <Compile Include="TSCodecDTSHD.cs" /> + <Compile Include="TSCodecLPCM.cs" /> + <Compile Include="TSCodecMPEG2.cs" /> + <Compile Include="TSCodecMVC.cs" /> + <Compile Include="TSCodecTrueHD.cs" /> + <Compile Include="TSCodecVC1.cs" /> + <Compile Include="TSInterleavedFile.cs" /> + <Compile Include="TSPlaylistFile.cs" /> + <Compile Include="TSStream.cs" /> + <Compile Include="TSStreamBuffer.cs" /> + <Compile Include="TSStreamClip.cs" /> + <Compile Include="TSStreamClipFile.cs" /> + <Compile Include="TSStreamFile.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj"> + <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project> + <Name>MediaBrowser.Model</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/BDInfo/BDInfo.nuget.targets b/BDInfo/BDInfo.nuget.targets new file mode 100644 index 000000000..e69ce0e64 --- /dev/null +++ b/BDInfo/BDInfo.nuget.targets @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Target Name="EmitMSBuildWarning" BeforeTargets="Build"> + <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." /> + </Target> +</Project>
\ No newline at end of file diff --git a/BDInfo/BDInfoSettings.cs b/BDInfo/BDInfoSettings.cs new file mode 100644 index 000000000..7abb67499 --- /dev/null +++ b/BDInfo/BDInfoSettings.cs @@ -0,0 +1,105 @@ + +namespace BDInfo +{ + class BDInfoSettings + { + public static bool GenerateStreamDiagnostics + { + get + { + return true; + } + } + + public static bool EnableSSIF + { + get + { + return true; + } + } + + public static bool AutosaveReport + { + get + { + return false; + } + } + + public static bool GenerateFrameDataFile + { + get + { + return false; + } + } + + public static bool FilterLoopingPlaylists + { + get + { + return true; + } + } + + public static bool FilterShortPlaylists + { + get + { + return false; + } + } + + public static int FilterShortPlaylistsValue + { + get + { + return 0; + } + } + + public static bool UseImagePrefix + { + get + { + return false; + } + } + + public static string UseImagePrefixValue + { + get + { + return null; + } + } + + /// <summary> + /// Setting this to false throws an IComparer error on some discs. + /// </summary> + public static bool KeepStreamOrder + { + get + { + return true; + } + } + + public static bool GenerateTextSummary + { + get + { + return false; + } + } + + public static string LastPath + { + get + { + return string.Empty; + } + } + } +} diff --git a/BDInfo/BDROM.cs b/BDInfo/BDROM.cs new file mode 100644 index 000000000..97dbfbf3b --- /dev/null +++ b/BDInfo/BDROM.cs @@ -0,0 +1,437 @@ +//============================================================================ +// 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; +using System.IO; +using System.Linq; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Text; + +namespace BDInfo +{ + public class BDROM + { + public FileSystemMetadata DirectoryRoot = null; + public FileSystemMetadata DirectoryBDMV = null; + public FileSystemMetadata DirectoryBDJO = null; + public FileSystemMetadata DirectoryCLIPINF = null; + public FileSystemMetadata DirectoryPLAYLIST = null; + public FileSystemMetadata DirectorySNP = null; + public FileSystemMetadata DirectorySSIF = null; + public FileSystemMetadata DirectorySTREAM = null; + + public string VolumeLabel = null; + public ulong Size = 0; + public bool IsBDPlus = false; + public bool IsBDJava = false; + public bool IsDBOX = false; + public bool IsPSP = false; + public bool Is3D = false; + public bool Is50Hz = false; + + private readonly IFileSystem _fileSystem; + + public Dictionary<string, TSPlaylistFile> PlaylistFiles = + new Dictionary<string, TSPlaylistFile>(); + public Dictionary<string, TSStreamClipFile> StreamClipFiles = + new Dictionary<string, TSStreamClipFile>(); + public Dictionary<string, TSStreamFile> StreamFiles = + new Dictionary<string, TSStreamFile>(); + public Dictionary<string, TSInterleavedFile> InterleavedFiles = + new Dictionary<string, TSInterleavedFile>(); + + private static List<string> ExcludeDirs = new List<string> { "ANY!", "AACS", "BDSVM", "ANYVM", "SLYVM" }; + + public delegate bool OnStreamClipFileScanError( + TSStreamClipFile streamClipFile, Exception ex); + + public event OnStreamClipFileScanError StreamClipFileScanError; + + public delegate bool OnStreamFileScanError( + TSStreamFile streamClipFile, Exception ex); + + public event OnStreamFileScanError StreamFileScanError; + + public delegate bool OnPlaylistFileScanError( + TSPlaylistFile playlistFile, Exception ex); + + public event OnPlaylistFileScanError PlaylistFileScanError; + + public BDROM( + string path, IFileSystem fileSystem, ITextEncoding textEncoding) + { + _fileSystem = fileSystem; + // + // Locate BDMV directories. + // + + DirectoryBDMV = + GetDirectoryBDMV(path); + + if (DirectoryBDMV == null) + { + throw new Exception("Unable to locate BD structure."); + } + + DirectoryRoot = + _fileSystem.GetDirectoryInfo(Path.GetDirectoryName(DirectoryBDMV.FullName)); + DirectoryBDJO = + GetDirectory("BDJO", DirectoryBDMV, 0); + DirectoryCLIPINF = + GetDirectory("CLIPINF", DirectoryBDMV, 0); + DirectoryPLAYLIST = + GetDirectory("PLAYLIST", DirectoryBDMV, 0); + DirectorySNP = + GetDirectory("SNP", DirectoryRoot, 0); + DirectorySTREAM = + GetDirectory("STREAM", DirectoryBDMV, 0); + DirectorySSIF = + GetDirectory("SSIF", DirectorySTREAM, 0); + + if (DirectoryCLIPINF == null + || DirectoryPLAYLIST == null) + { + throw new Exception("Unable to locate BD structure."); + } + + // + // Initialize basic disc properties. + // + + VolumeLabel = GetVolumeLabel(DirectoryRoot); + Size = (ulong)GetDirectorySize(DirectoryRoot); + + if (null != GetDirectory("BDSVM", DirectoryRoot, 0)) + { + IsBDPlus = true; + } + if (null != GetDirectory("SLYVM", DirectoryRoot, 0)) + { + IsBDPlus = true; + } + if (null != GetDirectory("ANYVM", DirectoryRoot, 0)) + { + IsBDPlus = true; + } + + if (DirectoryBDJO != null && + _fileSystem.GetFiles(DirectoryBDJO.FullName).Any()) + { + IsBDJava = true; + } + + if (DirectorySNP != null && + GetFiles(DirectorySNP.FullName, ".mnv").Any()) + { + IsPSP = true; + } + + if (DirectorySSIF != null && + _fileSystem.GetFiles(DirectorySSIF.FullName).Any()) + { + Is3D = true; + } + + if (_fileSystem.FileExists(Path.Combine(DirectoryRoot.FullName, "FilmIndex.xml"))) + { + IsDBOX = true; + } + + // + // Initialize file lists. + // + + if (DirectoryPLAYLIST != null) + { + FileSystemMetadata[] files = GetFiles(DirectoryPLAYLIST.FullName, ".mpls").ToArray(); + foreach (FileSystemMetadata file in files) + { + PlaylistFiles.Add( + file.Name.ToUpper(), new TSPlaylistFile(this, file, _fileSystem, textEncoding)); + } + } + + if (DirectorySTREAM != null) + { + FileSystemMetadata[] files = GetFiles(DirectorySTREAM.FullName, ".m2ts").ToArray(); + foreach (FileSystemMetadata file in files) + { + StreamFiles.Add( + file.Name.ToUpper(), new TSStreamFile(file, _fileSystem)); + } + } + + if (DirectoryCLIPINF != null) + { + FileSystemMetadata[] files = GetFiles(DirectoryCLIPINF.FullName, ".clpi").ToArray(); + foreach (FileSystemMetadata file in files) + { + StreamClipFiles.Add( + file.Name.ToUpper(), new TSStreamClipFile(file, _fileSystem, textEncoding)); + } + } + + if (DirectorySSIF != null) + { + FileSystemMetadata[] files = GetFiles(DirectorySSIF.FullName, ".ssif").ToArray(); + foreach (FileSystemMetadata file in files) + { + InterleavedFiles.Add( + file.Name.ToUpper(), new TSInterleavedFile(file)); + } + } + } + + private IEnumerable<FileSystemMetadata> GetFiles(string path, string extension) + { + return _fileSystem.GetFiles(path).Where(i => string.Equals(i.Extension, extension, StringComparison.OrdinalIgnoreCase)); + } + + public void Scan() + { + List<TSStreamClipFile> errorStreamClipFiles = new List<TSStreamClipFile>(); + foreach (TSStreamClipFile streamClipFile in StreamClipFiles.Values) + { + try + { + streamClipFile.Scan(); + } + catch (Exception ex) + { + errorStreamClipFiles.Add(streamClipFile); + if (StreamClipFileScanError != null) + { + if (StreamClipFileScanError(streamClipFile, ex)) + { + continue; + } + else + { + break; + } + } + else throw ex; + } + } + + foreach (TSStreamFile streamFile in StreamFiles.Values) + { + string ssifName = Path.GetFileNameWithoutExtension(streamFile.Name) + ".SSIF"; + if (InterleavedFiles.ContainsKey(ssifName)) + { + streamFile.InterleavedFile = InterleavedFiles[ssifName]; + } + } + + TSStreamFile[] streamFiles = new TSStreamFile[StreamFiles.Count]; + StreamFiles.Values.CopyTo(streamFiles, 0); + Array.Sort(streamFiles, CompareStreamFiles); + + List<TSPlaylistFile> errorPlaylistFiles = new List<TSPlaylistFile>(); + foreach (TSPlaylistFile playlistFile in PlaylistFiles.Values) + { + try + { + playlistFile.Scan(StreamFiles, StreamClipFiles); + } + catch (Exception ex) + { + errorPlaylistFiles.Add(playlistFile); + if (PlaylistFileScanError != null) + { + if (PlaylistFileScanError(playlistFile, ex)) + { + continue; + } + else + { + break; + } + } + else throw ex; + } + } + + List<TSStreamFile> errorStreamFiles = new List<TSStreamFile>(); + foreach (TSStreamFile streamFile in streamFiles) + { + try + { + List<TSPlaylistFile> playlists = new List<TSPlaylistFile>(); + foreach (TSPlaylistFile playlist in PlaylistFiles.Values) + { + foreach (TSStreamClip streamClip in playlist.StreamClips) + { + if (streamClip.Name == streamFile.Name) + { + playlists.Add(playlist); + break; + } + } + } + streamFile.Scan(playlists, false); + } + catch (Exception ex) + { + errorStreamFiles.Add(streamFile); + if (StreamFileScanError != null) + { + if (StreamFileScanError(streamFile, ex)) + { + continue; + } + else + { + break; + } + } + else throw ex; + } + } + + foreach (TSPlaylistFile playlistFile in PlaylistFiles.Values) + { + playlistFile.Initialize(); + if (!Is50Hz) + { + foreach (TSVideoStream videoStream in playlistFile.VideoStreams) + { + if (videoStream.FrameRate == TSFrameRate.FRAMERATE_25 || + videoStream.FrameRate == TSFrameRate.FRAMERATE_50) + { + Is50Hz = true; + } + } + } + } + } + + private FileSystemMetadata GetDirectoryBDMV( + string path) + { + FileSystemMetadata dir = _fileSystem.GetDirectoryInfo(path); + + while (dir != null) + { + if (dir.Name == "BDMV") + { + return dir; + } + dir = _fileSystem.GetDirectoryInfo(Path.GetDirectoryName(dir.FullName)); + } + + return GetDirectory("BDMV", _fileSystem.GetDirectoryInfo(path), 0); + } + + private FileSystemMetadata GetDirectory( + string name, + FileSystemMetadata dir, + int searchDepth) + { + if (dir != null) + { + FileSystemMetadata[] children = _fileSystem.GetDirectories(dir.FullName).ToArray(); + foreach (FileSystemMetadata child in children) + { + if (child.Name == name) + { + return child; + } + } + if (searchDepth > 0) + { + foreach (FileSystemMetadata child in children) + { + GetDirectory( + name, child, searchDepth - 1); + } + } + } + return null; + } + + private long GetDirectorySize(FileSystemMetadata directoryInfo) + { + long size = 0; + + //if (!ExcludeDirs.Contains(directoryInfo.Name.ToUpper())) // TODO: Keep? + { + FileSystemMetadata[] pathFiles = _fileSystem.GetFiles(directoryInfo.FullName).ToArray(); + foreach (FileSystemMetadata pathFile in pathFiles) + { + if (pathFile.Extension.ToUpper() == ".SSIF") + { + continue; + } + size += pathFile.Length; + } + + FileSystemMetadata[] pathChildren = _fileSystem.GetDirectories(directoryInfo.FullName).ToArray(); + foreach (FileSystemMetadata pathChild in pathChildren) + { + size += GetDirectorySize(pathChild); + } + } + + return size; + } + + private string GetVolumeLabel(FileSystemMetadata dir) + { + return dir.Name; + } + + public static int CompareStreamFiles( + TSStreamFile x, + TSStreamFile y) + { + // TODO: Use interleaved file sizes + + if ((x == null || x.FileInfo == null) && (y == null || y.FileInfo == null)) + { + return 0; + } + else if ((x == null || x.FileInfo == null) && (y != null && y.FileInfo != null)) + { + return 1; + } + else if ((x != null || x.FileInfo != null) && (y == null || y.FileInfo == null)) + { + return -1; + } + else + { + if (x.FileInfo.Length > y.FileInfo.Length) + { + return 1; + } + else if (y.FileInfo.Length > x.FileInfo.Length) + { + return -1; + } + else + { + return 0; + } + } + } + + } +} diff --git a/BDInfo/BitVector32.cs b/BDInfo/BitVector32.cs new file mode 100644 index 000000000..1ac94bfbe --- /dev/null +++ b/BDInfo/BitVector32.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BDInfo +{ + using System.Diagnostics; + using System.Text; + using System; + + /// <devdoc> + /// <para>Provides a simple light bit vector with easy integer or Boolean access to + /// a 32 bit storage.</para> + /// </devdoc> + public struct BitVector32 + { + private uint data; + + /// <devdoc> + /// <para>Initializes a new instance of the BitVector32 structure with the specified internal data.</para> + /// </devdoc> + public BitVector32(int data) + { + this.data = (uint)data; + } + + /// <devdoc> + /// <para>Initializes a new instance of the BitVector32 structure with the information in the specified + /// value.</para> + /// </devdoc> + public BitVector32(BitVector32 value) + { + this.data = value.data; + } + + /// <devdoc> + /// <para>Gets or sets a value indicating whether all the specified bits are set.</para> + /// </devdoc> + public bool this[int bit] + { + get + { + return (data & bit) == (uint)bit; + } + set + { + if (value) + { + data |= (uint)bit; + } + else + { + data &= ~(uint)bit; + } + } + } + + /// <devdoc> + /// <para>Gets or sets the value for the specified section.</para> + /// </devdoc> + public int this[Section section] + { + get + { + return (int)((data & (uint)(section.Mask << section.Offset)) >> section.Offset); + } + set + { + value <<= section.Offset; + int offsetMask = (0xFFFF & (int)section.Mask) << section.Offset; + data = (data & ~(uint)offsetMask) | ((uint)value & (uint)offsetMask); + } + } + + /// <devdoc> + /// returns the raw data stored in this bit vector... + /// </devdoc> + public int Data + { + get + { + return (int)data; + } + } + + private static short CountBitsSet(short mask) + { + + // yes, I know there are better algorithms, however, we know the + // bits are always right aligned, with no holes (i.e. always 00000111, + // never 000100011), so this is just fine... + // + short value = 0; + while ((mask & 0x1) != 0) + { + value++; + mask >>= 1; + } + return value; + } + + /// <devdoc> + /// <para> Creates the first mask in a series.</para> + /// </devdoc> + public static int CreateMask() + { + return CreateMask(0); + } + + /// <devdoc> + /// Creates the next mask in a series. + /// </devdoc> + public static int CreateMask(int previous) + { + if (previous == 0) + { + return 1; + } + + if (previous == unchecked((int)0x80000000)) + { + throw new InvalidOperationException("Bit vector full"); + } + + return previous << 1; + } + + /// <devdoc> + /// Given a highValue, creates the mask + /// </devdoc> + private static short CreateMaskFromHighValue(short highValue) + { + short required = 16; + while ((highValue & 0x8000) == 0) + { + required--; + highValue <<= 1; + } + + ushort value = 0; + while (required > 0) + { + required--; + value <<= 1; + value |= 0x1; + } + + return unchecked((short)value); + } + + /// <devdoc> + /// <para>Creates the first section in a series, with the specified maximum value.</para> + /// </devdoc> + public static Section CreateSection(short maxValue) + { + return CreateSectionHelper(maxValue, 0, 0); + } + + /// <devdoc> + /// <para>Creates the next section in a series, with the specified maximum value.</para> + /// </devdoc> + public static Section CreateSection(short maxValue, Section previous) + { + return CreateSectionHelper(maxValue, previous.Mask, previous.Offset); + } + + private static Section CreateSectionHelper(short maxValue, short priorMask, short priorOffset) + { + if (maxValue < 1) + { + throw new ArgumentOutOfRangeException("maxValue"); + } +#if DEBUG + int maskCheck = CreateMaskFromHighValue(maxValue); + int offsetCheck = priorOffset + CountBitsSet(priorMask); + Debug.Assert(maskCheck <= short.MaxValue && offsetCheck < 32, "Overflow on BitVector32"); +#endif + short offset = (short)(priorOffset + CountBitsSet(priorMask)); + if (offset >= 32) + { + throw new InvalidOperationException("Bit vector full"); + } + return new Section(CreateMaskFromHighValue(maxValue), offset); + } + + public override bool Equals(object o) + { + if (!(o is BitVector32)) + { + return false; + } + + return data == ((BitVector32)o).data; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// <devdoc> + /// </devdoc> + public static string ToString(BitVector32 value) + { + StringBuilder sb = new StringBuilder(/*"BitVector32{".Length*/12 + /*32 bits*/32 + /*"}".Length"*/1); + sb.Append("BitVector32{"); + int locdata = (int)value.data; + for (int i = 0; i < 32; i++) + { + if ((locdata & 0x80000000) != 0) + { + sb.Append("1"); + } + else + { + sb.Append("0"); + } + locdata <<= 1; + } + sb.Append("}"); + return sb.ToString(); + } + + /// <devdoc> + /// </devdoc> + public override string ToString() + { + return BitVector32.ToString(this); + } + + /// <devdoc> + /// <para> + /// Represents an section of the vector that can contain a integer number.</para> + /// </devdoc> + public struct Section + { + private readonly short mask; + private readonly short offset; + + internal Section(short mask, short offset) + { + this.mask = mask; + this.offset = offset; + } + + public short Mask + { + get + { + return mask; + } + } + + public short Offset + { + get + { + return offset; + } + } + + public override bool Equals(object o) + { + if (o is Section) + return Equals((Section)o); + else + return false; + } + + public bool Equals(Section obj) + { + return obj.mask == mask && obj.offset == offset; + } + + public static bool operator ==(Section a, Section b) + { + return a.Equals(b); + } + + public static bool operator !=(Section a, Section b) + { + return !(a == b); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// <devdoc> + /// </devdoc> + public static string ToString(Section value) + { + return "Section{0x" + Convert.ToString(value.Mask, 16) + ", 0x" + Convert.ToString(value.Offset, 16) + "}"; + } + + /// <devdoc> + /// </devdoc> + public override string ToString() + { + return Section.ToString(this); + } + + } + } +} diff --git a/BDInfo/LanguageCodes.cs b/BDInfo/LanguageCodes.cs new file mode 100644 index 000000000..90d0bccc4 --- /dev/null +++ b/BDInfo/LanguageCodes.cs @@ -0,0 +1,493 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + public abstract class LanguageCodes + { + public static string GetName(string code) + { + switch (code) + { + case "abk": return "Abkhazian"; + case "ace": return "Achinese"; + case "ach": return "Acoli"; + case "ada": return "Adangme"; + case "aar": return "Afar"; + case "afh": return "Afrihili"; + case "afr": return "Afrikaans"; + case "afa": return "Afro-Asiatic (Other)"; + case "aka": return "Akan"; + case "akk": return "Akkadian"; + case "alb": return "Albanian"; + case "sqi": return "Albanian"; + case "ale": return "Aleut"; + case "alg": return "Algonquian languages"; + case "tut": return "Altaic (Other)"; + case "amh": return "Amharic"; + case "apa": return "Apache languages"; + case "ara": return "Arabic"; + case "arc": return "Aramaic"; + case "arp": return "Arapaho"; + case "arn": return "Araucanian"; + case "arw": return "Arawak"; + case "arm": return "Armenian"; + case "hye": return "Armenian"; + case "art": return "Artificial (Other)"; + case "asm": return "Assamese"; + case "ath": return "Athapascan languages"; + case "aus": return "Australian languages"; + case "map": return "Austronesian (Other)"; + case "ava": return "Avaric"; + case "ave": return "Avestan"; + case "awa": return "Awadhi"; + case "aym": return "Aymara"; + case "aze": return "Azerbaijani"; + case "ban": return "Balinese"; + case "bat": return "Baltic (Other)"; + case "bal": return "Baluchi"; + case "bam": return "Bambara"; + case "bai": return "Bamileke languages"; + case "bad": return "Banda"; + case "bnt": return "Bantu (Other)"; + case "bas": return "Basa"; + case "bak": return "Bashkir"; + case "baq": return "Basque"; + case "eus": return "Basque"; + case "btk": return "Batak (Indonesia)"; + case "bej": return "Beja"; + case "bel": return "Belarusian"; + case "bem": return "Bemba"; + case "ben": return "Bengali"; + case "ber": return "Berber (Other)"; + case "bho": return "Bhojpuri"; + case "bih": return "Bihari"; + case "bik": return "Bikol"; + case "bin": return "Bini"; + case "bis": return "Bislama"; + case "bos": return "Bosnian"; + case "bra": return "Braj"; + case "bre": return "Breton"; + case "bug": return "Buginese"; + case "bul": return "Bulgarian"; + case "bua": return "Buriat"; + case "bur": return "Burmese"; + case "mya": return "Burmese"; + case "cad": return "Caddo"; + case "car": return "Carib"; + case "cat": return "Catalan"; + case "cau": return "Caucasian (Other)"; + case "ceb": return "Cebuano"; + case "cel": return "Celtic (Other)"; + case "cai": return "Central American Indian (Other)"; + case "chg": return "Chagatai"; + case "cmc": return "Chamic languages"; + case "cha": return "Chamorro"; + case "che": return "Chechen"; + case "chr": return "Cherokee"; + case "chy": return "Cheyenne"; + case "chb": return "Chibcha"; + case "chi": return "Chinese"; + case "zho": return "Chinese"; + case "chn": return "Chinook jargon"; + case "chp": return "Chipewyan"; + case "cho": return "Choctaw"; + case "chu": return "Church Slavic"; + case "chk": return "Chuukese"; + case "chv": return "Chuvash"; + case "cop": return "Coptic"; + case "cor": return "Cornish"; + case "cos": return "Corsican"; + case "cre": return "Cree"; + case "mus": return "Creek"; + case "crp": return "Creoles and pidgins (Other)"; + case "cpe": return "Creoles and pidgins,"; + case "cpf": return "Creoles and pidgins,"; + case "cpp": return "Creoles and pidgins,"; + case "scr": return "Croatian"; + case "hrv": return "Croatian"; + case "cus": return "Cushitic (Other)"; + case "cze": return "Czech"; + case "ces": return "Czech"; + case "dak": return "Dakota"; + case "dan": return "Danish"; + case "day": return "Dayak"; + case "del": return "Delaware"; + case "din": return "Dinka"; + case "div": return "Divehi"; + case "doi": return "Dogri"; + case "dgr": return "Dogrib"; + case "dra": return "Dravidian (Other)"; + case "dua": return "Duala"; + case "dut": return "Dutch"; + case "nld": return "Dutch"; + case "dum": return "Dutch, Middle (ca. 1050-1350)"; + case "dyu": return "Dyula"; + case "dzo": return "Dzongkha"; + case "efi": return "Efik"; + case "egy": return "Egyptian (Ancient)"; + case "eka": return "Ekajuk"; + case "elx": return "Elamite"; + case "eng": return "English"; + case "enm": return "English, Middle (1100-1500)"; + case "ang": return "English, Old (ca.450-1100)"; + case "epo": return "Esperanto"; + case "est": return "Estonian"; + case "ewe": return "Ewe"; + case "ewo": return "Ewondo"; + case "fan": return "Fang"; + case "fat": return "Fanti"; + case "fao": return "Faroese"; + case "fij": return "Fijian"; + case "fin": return "Finnish"; + case "fiu": return "Finno-Ugrian (Other)"; + case "fon": return "Fon"; + case "fre": return "French"; + case "fra": return "French"; + case "frm": return "French, Middle (ca.1400-1600)"; + case "fro": return "French, Old (842-ca.1400)"; + case "fry": return "Frisian"; + case "fur": return "Friulian"; + case "ful": return "Fulah"; + case "gaa": return "Ga"; + case "glg": return "Gallegan"; + case "lug": return "Ganda"; + case "gay": return "Gayo"; + case "gba": return "Gbaya"; + case "gez": return "Geez"; + case "geo": return "Georgian"; + case "kat": return "Georgian"; + case "ger": return "German"; + case "deu": return "German"; + case "nds": return "Saxon"; + case "gmh": return "German, Middle High (ca.1050-1500)"; + case "goh": return "German, Old High (ca.750-1050)"; + case "gem": return "Germanic (Other)"; + case "gil": return "Gilbertese"; + case "gon": return "Gondi"; + case "gor": return "Gorontalo"; + case "got": return "Gothic"; + case "grb": return "Grebo"; + case "grc": return "Greek, Ancient (to 1453)"; + case "gre": return "Greek"; + case "ell": return "Greek"; + case "grn": return "Guarani"; + case "guj": return "Gujarati"; + case "gwi": return "Gwich´in"; + case "hai": return "Haida"; + case "hau": return "Hausa"; + case "haw": return "Hawaiian"; + case "heb": return "Hebrew"; + case "her": return "Herero"; + case "hil": return "Hiligaynon"; + case "him": return "Himachali"; + case "hin": return "Hindi"; + case "hmo": return "Hiri Motu"; + case "hit": return "Hittite"; + case "hmn": return "Hmong"; + case "hun": return "Hungarian"; + case "hup": return "Hupa"; + case "iba": return "Iban"; + case "ice": return "Icelandic"; + case "isl": return "Icelandic"; + case "ibo": return "Igbo"; + case "ijo": return "Ijo"; + case "ilo": return "Iloko"; + case "inc": return "Indic (Other)"; + case "ine": return "Indo-European (Other)"; + case "ind": return "Indonesian"; + case "ina": return "Interlingua (International"; + case "ile": return "Interlingue"; + case "iku": return "Inuktitut"; + case "ipk": return "Inupiaq"; + case "ira": return "Iranian (Other)"; + case "gle": return "Irish"; + case "mga": return "Irish, Middle (900-1200)"; + case "sga": return "Irish, Old (to 900)"; + case "iro": return "Iroquoian languages"; + case "ita": return "Italian"; + case "jpn": return "Japanese"; + case "jav": return "Javanese"; + case "jrb": return "Judeo-Arabic"; + case "jpr": return "Judeo-Persian"; + case "kab": return "Kabyle"; + case "kac": return "Kachin"; + case "kal": return "Kalaallisut"; + case "kam": return "Kamba"; + case "kan": return "Kannada"; + case "kau": return "Kanuri"; + case "kaa": return "Kara-Kalpak"; + case "kar": return "Karen"; + case "kas": return "Kashmiri"; + case "kaw": return "Kawi"; + case "kaz": return "Kazakh"; + case "kha": return "Khasi"; + case "khm": return "Khmer"; + case "khi": return "Khoisan (Other)"; + case "kho": return "Khotanese"; + case "kik": return "Kikuyu"; + case "kmb": return "Kimbundu"; + case "kin": return "Kinyarwanda"; + case "kir": return "Kirghiz"; + case "kom": return "Komi"; + case "kon": return "Kongo"; + case "kok": return "Konkani"; + case "kor": return "Korean"; + case "kos": return "Kosraean"; + case "kpe": return "Kpelle"; + case "kro": return "Kru"; + case "kua": return "Kuanyama"; + case "kum": return "Kumyk"; + case "kur": return "Kurdish"; + case "kru": return "Kurukh"; + case "kut": return "Kutenai"; + case "lad": return "Ladino"; + case "lah": return "Lahnda"; + case "lam": return "Lamba"; + case "lao": return "Lao"; + case "lat": return "Latin"; + case "lav": return "Latvian"; + case "ltz": return "Letzeburgesch"; + case "lez": return "Lezghian"; + case "lin": return "Lingala"; + case "lit": return "Lithuanian"; + case "loz": return "Lozi"; + case "lub": return "Luba-Katanga"; + case "lua": return "Luba-Lulua"; + case "lui": return "Luiseno"; + case "lun": return "Lunda"; + case "luo": return "Luo (Kenya and Tanzania)"; + case "lus": return "Lushai"; + case "mac": return "Macedonian"; + case "mkd": return "Macedonian"; + case "mad": return "Madurese"; + case "mag": return "Magahi"; + case "mai": return "Maithili"; + case "mak": return "Makasar"; + case "mlg": return "Malagasy"; + case "may": return "Malay"; + case "msa": return "Malay"; + case "mal": return "Malayalam"; + case "mlt": return "Maltese"; + case "mnc": return "Manchu"; + case "mdr": return "Mandar"; + case "man": return "Mandingo"; + case "mni": return "Manipuri"; + case "mno": return "Manobo languages"; + case "glv": return "Manx"; + case "mao": return "Maori"; + case "mri": return "Maori"; + case "mar": return "Marathi"; + case "chm": return "Mari"; + case "mah": return "Marshall"; + case "mwr": return "Marwari"; + case "mas": return "Masai"; + case "myn": return "Mayan languages"; + case "men": return "Mende"; + case "mic": return "Micmac"; + case "min": return "Minangkabau"; + case "mis": return "Miscellaneous languages"; + case "moh": return "Mohawk"; + case "mol": return "Moldavian"; + case "mkh": return "Mon-Khmer (Other)"; + case "lol": return "Mongo"; + case "mon": return "Mongolian"; + case "mos": return "Mossi"; + case "mul": return "Multiple languages"; + case "mun": return "Munda languages"; + case "nah": return "Nahuatl"; + case "nau": return "Nauru"; + case "nav": return "Navajo"; + case "nde": return "Ndebele, North"; + case "nbl": return "Ndebele, South"; + case "ndo": return "Ndonga"; + case "nep": return "Nepali"; + case "new": return "Newari"; + case "nia": return "Nias"; + case "nic": return "Niger-Kordofanian (Other)"; + case "ssa": return "Nilo-Saharan (Other)"; + case "niu": return "Niuean"; + case "non": return "Norse, Old"; + case "nai": return "North American Indian (Other)"; + case "sme": return "Northern Sami"; + case "nor": return "Norwegian"; + case "nob": return "Norwegian Bokmål"; + case "nno": return "Norwegian Nynorsk"; + case "nub": return "Nubian languages"; + case "nym": return "Nyamwezi"; + case "nya": return "Nyanja"; + case "nyn": return "Nyankole"; + case "nyo": return "Nyoro"; + case "nzi": return "Nzima"; + case "oci": return "Occitan"; + case "oji": return "Ojibwa"; + case "ori": return "Oriya"; + case "orm": return "Oromo"; + case "osa": return "Osage"; + case "oss": return "Ossetian"; + case "oto": return "Otomian languages"; + case "pal": return "Pahlavi"; + case "pau": return "Palauan"; + case "pli": return "Pali"; + case "pam": return "Pampanga"; + case "pag": return "Pangasinan"; + case "pan": return "Panjabi"; + case "pap": return "Papiamento"; + case "paa": return "Papuan (Other)"; + case "per": return "Persian"; + case "fas": return "Persian"; + case "peo": return "Persian, Old (ca.600-400 B.C.)"; + case "phi": return "Philippine (Other)"; + case "phn": return "Phoenician"; + case "pon": return "Pohnpeian"; + case "pol": return "Polish"; + case "por": return "Portuguese"; + case "pra": return "Prakrit languages"; + case "pro": return "Provençal"; + case "pus": return "Pushto"; + case "que": return "Quechua"; + case "roh": return "Raeto-Romance"; + case "raj": return "Rajasthani"; + case "rap": return "Rapanui"; + case "rar": return "Rarotongan"; + case "roa": return "Romance (Other)"; + case "rum": return "Romanian"; + case "ron": return "Romanian"; + case "rom": return "Romany"; + case "run": return "Rundi"; + case "rus": return "Russian"; + case "sal": return "Salishan languages"; + case "sam": return "Samaritan Aramaic"; + case "smi": return "Sami languages (Other)"; + case "smo": return "Samoan"; + case "sad": return "Sandawe"; + case "sag": return "Sango"; + case "san": return "Sanskrit"; + case "sat": return "Santali"; + case "srd": return "Sardinian"; + case "sas": return "Sasak"; + case "sco": return "Scots"; + case "gla": return "Gaelic"; + case "sel": return "Selkup"; + case "sem": return "Semitic (Other)"; + case "scc": return "Serbian"; + case "srp": return "Serbian"; + case "srr": return "Serer"; + case "shn": return "Shan"; + case "sna": return "Shona"; + case "sid": return "Sidamo"; + case "sgn": return "Sign languages"; + case "bla": return "Siksika"; + case "snd": return "Sindhi"; + case "sin": return "Sinhalese"; + case "sit": return "Sino-Tibetan (Other)"; + case "sio": return "Siouan languages"; + case "den": return "Slave (Athapascan)"; + case "sla": return "Slavic (Other)"; + case "slo": return "Slovak"; + case "slk": return "Slovak"; + case "slv": return "Slovenian"; + case "sog": return "Sogdian"; + case "som": return "Somali"; + case "son": return "Songhai"; + case "snk": return "Soninke"; + case "wen": return "Sorbian languages"; + case "nso": return "Sotho, Northern"; + case "sot": return "Sotho, Southern"; + case "sai": return "South American Indian (Other)"; + case "spa": return "Spanish"; + case "suk": return "Sukuma"; + case "sux": return "Sumerian"; + case "sun": return "Sundanese"; + case "sus": return "Susu"; + case "swa": return "Swahili"; + case "ssw": return "Swati"; + case "swe": return "Swedish"; + case "syr": return "Syriac"; + case "tgl": return "Tagalog"; + case "tah": return "Tahitian"; + case "tai": return "Tai (Other)"; + case "tgk": return "Tajik"; + case "tmh": return "Tamashek"; + case "tam": return "Tamil"; + case "tat": return "Tatar"; + case "tel": return "Telugu"; + case "ter": return "Tereno"; + case "tet": return "Tetum"; + case "tha": return "Thai"; + case "tib": return "Tibetan"; + case "bod": return "Tibetan"; + case "tig": return "Tigre"; + case "tir": return "Tigrinya"; + case "tem": return "Timne"; + case "tiv": return "Tiv"; + case "tli": return "Tlingit"; + case "tpi": return "Tok Pisin"; + case "tkl": return "Tokelau"; + case "tog": return "Tonga (Nyasa)"; + case "ton": return "Tonga (Tonga Islands)"; + case "tsi": return "Tsimshian"; + case "tso": return "Tsonga"; + case "tsn": return "Tswana"; + case "tum": return "Tumbuka"; + case "tur": return "Turkish"; + case "ota": return "Turkish, Ottoman (1500-1928)"; + case "tuk": return "Turkmen"; + case "tvl": return "Tuvalu"; + case "tyv": return "Tuvinian"; + case "twi": return "Twi"; + case "uga": return "Ugaritic"; + case "uig": return "Uighur"; + case "ukr": return "Ukrainian"; + case "umb": return "Umbundu"; + case "und": return "Undetermined"; + case "urd": return "Urdu"; + case "uzb": return "Uzbek"; + case "vai": return "Vai"; + case "ven": return "Venda"; + case "vie": return "Vietnamese"; + case "vol": return "Volapük"; + case "vot": return "Votic"; + case "wak": return "Wakashan languages"; + case "wal": return "Walamo"; + case "war": return "Waray"; + case "was": return "Washo"; + case "wel": return "Welsh"; + case "cym": return "Welsh"; + case "wol": return "Wolof"; + case "xho": return "Xhosa"; + case "sah": return "Yakut"; + case "yao": return "Yao"; + case "yap": return "Yapese"; + case "yid": return "Yiddish"; + case "yor": return "Yoruba"; + case "ypk": return "Yupik languages"; + case "znd": return "Zande"; + case "zap": return "Zapotec"; + case "zen": return "Zenaga"; + case "zha": return "Zhuang"; + case "zul": return "Zulu"; + case "zun": return "Zuni"; + + default: return code; + } + } + } +} diff --git a/BDInfo/Properties/AssemblyInfo.cs b/BDInfo/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..aa44da190 --- /dev/null +++ b/BDInfo/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BDInfo")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BDInfo")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.1")]
\ No newline at end of file diff --git a/BDInfo/ReadMe.txt b/BDInfo/ReadMe.txt new file mode 100644 index 000000000..68326d560 --- /dev/null +++ b/BDInfo/ReadMe.txt @@ -0,0 +1,5 @@ +The source is taken from the BDRom folder of this project: + +http://www.cinemasquid.com/blu-ray/tools/bdinfo + +BDInfoSettings was taken from the FormSettings class, and changed so that the settings all return defaults.
\ No newline at end of file diff --git a/BDInfo/TSCodecAC3.cs b/BDInfo/TSCodecAC3.cs new file mode 100644 index 000000000..144141c30 --- /dev/null +++ b/BDInfo/TSCodecAC3.cs @@ -0,0 +1,309 @@ +//============================================================================ +// 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.IO; + +namespace BDInfo +{ + public abstract class TSCodecAC3 + { + private static byte[] eac3_blocks = new byte[] { 1, 2, 3, 6 }; + + public static void Scan( + TSAudioStream stream, + TSStreamBuffer buffer, + ref string tag) + { + if (stream.IsInitialized) return; + + byte[] sync = buffer.ReadBytes(2); + if (sync == null || + sync[0] != 0x0B || + sync[1] != 0x77) + { + return; + } + + int sr_code = 0; + int frame_size = 0; + int frame_size_code = 0; + int channel_mode = 0; + int lfe_on = 0; + int dial_norm = 0; + int num_blocks = 0; + + byte[] hdr = buffer.ReadBytes(4); + int bsid = (hdr[3] & 0xF8) >> 3; + buffer.Seek(-4, SeekOrigin.Current); + if (bsid <= 10) + { + byte[] crc = buffer.ReadBytes(2); + sr_code = buffer.ReadBits(2); + frame_size_code = buffer.ReadBits(6); + bsid = buffer.ReadBits(5); + int bsmod = buffer.ReadBits(3); + + channel_mode = buffer.ReadBits(3); + int cmixlev = 0; + if (((channel_mode & 0x1) > 0) && (channel_mode != 0x1)) + { + cmixlev = buffer.ReadBits(2); + } + int surmixlev = 0; + if ((channel_mode & 0x4) > 0) + { + surmixlev = buffer.ReadBits(2); + } + int dsurmod = 0; + if (channel_mode == 0x2) + { + dsurmod = buffer.ReadBits(2); + if (dsurmod == 0x2) + { + stream.AudioMode = TSAudioMode.Surround; + } + } + lfe_on = buffer.ReadBits(1); + dial_norm = buffer.ReadBits(5); + int compr = 0; + if (1 == buffer.ReadBits(1)) + { + compr = buffer.ReadBits(8); + } + int langcod = 0; + if (1 == buffer.ReadBits(1)) + { + langcod = buffer.ReadBits(8); + } + int mixlevel = 0; + int roomtyp = 0; + if (1 == buffer.ReadBits(1)) + { + mixlevel = buffer.ReadBits(5); + roomtyp = buffer.ReadBits(2); + } + if (channel_mode == 0) + { + int dialnorm2 = buffer.ReadBits(5); + int compr2 = 0; + if (1 == buffer.ReadBits(1)) + { + compr2 = buffer.ReadBits(8); + } + int langcod2 = 0; + if (1 == buffer.ReadBits(1)) + { + langcod2 = buffer.ReadBits(8); + } + int mixlevel2 = 0; + int roomtyp2 = 0; + if (1 == buffer.ReadBits(1)) + { + mixlevel2 = buffer.ReadBits(5); + roomtyp2 = buffer.ReadBits(2); + } + } + int copyrightb = buffer.ReadBits(1); + int origbs = buffer.ReadBits(1); + if (bsid == 6) + { + if (1 == buffer.ReadBits(1)) + { + int dmixmod = buffer.ReadBits(2); + int ltrtcmixlev = buffer.ReadBits(3); + int ltrtsurmixlev = buffer.ReadBits(3); + int lorocmixlev = buffer.ReadBits(3); + int lorosurmixlev = buffer.ReadBits(3); + } + if (1 == buffer.ReadBits(1)) + { + int dsurexmod = buffer.ReadBits(2); + int dheadphonmod = buffer.ReadBits(2); + if (dheadphonmod == 0x2) + { + // TODO + } + int adconvtyp = buffer.ReadBits(1); + int xbsi2 = buffer.ReadBits(8); + int encinfo = buffer.ReadBits(1); + if (dsurexmod == 2) + { + stream.AudioMode = TSAudioMode.Extended; + } + } + } + } + else + { + int frame_type = buffer.ReadBits(2); + int substreamid = buffer.ReadBits(3); + frame_size = (buffer.ReadBits(11) + 1) << 1; + + sr_code = buffer.ReadBits(2); + if (sr_code == 3) + { + sr_code = buffer.ReadBits(2); + } + else + { + num_blocks = buffer.ReadBits(2); + } + channel_mode = buffer.ReadBits(3); + lfe_on = buffer.ReadBits(1); + } + + switch (channel_mode) + { + case 0: // 1+1 + stream.ChannelCount = 2; + if (stream.AudioMode == TSAudioMode.Unknown) + { + stream.AudioMode = TSAudioMode.DualMono; + } + break; + case 1: // 1/0 + stream.ChannelCount = 1; + break; + case 2: // 2/0 + stream.ChannelCount = 2; + if (stream.AudioMode == TSAudioMode.Unknown) + { + stream.AudioMode = TSAudioMode.Stereo; + } + break; + case 3: // 3/0 + stream.ChannelCount = 3; + break; + case 4: // 2/1 + stream.ChannelCount = 3; + break; + case 5: // 3/1 + stream.ChannelCount = 4; + break; + case 6: // 2/2 + stream.ChannelCount = 4; + break; + case 7: // 3/2 + stream.ChannelCount = 5; + break; + default: + stream.ChannelCount = 0; + break; + } + + switch (sr_code) + { + case 0: + stream.SampleRate = 48000; + break; + case 1: + stream.SampleRate = 44100; + break; + case 2: + stream.SampleRate = 32000; + break; + default: + stream.SampleRate = 0; + break; + } + + if (bsid <= 10) + { + switch (frame_size_code >> 1) + { + case 18: + stream.BitRate = 640000; + break; + case 17: + stream.BitRate = 576000; + break; + case 16: + stream.BitRate = 512000; + break; + case 15: + stream.BitRate = 448000; + break; + case 14: + stream.BitRate = 384000; + break; + case 13: + stream.BitRate = 320000; + break; + case 12: + stream.BitRate = 256000; + break; + case 11: + stream.BitRate = 224000; + break; + case 10: + stream.BitRate = 192000; + break; + case 9: + stream.BitRate = 160000; + break; + case 8: + stream.BitRate = 128000; + break; + case 7: + stream.BitRate = 112000; + break; + case 6: + stream.BitRate = 96000; + break; + case 5: + stream.BitRate = 80000; + break; + case 4: + stream.BitRate = 64000; + break; + case 3: + stream.BitRate = 56000; + break; + case 2: + stream.BitRate = 48000; + break; + case 1: + stream.BitRate = 40000; + break; + case 0: + stream.BitRate = 32000; + break; + default: + stream.BitRate = 0; + break; + } + } + else + { + stream.BitRate = (long) + (4.0 * frame_size * stream.SampleRate / (num_blocks * 256)); + } + + stream.LFE = lfe_on; + if (stream.StreamType != TSStreamType.AC3_PLUS_AUDIO && + stream.StreamType != TSStreamType.AC3_PLUS_SECONDARY_AUDIO) + { + stream.DialNorm = dial_norm - 31; + } + stream.IsVBR = false; + stream.IsInitialized = true; + } + } +} diff --git a/BDInfo/TSCodecAVC.cs b/BDInfo/TSCodecAVC.cs new file mode 100644 index 000000000..43c6d6f85 --- /dev/null +++ b/BDInfo/TSCodecAVC.cs @@ -0,0 +1,148 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + public abstract class TSCodecAVC + { + public static void Scan( + TSVideoStream stream, + TSStreamBuffer buffer, + ref string tag) + { + uint parse = 0; + byte accessUnitDelimiterParse = 0; + byte sequenceParameterSetParse = 0; + string profile = null; + string level = null; + byte constraintSet0Flag = 0; + byte constraintSet1Flag = 0; + byte constraintSet2Flag = 0; + byte constraintSet3Flag = 0; + + for (int i = 0; i < buffer.Length; i++) + { + parse = (parse << 8) + buffer.ReadByte(); + + if (parse == 0x00000109) + { + accessUnitDelimiterParse = 1; + } + else if (accessUnitDelimiterParse > 0) + { + --accessUnitDelimiterParse; + if (accessUnitDelimiterParse == 0) + { + switch ((parse & 0xFF) >> 5) + { + case 0: // I + case 3: // SI + case 5: // I, SI + tag = "I"; + break; + + case 1: // I, P + case 4: // SI, SP + case 6: // I, SI, P, SP + tag = "P"; + break; + + case 2: // I, P, B + case 7: // I, SI, P, SP, B + tag = "B"; + break; + } + if (stream.IsInitialized) return; + } + } + else if (parse == 0x00000127 || parse == 0x00000167) + { + sequenceParameterSetParse = 3; + } + else if (sequenceParameterSetParse > 0) + { + --sequenceParameterSetParse; + switch (sequenceParameterSetParse) + { + case 2: + switch (parse & 0xFF) + { + case 66: + profile = "Baseline Profile"; + break; + case 77: + profile = "Main Profile"; + break; + case 88: + profile = "Extended Profile"; + break; + case 100: + profile = "High Profile"; + break; + case 110: + profile = "High 10 Profile"; + break; + case 122: + profile = "High 4:2:2 Profile"; + break; + case 144: + profile = "High 4:4:4 Profile"; + break; + default: + profile = "Unknown Profile"; + break; + } + break; + + case 1: + constraintSet0Flag = (byte) + ((parse & 0x80) >> 7); + constraintSet1Flag = (byte) + ((parse & 0x40) >> 6); + constraintSet2Flag = (byte) + ((parse & 0x20) >> 5); + constraintSet3Flag = (byte) + ((parse & 0x10) >> 4); + break; + + case 0: + byte b = (byte)(parse & 0xFF); + if (b == 11 && constraintSet3Flag == 1) + { + level = "1b"; + } + else + { + level = string.Format( + "{0:D}.{1:D}", + b / 10, (b - ((b / 10) * 10))); + } + stream.EncodingProfile = string.Format( + "{0} {1}", profile, level); + stream.IsVBR = true; + stream.IsInitialized = true; + break; + } + } + } + return; + } + } +} diff --git a/BDInfo/TSCodecDTS.cs b/BDInfo/TSCodecDTS.cs new file mode 100644 index 000000000..58eb60fc5 --- /dev/null +++ b/BDInfo/TSCodecDTS.cs @@ -0,0 +1,159 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + public abstract class TSCodecDTS + { + private static int[] dca_sample_rates = + { + 0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, + 12000, 24000, 48000, 96000, 192000 + }; + + private static int[] dca_bit_rates = + { + 32000, 56000, 64000, 96000, 112000, 128000, + 192000, 224000, 256000, 320000, 384000, + 448000, 512000, 576000, 640000, 768000, + 896000, 1024000, 1152000, 1280000, 1344000, + 1408000, 1411200, 1472000, 1509000, 1920000, + 2048000, 3072000, 3840000, 1/*open*/, 2/*variable*/, 3/*lossless*/ + }; + + private static int[] dca_channels = + { + 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8 + }; + + private static int[] dca_bits_per_sample = + { + 16, 16, 20, 20, 0, 24, 24 + }; + + public static void Scan( + TSAudioStream stream, + TSStreamBuffer buffer, + long bitrate, + ref string tag) + { + if (stream.IsInitialized) return; + + bool syncFound = false; + uint sync = 0; + for (int i = 0; i < buffer.Length; i++) + { + sync = (sync << 8) + buffer.ReadByte(); + if (sync == 0x7FFE8001) + { + syncFound = true; + break; + } + } + if (!syncFound) return; + + int frame_type = buffer.ReadBits(1); + int samples_deficit = buffer.ReadBits(5); + int crc_present = buffer.ReadBits(1); + int sample_blocks = buffer.ReadBits(7); + int frame_size = buffer.ReadBits(14); + if (frame_size < 95) + { + return; + } + int amode = buffer.ReadBits(6); + int sample_rate = buffer.ReadBits(4); + if (sample_rate < 0 || sample_rate >= dca_sample_rates.Length) + { + return; + } + int bit_rate = buffer.ReadBits(5); + if (bit_rate < 0 || bit_rate >= dca_bit_rates.Length) + { + return; + } + int downmix = buffer.ReadBits(1); + int dynrange = buffer.ReadBits(1); + int timestamp = buffer.ReadBits(1); + int aux_data = buffer.ReadBits(1); + int hdcd = buffer.ReadBits(1); + int ext_descr = buffer.ReadBits(3); + int ext_coding = buffer.ReadBits(1); + int aspf = buffer.ReadBits(1); + int lfe = buffer.ReadBits(2); + int predictor_history = buffer.ReadBits(1); + if (crc_present == 1) + { + int crc = buffer.ReadBits(16); + } + int multirate_inter = buffer.ReadBits(1); + int version = buffer.ReadBits(4); + int copy_history = buffer.ReadBits(2); + int source_pcm_res = buffer.ReadBits(3); + int front_sum = buffer.ReadBits(1); + int surround_sum = buffer.ReadBits(1); + int dialog_norm = buffer.ReadBits(4); + if (source_pcm_res < 0 || source_pcm_res >= dca_bits_per_sample.Length) + { + return; + } + int subframes = buffer.ReadBits(4); + int total_channels = buffer.ReadBits(3) + 1 + ext_coding; + + stream.SampleRate = dca_sample_rates[sample_rate]; + stream.ChannelCount = total_channels; + stream.LFE = (lfe > 0 ? 1 : 0); + stream.BitDepth = dca_bits_per_sample[source_pcm_res]; + stream.DialNorm = -dialog_norm; + if ((source_pcm_res & 0x1) == 0x1) + { + stream.AudioMode = TSAudioMode.Extended; + } + + stream.BitRate = (uint)dca_bit_rates[bit_rate]; + switch (stream.BitRate) + { + case 1: + if (bitrate > 0) + { + stream.BitRate = bitrate; + stream.IsVBR = false; + stream.IsInitialized = true; + } + else + { + stream.BitRate = 0; + } + break; + + case 2: + case 3: + stream.IsVBR = true; + stream.IsInitialized = true; + break; + + default: + stream.IsVBR = false; + stream.IsInitialized = true; + break; + } + } + } +} diff --git a/BDInfo/TSCodecDTSHD.cs b/BDInfo/TSCodecDTSHD.cs new file mode 100644 index 000000000..169a8077f --- /dev/null +++ b/BDInfo/TSCodecDTSHD.cs @@ -0,0 +1,246 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + public abstract class TSCodecDTSHD + { + private static int[] SampleRates = new int[] + { 0x1F40, 0x3E80, 0x7D00, 0x0FA00, 0x1F400, 0x5622, 0x0AC44, 0x15888, 0x2B110, 0x56220, 0x2EE0, 0x5DC0, 0x0BB80, 0x17700, 0x2EE00, 0x5DC00 }; + + public static void Scan( + TSAudioStream stream, + TSStreamBuffer buffer, + long bitrate, + ref string tag) + { + if (stream.IsInitialized && + (stream.StreamType == TSStreamType.DTS_HD_SECONDARY_AUDIO || + (stream.CoreStream != null && + stream.CoreStream.IsInitialized))) return; + + bool syncFound = false; + uint sync = 0; + for (int i = 0; i < buffer.Length; i++) + { + sync = (sync << 8) + buffer.ReadByte(); + if (sync == 0x64582025) + { + syncFound = true; + break; + } + } + + if (!syncFound) + { + tag = "CORE"; + if (stream.CoreStream == null) + { + stream.CoreStream = new TSAudioStream(); + stream.CoreStream.StreamType = TSStreamType.DTS_AUDIO; + } + if (!stream.CoreStream.IsInitialized) + { + buffer.BeginRead(); + TSCodecDTS.Scan(stream.CoreStream, buffer, bitrate, ref tag); + } + return; + } + + tag = "HD"; + int temp1 = buffer.ReadBits(8); + int nuSubStreamIndex = buffer.ReadBits(2); + int nuExtSSHeaderSize = 0; + int nuExtSSFSize = 0; + int bBlownUpHeader = buffer.ReadBits(1); + if (1 == bBlownUpHeader) + { + nuExtSSHeaderSize = buffer.ReadBits(12) + 1; + nuExtSSFSize = buffer.ReadBits(20) + 1; + } + else + { + nuExtSSHeaderSize = buffer.ReadBits(8) + 1; + nuExtSSFSize = buffer.ReadBits(16) + 1; + } + int nuNumAudioPresent = 1; + int nuNumAssets = 1; + int bStaticFieldsPresent = buffer.ReadBits(1); + if (1 == bStaticFieldsPresent) + { + int nuRefClockCode = buffer.ReadBits(2); + int nuExSSFrameDurationCode = buffer.ReadBits(3) + 1; + long nuTimeStamp = 0; + if (1 == buffer.ReadBits(1)) + { + nuTimeStamp = (buffer.ReadBits(18) << 18) + buffer.ReadBits(18); + } + nuNumAudioPresent = buffer.ReadBits(3) + 1; + nuNumAssets = buffer.ReadBits(3) + 1; + int[] nuActiveExSSMask = new int[nuNumAudioPresent]; + for (int i = 0; i < nuNumAudioPresent; i++) + { + nuActiveExSSMask[i] = buffer.ReadBits(nuSubStreamIndex + 1); //? + } + for (int i = 0; i < nuNumAudioPresent; i++) + { + for (int j = 0; j < nuSubStreamIndex + 1; j++) + { + if (((j + 1) % 2) == 1) + { + int mask = buffer.ReadBits(8); + } + } + } + if (1 == buffer.ReadBits(1)) + { + int nuMixMetadataAdjLevel = buffer.ReadBits(2); + int nuBits4MixOutMask = buffer.ReadBits(2) * 4 + 4; + int nuNumMixOutConfigs = buffer.ReadBits(2) + 1; + int[] nuMixOutChMask = new int[nuNumMixOutConfigs]; + for (int i = 0; i < nuNumMixOutConfigs; i++) + { + nuMixOutChMask[i] = buffer.ReadBits(nuBits4MixOutMask); + } + } + } + int[] AssetSizes = new int[nuNumAssets]; + for (int i = 0; i < nuNumAssets; i++) + { + if (1 == bBlownUpHeader) + { + AssetSizes[i] = buffer.ReadBits(20) + 1; + } + else + { + AssetSizes[i] = buffer.ReadBits(16) + 1; + } + } + for (int i = 0; i < nuNumAssets; i++) + { + long bufferPosition = buffer.Position; + int nuAssetDescriptorFSIZE = buffer.ReadBits(9) + 1; + int DescriptorDataForAssetIndex = buffer.ReadBits(3); + if (1 == bStaticFieldsPresent) + { + int AssetTypeDescrPresent = buffer.ReadBits(1); + if (1 == AssetTypeDescrPresent) + { + int AssetTypeDescriptor = buffer.ReadBits(4); + } + int LanguageDescrPresent = buffer.ReadBits(1); + if (1 == LanguageDescrPresent) + { + int LanguageDescriptor = buffer.ReadBits(24); + } + int bInfoTextPresent = buffer.ReadBits(1); + if (1 == bInfoTextPresent) + { + int nuInfoTextByteSize = buffer.ReadBits(10) + 1; + int[] InfoText = new int[nuInfoTextByteSize]; + for (int j = 0; j < nuInfoTextByteSize; j++) + { + InfoText[j] = buffer.ReadBits(8); + } + } + int nuBitResolution = buffer.ReadBits(5) + 1; + int nuMaxSampleRate = buffer.ReadBits(4); + int nuTotalNumChs = buffer.ReadBits(8) + 1; + int bOne2OneMapChannels2Speakers = buffer.ReadBits(1); + int nuSpkrActivityMask = 0; + if (1 == bOne2OneMapChannels2Speakers) + { + int bEmbeddedStereoFlag = 0; + if (nuTotalNumChs > 2) + { + bEmbeddedStereoFlag = buffer.ReadBits(1); + } + int bEmbeddedSixChFlag = 0; + if (nuTotalNumChs > 6) + { + bEmbeddedSixChFlag = buffer.ReadBits(1); + } + int bSpkrMaskEnabled = buffer.ReadBits(1); + int nuNumBits4SAMask = 0; + if (1 == bSpkrMaskEnabled) + { + nuNumBits4SAMask = buffer.ReadBits(2); + nuNumBits4SAMask = nuNumBits4SAMask * 4 + 4; + nuSpkrActivityMask = buffer.ReadBits(nuNumBits4SAMask); + } + // TODO... + } + stream.SampleRate = SampleRates[nuMaxSampleRate]; + stream.BitDepth = nuBitResolution; + + stream.LFE = 0; + if ((nuSpkrActivityMask & 0x8) == 0x8) + { + ++stream.LFE; + } + if ((nuSpkrActivityMask & 0x1000) == 0x1000) + { + ++stream.LFE; + } + stream.ChannelCount = nuTotalNumChs - stream.LFE; + } + if (nuNumAssets > 1) + { + // TODO... + break; + } + } + + // TODO + if (stream.CoreStream != null) + { + TSAudioStream coreStream = (TSAudioStream)stream.CoreStream; + if (coreStream.AudioMode == TSAudioMode.Extended && + stream.ChannelCount == 5) + { + stream.AudioMode = TSAudioMode.Extended; + } + /* + if (coreStream.DialNorm != 0) + { + stream.DialNorm = coreStream.DialNorm; + } + */ + } + + if (stream.StreamType == TSStreamType.DTS_HD_MASTER_AUDIO) + { + stream.IsVBR = true; + stream.IsInitialized = true; + } + else if (bitrate > 0) + { + stream.IsVBR = false; + stream.BitRate = bitrate; + if (stream.CoreStream != null) + { + stream.BitRate += stream.CoreStream.BitRate; + stream.IsInitialized = true; + } + stream.IsInitialized = (stream.BitRate > 0 ? true : false); + } + } + } +} diff --git a/BDInfo/TSCodecLPCM.cs b/BDInfo/TSCodecLPCM.cs new file mode 100644 index 000000000..d12674f0e --- /dev/null +++ b/BDInfo/TSCodecLPCM.cs @@ -0,0 +1,123 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + public abstract class TSCodecLPCM + { + public static void Scan( + TSAudioStream stream, + TSStreamBuffer buffer, + ref string tag) + { + if (stream.IsInitialized) return; + + byte[] header = buffer.ReadBytes(4); + int flags = (header[2] << 8) + header[3]; + + switch ((flags & 0xF000) >> 12) + { + case 1: // 1/0/0 + stream.ChannelCount = 1; + stream.LFE = 0; + break; + case 3: // 2/0/0 + stream.ChannelCount = 2; + stream.LFE = 0; + break; + case 4: // 3/0/0 + stream.ChannelCount = 3; + stream.LFE = 0; + break; + case 5: // 2/1/0 + stream.ChannelCount = 3; + stream.LFE = 0; + break; + case 6: // 3/1/0 + stream.ChannelCount = 4; + stream.LFE = 0; + break; + case 7: // 2/2/0 + stream.ChannelCount = 4; + stream.LFE = 0; + break; + case 8: // 3/2/0 + stream.ChannelCount = 5; + stream.LFE = 0; + break; + case 9: // 3/2/1 + stream.ChannelCount = 5; + stream.LFE = 1; + break; + case 10: // 3/4/0 + stream.ChannelCount = 7; + stream.LFE = 0; + break; + case 11: // 3/4/1 + stream.ChannelCount = 7; + stream.LFE = 1; + break; + default: + stream.ChannelCount = 0; + stream.LFE = 0; + break; + } + + switch ((flags & 0xC0) >> 6) + { + case 1: + stream.BitDepth = 16; + break; + case 2: + stream.BitDepth = 20; + break; + case 3: + stream.BitDepth = 24; + break; + default: + stream.BitDepth = 0; + break; + } + + switch ((flags & 0xF00) >> 8) + { + case 1: + stream.SampleRate = 48000; + break; + case 4: + stream.SampleRate = 96000; + break; + case 5: + stream.SampleRate = 192000; + break; + default: + stream.SampleRate = 0; + break; + } + + stream.BitRate = (uint) + (stream.SampleRate * stream.BitDepth * + (stream.ChannelCount + stream.LFE)); + + stream.IsVBR = false; + stream.IsInitialized = true; + } + } +} diff --git a/BDInfo/TSCodecMPEG2.cs b/BDInfo/TSCodecMPEG2.cs new file mode 100644 index 000000000..3413a06e9 --- /dev/null +++ b/BDInfo/TSCodecMPEG2.cs @@ -0,0 +1,208 @@ +//============================================================================ +// 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 + + +namespace BDInfo +{ + public abstract class TSCodecMPEG2 + { + public static void Scan( + TSVideoStream stream, + TSStreamBuffer buffer, + ref string tag) + { + int parse = 0; + int pictureParse = 0; + int sequenceHeaderParse = 0; + int extensionParse = 0; + int sequenceExtensionParse = 0; + + for (int i = 0; i < buffer.Length; i++) + { + parse = (parse << 8) + buffer.ReadByte(); + + if (parse == 0x00000100) + { + pictureParse = 2; + } + else if (parse == 0x000001B3) + { + sequenceHeaderParse = 7; + } + else if (sequenceHeaderParse > 0) + { + --sequenceHeaderParse; + switch (sequenceHeaderParse) + { +#if DEBUG + case 6: + break; + + case 5: + break; + + case 4: + stream.Width = + (int)((parse & 0xFFF000) >> 12); + stream.Height = + (int)(parse & 0xFFF); + break; + + case 3: + stream.AspectRatio = + (TSAspectRatio)((parse & 0xF0) >> 4); + + switch ((parse & 0xF0) >> 4) + { + case 0: // Forbidden + break; + case 1: // Square + break; + case 2: // 4:3 + break; + case 3: // 16:9 + break; + case 4: // 2.21:1 + break; + default: // Reserved + break; + } + + switch (parse & 0xF) + { + case 0: // Forbidden + break; + case 1: // 23.976 + stream.FrameRateEnumerator = 24000; + stream.FrameRateDenominator = 1001; + break; + case 2: // 24 + stream.FrameRateEnumerator = 24000; + stream.FrameRateDenominator = 1000; + break; + case 3: // 25 + stream.FrameRateEnumerator = 25000; + stream.FrameRateDenominator = 1000; + break; + case 4: // 29.97 + stream.FrameRateEnumerator = 30000; + stream.FrameRateDenominator = 1001; + break; + case 5: // 30 + stream.FrameRateEnumerator = 30000; + stream.FrameRateDenominator = 1000; + break; + case 6: // 50 + stream.FrameRateEnumerator = 50000; + stream.FrameRateDenominator = 1000; + break; + case 7: // 59.94 + stream.FrameRateEnumerator = 60000; + stream.FrameRateDenominator = 1001; + break; + case 8: // 60 + stream.FrameRateEnumerator = 60000; + stream.FrameRateDenominator = 1000; + break; + default: // Reserved + stream.FrameRateEnumerator = 0; + stream.FrameRateDenominator = 0; + break; + } + break; + + case 2: + break; + + case 1: + break; +#endif + + case 0: +#if DEBUG + stream.BitRate = + (((parse & 0xFFFFC0) >> 6) * 200); +#endif + stream.IsVBR = true; + stream.IsInitialized = true; + break; + } + } + else if (pictureParse > 0) + { + --pictureParse; + if (pictureParse == 0) + { + switch ((parse & 0x38) >> 3) + { + case 1: + tag = "I"; + break; + case 2: + tag = "P"; + break; + case 3: + tag = "B"; + break; + default: + break; + } + if (stream.IsInitialized) return; + } + } + else if (parse == 0x000001B5) + { + extensionParse = 1; + } + else if (extensionParse > 0) + { + --extensionParse; + if (extensionParse == 0) + { + if ((parse & 0xF0) == 0x10) + { + sequenceExtensionParse = 1; + } + } + } + else if (sequenceExtensionParse > 0) + { + --sequenceExtensionParse; +#if DEBUG + if (sequenceExtensionParse == 0) + { + uint sequenceExtension = + ((parse & 0x8) >> 3); + if (sequenceExtension == 0) + { + stream.IsInterlaced = true; + } + else + { + stream.IsInterlaced = false; + } + } +#endif + } + } + } + } +} diff --git a/BDInfo/TSCodecMVC.cs b/BDInfo/TSCodecMVC.cs new file mode 100644 index 000000000..80fed3886 --- /dev/null +++ b/BDInfo/TSCodecMVC.cs @@ -0,0 +1,36 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + // TODO: Do something more interesting here... + + public abstract class TSCodecMVC + { + public static void Scan( + TSVideoStream stream, + TSStreamBuffer buffer, + ref string tag) + { + stream.IsVBR = true; + stream.IsInitialized = true; + } + } +} diff --git a/BDInfo/TSCodecTrueHD.cs b/BDInfo/TSCodecTrueHD.cs new file mode 100644 index 000000000..baf4fa3df --- /dev/null +++ b/BDInfo/TSCodecTrueHD.cs @@ -0,0 +1,186 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + public abstract class TSCodecTrueHD + { + public static void Scan( + TSAudioStream stream, + TSStreamBuffer buffer, + ref string tag) + { + if (stream.IsInitialized && + stream.CoreStream != null && + stream.CoreStream.IsInitialized) return; + + bool syncFound = false; + uint sync = 0; + for (int i = 0; i < buffer.Length; i++) + { + sync = (sync << 8) + buffer.ReadByte(); + if (sync == 0xF8726FBA) + { + syncFound = true; + break; + } + } + + if (!syncFound) + { + tag = "CORE"; + if (stream.CoreStream == null) + { + stream.CoreStream = new TSAudioStream(); + stream.CoreStream.StreamType = TSStreamType.AC3_AUDIO; + } + if (!stream.CoreStream.IsInitialized) + { + buffer.BeginRead(); + TSCodecAC3.Scan(stream.CoreStream, buffer, ref tag); + } + return; + } + + tag = "HD"; + int ratebits = buffer.ReadBits(4); + if (ratebits != 0xF) + { + stream.SampleRate = + (((ratebits & 8) > 0 ? 44100 : 48000) << (ratebits & 7)); + } + int temp1 = buffer.ReadBits(8); + int channels_thd_stream1 = buffer.ReadBits(5); + int temp2 = buffer.ReadBits(2); + + stream.ChannelCount = 0; + stream.LFE = 0; + int c_LFE2 = buffer.ReadBits(1); + if (c_LFE2 == 1) + { + stream.LFE += 1; + } + int c_Cvh = buffer.ReadBits(1); + if (c_Cvh == 1) + { + stream.ChannelCount += 1; + } + int c_LRw = buffer.ReadBits(1); + if (c_LRw == 1) + { + stream.ChannelCount += 2; + } + int c_LRsd = buffer.ReadBits(1); + if (c_LRsd == 1) + { + stream.ChannelCount += 2; + } + int c_Ts = buffer.ReadBits(1); + if (c_Ts == 1) + { + stream.ChannelCount += 1; + } + int c_Cs = buffer.ReadBits(1); + if (c_Cs == 1) + { + stream.ChannelCount += 1; + } + int c_LRrs = buffer.ReadBits(1); + if (c_LRrs == 1) + { + stream.ChannelCount += 2; + } + int c_LRc = buffer.ReadBits(1); + if (c_LRc == 1) + { + stream.ChannelCount += 2; + } + int c_LRvh = buffer.ReadBits(1); + if (c_LRvh == 1) + { + stream.ChannelCount += 2; + } + int c_LRs = buffer.ReadBits(1); + if (c_LRs == 1) + { + stream.ChannelCount += 2; + } + int c_LFE = buffer.ReadBits(1); + if (c_LFE == 1) + { + stream.LFE += 1; + } + int c_C = buffer.ReadBits(1); + if (c_C == 1) + { + stream.ChannelCount += 1; + } + int c_LR = buffer.ReadBits(1); + if (c_LR == 1) + { + stream.ChannelCount += 2; + } + + int access_unit_size = 40 << (ratebits & 7); + int access_unit_size_pow2 = 64 << (ratebits & 7); + + int a1 = buffer.ReadBits(16); + int a2 = buffer.ReadBits(16); + int a3 = buffer.ReadBits(16); + + int is_vbr = buffer.ReadBits(1); + int peak_bitrate = buffer.ReadBits(15); + peak_bitrate = (peak_bitrate * stream.SampleRate) >> 4; + + double peak_bitdepth = + (double)peak_bitrate / + (stream.ChannelCount + stream.LFE) / + stream.SampleRate; + if (peak_bitdepth > 14) + { + stream.BitDepth = 24; + } + else + { + stream.BitDepth = 16; + } + +#if DEBUG + System.Diagnostics.Debug.WriteLine(string.Format( + "{0}\t{1}\t{2:F2}", + stream.PID, peak_bitrate, peak_bitdepth)); +#endif + /* + // TODO: Get THD dialnorm from metadata + if (stream.CoreStream != null) + { + TSAudioStream coreStream = (TSAudioStream)stream.CoreStream; + if (coreStream.DialNorm != 0) + { + stream.DialNorm = coreStream.DialNorm; + } + } + */ + + stream.IsVBR = true; + stream.IsInitialized = true; + } + } +} diff --git a/BDInfo/TSCodecVC1.cs b/BDInfo/TSCodecVC1.cs new file mode 100644 index 000000000..164ef8c47 --- /dev/null +++ b/BDInfo/TSCodecVC1.cs @@ -0,0 +1,131 @@ +//============================================================================ +// 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 +//============================================================================= + + +namespace BDInfo +{ + public abstract class TSCodecVC1 + { + public static void Scan( + TSVideoStream stream, + TSStreamBuffer buffer, + ref string tag) + { + int parse = 0; + byte frameHeaderParse = 0; + byte sequenceHeaderParse = 0; + bool isInterlaced = false; + + for (int i = 0; i < buffer.Length; i++) + { + parse = (parse << 8) + buffer.ReadByte(); + + if (parse == 0x0000010D) + { + frameHeaderParse = 4; + } + else if (frameHeaderParse > 0) + { + --frameHeaderParse; + if (frameHeaderParse == 0) + { + uint pictureType = 0; + if (isInterlaced) + { + if ((parse & 0x80000000) == 0) + { + pictureType = + (uint)((parse & 0x78000000) >> 13); + } + else + { + pictureType = + (uint)((parse & 0x3c000000) >> 12); + } + } + else + { + pictureType = + (uint)((parse & 0xf0000000) >> 14); + } + + if ((pictureType & 0x20000) == 0) + { + tag = "P"; + } + else if ((pictureType & 0x10000) == 0) + { + tag = "B"; + } + else if ((pictureType & 0x8000) == 0) + { + tag = "I"; + } + else if ((pictureType & 0x4000) == 0) + { + tag = "BI"; + } + else + { + tag = null; + } + if (stream.IsInitialized) return; + } + } + else if (parse == 0x0000010F) + { + sequenceHeaderParse = 6; + } + else if (sequenceHeaderParse > 0) + { + --sequenceHeaderParse; + switch (sequenceHeaderParse) + { + case 5: + int profileLevel = ((parse & 0x38) >> 3); + if (((parse & 0xC0) >> 6) == 3) + { + stream.EncodingProfile = string.Format( + "Advanced Profile {0}", profileLevel); + } + else + { + stream.EncodingProfile = string.Format( + "Main Profile {0}", profileLevel); + } + break; + + case 0: + if (((parse & 0x40) >> 6) > 0) + { + isInterlaced = true; + } + else + { + isInterlaced = false; + } + break; + } + stream.IsVBR = true; + stream.IsInitialized = true; + } + } + } + } +} diff --git a/BDInfo/TSInterleavedFile.cs b/BDInfo/TSInterleavedFile.cs new file mode 100644 index 000000000..44a16bbaa --- /dev/null +++ b/BDInfo/TSInterleavedFile.cs @@ -0,0 +1,38 @@ +//============================================================================ +// 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.IO; +using MediaBrowser.Model.IO; + +// TODO: Do more interesting things here... + +namespace BDInfo +{ + public class TSInterleavedFile + { + public FileSystemMetadata FileInfo = null; + public string Name = null; + + public TSInterleavedFile(FileSystemMetadata fileInfo) + { + FileInfo = fileInfo; + Name = fileInfo.Name.ToUpper(); + } + } +} diff --git a/BDInfo/TSPlaylistFile.cs b/BDInfo/TSPlaylistFile.cs new file mode 100644 index 000000000..46d66f513 --- /dev/null +++ b/BDInfo/TSPlaylistFile.cs @@ -0,0 +1,1292 @@ +//============================================================================ +// 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 System.Text; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Text; + +namespace BDInfo +{ + public class TSPlaylistFile + { + private readonly IFileSystem _fileSystem; + private readonly ITextEncoding _textEncoding; + private FileSystemMetadata FileInfo = null; + public string FileType = null; + public bool IsInitialized = false; + public string Name = null; + public BDROM BDROM = null; + public bool HasHiddenTracks = false; + public bool HasLoops = false; + public bool IsCustom = false; + + public List<double> Chapters = new List<double>(); + + public Dictionary<ushort, TSStream> Streams = + new Dictionary<ushort, TSStream>(); + public Dictionary<ushort, TSStream> PlaylistStreams = + new Dictionary<ushort, TSStream>(); + public List<TSStreamClip> StreamClips = + new List<TSStreamClip>(); + public List<Dictionary<ushort, TSStream>> AngleStreams = + new List<Dictionary<ushort, TSStream>>(); + public List<Dictionary<double, TSStreamClip>> AngleClips = + new List<Dictionary<double, TSStreamClip>>(); + public int AngleCount = 0; + + public List<TSStream> SortedStreams = + new List<TSStream>(); + public List<TSVideoStream> VideoStreams = + new List<TSVideoStream>(); + public List<TSAudioStream> AudioStreams = + new List<TSAudioStream>(); + public List<TSTextStream> TextStreams = + new List<TSTextStream>(); + public List<TSGraphicsStream> GraphicsStreams = + new List<TSGraphicsStream>(); + + public TSPlaylistFile( + BDROM bdrom, + FileSystemMetadata fileInfo, IFileSystem fileSystem, ITextEncoding textEncoding) + { + BDROM = bdrom; + FileInfo = fileInfo; + _fileSystem = fileSystem; + _textEncoding = textEncoding; + Name = fileInfo.Name.ToUpper(); + } + + public TSPlaylistFile( + BDROM bdrom, + string name, + List<TSStreamClip> clips, IFileSystem fileSystem, ITextEncoding textEncoding) + { + BDROM = bdrom; + Name = name; + _fileSystem = fileSystem; + _textEncoding = textEncoding; + IsCustom = true; + foreach (TSStreamClip clip in clips) + { + TSStreamClip newClip = new TSStreamClip( + clip.StreamFile, clip.StreamClipFile); + + newClip.Name = clip.Name; + newClip.TimeIn = clip.TimeIn; + newClip.TimeOut = clip.TimeOut; + newClip.Length = newClip.TimeOut - newClip.TimeIn; + newClip.RelativeTimeIn = TotalLength; + newClip.RelativeTimeOut = newClip.RelativeTimeIn + newClip.Length; + newClip.AngleIndex = clip.AngleIndex; + newClip.Chapters.Add(clip.TimeIn); + StreamClips.Add(newClip); + + if (newClip.AngleIndex > AngleCount) + { + AngleCount = newClip.AngleIndex; + } + if (newClip.AngleIndex == 0) + { + Chapters.Add(newClip.RelativeTimeIn); + } + } + LoadStreamClips(); + IsInitialized = true; + } + + public override string ToString() + { + return Name; + } + + public ulong InterleavedFileSize + { + get + { + ulong size = 0; + foreach (TSStreamClip clip in StreamClips) + { + size += clip.InterleavedFileSize; + } + return size; + } + } + public ulong FileSize + { + get + { + ulong size = 0; + foreach (TSStreamClip clip in StreamClips) + { + size += clip.FileSize; + } + return size; + } + } + public double TotalLength + { + get + { + double length = 0; + foreach (TSStreamClip clip in StreamClips) + { + if (clip.AngleIndex == 0) + { + length += clip.Length; + } + } + return length; + } + } + + public double TotalAngleLength + { + get + { + double length = 0; + foreach (TSStreamClip clip in StreamClips) + { + length += clip.Length; + } + return length; + } + } + + public ulong TotalSize + { + get + { + ulong size = 0; + foreach (TSStreamClip clip in StreamClips) + { + if (clip.AngleIndex == 0) + { + size += clip.PacketSize; + } + } + return size; + } + } + + public ulong TotalAngleSize + { + get + { + ulong size = 0; + foreach (TSStreamClip clip in StreamClips) + { + size += clip.PacketSize; + } + return size; + } + } + + public ulong TotalBitRate + { + get + { + if (TotalLength > 0) + { + return (ulong)Math.Round(((TotalSize * 8.0) / TotalLength)); + } + return 0; + } + } + + public ulong TotalAngleBitRate + { + get + { + if (TotalAngleLength > 0) + { + return (ulong)Math.Round(((TotalAngleSize * 8.0) / TotalAngleLength)); + } + return 0; + } + } + + public void Scan( + Dictionary<string, TSStreamFile> streamFiles, + Dictionary<string, TSStreamClipFile> streamClipFiles) + { + Stream fileStream = null; + BinaryReader fileReader = null; + + try + { + Streams.Clear(); + StreamClips.Clear(); + + fileStream = _fileSystem.OpenRead(FileInfo.FullName); + fileReader = new BinaryReader(fileStream); + + byte[] data = new byte[fileStream.Length]; + int dataLength = fileReader.Read(data, 0, data.Length); + + int pos = 0; + + FileType = ReadString(data, 8, ref pos); + if (FileType != "MPLS0100" && FileType != "MPLS0200") + { + throw new Exception(string.Format( + "Playlist {0} has an unknown file type {1}.", + FileInfo.Name, FileType)); + } + + int playlistOffset = ReadInt32(data, ref pos); + int chaptersOffset = ReadInt32(data, ref pos); + int extensionsOffset = ReadInt32(data, ref pos); + + pos = playlistOffset; + + int playlistLength = ReadInt32(data, ref pos); + int playlistReserved = ReadInt16(data, ref pos); + int itemCount = ReadInt16(data, ref pos); + int subitemCount = ReadInt16(data, ref pos); + + List<TSStreamClip> chapterClips = new List<TSStreamClip>(); + for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) + { + int itemStart = pos; + int itemLength = ReadInt16(data, ref pos); + string itemName = ReadString(data, 5, ref pos); + string itemType = ReadString(data, 4, ref pos); + + TSStreamFile streamFile = null; + string streamFileName = string.Format( + "{0}.M2TS", itemName); + if (streamFiles.ContainsKey(streamFileName)) + { + streamFile = streamFiles[streamFileName]; + } + if (streamFile == null) + { + // Error condition + } + + TSStreamClipFile streamClipFile = null; + string streamClipFileName = string.Format( + "{0}.CLPI", itemName); + if (streamClipFiles.ContainsKey(streamClipFileName)) + { + streamClipFile = streamClipFiles[streamClipFileName]; + } + if (streamClipFile == null) + { + throw new Exception(string.Format( + "Playlist {0} referenced missing file {1}.", + FileInfo.Name, streamFileName)); + } + + pos += 1; + int multiangle = (data[pos] >> 4) & 0x01; + int condition = data[pos] & 0x0F; + pos += 2; + + int inTime = ReadInt32(data, ref pos); + if (inTime < 0) inTime &= 0x7FFFFFFF; + double timeIn = (double)inTime / 45000; + + int outTime = ReadInt32(data, ref pos); + if (outTime < 0) outTime &= 0x7FFFFFFF; + double timeOut = (double)outTime / 45000; + + TSStreamClip streamClip = new TSStreamClip( + streamFile, streamClipFile); + + streamClip.Name = streamFileName; //TODO + streamClip.TimeIn = timeIn; + streamClip.TimeOut = timeOut; + streamClip.Length = streamClip.TimeOut - streamClip.TimeIn; + streamClip.RelativeTimeIn = TotalLength; + streamClip.RelativeTimeOut = streamClip.RelativeTimeIn + streamClip.Length; + StreamClips.Add(streamClip); + chapterClips.Add(streamClip); + + pos += 12; + if (multiangle > 0) + { + int angles = data[pos]; + pos += 2; + for (int angle = 0; angle < angles - 1; angle++) + { + string angleName = ReadString(data, 5, ref pos); + string angleType = ReadString(data, 4, ref pos); + pos += 1; + + TSStreamFile angleFile = null; + string angleFileName = string.Format( + "{0}.M2TS", angleName); + if (streamFiles.ContainsKey(angleFileName)) + { + angleFile = streamFiles[angleFileName]; + } + if (angleFile == null) + { + throw new Exception(string.Format( + "Playlist {0} referenced missing angle file {1}.", + FileInfo.Name, angleFileName)); + } + + TSStreamClipFile angleClipFile = null; + string angleClipFileName = string.Format( + "{0}.CLPI", angleName); + if (streamClipFiles.ContainsKey(angleClipFileName)) + { + angleClipFile = streamClipFiles[angleClipFileName]; + } + if (angleClipFile == null) + { + throw new Exception(string.Format( + "Playlist {0} referenced missing angle file {1}.", + FileInfo.Name, angleClipFileName)); + } + + TSStreamClip angleClip = + new TSStreamClip(angleFile, angleClipFile); + angleClip.AngleIndex = angle + 1; + angleClip.TimeIn = streamClip.TimeIn; + angleClip.TimeOut = streamClip.TimeOut; + angleClip.RelativeTimeIn = streamClip.RelativeTimeIn; + angleClip.RelativeTimeOut = streamClip.RelativeTimeOut; + angleClip.Length = streamClip.Length; + StreamClips.Add(angleClip); + } + if (angles - 1 > AngleCount) AngleCount = angles - 1; + } + + int streamInfoLength = ReadInt16(data, ref pos); + pos += 2; + int streamCountVideo = data[pos++]; + int streamCountAudio = data[pos++]; + int streamCountPG = data[pos++]; + int streamCountIG = data[pos++]; + int streamCountSecondaryAudio = data[pos++]; + int streamCountSecondaryVideo = data[pos++]; + int streamCountPIP = data[pos++]; + pos += 5; + +#if DEBUG + Debug.WriteLine(string.Format( + "{0} : {1} -> V:{2} A:{3} PG:{4} IG:{5} 2A:{6} 2V:{7} PIP:{8}", + Name, streamFileName, streamCountVideo, streamCountAudio, streamCountPG, streamCountIG, + streamCountSecondaryAudio, streamCountSecondaryVideo, streamCountPIP)); +#endif + + for (int i = 0; i < streamCountVideo; i++) + { + TSStream stream = CreatePlaylistStream(data, ref pos); + if (stream != null) PlaylistStreams[stream.PID] = stream; + } + for (int i = 0; i < streamCountAudio; i++) + { + TSStream stream = CreatePlaylistStream(data, ref pos); + if (stream != null) PlaylistStreams[stream.PID] = stream; + } + for (int i = 0; i < streamCountPG; i++) + { + TSStream stream = CreatePlaylistStream(data, ref pos); + if (stream != null) PlaylistStreams[stream.PID] = stream; + } + for (int i = 0; i < streamCountIG; i++) + { + TSStream stream = CreatePlaylistStream(data, ref pos); + if (stream != null) PlaylistStreams[stream.PID] = stream; + } + for (int i = 0; i < streamCountSecondaryAudio; i++) + { + TSStream stream = CreatePlaylistStream(data, ref pos); + if (stream != null) PlaylistStreams[stream.PID] = stream; + pos += 2; + } + for (int i = 0; i < streamCountSecondaryVideo; i++) + { + TSStream stream = CreatePlaylistStream(data, ref pos); + if (stream != null) PlaylistStreams[stream.PID] = stream; + pos += 6; + } + /* + * TODO + * + for (int i = 0; i < streamCountPIP; i++) + { + TSStream stream = CreatePlaylistStream(data, ref pos); + if (stream != null) PlaylistStreams[stream.PID] = stream; + } + */ + + pos += itemLength - (pos - itemStart) + 2; + } + + pos = chaptersOffset + 4; + + int chapterCount = ReadInt16(data, ref pos); + + for (int chapterIndex = 0; + chapterIndex < chapterCount; + chapterIndex++) + { + int chapterType = data[pos+1]; + + if (chapterType == 1) + { + int streamFileIndex = + ((int)data[pos + 2] << 8) + data[pos + 3]; + + long chapterTime = + ((long)data[pos + 4] << 24) + + ((long)data[pos + 5] << 16) + + ((long)data[pos + 6] << 8) + + ((long)data[pos + 7]); + + TSStreamClip streamClip = chapterClips[streamFileIndex]; + + double chapterSeconds = (double)chapterTime / 45000; + + double relativeSeconds = + chapterSeconds - + streamClip.TimeIn + + streamClip.RelativeTimeIn; + + // TODO: Ignore short last chapter? + if (TotalLength - relativeSeconds > 1.0) + { + streamClip.Chapters.Add(chapterSeconds); + this.Chapters.Add(relativeSeconds); + } + } + else + { + // TODO: Handle other chapter types? + } + pos += 14; + } + } + finally + { + if (fileReader != null) + { + fileReader.Dispose(); + } + if (fileStream != null) + { + fileStream.Dispose(); + } + } + } + + public void Initialize() + { + LoadStreamClips(); + + Dictionary<string, List<double>> clipTimes = new Dictionary<string, List<double>>(); + foreach (TSStreamClip clip in StreamClips) + { + if (clip.AngleIndex == 0) + { + if (clipTimes.ContainsKey(clip.Name)) + { + if (clipTimes[clip.Name].Contains(clip.TimeIn)) + { + HasLoops = true; + break; + } + else + { + clipTimes[clip.Name].Add(clip.TimeIn); + } + } + else + { + clipTimes[clip.Name] = new List<double> { clip.TimeIn }; + } + } + } + ClearBitrates(); + IsInitialized = true; + } + + protected TSStream CreatePlaylistStream(byte[] data, ref int pos) + { + TSStream stream = null; + + int start = pos; + + int headerLength = data[pos++]; + int headerPos = pos; + int headerType = data[pos++]; + + int pid = 0; + int subpathid = 0; + int subclipid = 0; + + switch (headerType) + { + case 1: + pid = ReadInt16(data, ref pos); + break; + case 2: + subpathid = data[pos++]; + subclipid = data[pos++]; + pid = ReadInt16(data, ref pos); + break; + case 3: + subpathid = data[pos++]; + pid = ReadInt16(data, ref pos); + break; + case 4: + subpathid = data[pos++]; + subclipid = data[pos++]; + pid = ReadInt16(data, ref pos); + break; + default: + break; + } + + pos = headerPos + headerLength; + + int streamLength = data[pos++]; + int streamPos = pos; + + TSStreamType streamType = (TSStreamType)data[pos++]; + switch (streamType) + { + case TSStreamType.MVC_VIDEO: + // TODO + break; + + case TSStreamType.AVC_VIDEO: + case TSStreamType.MPEG1_VIDEO: + case TSStreamType.MPEG2_VIDEO: + case TSStreamType.VC1_VIDEO: + + TSVideoFormat videoFormat = (TSVideoFormat) + (data[pos] >> 4); + TSFrameRate frameRate = (TSFrameRate) + (data[pos] & 0xF); + TSAspectRatio aspectRatio = (TSAspectRatio) + (data[pos + 1] >> 4); + + stream = new TSVideoStream(); + ((TSVideoStream)stream).VideoFormat = videoFormat; + ((TSVideoStream)stream).AspectRatio = aspectRatio; + ((TSVideoStream)stream).FrameRate = frameRate; + +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2} {3} {4}", + pid, + streamType, + videoFormat, + frameRate, + aspectRatio)); +#endif + + 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: + + int audioFormat = ReadByte(data, ref pos); + + TSChannelLayout channelLayout = (TSChannelLayout) + (audioFormat >> 4); + TSSampleRate sampleRate = (TSSampleRate) + (audioFormat & 0xF); + + string audioLanguage = ReadString(data, 3, ref pos); + + stream = new TSAudioStream(); + ((TSAudioStream)stream).ChannelLayout = channelLayout; + ((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate); + ((TSAudioStream)stream).LanguageCode = audioLanguage; + +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2} {3} {4}", + pid, + streamType, + audioLanguage, + channelLayout, + sampleRate)); +#endif + + break; + + case TSStreamType.INTERACTIVE_GRAPHICS: + case TSStreamType.PRESENTATION_GRAPHICS: + + string graphicsLanguage = ReadString(data, 3, ref pos); + + stream = new TSGraphicsStream(); + ((TSGraphicsStream)stream).LanguageCode = graphicsLanguage; + + if (data[pos] != 0) + { + } + +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2}", + pid, + streamType, + graphicsLanguage)); +#endif + + break; + + case TSStreamType.SUBTITLE: + + int code = ReadByte(data, ref pos); // TODO + string textLanguage = ReadString(data, 3, ref pos); + + stream = new TSTextStream(); + ((TSTextStream)stream).LanguageCode = textLanguage; + +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2}", + pid, + streamType, + textLanguage)); +#endif + + break; + + default: + break; + } + + pos = streamPos + streamLength; + + if (stream != null) + { + stream.PID = (ushort)pid; + stream.StreamType = streamType; + } + + return stream; + } + + private void LoadStreamClips() + { + AngleClips.Clear(); + if (AngleCount > 0) + { + for (int angleIndex = 0; angleIndex < AngleCount; angleIndex++) + { + AngleClips.Add(new Dictionary<double, TSStreamClip>()); + } + } + + TSStreamClip referenceClip = null; + if (StreamClips.Count > 0) + { + referenceClip = StreamClips[0]; + } + foreach (TSStreamClip clip in StreamClips) + { + if (clip.StreamClipFile.Streams.Count > referenceClip.StreamClipFile.Streams.Count) + { + referenceClip = clip; + } + else if (clip.Length > referenceClip.Length) + { + referenceClip = clip; + } + if (AngleCount > 0) + { + if (clip.AngleIndex == 0) + { + for (int angleIndex = 0; angleIndex < AngleCount; angleIndex++) + { + AngleClips[angleIndex][clip.RelativeTimeIn] = clip; + } + } + else + { + AngleClips[clip.AngleIndex - 1][clip.RelativeTimeIn] = clip; + } + } + } + + foreach (TSStream clipStream + in referenceClip.StreamClipFile.Streams.Values) + { + if (!Streams.ContainsKey(clipStream.PID)) + { + TSStream stream = clipStream.Clone(); + Streams[clipStream.PID] = stream; + + if (!IsCustom && !PlaylistStreams.ContainsKey(stream.PID)) + { + stream.IsHidden = true; + HasHiddenTracks = true; + } + + if (stream.IsVideoStream) + { + VideoStreams.Add((TSVideoStream)stream); + } + else if (stream.IsAudioStream) + { + AudioStreams.Add((TSAudioStream)stream); + } + else if (stream.IsGraphicsStream) + { + GraphicsStreams.Add((TSGraphicsStream)stream); + } + else if (stream.IsTextStream) + { + TextStreams.Add((TSTextStream)stream); + } + } + } + + if (referenceClip.StreamFile != null) + { + // TODO: Better way to add this in? + if (BDInfoSettings.EnableSSIF && + referenceClip.StreamFile.InterleavedFile != null && + referenceClip.StreamFile.Streams.ContainsKey(4114) && + !Streams.ContainsKey(4114)) + { + TSStream stream = referenceClip.StreamFile.Streams[4114].Clone(); + Streams[4114] = stream; + if (stream.IsVideoStream) + { + VideoStreams.Add((TSVideoStream)stream); + } + } + + foreach (TSStream clipStream + in referenceClip.StreamFile.Streams.Values) + { + if (Streams.ContainsKey(clipStream.PID)) + { + TSStream stream = Streams[clipStream.PID]; + + if (stream.StreamType != clipStream.StreamType) continue; + + if (clipStream.BitRate > stream.BitRate) + { + stream.BitRate = clipStream.BitRate; + } + stream.IsVBR = clipStream.IsVBR; + + if (stream.IsVideoStream && + clipStream.IsVideoStream) + { + ((TSVideoStream)stream).EncodingProfile = + ((TSVideoStream)clipStream).EncodingProfile; + } + else if (stream.IsAudioStream && + clipStream.IsAudioStream) + { + TSAudioStream audioStream = (TSAudioStream)stream; + TSAudioStream clipAudioStream = (TSAudioStream)clipStream; + + if (clipAudioStream.ChannelCount > audioStream.ChannelCount) + { + audioStream.ChannelCount = clipAudioStream.ChannelCount; + } + if (clipAudioStream.LFE > audioStream.LFE) + { + audioStream.LFE = clipAudioStream.LFE; + } + if (clipAudioStream.SampleRate > audioStream.SampleRate) + { + audioStream.SampleRate = clipAudioStream.SampleRate; + } + if (clipAudioStream.BitDepth > audioStream.BitDepth) + { + audioStream.BitDepth = clipAudioStream.BitDepth; + } + if (clipAudioStream.DialNorm < audioStream.DialNorm) + { + audioStream.DialNorm = clipAudioStream.DialNorm; + } + if (clipAudioStream.AudioMode != TSAudioMode.Unknown) + { + audioStream.AudioMode = clipAudioStream.AudioMode; + } + if (clipAudioStream.CoreStream != null && + audioStream.CoreStream == null) + { + audioStream.CoreStream = (TSAudioStream) + clipAudioStream.CoreStream.Clone(); + } + } + } + } + } + + for (int i = 0; i < AngleCount; i++) + { + AngleStreams.Add(new Dictionary<ushort, TSStream>()); + } + + if (!BDInfoSettings.KeepStreamOrder) + { + VideoStreams.Sort(CompareVideoStreams); + } + foreach (TSStream stream in VideoStreams) + { + SortedStreams.Add(stream); + for (int i = 0; i < AngleCount; i++) + { + TSStream angleStream = stream.Clone(); + angleStream.AngleIndex = i + 1; + AngleStreams[i][angleStream.PID] = angleStream; + SortedStreams.Add(angleStream); + } + } + + if (!BDInfoSettings.KeepStreamOrder) + { + AudioStreams.Sort(CompareAudioStreams); + } + foreach (TSStream stream in AudioStreams) + { + SortedStreams.Add(stream); + } + + if (!BDInfoSettings.KeepStreamOrder) + { + GraphicsStreams.Sort(CompareGraphicsStreams); + } + foreach (TSStream stream in GraphicsStreams) + { + SortedStreams.Add(stream); + } + + if (!BDInfoSettings.KeepStreamOrder) + { + TextStreams.Sort(CompareTextStreams); + } + foreach (TSStream stream in TextStreams) + { + SortedStreams.Add(stream); + } + } + + public void ClearBitrates() + { + foreach (TSStreamClip clip in StreamClips) + { + clip.PayloadBytes = 0; + clip.PacketCount = 0; + clip.PacketSeconds = 0; + + if (clip.StreamFile != null) + { + foreach (TSStream stream in clip.StreamFile.Streams.Values) + { + stream.PayloadBytes = 0; + stream.PacketCount = 0; + stream.PacketSeconds = 0; + } + + if (clip.StreamFile != null && + clip.StreamFile.StreamDiagnostics != null) + { + clip.StreamFile.StreamDiagnostics.Clear(); + } + } + } + + foreach (TSStream stream in SortedStreams) + { + stream.PayloadBytes = 0; + stream.PacketCount = 0; + stream.PacketSeconds = 0; + } + } + + public bool IsValid + { + get + { + if (!IsInitialized) return false; + + if (BDInfoSettings.FilterShortPlaylists && + TotalLength < BDInfoSettings.FilterShortPlaylistsValue) + { + return false; + } + + if (HasLoops && + BDInfoSettings.FilterLoopingPlaylists) + { + return false; + } + + return true; + } + } + + public static int CompareVideoStreams( + TSVideoStream x, + TSVideoStream y) + { + if (x == null && y == null) + { + return 0; + } + else if (x == null && y != null) + { + return 1; + } + else if (x != null && y == null) + { + return -1; + } + else + { + if (x.Height > y.Height) + { + return -1; + } + else if (y.Height > x.Height) + { + return 1; + } + else if (x.PID > y.PID) + { + return 1; + } + else if (y.PID > x.PID) + { + return -1; + } + else + { + return 0; + } + } + } + + public static int CompareAudioStreams( + TSAudioStream x, + TSAudioStream y) + { + if (x == y) + { + return 0; + } + else if (x == null && y == null) + { + return 0; + } + else if (x == null && y != null) + { + return -1; + } + else if (x != null && y == null) + { + return 1; + } + else + { + if (x.ChannelCount > y.ChannelCount) + { + return -1; + } + else if (y.ChannelCount > x.ChannelCount) + { + return 1; + } + else + { + int sortX = GetStreamTypeSortIndex(x.StreamType); + int sortY = GetStreamTypeSortIndex(y.StreamType); + + if (sortX > sortY) + { + return -1; + } + else if (sortY > sortX) + { + return 1; + } + else + { + if (x.LanguageCode == "eng") + { + return -1; + } + else if (y.LanguageCode == "eng") + { + return 1; + } + else if (x.LanguageCode != y.LanguageCode) + { + return string.Compare( + x.LanguageName, y.LanguageName); + } + else if (x.PID < y.PID) + { + return -1; + } + else if (y.PID < x.PID) + { + return 1; + } + return 0; + } + } + } + } + + public static int CompareTextStreams( + TSTextStream x, + TSTextStream y) + { + if (x == y) + { + return 0; + } + else if (x == null && y == null) + { + return 0; + } + else if (x == null && y != null) + { + return -1; + } + else if (x != null && y == null) + { + return 1; + } + else + { + if (x.LanguageCode == "eng") + { + return -1; + } + else if (y.LanguageCode == "eng") + { + return 1; + } + else + { + if (x.LanguageCode == y.LanguageCode) + { + if (x.PID > y.PID) + { + return 1; + } + else if (y.PID > x.PID) + { + return -1; + } + else + { + return 0; + } + } + else + { + return string.Compare( + x.LanguageName, y.LanguageName); + } + } + } + } + + private static int CompareGraphicsStreams( + TSGraphicsStream x, + TSGraphicsStream y) + { + if (x == y) + { + return 0; + } + else if (x == null && y == null) + { + return 0; + } + else if (x == null && y != null) + { + return -1; + } + else if (x != null && y == null) + { + return 1; + } + else + { + int sortX = GetStreamTypeSortIndex(x.StreamType); + int sortY = GetStreamTypeSortIndex(y.StreamType); + + if (sortX > sortY) + { + return -1; + } + else if (sortY > sortX) + { + return 1; + } + else if (x.LanguageCode == "eng") + { + return -1; + } + else if (y.LanguageCode == "eng") + { + return 1; + } + else + { + if (x.LanguageCode == y.LanguageCode) + { + if (x.PID > y.PID) + { + return 1; + } + else if (y.PID > x.PID) + { + return -1; + } + else + { + return 0; + } + } + else + { + return string.Compare(x.LanguageName, y.LanguageName); + } + } + } + } + + private static int GetStreamTypeSortIndex(TSStreamType streamType) + { + switch (streamType) + { + case TSStreamType.Unknown: + return 0; + case TSStreamType.MPEG1_VIDEO: + return 1; + case TSStreamType.MPEG2_VIDEO: + return 2; + case TSStreamType.AVC_VIDEO: + return 3; + case TSStreamType.VC1_VIDEO: + return 4; + case TSStreamType.MVC_VIDEO: + return 5; + + case TSStreamType.MPEG1_AUDIO: + return 1; + case TSStreamType.MPEG2_AUDIO: + return 2; + case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: + return 3; + case TSStreamType.DTS_HD_SECONDARY_AUDIO: + return 4; + case TSStreamType.AC3_AUDIO: + return 5; + case TSStreamType.DTS_AUDIO: + return 6; + case TSStreamType.AC3_PLUS_AUDIO: + return 7; + case TSStreamType.DTS_HD_AUDIO: + return 8; + case TSStreamType.AC3_TRUE_HD_AUDIO: + return 9; + case TSStreamType.DTS_HD_MASTER_AUDIO: + return 10; + case TSStreamType.LPCM_AUDIO: + return 11; + + case TSStreamType.SUBTITLE: + return 1; + case TSStreamType.INTERACTIVE_GRAPHICS: + return 2; + case TSStreamType.PRESENTATION_GRAPHICS: + return 3; + + default: + return 0; + } + } + + protected string ReadString( + byte[] data, + int count, + ref int pos) + { + string val = + _textEncoding.GetASCIIEncoding().GetString(data, pos, count); + + pos += count; + + return val; + } + + protected int ReadInt32( + byte[] data, + ref int pos) + { + int val = + ((int)data[pos] << 24) + + ((int)data[pos + 1] << 16) + + ((int)data[pos + 2] << 8) + + ((int)data[pos + 3]); + + pos += 4; + + return val; + } + + protected int ReadInt16( + byte[] data, + ref int pos) + { + int val = + ((int)data[pos] << 8) + + ((int)data[pos + 1]); + + pos += 2; + + return val; + } + + protected byte ReadByte( + byte[] data, + ref int pos) + { + return data[pos++]; + } + } +} diff --git a/BDInfo/TSStream.cs b/BDInfo/TSStream.cs new file mode 100644 index 000000000..5afb81c5e --- /dev/null +++ b/BDInfo/TSStream.cs @@ -0,0 +1,801 @@ +//============================================================================ +// 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() + { + TSDescriptor 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<TSDescriptor> 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 + { + get + { + return PacketCount * 192; + } + } + + private string _LanguageCode; + public string LanguageCode + { + get + { + return _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 + { + get + { + return ""; + } + } + + 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<TSDescriptor>(); + foreach (TSDescriptor 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 + { + return _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 + { + return _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() + { + TSVideoStream 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() + { + TSAudioStream 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() + { + TSGraphicsStream stream = new TSGraphicsStream(); + CopyTo(stream); + return stream; + } + } + + public class TSTextStream : TSStream + { + public TSTextStream() + { + IsVBR = true; + IsInitialized = true; + } + + public override TSStream Clone() + { + TSTextStream stream = new TSTextStream(); + CopyTo(stream); + return stream; + } + } +} diff --git a/BDInfo/TSStreamBuffer.cs b/BDInfo/TSStreamBuffer.cs new file mode 100644 index 000000000..2f9094876 --- /dev/null +++ b/BDInfo/TSStreamBuffer.cs @@ -0,0 +1,142 @@ +//============================================================================ +// 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.Specialized; +using System.IO; + +namespace BDInfo +{ + public class TSStreamBuffer + { + private MemoryStream Stream = new MemoryStream(); + private int SkipBits = 0; + private byte[] Buffer; + private int BufferLength = 0; + public int TransferLength = 0; + + public TSStreamBuffer() + { + Buffer = new byte[4096]; + Stream = new MemoryStream(Buffer); + } + + public long Length + { + get + { + return (long)BufferLength; + } + } + + public long Position + { + get + { + return Stream.Position; + } + } + + public void Add( + byte[] buffer, + int offset, + int length) + { + TransferLength += length; + + if (BufferLength + length >= Buffer.Length) + { + length = Buffer.Length - BufferLength; + } + if (length > 0) + { + Array.Copy(buffer, offset, Buffer, BufferLength, length); + BufferLength += length; + } + } + + public void Seek( + long offset, + SeekOrigin loc) + { + Stream.Seek(offset, loc); + } + + public void Reset() + { + BufferLength = 0; + TransferLength = 0; + } + + public void BeginRead() + { + SkipBits = 0; + Stream.Seek(0, SeekOrigin.Begin); + } + + public void EndRead() + { + } + + public byte[] ReadBytes(int bytes) + { + if (Stream.Position + bytes >= BufferLength) + { + return null; + } + + byte[] value = new byte[bytes]; + Stream.Read(value, 0, bytes); + return value; + } + + public byte ReadByte() + { + return (byte)Stream.ReadByte(); + } + + public int ReadBits(int bits) + { + long pos = Stream.Position; + + int shift = 24; + int data = 0; + for (int i = 0; i < 4; i++) + { + if (pos + i >= BufferLength) break; + data += (Stream.ReadByte() << shift); + shift -= 8; + } + BitVector32 vector = new BitVector32(data); + + int value = 0; + for (int i = SkipBits; i < SkipBits + bits; i++) + { + value <<= 1; + value += (vector[1 << (32 - i - 1)] ? 1 : 0); + } + + SkipBits += bits; + Stream.Seek(pos + (SkipBits >> 3), SeekOrigin.Begin); + SkipBits = SkipBits % 8; + + return value; + } + } +} diff --git a/BDInfo/TSStreamClip.cs b/BDInfo/TSStreamClip.cs new file mode 100644 index 000000000..d7592a71a --- /dev/null +++ b/BDInfo/TSStreamClip.cs @@ -0,0 +1,113 @@ +//============================================================================ +// 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 class TSStreamClip + { + public int AngleIndex = 0; + public string Name; + public double TimeIn; + public double TimeOut; + public double RelativeTimeIn; + public double RelativeTimeOut; + public double Length; + + public ulong FileSize = 0; + public ulong InterleavedFileSize = 0; + public ulong PayloadBytes = 0; + public ulong PacketCount = 0; + public double PacketSeconds = 0; + + public List<double> Chapters = new List<double>(); + + public TSStreamFile StreamFile = null; + public TSStreamClipFile StreamClipFile = null; + + public TSStreamClip( + TSStreamFile streamFile, + TSStreamClipFile streamClipFile) + { + if (streamFile != null) + { + Name = streamFile.Name; + StreamFile = streamFile; + FileSize = (ulong)StreamFile.FileInfo.Length; + if (StreamFile.InterleavedFile != null) + { + InterleavedFileSize = (ulong)StreamFile.InterleavedFile.FileInfo.Length; + } + } + StreamClipFile = streamClipFile; + } + + public string DisplayName + { + get + { + if (StreamFile != null && + StreamFile.InterleavedFile != null && + BDInfoSettings.EnableSSIF) + { + return StreamFile.InterleavedFile.Name; + } + return Name; + } + } + + public ulong PacketSize + { + get + { + return PacketCount * 192; + } + } + + public ulong PacketBitRate + { + get + { + if (PacketSeconds > 0) + { + return (ulong)Math.Round(((PacketSize * 8.0) / PacketSeconds)); + } + return 0; + } + } + + public bool IsCompatible(TSStreamClip clip) + { + foreach (TSStream stream1 in StreamFile.Streams.Values) + { + if (clip.StreamFile.Streams.ContainsKey(stream1.PID)) + { + TSStream stream2 = clip.StreamFile.Streams[stream1.PID]; + if (stream1.StreamType != stream2.StreamType) + { + return false; + } + } + } + return true; + } + } +} diff --git a/BDInfo/TSStreamClipFile.cs b/BDInfo/TSStreamClipFile.cs new file mode 100644 index 000000000..f2accb88d --- /dev/null +++ b/BDInfo/TSStreamClipFile.cs @@ -0,0 +1,253 @@ +//============================================================================ +// 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 System.Text; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Text; + +namespace BDInfo +{ + public class TSStreamClipFile + { + private readonly IFileSystem _fileSystem; + private readonly ITextEncoding _textEncoding; + public FileSystemMetadata FileInfo = null; + public string FileType = null; + public bool IsValid = false; + public string Name = null; + + public Dictionary<ushort, TSStream> Streams = + new Dictionary<ushort,TSStream>(); + + public TSStreamClipFile( + FileSystemMetadata fileInfo, IFileSystem fileSystem, ITextEncoding textEncoding) + { + FileInfo = fileInfo; + _fileSystem = fileSystem; + _textEncoding = textEncoding; + Name = fileInfo.Name.ToUpper(); + } + + public void Scan() + { + Stream fileStream = null; + BinaryReader fileReader = null; + + try + { +#if DEBUG + Debug.WriteLine(string.Format( + "Scanning {0}...", Name)); +#endif + Streams.Clear(); + + fileStream = _fileSystem.OpenRead(FileInfo.FullName); + fileReader = new BinaryReader(fileStream); + + byte[] data = new byte[fileStream.Length]; + fileReader.Read(data, 0, data.Length); + + byte[] fileType = new byte[8]; + Array.Copy(data, 0, fileType, 0, fileType.Length); + + FileType = _textEncoding.GetASCIIEncoding().GetString(fileType, 0, fileType.Length); + if (FileType != "HDMV0100" && + FileType != "HDMV0200") + { + throw new Exception(string.Format( + "Clip info file {0} has an unknown file type {1}.", + FileInfo.Name, FileType)); + } +#if DEBUG + Debug.WriteLine(string.Format( + "\tFileType: {0}", FileType)); +#endif + int clipIndex = + ((int)data[12] << 24) + + ((int)data[13] << 16) + + ((int)data[14] << 8) + + ((int)data[15]); + + int clipLength = + ((int)data[clipIndex] << 24) + + ((int)data[clipIndex + 1] << 16) + + ((int)data[clipIndex + 2] << 8) + + ((int)data[clipIndex + 3]); + + byte[] clipData = new byte[clipLength]; + Array.Copy(data, clipIndex + 4, clipData, 0, clipData.Length); + + int streamCount = clipData[8]; +#if DEBUG + Debug.WriteLine(string.Format( + "\tStreamCount: {0}", streamCount)); +#endif + int streamOffset = 10; + for (int streamIndex = 0; + streamIndex < streamCount; + streamIndex++) + { + TSStream stream = null; + + ushort PID = (ushort) + ((clipData[streamOffset] << 8) + + clipData[streamOffset + 1]); + + streamOffset += 2; + + TSStreamType streamType = (TSStreamType) + clipData[streamOffset + 1]; + switch (streamType) + { + case TSStreamType.MVC_VIDEO: + // TODO + break; + + case TSStreamType.AVC_VIDEO: + case TSStreamType.MPEG1_VIDEO: + case TSStreamType.MPEG2_VIDEO: + case TSStreamType.VC1_VIDEO: + { + TSVideoFormat videoFormat = (TSVideoFormat) + (clipData[streamOffset + 2] >> 4); + TSFrameRate frameRate = (TSFrameRate) + (clipData[streamOffset + 2] & 0xF); + TSAspectRatio aspectRatio = (TSAspectRatio) + (clipData[streamOffset + 3] >> 4); + + stream = new TSVideoStream(); + ((TSVideoStream)stream).VideoFormat = videoFormat; + ((TSVideoStream)stream).AspectRatio = aspectRatio; + ((TSVideoStream)stream).FrameRate = frameRate; +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2} {3} {4}", + PID, + streamType, + videoFormat, + frameRate, + aspectRatio)); +#endif + } + 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: + { + byte[] languageBytes = new byte[3]; + Array.Copy(clipData, streamOffset + 3, + languageBytes, 0, languageBytes.Length); + string languageCode = + _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length); + + TSChannelLayout channelLayout = (TSChannelLayout) + (clipData[streamOffset + 2] >> 4); + TSSampleRate sampleRate = (TSSampleRate) + (clipData[streamOffset + 2] & 0xF); + + stream = new TSAudioStream(); + ((TSAudioStream)stream).LanguageCode = languageCode; + ((TSAudioStream)stream).ChannelLayout = channelLayout; + ((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate); + ((TSAudioStream)stream).LanguageCode = languageCode; +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2} {3} {4}", + PID, + streamType, + languageCode, + channelLayout, + sampleRate)); +#endif + } + break; + + case TSStreamType.INTERACTIVE_GRAPHICS: + case TSStreamType.PRESENTATION_GRAPHICS: + { + byte[] languageBytes = new byte[3]; + Array.Copy(clipData, streamOffset + 2, + languageBytes, 0, languageBytes.Length); + string languageCode = + _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length); + + stream = new TSGraphicsStream(); + stream.LanguageCode = languageCode; +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2}", + PID, + streamType, + languageCode)); +#endif + } + break; + + case TSStreamType.SUBTITLE: + { + byte[] languageBytes = new byte[3]; + Array.Copy(clipData, streamOffset + 3, + languageBytes, 0, languageBytes.Length); + string languageCode = + _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length); +#if DEBUG + Debug.WriteLine(string.Format( + "\t{0} {1} {2}", + PID, + streamType, + languageCode)); +#endif + stream = new TSTextStream(); + stream.LanguageCode = languageCode; + } + break; + } + + if (stream != null) + { + stream.PID = PID; + stream.StreamType = streamType; + Streams.Add(PID, stream); + } + + streamOffset += clipData[streamOffset] + 1; + } + IsValid = true; + } + finally + { + if (fileReader != null) fileReader.Dispose(); + if (fileStream != null) fileStream.Dispose(); + } + } + } +} diff --git a/BDInfo/TSStreamFile.cs b/BDInfo/TSStreamFile.cs new file mode 100644 index 000000000..31020cbf4 --- /dev/null +++ b/BDInfo/TSStreamFile.cs @@ -0,0 +1,1553 @@ +//============================================================================ +// 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<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 FileSystemMetadata 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; + + 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<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; + 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<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; + } + } +} diff --git a/BDInfo/project.json b/BDInfo/project.json new file mode 100644 index 000000000..fbbe9eaf3 --- /dev/null +++ b/BDInfo/project.json @@ -0,0 +1,17 @@ +{ + "frameworks":{ + "netstandard1.6":{ + "dependencies":{ + "NETStandard.Library":"1.6.0", + } + }, + ".NETPortable,Version=v4.5,Profile=Profile7":{ + "buildOptions": { + "define": [ ] + }, + "frameworkAssemblies":{ + + } + } + } +}
\ No newline at end of file |
