mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
commit
e7cebb91a7
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,6 +13,8 @@ tmp/
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
project.fragment.lock.json
|
||||
project.lock.json
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
|
77
BDInfo/BDInfo.csproj
Normal file
77
BDInfo/BDInfo.csproj
Normal file
@ -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>
|
6
BDInfo/BDInfo.nuget.targets
Normal file
6
BDInfo/BDInfo.nuget.targets
Normal file
@ -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>
|
105
BDInfo/BDInfoSettings.cs
Normal file
105
BDInfo/BDInfoSettings.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
437
BDInfo/BDROM.cs
Normal file
437
BDInfo/BDROM.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
308
BDInfo/BitVector32.cs
Normal file
308
BDInfo/BitVector32.cs
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
493
BDInfo/LanguageCodes.cs
Normal file
493
BDInfo/LanguageCodes.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,17 @@
|
||||
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("MediaBrowser.Model.Portable")]
|
||||
[assembly: AssemblyTitle("BDInfo")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.Model.Portable")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[assembly: AssemblyProduct("BDInfo")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
@ -21,3 +23,7 @@ using System.Reflection;
|
||||
// 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")]
|
5
BDInfo/ReadMe.txt
Normal file
5
BDInfo/ReadMe.txt
Normal file
@ -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.
|
309
BDInfo/TSCodecAC3.cs
Normal file
309
BDInfo/TSCodecAC3.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
148
BDInfo/TSCodecAVC.cs
Normal file
148
BDInfo/TSCodecAVC.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
159
BDInfo/TSCodecDTS.cs
Normal file
159
BDInfo/TSCodecDTS.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
246
BDInfo/TSCodecDTSHD.cs
Normal file
246
BDInfo/TSCodecDTSHD.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
BDInfo/TSCodecLPCM.cs
Normal file
123
BDInfo/TSCodecLPCM.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
208
BDInfo/TSCodecMPEG2.cs
Normal file
208
BDInfo/TSCodecMPEG2.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
BDInfo/TSCodecMVC.cs
Normal file
36
BDInfo/TSCodecMVC.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
186
BDInfo/TSCodecTrueHD.cs
Normal file
186
BDInfo/TSCodecTrueHD.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
131
BDInfo/TSCodecVC1.cs
Normal file
131
BDInfo/TSCodecVC1.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
BDInfo/TSInterleavedFile.cs
Normal file
38
BDInfo/TSInterleavedFile.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
1292
BDInfo/TSPlaylistFile.cs
Normal file
1292
BDInfo/TSPlaylistFile.cs
Normal file
File diff suppressed because it is too large
Load Diff
801
BDInfo/TSStream.cs
Normal file
801
BDInfo/TSStream.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
142
BDInfo/TSStreamBuffer.cs
Normal file
142
BDInfo/TSStreamBuffer.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
113
BDInfo/TSStreamClip.cs
Normal file
113
BDInfo/TSStreamClip.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
253
BDInfo/TSStreamClipFile.cs
Normal file
253
BDInfo/TSStreamClipFile.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1553
BDInfo/TSStreamFile.cs
Normal file
1553
BDInfo/TSStreamFile.cs
Normal file
File diff suppressed because it is too large
Load Diff
17
BDInfo/project.json
Normal file
17
BDInfo/project.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"frameworks":{
|
||||
"netstandard1.6":{
|
||||
"dependencies":{
|
||||
"NETStandard.Library":"1.6.0",
|
||||
}
|
||||
},
|
||||
".NETPortable,Version=v4.5,Profile=Profile7":{
|
||||
"buildOptions": {
|
||||
"define": [ ]
|
||||
},
|
||||
"frameworkAssemblies":{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
DvdLib/BigEndianBinaryReader.cs
Normal file
33
DvdLib/BigEndianBinaryReader.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib
|
||||
{
|
||||
public class BigEndianBinaryReader : BinaryReader
|
||||
{
|
||||
public BigEndianBinaryReader(Stream input)
|
||||
: base(input)
|
||||
{
|
||||
}
|
||||
|
||||
public override ushort ReadUInt16()
|
||||
{
|
||||
return BitConverter.ToUInt16(ReadAndReverseBytes(2), 0);
|
||||
}
|
||||
|
||||
public override uint ReadUInt32()
|
||||
{
|
||||
return BitConverter.ToUInt32(ReadAndReverseBytes(4), 0);
|
||||
}
|
||||
|
||||
private byte[] ReadAndReverseBytes(int count)
|
||||
{
|
||||
byte[] val = base.ReadBytes(count);
|
||||
Array.Reverse(val, 0, count);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
71
DvdLib/DvdLib.csproj
Normal file
71
DvdLib/DvdLib.csproj
Normal file
@ -0,0 +1,71 @@
|
||||
<?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>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DvdLib</RootNamespace>
|
||||
<AssemblyName>DvdLib</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="BigEndianBinaryReader.cs" />
|
||||
<Compile Include="Ifo\AudioAttributes.cs" />
|
||||
<Compile Include="Ifo\Cell.cs" />
|
||||
<Compile Include="Ifo\CellPlaybackInfo.cs" />
|
||||
<Compile Include="Ifo\CellPositionInfo.cs" />
|
||||
<Compile Include="Ifo\Chapter.cs" />
|
||||
<Compile Include="Ifo\Dvd.cs" />
|
||||
<Compile Include="Ifo\DvdTime.cs" />
|
||||
<Compile Include="Ifo\PgcCommandTable.cs" />
|
||||
<Compile Include="Ifo\Program.cs" />
|
||||
<Compile Include="Ifo\ProgramChain.cs" />
|
||||
<Compile Include="Ifo\Title.cs" />
|
||||
<Compile Include="Ifo\UserOperation.cs" />
|
||||
<Compile Include="Ifo\VideoAttributes.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.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>
|
6
DvdLib/DvdLib.nuget.targets
Normal file
6
DvdLib/DvdLib.nuget.targets
Normal file
@ -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>
|
41
DvdLib/Ifo/AudioAttributes.cs
Normal file
41
DvdLib/Ifo/AudioAttributes.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public enum AudioCodec
|
||||
{
|
||||
AC3 = 0,
|
||||
MPEG1 = 2,
|
||||
MPEG2ext = 3,
|
||||
LPCM = 4,
|
||||
DTS = 6,
|
||||
}
|
||||
|
||||
public enum ApplicationMode
|
||||
{
|
||||
Unspecified = 0,
|
||||
Karaoke = 1,
|
||||
Surround = 2,
|
||||
}
|
||||
|
||||
public class AudioAttributes
|
||||
{
|
||||
public readonly AudioCodec Codec;
|
||||
public readonly bool MultichannelExtensionPresent;
|
||||
public readonly ApplicationMode Mode;
|
||||
public readonly byte QuantDRC;
|
||||
public readonly byte SampleRate;
|
||||
public readonly byte Channels;
|
||||
public readonly ushort LanguageCode;
|
||||
public readonly byte LanguageExtension;
|
||||
public readonly byte CodeExtension;
|
||||
}
|
||||
|
||||
public class MultiChannelExtension
|
||||
{
|
||||
|
||||
}
|
||||
}
|
24
DvdLib/Ifo/Cell.cs
Normal file
24
DvdLib/Ifo/Cell.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class Cell
|
||||
{
|
||||
public CellPlaybackInfo PlaybackInfo { get; private set; }
|
||||
public CellPositionInfo PositionInfo { get; private set; }
|
||||
|
||||
internal void ParsePlayback(BinaryReader br)
|
||||
{
|
||||
PlaybackInfo = new CellPlaybackInfo(br);
|
||||
}
|
||||
|
||||
internal void ParsePosition(BinaryReader br)
|
||||
{
|
||||
PositionInfo = new CellPositionInfo(br);
|
||||
}
|
||||
}
|
||||
}
|
54
DvdLib/Ifo/CellPlaybackInfo.cs
Normal file
54
DvdLib/Ifo/CellPlaybackInfo.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public enum BlockMode
|
||||
{
|
||||
NotInBlock = 0,
|
||||
FirstCell = 1,
|
||||
InBlock = 2,
|
||||
LastCell = 3,
|
||||
}
|
||||
|
||||
public enum BlockType
|
||||
{
|
||||
Normal = 0,
|
||||
Angle = 1,
|
||||
}
|
||||
|
||||
public enum PlaybackMode
|
||||
{
|
||||
Normal = 0,
|
||||
StillAfterEachVOBU = 1,
|
||||
}
|
||||
|
||||
public class CellPlaybackInfo
|
||||
{
|
||||
public readonly BlockMode Mode;
|
||||
public readonly BlockType Type;
|
||||
public readonly bool SeamlessPlay;
|
||||
public readonly bool Interleaved;
|
||||
public readonly bool STCDiscontinuity;
|
||||
public readonly bool SeamlessAngle;
|
||||
public readonly PlaybackMode PlaybackMode;
|
||||
public readonly bool Restricted;
|
||||
public readonly byte StillTime;
|
||||
public readonly byte CommandNumber;
|
||||
public readonly DvdTime PlaybackTime;
|
||||
public readonly uint FirstSector;
|
||||
public readonly uint FirstILVUEndSector;
|
||||
public readonly uint LastVOBUStartSector;
|
||||
public readonly uint LastSector;
|
||||
|
||||
internal CellPlaybackInfo(BinaryReader br)
|
||||
{
|
||||
br.BaseStream.Seek(0x4, SeekOrigin.Current);
|
||||
PlaybackTime = new DvdTime(br.ReadBytes(4));
|
||||
br.BaseStream.Seek(0x10, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
}
|
21
DvdLib/Ifo/CellPositionInfo.cs
Normal file
21
DvdLib/Ifo/CellPositionInfo.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class CellPositionInfo
|
||||
{
|
||||
public readonly ushort VOBId;
|
||||
public readonly byte CellId;
|
||||
|
||||
internal CellPositionInfo(BinaryReader br)
|
||||
{
|
||||
VOBId = br.ReadUInt16();
|
||||
br.ReadByte();
|
||||
CellId = br.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
21
DvdLib/Ifo/Chapter.cs
Normal file
21
DvdLib/Ifo/Chapter.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class Chapter
|
||||
{
|
||||
public ushort ProgramChainNumber { get; private set; }
|
||||
public ushort ProgramNumber { get; private set; }
|
||||
public uint ChapterNumber { get; private set; }
|
||||
|
||||
public Chapter(ushort pgcNum, ushort programNum, uint chapterNum)
|
||||
{
|
||||
ProgramChainNumber = pgcNum;
|
||||
ProgramNumber = programNum;
|
||||
ChapterNumber = chapterNum;
|
||||
}
|
||||
}
|
||||
}
|
161
DvdLib/Ifo/Dvd.cs
Normal file
161
DvdLib/Ifo/Dvd.cs
Normal file
@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class Dvd
|
||||
{
|
||||
private readonly ushort _titleSetCount;
|
||||
public readonly List<Title> Titles;
|
||||
|
||||
private ushort _titleCount;
|
||||
public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>();
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public Dvd(string path, IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
Titles = new List<Title>();
|
||||
var allFiles = _fileSystem.GetFiles(path, true).ToList();
|
||||
|
||||
var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ??
|
||||
allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (vmgPath == null)
|
||||
{
|
||||
var allIfos = allFiles.Where(i => string.Equals(i.Extension, ".ifo", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
foreach (var ifo in allIfos)
|
||||
{
|
||||
var num = ifo.Name.Split('_').ElementAtOrDefault(1);
|
||||
ushort ifoNumber;
|
||||
var numbersRead = new List<ushort>();
|
||||
|
||||
if (!string.IsNullOrEmpty(num) && ushort.TryParse(num, out ifoNumber) && !numbersRead.Contains(ifoNumber))
|
||||
{
|
||||
ReadVTS(ifoNumber, ifo.FullName);
|
||||
numbersRead.Add(ifoNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
|
||||
{
|
||||
using (BigEndianBinaryReader vmgRead = new BigEndianBinaryReader(vmgFs))
|
||||
{
|
||||
vmgFs.Seek(0x3E, SeekOrigin.Begin);
|
||||
_titleSetCount = vmgRead.ReadUInt16();
|
||||
|
||||
// read address of TT_SRPT
|
||||
vmgFs.Seek(0xC4, SeekOrigin.Begin);
|
||||
uint ttSectorPtr = vmgRead.ReadUInt32();
|
||||
vmgFs.Seek(ttSectorPtr * 2048, SeekOrigin.Begin);
|
||||
ReadTT_SRPT(vmgRead);
|
||||
}
|
||||
}
|
||||
|
||||
for (ushort titleSetNum = 1; titleSetNum <= _titleSetCount; titleSetNum++)
|
||||
{
|
||||
ReadVTS(titleSetNum, allFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadTT_SRPT(BinaryReader read)
|
||||
{
|
||||
_titleCount = read.ReadUInt16();
|
||||
read.BaseStream.Seek(6, SeekOrigin.Current);
|
||||
for (uint titleNum = 1; titleNum <= _titleCount; titleNum++)
|
||||
{
|
||||
Title t = new Title(titleNum);
|
||||
t.ParseTT_SRPT(read);
|
||||
Titles.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadVTS(ushort vtsNum, List<FileSystemMetadata> allFiles)
|
||||
{
|
||||
var filename = String.Format("VTS_{0:00}_0.IFO", vtsNum);
|
||||
|
||||
var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ??
|
||||
allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (vtsPath == null)
|
||||
{
|
||||
throw new FileNotFoundException("Unable to find VTS IFO file");
|
||||
}
|
||||
|
||||
ReadVTS(vtsNum, vtsPath.FullName);
|
||||
}
|
||||
|
||||
private void ReadVTS(ushort vtsNum, string vtsPath)
|
||||
{
|
||||
VTSPaths[vtsNum] = vtsPath;
|
||||
|
||||
using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
|
||||
{
|
||||
using (BigEndianBinaryReader vtsRead = new BigEndianBinaryReader(vtsFs))
|
||||
{
|
||||
// Read VTS_PTT_SRPT
|
||||
vtsFs.Seek(0xC8, SeekOrigin.Begin);
|
||||
uint vtsPttSrptSecPtr = vtsRead.ReadUInt32();
|
||||
uint baseAddr = (vtsPttSrptSecPtr * 2048);
|
||||
vtsFs.Seek(baseAddr, SeekOrigin.Begin);
|
||||
|
||||
ushort numTitles = vtsRead.ReadUInt16();
|
||||
vtsRead.ReadUInt16();
|
||||
uint endaddr = vtsRead.ReadUInt32();
|
||||
uint[] offsets = new uint[numTitles];
|
||||
for (ushort titleNum = 0; titleNum < numTitles; titleNum++)
|
||||
{
|
||||
offsets[titleNum] = vtsRead.ReadUInt32();
|
||||
}
|
||||
|
||||
for (uint titleNum = 0; titleNum < numTitles; titleNum++)
|
||||
{
|
||||
uint chapNum = 1;
|
||||
vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
|
||||
Title t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
|
||||
if (t == null) continue;
|
||||
|
||||
do
|
||||
{
|
||||
t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum));
|
||||
if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) break;
|
||||
chapNum++;
|
||||
}
|
||||
while (vtsFs.Position < (baseAddr + endaddr));
|
||||
}
|
||||
|
||||
// Read VTS_PGCI
|
||||
vtsFs.Seek(0xCC, SeekOrigin.Begin);
|
||||
uint vtsPgciSecPtr = vtsRead.ReadUInt32();
|
||||
vtsFs.Seek(vtsPgciSecPtr * 2048, SeekOrigin.Begin);
|
||||
|
||||
long startByte = vtsFs.Position;
|
||||
|
||||
ushort numPgcs = vtsRead.ReadUInt16();
|
||||
vtsFs.Seek(6, SeekOrigin.Current);
|
||||
for (ushort pgcNum = 1; pgcNum <= numPgcs; pgcNum++)
|
||||
{
|
||||
byte pgcCat = vtsRead.ReadByte();
|
||||
bool entryPgc = (pgcCat & 0x80) != 0;
|
||||
uint titleNum = (uint)(pgcCat & 0x7F);
|
||||
|
||||
vtsFs.Seek(3, SeekOrigin.Current);
|
||||
uint vtsPgcOffset = vtsRead.ReadUInt32();
|
||||
|
||||
Title t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
|
||||
if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
DvdLib/Ifo/DvdTime.cs
Normal file
34
DvdLib/Ifo/DvdTime.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class DvdTime
|
||||
{
|
||||
public readonly byte Hour, Minute, Second, Frames, FrameRate;
|
||||
|
||||
public DvdTime(byte[] data)
|
||||
{
|
||||
Hour = GetBCDValue(data[0]);
|
||||
Minute = GetBCDValue(data[1]);
|
||||
Second = GetBCDValue(data[2]);
|
||||
Frames = GetBCDValue((byte)(data[3] & 0x3F));
|
||||
|
||||
if ((data[3] & 0x80) != 0) FrameRate = 30;
|
||||
else if ((data[3] & 0x40) != 0) FrameRate = 25;
|
||||
}
|
||||
|
||||
private byte GetBCDValue(byte data)
|
||||
{
|
||||
return (byte)((((data & 0xF0) >> 4) * 10) + (data & 0x0F));
|
||||
}
|
||||
|
||||
public static explicit operator TimeSpan(DvdTime time)
|
||||
{
|
||||
int ms = (int)(((1.0 / (double)time.FrameRate) * time.Frames) * 1000.0);
|
||||
return new TimeSpan(0, time.Hour, time.Minute, time.Second, ms);
|
||||
}
|
||||
}
|
||||
}
|
20
DvdLib/Ifo/PgcCommandTable.cs
Normal file
20
DvdLib/Ifo/PgcCommandTable.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class ProgramChainCommandTable
|
||||
{
|
||||
public readonly ushort LastByteAddress;
|
||||
public readonly List<VirtualMachineCommand> PreCommands;
|
||||
public readonly List<VirtualMachineCommand> PostCommands;
|
||||
public readonly List<VirtualMachineCommand> CellCommands;
|
||||
}
|
||||
|
||||
public class VirtualMachineCommand
|
||||
{
|
||||
public readonly byte[] Command;
|
||||
}
|
||||
}
|
17
DvdLib/Ifo/Program.cs
Normal file
17
DvdLib/Ifo/Program.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public readonly List<Cell> Cells;
|
||||
|
||||
public Program(List<Cell> cells)
|
||||
{
|
||||
Cells = cells;
|
||||
}
|
||||
}
|
||||
}
|
117
DvdLib/Ifo/ProgramChain.cs
Normal file
117
DvdLib/Ifo/ProgramChain.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public enum ProgramPlaybackMode
|
||||
{
|
||||
Sequential,
|
||||
Random,
|
||||
Shuffle
|
||||
}
|
||||
|
||||
public class ProgramChain
|
||||
{
|
||||
private ushort _unknown1;
|
||||
|
||||
private byte _programCount;
|
||||
public readonly List<Program> Programs;
|
||||
|
||||
private byte _cellCount;
|
||||
public readonly List<Cell> Cells;
|
||||
|
||||
public DvdTime PlaybackTime { get; private set; }
|
||||
public UserOperation ProhibitedUserOperations { get; private set; }
|
||||
public byte[] AudioStreamControl { get; private set; } // 8*2 entries
|
||||
public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
|
||||
|
||||
private ushort _nextProgramNumber;
|
||||
public readonly ProgramChain Next;
|
||||
|
||||
private ushort _prevProgramNumber;
|
||||
public readonly ProgramChain Previous;
|
||||
|
||||
private ushort _goupProgramNumber;
|
||||
public readonly ProgramChain Goup; // ?? maybe Group
|
||||
|
||||
private byte _playbackMode;
|
||||
public ProgramPlaybackMode PlaybackMode { get; private set; }
|
||||
public uint ProgramCount { get; private set; }
|
||||
|
||||
public byte StillTime { get; private set; }
|
||||
public byte[] Palette { get; private set; } // 16*4 entries
|
||||
|
||||
private ushort _commandTableOffset;
|
||||
public readonly ProgramChainCommandTable CommandTable;
|
||||
|
||||
private ushort _programMapOffset;
|
||||
private ushort _cellPlaybackOffset;
|
||||
private ushort _cellPositionOffset;
|
||||
|
||||
public readonly uint VideoTitleSetIndex;
|
||||
|
||||
internal ProgramChain(uint vtsPgcNum)
|
||||
{
|
||||
VideoTitleSetIndex = vtsPgcNum;
|
||||
Cells = new List<Cell>();
|
||||
Programs = new List<Program>();
|
||||
}
|
||||
|
||||
internal void ParseHeader(BinaryReader br)
|
||||
{
|
||||
long startPos = br.BaseStream.Position;
|
||||
|
||||
br.ReadUInt16();
|
||||
_programCount = br.ReadByte();
|
||||
_cellCount = br.ReadByte();
|
||||
PlaybackTime = new DvdTime(br.ReadBytes(4));
|
||||
ProhibitedUserOperations = (UserOperation)br.ReadUInt32();
|
||||
AudioStreamControl = br.ReadBytes(16);
|
||||
SubpictureStreamControl = br.ReadBytes(128);
|
||||
|
||||
_nextProgramNumber = br.ReadUInt16();
|
||||
_prevProgramNumber = br.ReadUInt16();
|
||||
_goupProgramNumber = br.ReadUInt16();
|
||||
|
||||
StillTime = br.ReadByte();
|
||||
byte pbMode = br.ReadByte();
|
||||
if (pbMode == 0) PlaybackMode = ProgramPlaybackMode.Sequential;
|
||||
else PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
|
||||
ProgramCount = (uint)(pbMode & 0x7F);
|
||||
|
||||
Palette = br.ReadBytes(64);
|
||||
_commandTableOffset = br.ReadUInt16();
|
||||
_programMapOffset = br.ReadUInt16();
|
||||
_cellPlaybackOffset = br.ReadUInt16();
|
||||
_cellPositionOffset = br.ReadUInt16();
|
||||
|
||||
// read position info
|
||||
br.BaseStream.Seek(startPos + _cellPositionOffset, SeekOrigin.Begin);
|
||||
for (int cellNum = 0; cellNum < _cellCount; cellNum++)
|
||||
{
|
||||
Cell c = new Cell();
|
||||
c.ParsePosition(br);
|
||||
Cells.Add(c);
|
||||
}
|
||||
|
||||
br.BaseStream.Seek(startPos + _cellPlaybackOffset, SeekOrigin.Begin);
|
||||
for (int cellNum = 0; cellNum < _cellCount; cellNum++)
|
||||
{
|
||||
Cells[cellNum].ParsePlayback(br);
|
||||
}
|
||||
|
||||
br.BaseStream.Seek(startPos + _programMapOffset, SeekOrigin.Begin);
|
||||
List<int> cellNumbers = new List<int>();
|
||||
for (int progNum = 0; progNum < _programCount; progNum++) cellNumbers.Add(br.ReadByte() - 1);
|
||||
|
||||
for (int i = 0; i < cellNumbers.Count; i++)
|
||||
{
|
||||
int max = (i + 1 == cellNumbers.Count) ? _cellCount : cellNumbers[i+1];
|
||||
Programs.Add(new Program(Cells.Where((c, idx) => idx >= cellNumbers[i] && idx < max).ToList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
DvdLib/Ifo/Title.cs
Normal file
64
DvdLib/Ifo/Title.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class Title
|
||||
{
|
||||
public uint TitleNumber { get; private set; }
|
||||
public uint AngleCount { get; private set; }
|
||||
public ushort ChapterCount { get; private set; }
|
||||
public byte VideoTitleSetNumber { get; private set; }
|
||||
|
||||
private ushort _parentalManagementMask;
|
||||
private byte _titleNumberInVTS;
|
||||
private uint _vtsStartSector; // relative to start of entire disk
|
||||
|
||||
public ProgramChain EntryProgramChain { get; private set; }
|
||||
public readonly List<ProgramChain> ProgramChains;
|
||||
|
||||
public readonly List<Chapter> Chapters;
|
||||
|
||||
public Title(uint titleNum)
|
||||
{
|
||||
ProgramChains = new List<ProgramChain>();
|
||||
Chapters = new List<Chapter>();
|
||||
Chapters = new List<Chapter>();
|
||||
TitleNumber = titleNum;
|
||||
}
|
||||
|
||||
public bool IsVTSTitle(uint vtsNum, uint vtsTitleNum)
|
||||
{
|
||||
return (vtsNum == VideoTitleSetNumber && vtsTitleNum == _titleNumberInVTS);
|
||||
}
|
||||
|
||||
internal void ParseTT_SRPT(BinaryReader br)
|
||||
{
|
||||
byte titleType = br.ReadByte();
|
||||
// TODO parse Title Type
|
||||
|
||||
AngleCount = br.ReadByte();
|
||||
ChapterCount = br.ReadUInt16();
|
||||
_parentalManagementMask = br.ReadUInt16();
|
||||
VideoTitleSetNumber = br.ReadByte();
|
||||
_titleNumberInVTS = br.ReadByte();
|
||||
_vtsStartSector = br.ReadUInt32();
|
||||
}
|
||||
|
||||
internal void AddPgc(BinaryReader br, long startByte, bool entryPgc, uint pgcNum)
|
||||
{
|
||||
long curPos = br.BaseStream.Position;
|
||||
br.BaseStream.Seek(startByte, SeekOrigin.Begin);
|
||||
|
||||
ProgramChain pgc = new ProgramChain(pgcNum);
|
||||
pgc.ParseHeader(br);
|
||||
ProgramChains.Add(pgc);
|
||||
if (entryPgc) EntryProgramChain = pgc;
|
||||
|
||||
br.BaseStream.Seek(curPos, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
}
|
38
DvdLib/Ifo/UserOperation.cs
Normal file
38
DvdLib/Ifo/UserOperation.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
[Flags]
|
||||
public enum UserOperation
|
||||
{
|
||||
None = 0,
|
||||
TitleOrTimePlay = 1,
|
||||
ChapterSearchOrPlay = 2,
|
||||
TitlePlay = 4,
|
||||
Stop = 8,
|
||||
GoUp = 16,
|
||||
TimeOrChapterSearch = 32,
|
||||
PrevOrTopProgramSearch = 64,
|
||||
NextProgramSearch = 128,
|
||||
ForwardScan = 256,
|
||||
BackwardScan = 512,
|
||||
TitleMenuCall = 1024,
|
||||
RootMenuCall = 2048,
|
||||
SubpictureMenuCall = 4096,
|
||||
AudioMenuCall = 8192,
|
||||
AngleMenuCall = 16384,
|
||||
ChapterMenuCall = 32768,
|
||||
Resume = 65536,
|
||||
ButtonSelectOrActive = 131072,
|
||||
StillOff = 262144,
|
||||
PauseOn = 524288,
|
||||
AudioStreamChange = 1048576,
|
||||
SubpictureStreamChange = 2097152,
|
||||
AngleChange = 4194304,
|
||||
KaraokeAudioPresentationModeChange = 8388608,
|
||||
VideoPresentationModeChange = 16777216,
|
||||
}
|
||||
}
|
51
DvdLib/Ifo/VideoAttributes.cs
Normal file
51
DvdLib/Ifo/VideoAttributes.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public enum VideoCodec
|
||||
{
|
||||
MPEG1 = 0,
|
||||
MPEG2 = 1,
|
||||
}
|
||||
|
||||
public enum VideoFormat
|
||||
{
|
||||
NTSC = 0,
|
||||
PAL = 1,
|
||||
}
|
||||
|
||||
public enum AspectRatio
|
||||
{
|
||||
ar4to3 = 0,
|
||||
ar16to9 = 3
|
||||
}
|
||||
|
||||
public enum FilmMode
|
||||
{
|
||||
None = -1,
|
||||
Camera = 0,
|
||||
Film = 1,
|
||||
}
|
||||
|
||||
public class VideoAttributes
|
||||
{
|
||||
public readonly VideoCodec Codec;
|
||||
public readonly VideoFormat Format;
|
||||
public readonly AspectRatio Aspect;
|
||||
public readonly bool AutomaticPanScan;
|
||||
public readonly bool AutomaticLetterBox;
|
||||
public readonly bool Line21CCField1;
|
||||
public readonly bool Line21CCField2;
|
||||
public readonly int Width;
|
||||
public readonly int Height;
|
||||
public readonly bool Letterboxed;
|
||||
public readonly FilmMode FilmMode;
|
||||
|
||||
public VideoAttributes()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
29
DvdLib/Properties/AssemblyInfo.cs
Normal file
29
DvdLib/Properties/AssemblyInfo.cs
Normal file
@ -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("DvdLib")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DvdLib")]
|
||||
[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")]
|
17
DvdLib/project.json
Normal file
17
DvdLib/project.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"frameworks":{
|
||||
"netstandard1.6":{
|
||||
"dependencies":{
|
||||
"NETStandard.Library":"1.6.0",
|
||||
}
|
||||
},
|
||||
".NETPortable,Version=v4.5,Profile=Profile7":{
|
||||
"buildOptions": {
|
||||
"define": [ ]
|
||||
},
|
||||
"frameworkAssemblies":{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,20 @@
|
||||
using MediaBrowser.Model.IO;
|
||||
using SharpCompress.Archive.Rar;
|
||||
using SharpCompress.Archive.SevenZip;
|
||||
using SharpCompress.Archive.Tar;
|
||||
using System.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Reader.Zip;
|
||||
using System.IO;
|
||||
using CommonIO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Zip;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Archiving
|
||||
namespace Emby.Common.Implementations.Archiving
|
||||
{
|
||||
/// <summary>
|
||||
/// Class DotNetZipClient
|
||||
/// </summary>
|
||||
public class ZipClient : IZipClient
|
||||
{
|
||||
private IFileSystem _fileSystem;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public ZipClient(IFileSystem fileSystem)
|
||||
{
|
||||
@ -46,11 +45,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
||||
{
|
||||
using (var reader = ReaderFactory.Open(source))
|
||||
{
|
||||
var options = ExtractOptions.ExtractFullPath;
|
||||
var options = new ExtractionOptions();
|
||||
options.ExtractFullPath = true;
|
||||
|
||||
if (overwriteExistingFiles)
|
||||
{
|
||||
options = options | ExtractOptions.Overwrite;
|
||||
options.Overwrite = true;
|
||||
}
|
||||
|
||||
reader.WriteAllToDirectory(targetPath, options);
|
||||
@ -61,11 +61,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
||||
{
|
||||
using (var reader = ZipReader.Open(source))
|
||||
{
|
||||
var options = ExtractOptions.ExtractFullPath;
|
||||
var options = new ExtractionOptions();
|
||||
options.ExtractFullPath = true;
|
||||
|
||||
if (overwriteExistingFiles)
|
||||
{
|
||||
options = options | ExtractOptions.Overwrite;
|
||||
options.Overwrite = true;
|
||||
}
|
||||
|
||||
reader.WriteAllToDirectory(targetPath, options);
|
||||
@ -98,11 +99,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
||||
{
|
||||
using (var reader = archive.ExtractAllEntries())
|
||||
{
|
||||
var options = ExtractOptions.ExtractFullPath;
|
||||
var options = new ExtractionOptions();
|
||||
options.ExtractFullPath = true;
|
||||
|
||||
if (overwriteExistingFiles)
|
||||
{
|
||||
options = options | ExtractOptions.Overwrite;
|
||||
options.Overwrite = true;
|
||||
}
|
||||
|
||||
reader.WriteAllToDirectory(targetPath, options);
|
||||
@ -137,11 +139,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
||||
{
|
||||
using (var reader = archive.ExtractAllEntries())
|
||||
{
|
||||
var options = ExtractOptions.ExtractFullPath;
|
||||
var options = new ExtractionOptions();
|
||||
options.ExtractFullPath = true;
|
||||
|
||||
if (overwriteExistingFiles)
|
||||
{
|
||||
options = options | ExtractOptions.Overwrite;
|
||||
options.Overwrite = true;
|
||||
}
|
||||
|
||||
reader.WriteAllToDirectory(targetPath, options);
|
||||
@ -175,11 +178,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
||||
{
|
||||
using (var reader = archive.ExtractAllEntries())
|
||||
{
|
||||
var options = ExtractOptions.ExtractFullPath;
|
||||
var options = new ExtractionOptions();
|
||||
options.ExtractFullPath = true;
|
||||
|
||||
if (overwriteExistingFiles)
|
||||
{
|
||||
options = options | ExtractOptions.Overwrite;
|
||||
options.Overwrite = true;
|
||||
}
|
||||
|
||||
reader.WriteAllToDirectory(targetPath, options);
|
@ -1,16 +1,12 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Implementations.Archiving;
|
||||
using MediaBrowser.Common.Implementations.Devices;
|
||||
using MediaBrowser.Common.Implementations.IO;
|
||||
using MediaBrowser.Common.Implementations.ScheduledTasks;
|
||||
using MediaBrowser.Common.Implementations.Security;
|
||||
using MediaBrowser.Common.Implementations.Serialization;
|
||||
using MediaBrowser.Common.Implementations.Updates;
|
||||
using Emby.Common.Implementations.Devices;
|
||||
using Emby.Common.Implementations.IO;
|
||||
using Emby.Common.Implementations.ScheduledTasks;
|
||||
using Emby.Common.Implementations.Serialization;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Model.Events;
|
||||
@ -18,27 +14,42 @@ using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Updates;
|
||||
using ServiceStack;
|
||||
using SimpleInjector;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Emby.Common.Implementations.Cryptography;
|
||||
using Emby.Common.Implementations.Diagnostics;
|
||||
using Emby.Common.Implementations.Net;
|
||||
using Emby.Common.Implementations.EnvironmentInfo;
|
||||
using Emby.Common.Implementations.Threading;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.Diagnostics;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations
|
||||
#if NETSTANDARD1_6
|
||||
using System.Runtime.Loader;
|
||||
#endif
|
||||
|
||||
namespace Emby.Common.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseApplicationHost
|
||||
/// </summary>
|
||||
/// <typeparam name="TApplicationPathsType">The type of the T application paths type.</typeparam>
|
||||
public abstract class BaseApplicationHost<TApplicationPathsType> : IApplicationHost, IDependencyContainer
|
||||
public abstract class BaseApplicationHost<TApplicationPathsType> : IApplicationHost
|
||||
where TApplicationPathsType : class, IApplicationPaths
|
||||
{
|
||||
/// <summary>
|
||||
@ -67,7 +78,7 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// Gets or sets the plugins.
|
||||
/// </summary>
|
||||
/// <value>The plugins.</value>
|
||||
public IEnumerable<IPlugin> Plugins { get; protected set; }
|
||||
public IPlugin[] Plugins { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log manager.
|
||||
@ -81,11 +92,6 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <value>The application paths.</value>
|
||||
protected TApplicationPathsType ApplicationPaths { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The container
|
||||
/// </summary>
|
||||
protected readonly Container Container = new Container();
|
||||
|
||||
/// <summary>
|
||||
/// The json serializer
|
||||
/// </summary>
|
||||
@ -125,11 +131,6 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <value>The kernel.</value>
|
||||
protected ITaskManager TaskManager { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the security manager.
|
||||
/// </summary>
|
||||
/// <value>The security manager.</value>
|
||||
protected ISecurityManager SecurityManager { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the HTTP client.
|
||||
/// </summary>
|
||||
/// <value>The HTTP client.</value>
|
||||
@ -146,22 +147,14 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <value>The configuration manager.</value>
|
||||
protected IConfigurationManager ConfigurationManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the installation manager.
|
||||
/// </summary>
|
||||
/// <value>The installation manager.</value>
|
||||
protected IInstallationManager InstallationManager { get; private set; }
|
||||
|
||||
protected IFileSystem FileSystemManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the zip client.
|
||||
/// </summary>
|
||||
/// <value>The zip client.</value>
|
||||
protected IZipClient ZipClient { get; private set; }
|
||||
|
||||
protected IIsoManager IsoManager { get; private set; }
|
||||
|
||||
protected IProcessFactory ProcessFactory { get; private set; }
|
||||
protected ITimerFactory TimerFactory { get; private set; }
|
||||
protected ISocketFactory SocketFactory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
@ -174,6 +167,10 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value>
|
||||
public abstract bool IsRunningAsService { get; }
|
||||
|
||||
protected ICryptoProvider CryptographyProvider = new CryptographyProvider();
|
||||
|
||||
protected IEnvironmentInfo EnvironmentInfo { get; private set; }
|
||||
|
||||
private DeviceId _deviceId;
|
||||
public string SystemId
|
||||
{
|
||||
@ -190,19 +187,37 @@ namespace MediaBrowser.Common.Implementations
|
||||
|
||||
public virtual string OperatingSystemDisplayName
|
||||
{
|
||||
get { return Environment.OSVersion.VersionString; }
|
||||
get { return EnvironmentInfo.OperatingSystemName; }
|
||||
}
|
||||
|
||||
public IMemoryStreamProvider MemoryStreamProvider { get; set; }
|
||||
/// <summary>
|
||||
/// The container
|
||||
/// </summary>
|
||||
protected readonly SimpleInjector.Container Container = new SimpleInjector.Container();
|
||||
|
||||
protected ISystemEvents SystemEvents { get; private set; }
|
||||
protected IMemoryStreamFactory MemoryStreamFactory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class.
|
||||
/// </summary>
|
||||
protected BaseApplicationHost(TApplicationPathsType applicationPaths,
|
||||
ILogManager logManager,
|
||||
IFileSystem fileSystem)
|
||||
IFileSystem fileSystem,
|
||||
IEnvironmentInfo environmentInfo,
|
||||
ISystemEvents systemEvents,
|
||||
IMemoryStreamFactory memoryStreamFactory,
|
||||
INetworkManager networkManager)
|
||||
{
|
||||
XmlSerializer = new XmlSerializer (fileSystem, logManager.GetLogger("XmlSerializer"));
|
||||
NetworkManager = networkManager;
|
||||
EnvironmentInfo = environmentInfo;
|
||||
SystemEvents = systemEvents;
|
||||
MemoryStreamFactory = memoryStreamFactory;
|
||||
|
||||
// hack alert, until common can target .net core
|
||||
BaseExtensions.CryptographyProvider = CryptographyProvider;
|
||||
|
||||
XmlSerializer = new MyXmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer"));
|
||||
FailedAssemblies = new List<string>();
|
||||
|
||||
ApplicationPaths = applicationPaths;
|
||||
@ -221,28 +236,10 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <returns>Task.</returns>
|
||||
public virtual async Task Init(IProgress<double> progress)
|
||||
{
|
||||
try
|
||||
{
|
||||
// https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4
|
||||
Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0=");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Failing under mono
|
||||
}
|
||||
progress.Report(1);
|
||||
|
||||
JsonSerializer = CreateJsonSerializer();
|
||||
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
{
|
||||
MemoryStreamProvider = new RecyclableMemoryStreamProvider();
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryStreamProvider = new MemoryStreamProvider();
|
||||
}
|
||||
|
||||
OnLoggerLoaded(true);
|
||||
LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false);
|
||||
|
||||
@ -310,11 +307,10 @@ namespace MediaBrowser.Common.Implementations
|
||||
|
||||
builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs())));
|
||||
|
||||
#if NET46
|
||||
builder.AppendLine(string.Format("Operating system: {0}", Environment.OSVersion));
|
||||
builder.AppendLine(string.Format("Processor count: {0}", Environment.ProcessorCount));
|
||||
builder.AppendLine(string.Format("64-Bit OS: {0}", Environment.Is64BitOperatingSystem));
|
||||
builder.AppendLine(string.Format("64-Bit Process: {0}", Environment.Is64BitProcess));
|
||||
builder.AppendLine(string.Format("Program data path: {0}", appPaths.ProgramDataPath));
|
||||
|
||||
Type type = Type.GetType("Mono.Runtime");
|
||||
if (type != null)
|
||||
@ -325,23 +321,25 @@ namespace MediaBrowser.Common.Implementations
|
||||
builder.AppendLine("Mono: " + displayName.Invoke(null, null));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
builder.AppendLine(string.Format("Application Path: {0}", appPaths.ApplicationPath));
|
||||
builder.AppendLine(string.Format("Processor count: {0}", Environment.ProcessorCount));
|
||||
builder.AppendLine(string.Format("Program data path: {0}", appPaths.ProgramDataPath));
|
||||
builder.AppendLine(string.Format("Application directory: {0}", appPaths.ProgramSystemPath));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected virtual IJsonSerializer CreateJsonSerializer()
|
||||
{
|
||||
return new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer"));
|
||||
}
|
||||
protected abstract IJsonSerializer CreateJsonSerializer();
|
||||
|
||||
private void SetHttpLimit()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Increase the max http request limit
|
||||
#if NET46
|
||||
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
|
||||
#endif
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -388,11 +386,11 @@ namespace MediaBrowser.Common.Implementations
|
||||
{
|
||||
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
|
||||
|
||||
ConfigureAutorun ();
|
||||
ConfigureAutorun();
|
||||
|
||||
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
|
||||
|
||||
return Task.FromResult (true);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -427,10 +425,56 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// </summary>
|
||||
protected virtual void FindParts()
|
||||
{
|
||||
RegisterModules();
|
||||
|
||||
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
||||
Plugins = GetExports<IPlugin>();
|
||||
Plugins = GetExports<IPlugin>().Select(LoadPlugin).Where(i => i != null).ToArray();
|
||||
}
|
||||
|
||||
private IPlugin LoadPlugin(IPlugin plugin)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assemblyPlugin = plugin as IPluginAssembly;
|
||||
|
||||
if (assemblyPlugin != null)
|
||||
{
|
||||
#if NET46
|
||||
var assembly = plugin.GetType().Assembly;
|
||||
var assemblyName = assembly.GetName();
|
||||
|
||||
var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
|
||||
var assemblyId = new Guid(attribute.Value);
|
||||
|
||||
var assemblyFileName = assemblyName.Name + ".dll";
|
||||
var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName);
|
||||
|
||||
assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version, assemblyId);
|
||||
#elif NETSTANDARD1_6
|
||||
var typeInfo = plugin.GetType().GetTypeInfo();
|
||||
var assembly = typeInfo.Assembly;
|
||||
var assemblyName = assembly.GetName();
|
||||
|
||||
var attribute = (GuidAttribute)assembly.GetCustomAttribute(typeof(GuidAttribute));
|
||||
var assemblyId = new Guid(attribute.Value);
|
||||
|
||||
var assemblyFileName = assemblyName.Name + ".dll";
|
||||
var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName);
|
||||
|
||||
assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version, assemblyId);
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
var isFirstRun = !File.Exists(plugin.ConfigurationFilePath);
|
||||
plugin.SetStartupInfo(isFirstRun, File.GetLastWriteTimeUtc, s => Directory.CreateDirectory(s));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error loading plugin {0}", ex, plugin.GetType().FullName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -449,7 +493,17 @@ namespace MediaBrowser.Common.Implementations
|
||||
|
||||
AllConcreteTypes = assemblies
|
||||
.SelectMany(GetTypes)
|
||||
.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType)
|
||||
.Where(t =>
|
||||
{
|
||||
#if NET46
|
||||
return t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType;
|
||||
#endif
|
||||
#if NETSTANDARD1_6
|
||||
var typeInfo = t.GetTypeInfo();
|
||||
return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsInterface && !typeInfo.IsGenericType;
|
||||
#endif
|
||||
return false;
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
@ -464,57 +518,41 @@ namespace MediaBrowser.Common.Implementations
|
||||
|
||||
RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
|
||||
|
||||
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager);
|
||||
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager, SystemEvents);
|
||||
|
||||
RegisterSingleInstance(JsonSerializer);
|
||||
RegisterSingleInstance(XmlSerializer);
|
||||
RegisterSingleInstance(MemoryStreamProvider);
|
||||
RegisterSingleInstance(MemoryStreamFactory);
|
||||
RegisterSingleInstance(SystemEvents);
|
||||
|
||||
RegisterSingleInstance(LogManager);
|
||||
RegisterSingleInstance(Logger);
|
||||
|
||||
RegisterSingleInstance(TaskManager);
|
||||
RegisterSingleInstance(EnvironmentInfo);
|
||||
|
||||
RegisterSingleInstance(FileSystemManager);
|
||||
|
||||
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamProvider);
|
||||
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory);
|
||||
RegisterSingleInstance(HttpClient);
|
||||
|
||||
NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager"));
|
||||
RegisterSingleInstance(NetworkManager);
|
||||
|
||||
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager);
|
||||
RegisterSingleInstance(SecurityManager);
|
||||
|
||||
InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
|
||||
RegisterSingleInstance(InstallationManager);
|
||||
|
||||
ZipClient = new ZipClient(FileSystemManager);
|
||||
RegisterSingleInstance(ZipClient);
|
||||
|
||||
IsoManager = new IsoManager();
|
||||
RegisterSingleInstance(IsoManager);
|
||||
|
||||
return Task.FromResult (true);
|
||||
}
|
||||
ProcessFactory = new ProcessFactory();
|
||||
RegisterSingleInstance(ProcessFactory);
|
||||
|
||||
private void RegisterModules()
|
||||
{
|
||||
var moduleTypes = GetExportTypes<IDependencyModule>();
|
||||
TimerFactory = new TimerFactory();
|
||||
RegisterSingleInstance(TimerFactory);
|
||||
|
||||
foreach (var type in moduleTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var instance = Activator.CreateInstance(type) as IDependencyModule;
|
||||
if (instance != null)
|
||||
instance.BindDependencies(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error setting up dependency bindings for " + type.Name, ex);
|
||||
}
|
||||
}
|
||||
SocketFactory = new SocketFactory(LogManager.GetLogger("SocketFactory"));
|
||||
RegisterSingleInstance(SocketFactory);
|
||||
|
||||
RegisterSingleInstance(CryptographyProvider);
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -550,8 +588,6 @@ namespace MediaBrowser.Common.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract INetworkManager CreateNetworkManager(ILogger logger);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of type and resolves all constructor dependancies
|
||||
/// </summary>
|
||||
@ -565,7 +601,7 @@ namespace MediaBrowser.Common.Implementations
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error creating {0}", ex, type.Name);
|
||||
Logger.ErrorException("Error creating {0}", ex, type.FullName);
|
||||
|
||||
throw;
|
||||
}
|
||||
@ -584,17 +620,12 @@ namespace MediaBrowser.Common.Implementations
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error creating {0}", ex, type.Name);
|
||||
Logger.ErrorException("Error creating {0}", ex, type.FullName);
|
||||
// Don't blow up in release mode
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void IDependencyContainer.RegisterSingleInstance<T>(T obj, bool manageLifetime)
|
||||
{
|
||||
RegisterSingleInstance(obj, manageLifetime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the specified obj.
|
||||
/// </summary>
|
||||
@ -617,11 +648,6 @@ namespace MediaBrowser.Common.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
void IDependencyContainer.RegisterSingleInstance<T>(Func<T> func)
|
||||
{
|
||||
RegisterSingleInstance(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the single instance.
|
||||
/// </summary>
|
||||
@ -633,11 +659,6 @@ namespace MediaBrowser.Common.Implementations
|
||||
Container.RegisterSingleton(func);
|
||||
}
|
||||
|
||||
void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
|
||||
{
|
||||
Container.Register(typeInterface, typeImplementation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves this instance.
|
||||
/// </summary>
|
||||
@ -673,7 +694,13 @@ namespace MediaBrowser.Common.Implementations
|
||||
{
|
||||
try
|
||||
{
|
||||
#if NET46
|
||||
return Assembly.Load(File.ReadAllBytes(file));
|
||||
#elif NETSTANDARD1_6
|
||||
|
||||
return AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(file)));
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -692,7 +719,14 @@ namespace MediaBrowser.Common.Implementations
|
||||
{
|
||||
var currentType = typeof(T);
|
||||
|
||||
return AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom);
|
||||
#if NET46
|
||||
return AllConcreteTypes.Where(currentType.IsAssignableFrom);
|
||||
#elif NETSTANDARD1_6
|
||||
var currentTypeInfo = currentType.GetTypeInfo();
|
||||
|
||||
return AllConcreteTypes.Where(currentTypeInfo.IsAssignableFrom);
|
||||
#endif
|
||||
return new List<Type>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -747,7 +781,7 @@ namespace MediaBrowser.Common.Implementations
|
||||
{
|
||||
var list = Plugins.ToList();
|
||||
list.Remove(plugin);
|
||||
Plugins = list;
|
||||
Plugins = list.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
@ -1,7 +1,7 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations
|
||||
namespace Emby.Common.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class to hold common application paths used by both the Ui and Server.
|
||||
@ -12,22 +12,18 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
|
||||
/// </summary>
|
||||
protected BaseApplicationPaths(string programDataPath, string applicationPath)
|
||||
protected BaseApplicationPaths(string programDataPath, string appFolderPath)
|
||||
{
|
||||
ProgramDataPath = programDataPath;
|
||||
ApplicationPath = applicationPath;
|
||||
ProgramSystemPath = appFolderPath;
|
||||
}
|
||||
|
||||
public string ApplicationPath { get; private set; }
|
||||
public string ProgramDataPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the system folder
|
||||
/// </summary>
|
||||
public string ProgramSystemPath
|
||||
{
|
||||
get { return Path.GetDirectoryName(ApplicationPath); }
|
||||
}
|
||||
public string ProgramSystemPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _data directory
|
@ -1,18 +1,19 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Emby.Common.Implementations;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Configuration
|
||||
namespace Emby.Common.Implementations.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseConfigurationManager
|
||||
@ -79,7 +80,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
get
|
||||
{
|
||||
// Lazy load
|
||||
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer));
|
||||
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer, FileSystem));
|
||||
return _configuration;
|
||||
}
|
||||
protected set
|
||||
@ -126,7 +127,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
Logger.Info("Saving system configuration");
|
||||
var path = CommonApplicationPaths.SystemConfigurationFilePath;
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_configurationSyncLock)
|
||||
{
|
||||
@ -196,9 +197,9 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
|
||||
{
|
||||
// Validate
|
||||
if (!Directory.Exists(newPath))
|
||||
if (!FileSystem.DirectoryExists(newPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
|
||||
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
|
||||
}
|
||||
|
||||
EnsureWriteAccess(newPath);
|
||||
@ -253,7 +254,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
{
|
||||
return Activator.CreateInstance(configurationType);
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
catch (IOException)
|
||||
{
|
||||
return Activator.CreateInstance(configurationType);
|
||||
}
|
||||
@ -293,7 +294,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
||||
|
||||
var path = GetConfigurationFile(key);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_configurationSyncLock)
|
||||
{
|
@ -1,9 +1,10 @@
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Configuration
|
||||
namespace Emby.Common.Implementations.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ConfigurationHelper
|
||||
@ -18,7 +19,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="xmlSerializer">The XML serializer.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer)
|
||||
public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
||||
{
|
||||
object configuration;
|
||||
|
||||
@ -27,7 +28,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
// Use try/catch to avoid the extra file system lookup using File.Exists
|
||||
try
|
||||
{
|
||||
buffer = File.ReadAllBytes(path);
|
||||
buffer = fileSystem.ReadAllBytes(path);
|
||||
|
||||
configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
|
||||
}
|
||||
@ -46,10 +47,10 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
||||
// If the file didn't exist before, or if something has changed, re-save
|
||||
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
// Save it after load in case we got new items
|
||||
File.WriteAllBytes(path, newBytes);
|
||||
fileSystem.WriteAllBytes(path, newBytes);
|
||||
}
|
||||
|
||||
return configuration;
|
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
|
||||
namespace Emby.Common.Implementations.Cryptography
|
||||
{
|
||||
public class CryptographyProvider : ICryptoProvider
|
||||
{
|
||||
public Guid GetMD5(string str)
|
||||
{
|
||||
return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
|
||||
}
|
||||
|
||||
public byte[] ComputeSHA1(byte[] bytes)
|
||||
{
|
||||
using (var provider = SHA1.Create())
|
||||
{
|
||||
return provider.ComputeHash(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ComputeMD5(Stream str)
|
||||
{
|
||||
using (var provider = MD5.Create())
|
||||
{
|
||||
return provider.ComputeHash(str);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ComputeMD5(byte[] bytes)
|
||||
{
|
||||
using (var provider = MD5.Create())
|
||||
{
|
||||
return provider.ComputeHash(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Devices
|
||||
namespace Emby.Common.Implementations.Devices
|
||||
{
|
||||
public class DeviceId
|
||||
{
|
108
Emby.Common.Implementations/Diagnostics/CommonProcess.cs
Normal file
108
Emby.Common.Implementations/Diagnostics/CommonProcess.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Diagnostics;
|
||||
|
||||
namespace Emby.Common.Implementations.Diagnostics
|
||||
{
|
||||
public class CommonProcess : IProcess
|
||||
{
|
||||
public event EventHandler Exited;
|
||||
|
||||
private readonly ProcessOptions _options;
|
||||
private readonly Process _process;
|
||||
|
||||
public CommonProcess(ProcessOptions options)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
Arguments = options.Arguments,
|
||||
FileName = options.FileName,
|
||||
WorkingDirectory = options.WorkingDirectory,
|
||||
UseShellExecute = options.UseShellExecute,
|
||||
CreateNoWindow = options.CreateNoWindow,
|
||||
RedirectStandardError = options.RedirectStandardError,
|
||||
RedirectStandardInput = options.RedirectStandardInput,
|
||||
RedirectStandardOutput = options.RedirectStandardOutput
|
||||
};
|
||||
|
||||
#if NET46
|
||||
startInfo.ErrorDialog = options.ErrorDialog;
|
||||
|
||||
if (options.IsHidden)
|
||||
{
|
||||
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
}
|
||||
#endif
|
||||
|
||||
_process = new Process
|
||||
{
|
||||
StartInfo = startInfo
|
||||
};
|
||||
|
||||
if (options.EnableRaisingEvents)
|
||||
{
|
||||
_process.EnableRaisingEvents = true;
|
||||
_process.Exited += _process_Exited;
|
||||
}
|
||||
}
|
||||
|
||||
private void _process_Exited(object sender, EventArgs e)
|
||||
{
|
||||
if (Exited != null)
|
||||
{
|
||||
Exited(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ProcessOptions StartInfo
|
||||
{
|
||||
get { return _options; }
|
||||
}
|
||||
|
||||
public StreamWriter StandardInput
|
||||
{
|
||||
get { return _process.StandardInput; }
|
||||
}
|
||||
|
||||
public StreamReader StandardError
|
||||
{
|
||||
get { return _process.StandardError; }
|
||||
}
|
||||
|
||||
public StreamReader StandardOutput
|
||||
{
|
||||
get { return _process.StandardOutput; }
|
||||
}
|
||||
|
||||
public int ExitCode
|
||||
{
|
||||
get { return _process.ExitCode; }
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_process.Start();
|
||||
}
|
||||
|
||||
public void Kill()
|
||||
{
|
||||
_process.Kill();
|
||||
}
|
||||
|
||||
public bool WaitForExit(int timeMs)
|
||||
{
|
||||
return _process.WaitForExit(timeMs);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_process.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
12
Emby.Common.Implementations/Diagnostics/ProcessFactory.cs
Normal file
12
Emby.Common.Implementations/Diagnostics/ProcessFactory.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using MediaBrowser.Model.Diagnostics;
|
||||
|
||||
namespace Emby.Common.Implementations.Diagnostics
|
||||
{
|
||||
public class ProcessFactory : IProcessFactory
|
||||
{
|
||||
public IProcess Create(ProcessOptions options)
|
||||
{
|
||||
return new CommonProcess(options);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>5a27010a-09c6-4e86-93ea-437484c10917</ProjectGuid>
|
||||
<RootNamespace>Emby.Common.Implementations</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
119
Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs
Normal file
119
Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.System;
|
||||
|
||||
namespace Emby.Common.Implementations.EnvironmentInfo
|
||||
{
|
||||
public class EnvironmentInfo : IEnvironmentInfo
|
||||
{
|
||||
public MediaBrowser.Model.System.Architecture? CustomArchitecture { get; set; }
|
||||
|
||||
public MediaBrowser.Model.System.OperatingSystem OperatingSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET46
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
return MediaBrowser.Model.System.OperatingSystem.OSX;
|
||||
case PlatformID.Win32NT:
|
||||
return MediaBrowser.Model.System.OperatingSystem.Windows;
|
||||
case PlatformID.Unix:
|
||||
return MediaBrowser.Model.System.OperatingSystem.Linux;
|
||||
}
|
||||
#elif NETSTANDARD1_6
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return OperatingSystem.OSX;
|
||||
}
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return OperatingSystem.Windows;
|
||||
}
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return OperatingSystem.Linux;
|
||||
}
|
||||
#endif
|
||||
return MediaBrowser.Model.System.OperatingSystem.Windows;
|
||||
}
|
||||
}
|
||||
|
||||
public string OperatingSystemName
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET46
|
||||
return Environment.OSVersion.Platform.ToString();
|
||||
#elif NETSTANDARD1_6
|
||||
return System.Runtime.InteropServices.RuntimeInformation.OSDescription;
|
||||
#endif
|
||||
return "Operating System";
|
||||
}
|
||||
}
|
||||
|
||||
public string OperatingSystemVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET46
|
||||
return Environment.OSVersion.Version.ToString() + " " + Environment.OSVersion.ServicePack.ToString();
|
||||
#elif NETSTANDARD1_6
|
||||
return System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
|
||||
#endif
|
||||
return "1.0";
|
||||
}
|
||||
}
|
||||
|
||||
public MediaBrowser.Model.System.Architecture SystemArchitecture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CustomArchitecture.HasValue)
|
||||
{
|
||||
return CustomArchitecture.Value;
|
||||
}
|
||||
#if NET46
|
||||
return Environment.Is64BitOperatingSystem ? MediaBrowser.Model.System.Architecture.X64 : MediaBrowser.Model.System.Architecture.X86;
|
||||
#elif NETSTANDARD1_6
|
||||
switch(System.Runtime.InteropServices.RuntimeInformation.OSArchitecture)
|
||||
{
|
||||
case System.Runtime.InteropServices.Architecture.Arm:
|
||||
return MediaBrowser.Model.System.Architecture.Arm;
|
||||
case System.Runtime.InteropServices.Architecture.Arm64:
|
||||
return MediaBrowser.Model.System.Architecture.Arm64;
|
||||
case System.Runtime.InteropServices.Architecture.X64:
|
||||
return MediaBrowser.Model.System.Architecture.X64;
|
||||
case System.Runtime.InteropServices.Architecture.X86:
|
||||
return MediaBrowser.Model.System.Architecture.X86;
|
||||
}
|
||||
#endif
|
||||
return MediaBrowser.Model.System.Architecture.X64;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetEnvironmentVariable(string name)
|
||||
{
|
||||
return Environment.GetEnvironmentVariable(name);
|
||||
}
|
||||
|
||||
public virtual string GetUserId()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public string StackTrace
|
||||
{
|
||||
get { return Environment.StackTrace; }
|
||||
}
|
||||
|
||||
public void SetProcessEnvironmentVariable(string name, string value)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(name, value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
namespace Emby.Common.Implementations.HttpClientManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HttpClientInfo
|
@ -13,13 +13,13 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Cache;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using Emby.Common.Implementations.HttpClientManager;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
namespace Emby.Common.Implementations.HttpClientManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HttpClientManager
|
||||
@ -42,7 +42,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IMemoryStreamProvider _memoryStreamProvider;
|
||||
private readonly IMemoryStreamFactory _memoryStreamProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpClientManager" /> class.
|
||||
@ -53,7 +53,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
/// <exception cref="System.ArgumentNullException">appPaths
|
||||
/// or
|
||||
/// logger</exception>
|
||||
public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider)
|
||||
public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamFactory memoryStreamProvider)
|
||||
{
|
||||
if (appPaths == null)
|
||||
{
|
||||
@ -69,11 +69,13 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
_memoryStreamProvider = memoryStreamProvider;
|
||||
_appPaths = appPaths;
|
||||
|
||||
#if NET46
|
||||
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
|
||||
// Trakt requests sometimes fail without this
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -130,6 +132,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
|
||||
private void AddIpv4Option(HttpWebRequest request, HttpRequestOptions options)
|
||||
{
|
||||
#if NET46
|
||||
request.ServicePoint.BindIPEndPointDelegate = (servicePount, remoteEndPoint, retryCount) =>
|
||||
{
|
||||
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork)
|
||||
@ -138,6 +141,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
}
|
||||
throw new InvalidOperationException("no IPv4 address");
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
private WebRequest GetRequest(HttpRequestOptions options, string method)
|
||||
@ -164,34 +168,66 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
|
||||
AddRequestHeaders(httpWebRequest, options);
|
||||
|
||||
httpWebRequest.AutomaticDecompression = options.EnableHttpCompression ?
|
||||
(options.DecompressionMethod ?? DecompressionMethods.Deflate) :
|
||||
DecompressionMethods.None;
|
||||
#if NET46
|
||||
if (options.EnableHttpCompression)
|
||||
{
|
||||
if (options.DecompressionMethod.HasValue)
|
||||
{
|
||||
httpWebRequest.AutomaticDecompression = options.DecompressionMethod.Value == CompressionMethod.Gzip
|
||||
? DecompressionMethods.GZip
|
||||
: DecompressionMethods.Deflate;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
httpWebRequest.AutomaticDecompression = DecompressionMethods.None;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
|
||||
|
||||
|
||||
#if NET46
|
||||
request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.BypassCache);
|
||||
#endif
|
||||
|
||||
if (httpWebRequest != null)
|
||||
{
|
||||
if (options.EnableKeepAlive)
|
||||
{
|
||||
#if NET46
|
||||
httpWebRequest.KeepAlive = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
request.Method = method;
|
||||
#if NET46
|
||||
request.Timeout = options.TimeoutMs;
|
||||
#endif
|
||||
|
||||
if (httpWebRequest != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(options.Host))
|
||||
{
|
||||
#if NET46
|
||||
httpWebRequest.Host = options.Host;
|
||||
#elif NETSTANDARD1_6
|
||||
httpWebRequest.Headers["Host"] = options.Host;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(options.Referer))
|
||||
{
|
||||
#if NET46
|
||||
httpWebRequest.Referer = options.Referer;
|
||||
#elif NETSTANDARD1_6
|
||||
httpWebRequest.Headers["Referer"] = options.Referer;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +237,10 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
request.Credentials = GetCredential(url, parts[0], parts[1]);
|
||||
// TODO: .net core ??
|
||||
#if NET46
|
||||
request.PreAuthenticate = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,11 +265,19 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
}
|
||||
else if (string.Equals(header.Key, "User-Agent", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
#if NET46
|
||||
request.UserAgent = header.Value;
|
||||
#elif NETSTANDARD1_6
|
||||
request.Headers["User-Agent"] = header.Value;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NET46
|
||||
request.Headers.Set(header.Key, header.Value);
|
||||
#elif NETSTANDARD1_6
|
||||
request.Headers[header.Key] = header.Value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -330,7 +377,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
{
|
||||
if (_fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow)
|
||||
{
|
||||
using (var stream = _fileSystem.GetFileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
|
||||
using (var stream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
|
||||
{
|
||||
var memoryStream = _memoryStreamProvider.CreateNew();
|
||||
|
||||
@ -342,7 +389,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
ResponseUrl = url,
|
||||
Content = memoryStream,
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Headers = new NameValueCollection(),
|
||||
ContentLength = memoryStream.Length
|
||||
};
|
||||
}
|
||||
@ -370,7 +416,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileMode.Create, FileAccess.Write, FileShare.None, true))
|
||||
using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
|
||||
{
|
||||
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
|
||||
@ -407,8 +453,10 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
|
||||
httpWebRequest.ContentType = options.RequestContentType ?? "application/x-www-form-urlencoded";
|
||||
|
||||
#if NET46
|
||||
httpWebRequest.ContentLength = bytes.Length;
|
||||
httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length);
|
||||
#endif
|
||||
(await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false)).Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
if (options.ResourcePool != null)
|
||||
@ -487,7 +535,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
|
||||
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable)
|
||||
{
|
||||
return new HttpResponseInfo(disposable)
|
||||
var responseInfo = new HttpResponseInfo(disposable)
|
||||
{
|
||||
Content = content,
|
||||
|
||||
@ -495,17 +543,22 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
|
||||
ContentType = httpResponse.ContentType,
|
||||
|
||||
Headers = new NameValueCollection(httpResponse.Headers),
|
||||
|
||||
ContentLength = contentLength,
|
||||
|
||||
ResponseUrl = httpResponse.ResponseUri.ToString()
|
||||
};
|
||||
|
||||
if (httpResponse.Headers != null)
|
||||
{
|
||||
SetHeaders(httpResponse.Headers, responseInfo);
|
||||
}
|
||||
|
||||
return responseInfo;
|
||||
}
|
||||
|
||||
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, string tempFile, long? contentLength)
|
||||
{
|
||||
return new HttpResponseInfo
|
||||
var responseInfo = new HttpResponseInfo
|
||||
{
|
||||
TempFilePath = tempFile,
|
||||
|
||||
@ -513,10 +566,23 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
|
||||
ContentType = httpResponse.ContentType,
|
||||
|
||||
Headers = httpResponse.Headers,
|
||||
|
||||
ContentLength = contentLength
|
||||
};
|
||||
|
||||
if (httpResponse.Headers != null)
|
||||
{
|
||||
SetHeaders(httpResponse.Headers, responseInfo);
|
||||
}
|
||||
|
||||
return responseInfo;
|
||||
}
|
||||
|
||||
private void SetHeaders(WebHeaderCollection headers, HttpResponseInfo responseInfo)
|
||||
{
|
||||
foreach (var key in headers.AllKeys)
|
||||
{
|
||||
responseInfo.Headers[key] = headers[key];
|
||||
}
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
|
||||
@ -621,7 +687,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
// We're not able to track progress
|
||||
using (var stream = httpResponse.GetResponseStream())
|
||||
{
|
||||
using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||
using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
|
||||
{
|
||||
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@ -631,7 +697,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
{
|
||||
using (var stream = ProgressStream.CreateReadProgressStream(httpResponse.GetResponseStream(), options.Progress.Report, contentLength.Value))
|
||||
{
|
||||
using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||
using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
|
||||
{
|
||||
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@ -867,6 +933,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
|
||||
private Task<WebResponse> GetResponseAsync(WebRequest request, TimeSpan timeout)
|
||||
{
|
||||
#if NET46
|
||||
var taskCompletion = new TaskCompletionSource<WebResponse>();
|
||||
|
||||
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
|
||||
@ -879,6 +946,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
return taskCompletion.Task;
|
||||
#endif
|
||||
|
||||
return request.GetResponseAsync();
|
||||
}
|
||||
|
||||
private static void TimeoutCallback(object state, bool timedOut)
|
@ -1,11 +1,11 @@
|
||||
using MediaBrowser.Model.IO;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.IO
|
||||
namespace Emby.Common.Implementations.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Class IsoManager
|
794
Emby.Common.Implementations/IO/ManagedFileSystem.cs
Normal file
794
Emby.Common.Implementations/IO/ManagedFileSystem.cs
Normal file
@ -0,0 +1,794 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace Emby.Common.Implementations.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ManagedFileSystem
|
||||
/// </summary>
|
||||
public class ManagedFileSystem : IFileSystem
|
||||
{
|
||||
protected ILogger Logger;
|
||||
|
||||
private readonly bool _supportsAsyncFileStreams;
|
||||
private char[] _invalidFileNameChars;
|
||||
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
|
||||
private bool EnableFileSystemRequestConcat = true;
|
||||
|
||||
public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat)
|
||||
{
|
||||
Logger = logger;
|
||||
_supportsAsyncFileStreams = supportsAsyncFileStreams;
|
||||
EnableFileSystemRequestConcat = enableFileSystemRequestConcat;
|
||||
SetInvalidFileNameChars(enableManagedInvalidFileNameChars);
|
||||
}
|
||||
|
||||
public void AddShortcutHandler(IShortcutHandler handler)
|
||||
{
|
||||
_shortcutHandlers.Add(handler);
|
||||
}
|
||||
|
||||
protected void SetInvalidFileNameChars(bool enableManagedInvalidFileNameChars)
|
||||
{
|
||||
if (enableManagedInvalidFileNameChars)
|
||||
{
|
||||
_invalidFileNameChars = Path.GetInvalidFileNameChars();
|
||||
}
|
||||
else
|
||||
{
|
||||
// GetInvalidFileNameChars is less restrictive in Linux/Mac than Windows, this mimic Windows behavior for mono under Linux/Mac.
|
||||
_invalidFileNameChars = new char[41] { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||
'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12',
|
||||
'\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D',
|
||||
'\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/' };
|
||||
}
|
||||
}
|
||||
|
||||
public char DirectorySeparatorChar
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
public char PathSeparator
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.PathSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFullPath(string path)
|
||||
{
|
||||
return Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified filename is shortcut.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
||||
public virtual bool IsShortcut(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(filename);
|
||||
return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the shortcut.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
||||
public virtual string ResolveShortcut(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(filename);
|
||||
var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
return handler.Resolve(filename);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the shortcut.
|
||||
/// </summary>
|
||||
/// <param name="shortcutPath">The shortcut path.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// shortcutPath
|
||||
/// or
|
||||
/// target
|
||||
/// </exception>
|
||||
public void CreateShortcut(string shortcutPath, string target)
|
||||
{
|
||||
if (string.IsNullOrEmpty(shortcutPath))
|
||||
{
|
||||
throw new ArgumentNullException("shortcutPath");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(target))
|
||||
{
|
||||
throw new ArgumentNullException("target");
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(shortcutPath);
|
||||
var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
handler.Create(shortcutPath, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="FileSystemMetadata"/> object for the specified file or directory path.
|
||||
/// </summary>
|
||||
/// <param name="path">A path to a file or directory.</param>
|
||||
/// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
|
||||
/// <remarks>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
|
||||
/// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will reflect the properties of the directory.</remarks>
|
||||
public FileSystemMetadata GetFileSystemInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
// Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
|
||||
if (Path.HasExtension(path))
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
return GetFileSystemMetadata(new DirectoryInfo(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileInfo = new DirectoryInfo(path);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
return GetFileSystemMetadata(new FileInfo(path));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="FileSystemMetadata"/> object for the specified file path.
|
||||
/// </summary>
|
||||
/// <param name="path">A path to a file.</param>
|
||||
/// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
|
||||
/// <remarks><para>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
|
||||
/// <see cref="FileSystemMetadata.IsDirectory"/> property and the <see cref="FileSystemMetadata.Exists"/> property will both be set to false.</para>
|
||||
/// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
|
||||
public FileSystemMetadata GetFileInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="FileSystemMetadata"/> object for the specified directory path.
|
||||
/// </summary>
|
||||
/// <param name="path">A path to a directory.</param>
|
||||
/// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
|
||||
/// <remarks><para>If the specified path points to a file, the returned <see cref="FileSystemMetadata"/> object's
|
||||
/// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and the <see cref="FileSystemMetadata.Exists"/> property will be set to false.</para>
|
||||
/// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
|
||||
public FileSystemMetadata GetDirectoryInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var fileInfo = new DirectoryInfo(path);
|
||||
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
private FileSystemMetadata GetFileSystemMetadata(FileSystemInfo info)
|
||||
{
|
||||
var result = new FileSystemMetadata();
|
||||
|
||||
result.Exists = info.Exists;
|
||||
result.FullName = info.FullName;
|
||||
result.Extension = info.Extension;
|
||||
result.Name = info.Name;
|
||||
|
||||
if (result.Exists)
|
||||
{
|
||||
var attributes = info.Attributes;
|
||||
result.IsDirectory = info is DirectoryInfo || (attributes & FileAttributes.Directory) == FileAttributes.Directory;
|
||||
result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
||||
result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
|
||||
|
||||
var fileInfo = info as FileInfo;
|
||||
if (fileInfo != null)
|
||||
{
|
||||
result.Length = fileInfo.Length;
|
||||
result.DirectoryName = fileInfo.DirectoryName;
|
||||
}
|
||||
|
||||
result.CreationTimeUtc = GetCreationTimeUtc(info);
|
||||
result.LastWriteTimeUtc = GetLastWriteTimeUtc(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.IsDirectory = info is DirectoryInfo;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The space char
|
||||
/// </summary>
|
||||
private const char SpaceChar = ' ';
|
||||
|
||||
/// <summary>
|
||||
/// Takes a filename and removes invalid characters
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
||||
public string GetValidFilename(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
var builder = new StringBuilder(filename);
|
||||
|
||||
foreach (var c in _invalidFileNameChars)
|
||||
{
|
||||
builder = builder.Replace(c, SpaceChar);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time UTC.
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetCreationTimeUtc(FileSystemInfo info)
|
||||
{
|
||||
// This could throw an error on some file systems that have dates out of range
|
||||
try
|
||||
{
|
||||
return info.CreationTimeUtc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining CreationTimeUtc for {0}", ex, info.FullName);
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time UTC.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetCreationTimeUtc(string path)
|
||||
{
|
||||
return GetCreationTimeUtc(GetFileSystemInfo(path));
|
||||
}
|
||||
|
||||
public DateTime GetCreationTimeUtc(FileSystemMetadata info)
|
||||
{
|
||||
return info.CreationTimeUtc;
|
||||
}
|
||||
|
||||
public DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
|
||||
{
|
||||
return info.LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time UTC.
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
|
||||
{
|
||||
// This could throw an error on some file systems that have dates out of range
|
||||
try
|
||||
{
|
||||
return info.LastWriteTimeUtc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining LastAccessTimeUtc for {0}", ex, info.FullName);
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last write time UTC.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetLastWriteTimeUtc(string path)
|
||||
{
|
||||
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file stream.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="mode">The mode.</param>
|
||||
/// <param name="access">The access.</param>
|
||||
/// <param name="share">The share.</param>
|
||||
/// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
|
||||
/// <returns>FileStream.</returns>
|
||||
public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false)
|
||||
{
|
||||
if (_supportsAsyncFileStreams && isAsync)
|
||||
{
|
||||
return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144, true);
|
||||
}
|
||||
|
||||
return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144);
|
||||
}
|
||||
|
||||
private FileMode GetFileMode(FileOpenMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FileOpenMode.Append:
|
||||
return FileMode.Append;
|
||||
case FileOpenMode.Create:
|
||||
return FileMode.Create;
|
||||
case FileOpenMode.CreateNew:
|
||||
return FileMode.CreateNew;
|
||||
case FileOpenMode.Open:
|
||||
return FileMode.Open;
|
||||
case FileOpenMode.OpenOrCreate:
|
||||
return FileMode.OpenOrCreate;
|
||||
case FileOpenMode.Truncate:
|
||||
return FileMode.Truncate;
|
||||
default:
|
||||
throw new Exception("Unrecognized FileOpenMode");
|
||||
}
|
||||
}
|
||||
|
||||
private FileAccess GetFileAccess(FileAccessMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FileAccessMode.ReadWrite:
|
||||
return FileAccess.ReadWrite;
|
||||
case FileAccessMode.Write:
|
||||
return FileAccess.Write;
|
||||
case FileAccessMode.Read:
|
||||
return FileAccess.Read;
|
||||
default:
|
||||
throw new Exception("Unrecognized FileAccessMode");
|
||||
}
|
||||
}
|
||||
|
||||
private FileShare GetFileShare(FileShareMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FileShareMode.ReadWrite:
|
||||
return FileShare.ReadWrite;
|
||||
case FileShareMode.Write:
|
||||
return FileShare.Write;
|
||||
case FileShareMode.Read:
|
||||
return FileShare.Read;
|
||||
case FileShareMode.None:
|
||||
return FileShare.None;
|
||||
default:
|
||||
throw new Exception("Unrecognized FileShareMode");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHidden(string path, bool isHidden)
|
||||
{
|
||||
var info = GetFileInfo(path);
|
||||
|
||||
if (info.Exists && info.IsHidden != isHidden)
|
||||
{
|
||||
if (isHidden)
|
||||
{
|
||||
File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileAttributes attributes = File.GetAttributes(path);
|
||||
attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
|
||||
File.SetAttributes(path, attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetReadOnly(string path, bool isReadOnly)
|
||||
{
|
||||
var info = GetFileInfo(path);
|
||||
|
||||
if (info.Exists && info.IsReadOnly != isReadOnly)
|
||||
{
|
||||
if (isReadOnly)
|
||||
{
|
||||
File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.ReadOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileAttributes attributes = File.GetAttributes(path);
|
||||
attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
|
||||
File.SetAttributes(path, attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
|
||||
{
|
||||
return attributes & ~attributesToRemove;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swaps the files.
|
||||
/// </summary>
|
||||
/// <param name="file1">The file1.</param>
|
||||
/// <param name="file2">The file2.</param>
|
||||
public void SwapFiles(string file1, string file2)
|
||||
{
|
||||
if (string.IsNullOrEmpty(file1))
|
||||
{
|
||||
throw new ArgumentNullException("file1");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(file2))
|
||||
{
|
||||
throw new ArgumentNullException("file2");
|
||||
}
|
||||
|
||||
var temp1 = Path.GetTempFileName();
|
||||
|
||||
// Copying over will fail against hidden files
|
||||
RemoveHiddenAttribute(file1);
|
||||
RemoveHiddenAttribute(file2);
|
||||
|
||||
CopyFile(file1, temp1, true);
|
||||
|
||||
CopyFile(file2, file1, true);
|
||||
CopyFile(temp1, file2, true);
|
||||
|
||||
DeleteFile(temp1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the hidden attribute.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
private void RemoveHiddenAttribute(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var currentFile = new FileInfo(path);
|
||||
|
||||
// This will fail if the file is hidden
|
||||
if (currentFile.Exists)
|
||||
{
|
||||
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||
{
|
||||
currentFile.Attributes &= ~FileAttributes.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsSubPath(string parentPath, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(parentPath))
|
||||
{
|
||||
throw new ArgumentNullException("parentPath");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
return path.IndexOf(parentPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
public bool IsRootPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var parent = Path.GetDirectoryName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string NormalizePath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
public string GetFileNameWithoutExtension(FileSystemMetadata info)
|
||||
{
|
||||
if (info.IsDirectory)
|
||||
{
|
||||
return info.Name;
|
||||
}
|
||||
|
||||
return Path.GetFileNameWithoutExtension(info.FullName);
|
||||
}
|
||||
|
||||
public string GetFileNameWithoutExtension(string path)
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
|
||||
public bool IsPathFile(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
// Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
|
||||
|
||||
if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
|
||||
!path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
//return Path.IsPathRooted(path);
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
{
|
||||
var fileInfo = GetFileInfo(path);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
if (fileInfo.IsHidden)
|
||||
{
|
||||
SetHidden(path, false);
|
||||
}
|
||||
if (fileInfo.IsReadOnly)
|
||||
{
|
||||
SetReadOnly(path, false);
|
||||
}
|
||||
}
|
||||
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path, bool recursive)
|
||||
{
|
||||
Directory.Delete(path, recursive);
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public List<FileSystemMetadata> GetDrives()
|
||||
{
|
||||
// Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
|
||||
return DriveInfo.GetDrives().Where(d => d.IsReady).Select(d => new FileSystemMetadata
|
||||
{
|
||||
Name = GetName(d),
|
||||
FullName = d.RootDirectory.FullName,
|
||||
IsDirectory = true
|
||||
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private string GetName(DriveInfo drive)
|
||||
{
|
||||
return drive.Name;
|
||||
}
|
||||
|
||||
public IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
|
||||
return ToMetadata(path, new DirectoryInfo(path).EnumerateDirectories("*", searchOption));
|
||||
}
|
||||
|
||||
public IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
|
||||
return ToMetadata(path, new DirectoryInfo(path).EnumerateFiles("*", searchOption));
|
||||
}
|
||||
|
||||
public IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(path);
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
|
||||
if (EnableFileSystemRequestConcat)
|
||||
{
|
||||
return ToMetadata(path, directoryInfo.EnumerateDirectories("*", searchOption))
|
||||
.Concat(ToMetadata(path, directoryInfo.EnumerateFiles("*", searchOption)));
|
||||
}
|
||||
|
||||
return ToMetadata(path, directoryInfo.EnumerateFileSystemInfos("*", searchOption));
|
||||
}
|
||||
|
||||
private IEnumerable<FileSystemMetadata> ToMetadata(string parentPath, IEnumerable<FileSystemInfo> infos)
|
||||
{
|
||||
return infos.Select(i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetFileSystemMetadata(i);
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
// Can't log using the FullName because it will throw the PathTooLongExceptiona again
|
||||
//Logger.Warn("Path too long: {0}", i.FullName);
|
||||
Logger.Warn("File or directory path too long. Parent folder: {0}", parentPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
}).Where(i => i != null);
|
||||
}
|
||||
|
||||
public string[] ReadAllLines(string path)
|
||||
{
|
||||
return File.ReadAllLines(path);
|
||||
}
|
||||
|
||||
public void WriteAllLines(string path, IEnumerable<string> lines)
|
||||
{
|
||||
File.WriteAllLines(path, lines);
|
||||
}
|
||||
|
||||
public Stream OpenRead(string path)
|
||||
{
|
||||
return File.OpenRead(path);
|
||||
}
|
||||
|
||||
public void CopyFile(string source, string target, bool overwrite)
|
||||
{
|
||||
File.Copy(source, target, overwrite);
|
||||
}
|
||||
|
||||
public void MoveFile(string source, string target)
|
||||
{
|
||||
File.Move(source, target);
|
||||
}
|
||||
|
||||
public void MoveDirectory(string source, string target)
|
||||
{
|
||||
Directory.Move(source, target);
|
||||
}
|
||||
|
||||
public bool DirectoryExists(string path)
|
||||
{
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
public bool FileExists(string path)
|
||||
{
|
||||
return File.Exists(path);
|
||||
}
|
||||
|
||||
public string ReadAllText(string path)
|
||||
{
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
public byte[] ReadAllBytes(string path)
|
||||
{
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
public void WriteAllText(string path, string text, Encoding encoding)
|
||||
{
|
||||
File.WriteAllText(path, text, encoding);
|
||||
}
|
||||
|
||||
public void WriteAllText(string path, string text)
|
||||
{
|
||||
File.WriteAllText(path, text);
|
||||
}
|
||||
|
||||
public void WriteAllBytes(string path, byte[] bytes)
|
||||
{
|
||||
File.WriteAllBytes(path, bytes);
|
||||
}
|
||||
|
||||
public string ReadAllText(string path, Encoding encoding)
|
||||
{
|
||||
return File.ReadAllText(path, encoding);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
return Directory.EnumerateDirectories(path, "*", searchOption);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFilePaths(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
return Directory.EnumerateFiles(path, "*", searchOption);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
return Directory.EnumerateFileSystemEntries(path, "*", searchOption);
|
||||
}
|
||||
|
||||
public virtual void SetExecutable(string path)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Logging
|
||||
namespace Emby.Common.Implementations.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Class NLogger
|
544
Emby.Common.Implementations/Logging/NlogManager.cs
Normal file
544
Emby.Common.Implementations/Logging/NlogManager.cs
Normal file
@ -0,0 +1,544 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Filters;
|
||||
using NLog.Targets;
|
||||
using NLog.Targets.Wrappers;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace Emby.Common.Implementations.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Class NlogManager
|
||||
/// </summary>
|
||||
public class NlogManager : ILogManager
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private LogSeverity _severity = LogSeverity.Debug;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log directory.
|
||||
/// </summary>
|
||||
/// <value>The log directory.</value>
|
||||
private readonly string LogDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log file prefix.
|
||||
/// </summary>
|
||||
/// <value>The log file prefix.</value>
|
||||
private readonly string LogFilePrefix;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Declarations
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [logger loaded].
|
||||
/// </summary>
|
||||
public event EventHandler LoggerLoaded;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log file path.
|
||||
/// </summary>
|
||||
/// <value>The log file path.</value>
|
||||
public string LogFilePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exception message prefix.
|
||||
/// </summary>
|
||||
/// <value>The exception message prefix.</value>
|
||||
public string ExceptionMessagePrefix { get; set; }
|
||||
|
||||
public string NLogConfigurationFilePath { get; set; }
|
||||
|
||||
public LogSeverity LogSeverity
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
return _severity;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"SET LogSeverity, _severity = [{0}], value = [{1}]",
|
||||
_severity.ToString(),
|
||||
value.ToString()
|
||||
));
|
||||
|
||||
var changed = _severity != value;
|
||||
|
||||
_severity = value;
|
||||
|
||||
if (changed)
|
||||
{
|
||||
UpdateLogLevel(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor(s)
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NlogManager" /> class.
|
||||
/// </summary>
|
||||
/// <param name="logDirectory">The log directory.</param>
|
||||
/// <param name="logFileNamePrefix">The log file name prefix.</param>
|
||||
public NlogManager(string logDirectory, string logFileNamePrefix)
|
||||
{
|
||||
DebugFileWriter(
|
||||
logDirectory, String.Format(
|
||||
"NlogManager constructor called, logDirectory is [{0}], logFileNamePrefix is [{1}], _severity is [{2}].",
|
||||
logDirectory,
|
||||
logFileNamePrefix,
|
||||
_severity.ToString()
|
||||
));
|
||||
|
||||
LogDirectory = logDirectory;
|
||||
LogFilePrefix = logFileNamePrefix;
|
||||
|
||||
LogManager.Configuration = new LoggingConfiguration();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NlogManager" /> class.
|
||||
/// </summary>
|
||||
/// <param name="logDirectory">The log directory.</param>
|
||||
/// <param name="logFileNamePrefix">The log file name prefix.</param>
|
||||
public NlogManager(string logDirectory, string logFileNamePrefix, LogSeverity initialSeverity) : this(logDirectory, logFileNamePrefix)
|
||||
{
|
||||
_severity = initialSeverity;
|
||||
|
||||
DebugFileWriter(
|
||||
logDirectory, String.Format(
|
||||
"NlogManager constructor called, logDirectory is [{0}], logFileNamePrefix is [{1}], _severity is [{2}].",
|
||||
logDirectory,
|
||||
logFileNamePrefix,
|
||||
_severity.ToString()
|
||||
));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the file target.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="level">The level.</param>
|
||||
private void AddFileTarget(string path, LogSeverity level)
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"AddFileTarget called, path = [{0}], level = [{1}].",
|
||||
path,
|
||||
level.ToString()
|
||||
));
|
||||
|
||||
RemoveTarget("ApplicationLogFileWrapper");
|
||||
|
||||
var wrapper = new AsyncTargetWrapper();
|
||||
wrapper.Name = "ApplicationLogFileWrapper";
|
||||
|
||||
var logFile = new FileTarget
|
||||
{
|
||||
FileName = path,
|
||||
Layout = "${longdate} ${level} ${logger}: ${message}"
|
||||
};
|
||||
|
||||
logFile.Name = "ApplicationLogFile";
|
||||
|
||||
wrapper.WrappedTarget = logFile;
|
||||
|
||||
AddLogTarget(wrapper, level);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log level.
|
||||
/// </summary>
|
||||
/// <param name="severity">The severity.</param>
|
||||
/// <returns>LogLevel.</returns>
|
||||
/// <exception cref="System.ArgumentException">Unrecognized LogSeverity</exception>
|
||||
private LogLevel GetLogLevel(LogSeverity severity)
|
||||
{
|
||||
switch (severity)
|
||||
{
|
||||
case LogSeverity.Debug:
|
||||
return LogLevel.Debug;
|
||||
case LogSeverity.Error:
|
||||
return LogLevel.Error;
|
||||
case LogSeverity.Fatal:
|
||||
return LogLevel.Fatal;
|
||||
case LogSeverity.Info:
|
||||
return LogLevel.Info;
|
||||
case LogSeverity.Warn:
|
||||
return LogLevel.Warn;
|
||||
default:
|
||||
throw new ArgumentException("Unrecognized LogSeverity");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLogLevel(LogSeverity newLevel)
|
||||
{
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"UpdateLogLevel called, newLevel = [{0}].",
|
||||
newLevel.ToString()
|
||||
));
|
||||
|
||||
var level = GetLogLevel(newLevel);
|
||||
|
||||
var rules = LogManager.Configuration.LoggingRules;
|
||||
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
if (!rule.IsLoggingEnabledForLevel(level))
|
||||
{
|
||||
rule.EnableLoggingForLevel(level);
|
||||
}
|
||||
foreach (var lev in rule.Levels.ToArray())
|
||||
{
|
||||
if (lev < level)
|
||||
{
|
||||
rule.DisableLoggingForLevel(lev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCustomFilters(string defaultLoggerNamePattern, LoggingRule defaultRule)
|
||||
{
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"AddCustomFilters called, defaultLoggerNamePattern = [{0}], defaultRule.LoggerNamePattern = [{1}].",
|
||||
defaultLoggerNamePattern,
|
||||
defaultRule.LoggerNamePattern
|
||||
));
|
||||
|
||||
try
|
||||
{
|
||||
var customConfig = new NLog.Config.XmlLoggingConfiguration(NLogConfigurationFilePath);
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Custom Configuration Loaded, Rule Count = [{0}].",
|
||||
customConfig.LoggingRules.Count.ToString()
|
||||
));
|
||||
|
||||
foreach (var customRule in customConfig.LoggingRules)
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Read Custom Rule, LoggerNamePattern = [{0}], Targets = [{1}].",
|
||||
customRule.LoggerNamePattern,
|
||||
string.Join(",", customRule.Targets.Select(x => x.Name).ToList())
|
||||
));
|
||||
|
||||
if (customRule.LoggerNamePattern.Equals(defaultLoggerNamePattern))
|
||||
{
|
||||
|
||||
if (customRule.Targets.Any((arg) => arg.Name.Equals(defaultRule.Targets.First().Name)))
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Custom rule filters can be applied to this target, Filter Count = [{0}].",
|
||||
customRule.Filters.Count.ToString()
|
||||
));
|
||||
|
||||
foreach (ConditionBasedFilter customFilter in customRule.Filters)
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Read Custom Filter, Filter = [{0}], Action = [{1}], Type = [{2}].",
|
||||
customFilter.Condition.ToString(),
|
||||
customFilter.Action.ToString(),
|
||||
customFilter.GetType().ToString()
|
||||
));
|
||||
|
||||
defaultRule.Filters.Add(customFilter);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Ignoring custom rule as [Target] does not match."
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Ignoring custom rule as [LoggerNamePattern] does not match."
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Intentionally do nothing, prevent issues affecting normal execution.
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Exception in AddCustomFilters, ex.Message = [{0}].",
|
||||
ex.Message
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>ILogger.</returns>
|
||||
public MediaBrowser.Model.Logging.ILogger GetLogger(string name)
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"GetLogger called, name = [{0}].",
|
||||
name
|
||||
));
|
||||
|
||||
return new NLogger(name, this);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the log target.
|
||||
/// </summary>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="level">The level.</param>
|
||||
public void AddLogTarget(Target target, LogSeverity level)
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"AddLogTarget called, target.Name = [{0}], level = [{1}].",
|
||||
target.Name,
|
||||
level.ToString()
|
||||
));
|
||||
|
||||
string loggerNamePattern = "*";
|
||||
var config = LogManager.Configuration;
|
||||
var rule = new LoggingRule(loggerNamePattern, GetLogLevel(level), target);
|
||||
|
||||
config.AddTarget(target.Name, target);
|
||||
|
||||
AddCustomFilters(loggerNamePattern, rule);
|
||||
|
||||
config.LoggingRules.Add(rule);
|
||||
|
||||
LogManager.Configuration = config;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the target.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
public void RemoveTarget(string name)
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"RemoveTarget called, name = [{0}].",
|
||||
name
|
||||
));
|
||||
|
||||
var config = LogManager.Configuration;
|
||||
|
||||
var target = config.FindTargetByName(name);
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
foreach (var rule in config.LoggingRules.ToList())
|
||||
{
|
||||
var contains = rule.Targets.Contains(target);
|
||||
|
||||
rule.Targets.Remove(target);
|
||||
|
||||
if (contains)
|
||||
{
|
||||
config.LoggingRules.Remove(rule);
|
||||
}
|
||||
}
|
||||
|
||||
config.RemoveTarget(name);
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddConsoleOutput()
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"AddConsoleOutput called."
|
||||
));
|
||||
|
||||
RemoveTarget("ConsoleTargetWrapper");
|
||||
|
||||
var wrapper = new AsyncTargetWrapper();
|
||||
wrapper.Name = "ConsoleTargetWrapper";
|
||||
|
||||
var target = new ConsoleTarget()
|
||||
{
|
||||
Layout = "${level}, ${logger}, ${message}",
|
||||
Error = false
|
||||
};
|
||||
|
||||
target.Name = "ConsoleTarget";
|
||||
|
||||
wrapper.WrappedTarget = target;
|
||||
|
||||
AddLogTarget(wrapper, LogSeverity);
|
||||
|
||||
}
|
||||
|
||||
public void RemoveConsoleOutput()
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"RemoveConsoleOutput called."
|
||||
));
|
||||
|
||||
RemoveTarget("ConsoleTargetWrapper");
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the logger, maintaining the current log level.
|
||||
/// </summary>
|
||||
public void ReloadLogger()
|
||||
{
|
||||
ReloadLogger(LogSeverity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the logger, using the specified logging level.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
public void ReloadLogger(LogSeverity level)
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"ReloadLogger called, level = [{0}], LogFilePath (existing) = [{1}].",
|
||||
level.ToString(),
|
||||
LogFilePath
|
||||
));
|
||||
|
||||
LogFilePath = Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Floor(DateTime.Now.Ticks / 10000000) + ".txt");
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
|
||||
|
||||
AddFileTarget(LogFilePath, level);
|
||||
|
||||
LogSeverity = level;
|
||||
|
||||
if (LoggerLoaded != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"ReloadLogger called, raised event LoggerLoaded."
|
||||
));
|
||||
|
||||
LoggerLoaded(this, EventArgs.Empty);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes this instance.
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
|
||||
DebugFileWriter(
|
||||
LogDirectory, String.Format(
|
||||
"Flush called."
|
||||
));
|
||||
|
||||
LogManager.Flush();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conditional Debug Methods
|
||||
|
||||
/// <summary>
|
||||
/// DEBUG: Standalone method to write out debug to assist with logger development/troubleshooting.
|
||||
/// <list type="bullet">
|
||||
/// <item><description>The output file will be written to the server's log directory.</description></item>
|
||||
/// <item><description>Calls to the method are safe and will never throw any exceptions.</description></item>
|
||||
/// <item><description>Method calls will be omitted unless the library is compiled with DEBUG defined.</description></item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
private static void DebugFileWriter(string logDirectory, string message)
|
||||
{
|
||||
#if DEBUG
|
||||
try
|
||||
{
|
||||
|
||||
System.IO.File.AppendAllText(
|
||||
Path.Combine(logDirectory, "NlogManager.txt"),
|
||||
String.Format(
|
||||
"{0} : {1}{2}",
|
||||
System.DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
|
||||
message,
|
||||
System.Environment.NewLine
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Intentionally do nothing, prevent issues affecting normal execution.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Emby.Common.Implementations.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property.
|
||||
/// </summary>
|
||||
public abstract class DisposableManagedObjectBase : IDisposable
|
||||
{
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Override this method and dispose any objects you own the lifetime of if disposing is true;
|
||||
/// </summary>
|
||||
/// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param>
|
||||
protected abstract void Dispose(bool disposing);
|
||||
|
||||
/// <summary>
|
||||
/// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true.
|
||||
/// </summary>
|
||||
/// <seealso cref="IsDisposed"/>
|
||||
/// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception>
|
||||
/// <seealso cref="Dispose()"/>
|
||||
protected virtual void ThrowIfDisposed()
|
||||
{
|
||||
if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Sets or returns a boolean indicating whether or not this instance has been disposed.
|
||||
/// </summary>
|
||||
/// <seealso cref="Dispose()"/>
|
||||
public bool IsDisposed
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Disposes this object instance and all internally managed resources.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Sets the <see cref="IsDisposed"/> property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behaviour of derived classes.</para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="IsDisposed"/>
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsDisposed = true;
|
||||
|
||||
Dispose(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
97
Emby.Common.Implementations/Net/NetSocket.cs
Normal file
97
Emby.Common.Implementations/Net/NetSocket.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using Emby.Common.Implementations.Networking;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace Emby.Common.Implementations.Net
|
||||
{
|
||||
public class NetSocket : ISocket
|
||||
{
|
||||
public Socket Socket { get; private set; }
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public bool DualMode { get; private set; }
|
||||
|
||||
public NetSocket(Socket socket, ILogger logger, bool isDualMode)
|
||||
{
|
||||
if (socket == null)
|
||||
{
|
||||
throw new ArgumentNullException("socket");
|
||||
}
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException("logger");
|
||||
}
|
||||
|
||||
Socket = socket;
|
||||
_logger = logger;
|
||||
DualMode = isDualMode;
|
||||
}
|
||||
|
||||
public IpEndPointInfo LocalEndPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return NetworkManager.ToIpEndPointInfo((IPEndPoint)Socket.LocalEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public IpEndPointInfo RemoteEndPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return NetworkManager.ToIpEndPointInfo((IPEndPoint)Socket.RemoteEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
#if NET46
|
||||
Socket.Close();
|
||||
#else
|
||||
Socket.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Shutdown(bool both)
|
||||
{
|
||||
if (both)
|
||||
{
|
||||
Socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Change interface if ever needed
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Listen(int backlog)
|
||||
{
|
||||
Socket.Listen(backlog);
|
||||
}
|
||||
|
||||
public void Bind(IpEndPointInfo endpoint)
|
||||
{
|
||||
var nativeEndpoint = NetworkManager.ToIPEndPoint(endpoint);
|
||||
|
||||
Socket.Bind(nativeEndpoint);
|
||||
}
|
||||
|
||||
private SocketAcceptor _acceptor;
|
||||
public void StartAccept(Action<ISocket> onAccept, Func<bool> isClosed)
|
||||
{
|
||||
_acceptor = new SocketAcceptor(_logger, Socket, onAccept, isClosed, DualMode);
|
||||
|
||||
_acceptor.StartAccept();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Socket.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
127
Emby.Common.Implementations/Net/SocketAcceptor.cs
Normal file
127
Emby.Common.Implementations/Net/SocketAcceptor.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
||||
namespace Emby.Common.Implementations.Net
|
||||
{
|
||||
public class SocketAcceptor
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly Socket _originalSocket;
|
||||
private readonly Func<bool> _isClosed;
|
||||
private readonly Action<ISocket> _onAccept;
|
||||
private readonly bool _isDualMode;
|
||||
|
||||
public SocketAcceptor(ILogger logger, Socket originalSocket, Action<ISocket> onAccept, Func<bool> isClosed, bool isDualMode)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException("logger");
|
||||
}
|
||||
if (originalSocket == null)
|
||||
{
|
||||
throw new ArgumentNullException("originalSocket");
|
||||
}
|
||||
if (onAccept == null)
|
||||
{
|
||||
throw new ArgumentNullException("onAccept");
|
||||
}
|
||||
if (isClosed == null)
|
||||
{
|
||||
throw new ArgumentNullException("isClosed");
|
||||
}
|
||||
|
||||
_logger = logger;
|
||||
_originalSocket = originalSocket;
|
||||
_isClosed = isClosed;
|
||||
_isDualMode = isDualMode;
|
||||
_onAccept = onAccept;
|
||||
}
|
||||
|
||||
public void StartAccept()
|
||||
{
|
||||
Socket dummy = null;
|
||||
StartAccept(null, ref dummy);
|
||||
}
|
||||
|
||||
public void StartAccept(SocketAsyncEventArgs acceptEventArg, ref Socket accepted)
|
||||
{
|
||||
if (acceptEventArg == null)
|
||||
{
|
||||
acceptEventArg = new SocketAsyncEventArgs();
|
||||
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// socket must be cleared since the context object is being reused
|
||||
acceptEventArg.AcceptSocket = null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool willRaiseEvent = _originalSocket.AcceptAsync(acceptEventArg);
|
||||
|
||||
if (!willRaiseEvent)
|
||||
{
|
||||
ProcessAccept(acceptEventArg);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (accepted != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if NET46
|
||||
accepted.Close();
|
||||
#else
|
||||
accepted.Dispose();
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
accepted = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This method is the callback method associated with Socket.AcceptAsync
|
||||
// operations and is invoked when an accept operation is complete
|
||||
//
|
||||
void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
|
||||
{
|
||||
ProcessAccept(e);
|
||||
}
|
||||
|
||||
private void ProcessAccept(SocketAsyncEventArgs e)
|
||||
{
|
||||
if (_isClosed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.acceptasync%28v=vs.110%29.aspx
|
||||
// Under certain conditions ConnectionReset can occur
|
||||
// Need to attept to re-accept
|
||||
if (e.SocketError == SocketError.ConnectionReset)
|
||||
{
|
||||
_logger.Error("SocketError.ConnectionReset reported. Attempting to re-accept.");
|
||||
Socket dummy = null;
|
||||
StartAccept(e, ref dummy);
|
||||
return;
|
||||
}
|
||||
|
||||
var acceptSocket = e.AcceptSocket;
|
||||
if (acceptSocket != null)
|
||||
{
|
||||
//ProcessAccept(acceptSocket);
|
||||
_onAccept(new NetSocket(acceptSocket, _logger, _isDualMode));
|
||||
}
|
||||
|
||||
// Accept the next connection request
|
||||
StartAccept(e, ref acceptSocket);
|
||||
}
|
||||
}
|
||||
}
|
160
Emby.Common.Implementations/Net/SocketFactory.cs
Normal file
160
Emby.Common.Implementations/Net/SocketFactory.cs
Normal file
@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Common.Implementations.Networking;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
||||
namespace Emby.Common.Implementations.Net
|
||||
{
|
||||
public class SocketFactory : ISocketFactory
|
||||
{
|
||||
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
|
||||
// Be careful to check any changes compile and work for all platform projects it is shared in.
|
||||
|
||||
// Not entirely happy with this. Would have liked to have done something more generic/reusable,
|
||||
// but that wasn't really the point so kept to YAGNI principal for now, even if the
|
||||
// interfaces are a bit ugly, specific and make assumptions.
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SocketFactory(ILogger logger)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException("logger");
|
||||
}
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public ISocket CreateSocket(IpAddressFamily family, MediaBrowser.Model.Net.SocketType socketType, MediaBrowser.Model.Net.ProtocolType protocolType, bool dualMode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var addressFamily = family == IpAddressFamily.InterNetwork
|
||||
? AddressFamily.InterNetwork
|
||||
: AddressFamily.InterNetworkV6;
|
||||
|
||||
var socket = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||
|
||||
if (dualMode)
|
||||
{
|
||||
socket.DualMode = true;
|
||||
}
|
||||
|
||||
return new NetSocket(socket, _logger, dualMode);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
#region ISocketFactory Members
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new UDP socket and binds it to the specified local port.
|
||||
/// </summary>
|
||||
/// <param name="localPort">An integer specifying the local port to bind the socket to.</param>
|
||||
public IUdpSocket CreateUdpSocket(int localPort)
|
||||
{
|
||||
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
|
||||
|
||||
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
|
||||
try
|
||||
{
|
||||
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
return new UdpSocket(retVal, localPort, IPAddress.Any);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (retVal != null)
|
||||
retVal.Dispose();
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new UDP socket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
|
||||
/// </summary>
|
||||
/// <returns>An implementation of the <see cref="IUdpSocket"/> interface used by RSSDP components to perform socket operations.</returns>
|
||||
public IUdpSocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort)
|
||||
{
|
||||
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
|
||||
|
||||
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
|
||||
try
|
||||
{
|
||||
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
|
||||
|
||||
var localIp = NetworkManager.ToIPAddress(localIpAddress);
|
||||
|
||||
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIp));
|
||||
return new UdpSocket(retVal, localPort, localIp);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (retVal != null)
|
||||
retVal.Dispose();
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new UDP socket that is a member of the specified multicast IP address, and binds it to the specified local port.
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">The multicast IP address to make the socket a member of.</param>
|
||||
/// <param name="multicastTimeToLive">The multicast time to live value for the socket.</param>
|
||||
/// <param name="localPort">The number of the local port to bind to.</param>
|
||||
/// <returns></returns>
|
||||
public IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort)
|
||||
{
|
||||
if (ipAddress == null) throw new ArgumentNullException("ipAddress");
|
||||
if (ipAddress.Length == 0) throw new ArgumentException("ipAddress cannot be an empty string.", "ipAddress");
|
||||
if (multicastTimeToLive <= 0) throw new ArgumentException("multicastTimeToLive cannot be zero or less.", "multicastTimeToLive");
|
||||
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
|
||||
|
||||
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
|
||||
|
||||
try
|
||||
{
|
||||
#if NET46
|
||||
retVal.ExclusiveAddressUse = false;
|
||||
#else
|
||||
// The ExclusiveAddressUse socket option is a Windows-specific option that, when set to "true," tells Windows not to allow another socket to use the same local address as this socket
|
||||
// See https://github.com/dotnet/corefx/pull/11509 for more details
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
|
||||
{
|
||||
retVal.ExclusiveAddressUse = false;
|
||||
}
|
||||
#endif
|
||||
//retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
|
||||
|
||||
var localIp = IPAddress.Any;
|
||||
|
||||
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(ipAddress), localIp));
|
||||
retVal.MulticastLoopback = true;
|
||||
|
||||
return new UdpSocket(retVal, localPort, localIp);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (retVal != null)
|
||||
retVal.Dispose();
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
242
Emby.Common.Implementations/Net/UdpSocket.cs
Normal file
242
Emby.Common.Implementations/Net/UdpSocket.cs
Normal file
@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Common.Implementations.Networking;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
||||
namespace Emby.Common.Implementations.Net
|
||||
{
|
||||
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
|
||||
// Be careful to check any changes compile and work for all platform projects it is shared in.
|
||||
|
||||
internal sealed class UdpSocket : DisposableManagedObjectBase, IUdpSocket
|
||||
{
|
||||
|
||||
#region Fields
|
||||
|
||||
private Socket _Socket;
|
||||
private int _LocalPort;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public UdpSocket(Socket socket, int localPort, IPAddress ip)
|
||||
{
|
||||
if (socket == null) throw new ArgumentNullException("socket");
|
||||
|
||||
_Socket = socket;
|
||||
_LocalPort = localPort;
|
||||
LocalIPAddress = NetworkManager.ToIpAddressInfo(ip);
|
||||
|
||||
_Socket.Bind(new IPEndPoint(ip, _LocalPort));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public IpAddressInfo LocalIPAddress
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#region IUdpSocket Members
|
||||
|
||||
public Task<SocketReceiveResult> ReceiveAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var tcs = new TaskCompletionSource<SocketReceiveResult>();
|
||||
|
||||
EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);
|
||||
state.TaskCompletionSource = tcs;
|
||||
|
||||
#if NETSTANDARD1_6
|
||||
_Socket.ReceiveFromAsync(new ArraySegment<Byte>(state.Buffer),SocketFlags.None, state.RemoteEndPoint)
|
||||
.ContinueWith((task, asyncState) =>
|
||||
{
|
||||
if (task.Status != TaskStatus.Faulted)
|
||||
{
|
||||
var receiveState = asyncState as AsyncReceiveState;
|
||||
receiveState.RemoteEndPoint = task.Result.RemoteEndPoint;
|
||||
ProcessResponse(receiveState, () => task.Result.ReceivedBytes, LocalIPAddress);
|
||||
}
|
||||
}, state);
|
||||
#else
|
||||
_Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state);
|
||||
#endif
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public Task SendAsync(byte[] buffer, int size, IpEndPointInfo endPoint)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (buffer == null) throw new ArgumentNullException("messageData");
|
||||
if (endPoint == null) throw new ArgumentNullException("endPoint");
|
||||
|
||||
var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint);
|
||||
|
||||
#if NETSTANDARD1_6
|
||||
|
||||
if (size != buffer.Length)
|
||||
{
|
||||
byte[] copy = new byte[size];
|
||||
Buffer.BlockCopy(buffer, 0, copy, 0, size);
|
||||
buffer = copy;
|
||||
}
|
||||
|
||||
_Socket.SendTo(buffer, ipEndPoint);
|
||||
return Task.FromResult(true);
|
||||
#else
|
||||
var taskSource = new TaskCompletionSource<bool>();
|
||||
|
||||
try
|
||||
{
|
||||
_Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, ipEndPoint, result =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_Socket.EndSend(result);
|
||||
taskSource.TrySetResult(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
taskSource.TrySetException(ex);
|
||||
}
|
||||
|
||||
}, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
taskSource.TrySetException(ex);
|
||||
}
|
||||
|
||||
//_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port));
|
||||
|
||||
return taskSource.Task;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
var socket = _Socket;
|
||||
if (socket != null)
|
||||
socket.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void ProcessResponse(AsyncReceiveState state, Func<int> receiveData, IpAddressInfo localIpAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytesRead = receiveData();
|
||||
|
||||
var ipEndPoint = state.RemoteEndPoint as IPEndPoint;
|
||||
state.TaskCompletionSource.SetResult(
|
||||
new SocketReceiveResult
|
||||
{
|
||||
Buffer = state.Buffer,
|
||||
ReceivedBytes = bytesRead,
|
||||
RemoteEndPoint = ToIpEndPointInfo(ipEndPoint),
|
||||
LocalIPAddress = localIpAddress
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
state.TaskCompletionSource.SetCanceled();
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown)
|
||||
state.TaskCompletionSource.SetException(se);
|
||||
else
|
||||
state.TaskCompletionSource.SetCanceled();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state.TaskCompletionSource.SetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
|
||||
{
|
||||
if (endpoint == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return NetworkManager.ToIpEndPointInfo(endpoint);
|
||||
}
|
||||
|
||||
private void ProcessResponse(IAsyncResult asyncResult)
|
||||
{
|
||||
#if NET46
|
||||
var state = asyncResult.AsyncState as AsyncReceiveState;
|
||||
try
|
||||
{
|
||||
var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint);
|
||||
|
||||
var ipEndPoint = state.RemoteEndPoint as IPEndPoint;
|
||||
state.TaskCompletionSource.SetResult(
|
||||
new SocketReceiveResult
|
||||
{
|
||||
Buffer = state.Buffer,
|
||||
ReceivedBytes = bytesRead,
|
||||
RemoteEndPoint = ToIpEndPointInfo(ipEndPoint),
|
||||
LocalIPAddress = LocalIPAddress
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
state.TaskCompletionSource.SetCanceled();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state.TaskCompletionSource.SetException(ex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Classes
|
||||
|
||||
private class AsyncReceiveState
|
||||
{
|
||||
public AsyncReceiveState(Socket socket, EndPoint remoteEndPoint)
|
||||
{
|
||||
this.Socket = socket;
|
||||
this.RemoteEndPoint = remoteEndPoint;
|
||||
}
|
||||
|
||||
public EndPoint RemoteEndPoint;
|
||||
public byte[] Buffer = new byte[8192];
|
||||
|
||||
public Socket Socket { get; private set; }
|
||||
|
||||
public TaskCompletionSource<SocketReceiveResult> TaskCompletionSource { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
@ -6,28 +6,28 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using MoreLinq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Networking
|
||||
namespace Emby.Common.Implementations.Networking
|
||||
{
|
||||
public abstract class BaseNetworkManager
|
||||
public class NetworkManager : INetworkManager
|
||||
{
|
||||
protected ILogger Logger { get; private set; }
|
||||
private DateTime _lastRefresh;
|
||||
|
||||
protected BaseNetworkManager(ILogger logger)
|
||||
public NetworkManager(ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private List<IPAddress> _localIpAddresses;
|
||||
private List<IpAddressInfo> _localIpAddresses;
|
||||
private readonly object _localIpAddressSyncLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the machine's local ip address
|
||||
/// </summary>
|
||||
/// <returns>IPAddress.</returns>
|
||||
public IEnumerable<IPAddress> GetLocalIpAddresses()
|
||||
public List<IpAddressInfo> GetLocalIpAddresses()
|
||||
{
|
||||
const int cacheMinutes = 5;
|
||||
|
||||
@ -37,7 +37,7 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
|
||||
if (_localIpAddresses == null || forceRefresh)
|
||||
{
|
||||
var addresses = GetLocalIpAddressesInternal().ToList();
|
||||
var addresses = GetLocalIpAddressesInternal().Select(ToIpAddressInfo).ToList();
|
||||
|
||||
_localIpAddresses = addresses;
|
||||
_lastRefresh = DateTime.UtcNow;
|
||||
@ -56,7 +56,7 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
list.AddRange(GetLocalIpAddressesFallback());
|
||||
list.AddRange(GetLocalIpAddressesFallback().Result);
|
||||
}
|
||||
|
||||
return list.Where(FilterIpAddress).DistinctBy(i => i.ToString());
|
||||
@ -64,7 +64,7 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
|
||||
private bool FilterIpAddress(IPAddress address)
|
||||
{
|
||||
var addressString = address.ToString ();
|
||||
var addressString = address.ToString();
|
||||
|
||||
if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@ -170,7 +170,7 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
var host = uri.DnsSafeHost;
|
||||
Logger.Debug("Resolving host {0}", host);
|
||||
|
||||
address = GetIpAddresses(host).FirstOrDefault();
|
||||
address = GetIpAddresses(host).Result.FirstOrDefault();
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
@ -193,18 +193,28 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<IPAddress> GetIpAddresses(string hostName)
|
||||
private Task<IPAddress[]> GetIpAddresses(string hostName)
|
||||
{
|
||||
return Dns.GetHostAddresses(hostName);
|
||||
return Dns.GetHostAddressesAsync(hostName);
|
||||
}
|
||||
|
||||
private readonly List<NetworkInterfaceType> _validNetworkInterfaceTypes = new List<NetworkInterfaceType>
|
||||
{
|
||||
NetworkInterfaceType.Ethernet,
|
||||
NetworkInterfaceType.Wireless80211
|
||||
};
|
||||
|
||||
private List<IPAddress> GetIPsDefault()
|
||||
{
|
||||
NetworkInterface[] interfaces;
|
||||
|
||||
try
|
||||
{
|
||||
interfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
var validStatuses = new[] { OperationalStatus.Up, OperationalStatus.Unknown };
|
||||
|
||||
interfaces = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(i => validStatuses.Contains(i.OperationalStatus))
|
||||
.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -212,16 +222,30 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
return new List<IPAddress>();
|
||||
}
|
||||
|
||||
return interfaces.SelectMany(network => {
|
||||
return interfaces.SelectMany(network =>
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
|
||||
|
||||
var properties = network.GetIPProperties();
|
||||
var ipProperties = network.GetIPProperties();
|
||||
|
||||
return properties.UnicastAddresses
|
||||
.Where(i => i.IsDnsEligible)
|
||||
// Try to exclude virtual adapters
|
||||
// http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms
|
||||
var addr = ipProperties.GatewayAddresses.FirstOrDefault();
|
||||
if (addr == null|| string.Equals(addr.Address.ToString(), "0.0.0.0", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new List<IPAddress>();
|
||||
}
|
||||
|
||||
//if (!_validNetworkInterfaceTypes.Contains(network.NetworkInterfaceType))
|
||||
//{
|
||||
// return new List<IPAddress>();
|
||||
//}
|
||||
|
||||
return ipProperties.UnicastAddresses
|
||||
//.Where(i => i.IsDnsEligible)
|
||||
.Select(i => i.Address)
|
||||
.Where(i => i.AddressFamily == AddressFamily.InterNetwork)
|
||||
.ToList();
|
||||
@ -236,9 +260,9 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<IPAddress> GetLocalIpAddressesFallback()
|
||||
private async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback()
|
||||
{
|
||||
var host = Dns.GetHostEntry(Dns.GetHostName());
|
||||
var host = await Dns.GetHostEntryAsync(Dns.GetHostName()).ConfigureAwait(false);
|
||||
|
||||
// Reverse them because the last one is usually the correct one
|
||||
// It's not fool-proof so ultimately the consumer will have to examine them and decide
|
||||
@ -279,7 +303,7 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
/// <returns>IPEndPoint.</returns>
|
||||
public IPEndPoint Parse(string endpointstring)
|
||||
{
|
||||
return Parse(endpointstring, -1);
|
||||
return Parse(endpointstring, -1).Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -290,7 +314,7 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
/// <returns>IPEndPoint.</returns>
|
||||
/// <exception cref="System.ArgumentException">Endpoint descriptor may not be empty.</exception>
|
||||
/// <exception cref="System.FormatException"></exception>
|
||||
private static IPEndPoint Parse(string endpointstring, int defaultport)
|
||||
private static async Task<IPEndPoint> Parse(string endpointstring, int defaultport)
|
||||
{
|
||||
if (String.IsNullOrEmpty(endpointstring)
|
||||
|| endpointstring.Trim().Length == 0)
|
||||
@ -316,7 +340,7 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
|
||||
//try to use the address as IPv4, otherwise get hostname
|
||||
if (!IPAddress.TryParse(values[0], out ipaddy))
|
||||
ipaddy = GetIPfromHost(values[0]);
|
||||
ipaddy = await GetIPfromHost(values[0]).ConfigureAwait(false);
|
||||
}
|
||||
else if (values.Length > 2) //ipv6
|
||||
{
|
||||
@ -372,14 +396,130 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||
/// <param name="p">The p.</param>
|
||||
/// <returns>IPAddress.</returns>
|
||||
/// <exception cref="System.ArgumentException"></exception>
|
||||
private static IPAddress GetIPfromHost(string p)
|
||||
private static async Task<IPAddress> GetIPfromHost(string p)
|
||||
{
|
||||
var hosts = Dns.GetHostAddresses(p);
|
||||
var hosts = await Dns.GetHostAddressesAsync(p).ConfigureAwait(false);
|
||||
|
||||
if (hosts == null || hosts.Length == 0)
|
||||
throw new ArgumentException(String.Format("Host not found: {0}", p));
|
||||
|
||||
return hosts[0];
|
||||
}
|
||||
|
||||
public IpAddressInfo ParseIpAddress(string ipAddress)
|
||||
{
|
||||
IpAddressInfo info;
|
||||
if (TryParseIpAddress(ipAddress, out info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Invalid ip address: " + ipAddress);
|
||||
}
|
||||
|
||||
public bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo)
|
||||
{
|
||||
IPAddress address;
|
||||
if (IPAddress.TryParse(ipAddress, out address))
|
||||
{
|
||||
ipAddressInfo = ToIpAddressInfo(address);
|
||||
return true;
|
||||
}
|
||||
|
||||
ipAddressInfo = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
|
||||
{
|
||||
if (endpoint == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IpEndPointInfo(ToIpAddressInfo(endpoint.Address), endpoint.Port);
|
||||
}
|
||||
|
||||
public static IPEndPoint ToIPEndPoint(IpEndPointInfo endpoint)
|
||||
{
|
||||
if (endpoint == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IPEndPoint(ToIPAddress(endpoint.IpAddress), endpoint.Port);
|
||||
}
|
||||
|
||||
public static IPAddress ToIPAddress(IpAddressInfo address)
|
||||
{
|
||||
if (address.Equals(IpAddressInfo.Any))
|
||||
{
|
||||
return IPAddress.Any;
|
||||
}
|
||||
if (address.Equals(IpAddressInfo.IPv6Any))
|
||||
{
|
||||
return IPAddress.IPv6Any;
|
||||
}
|
||||
if (address.Equals(IpAddressInfo.Loopback))
|
||||
{
|
||||
return IPAddress.Loopback;
|
||||
}
|
||||
if (address.Equals(IpAddressInfo.IPv6Loopback))
|
||||
{
|
||||
return IPAddress.IPv6Loopback;
|
||||
}
|
||||
|
||||
return IPAddress.Parse(address.Address);
|
||||
}
|
||||
|
||||
public static IpAddressInfo ToIpAddressInfo(IPAddress address)
|
||||
{
|
||||
if (address.Equals(IPAddress.Any))
|
||||
{
|
||||
return IpAddressInfo.Any;
|
||||
}
|
||||
if (address.Equals(IPAddress.IPv6Any))
|
||||
{
|
||||
return IpAddressInfo.IPv6Any;
|
||||
}
|
||||
if (address.Equals(IPAddress.Loopback))
|
||||
{
|
||||
return IpAddressInfo.Loopback;
|
||||
}
|
||||
if (address.Equals(IPAddress.IPv6Loopback))
|
||||
{
|
||||
return IpAddressInfo.IPv6Loopback;
|
||||
}
|
||||
return new IpAddressInfo
|
||||
{
|
||||
Address = address.ToString(),
|
||||
AddressFamily = address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IpAddressInfo[]> GetHostAddressesAsync(string host)
|
||||
{
|
||||
var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);
|
||||
return addresses.Select(ToIpAddressInfo).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network shares.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>IEnumerable{NetworkShare}.</returns>
|
||||
public virtual IEnumerable<NetworkShare> GetNetworkShares(string path)
|
||||
{
|
||||
return new List<NetworkShare>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets available devices within the domain
|
||||
/// </summary>
|
||||
/// <returns>PC's in the Domain</returns>
|
||||
public virtual IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
|
||||
{
|
||||
return new List<FileSystemEntryInfo>();
|
||||
}
|
||||
}
|
||||
}
|
19
Emby.Common.Implementations/Properties/AssemblyInfo.cs
Normal file
19
Emby.Common.Implementations/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,19 @@
|
||||
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: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Emby.Common.Implementations")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("5a27010a-09c6-4e86-93ea-437484c10917")]
|
31
Emby.Common.Implementations/Reflection/AssemblyInfo.cs
Normal file
31
Emby.Common.Implementations/Reflection/AssemblyInfo.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using MediaBrowser.Model.Reflection;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Emby.Common.Implementations.Reflection
|
||||
{
|
||||
public class AssemblyInfo : IAssemblyInfo
|
||||
{
|
||||
public Stream GetManifestResourceStream(Type type, string resource)
|
||||
{
|
||||
#if NET46
|
||||
return type.Assembly.GetManifestResourceStream(resource);
|
||||
#endif
|
||||
return type.GetTypeInfo().Assembly.GetManifestResourceStream(resource);
|
||||
}
|
||||
|
||||
public string[] GetManifestResourceNames(Type type)
|
||||
{
|
||||
#if NET46
|
||||
return type.Assembly.GetManifestResourceNames();
|
||||
#endif
|
||||
return type.GetTypeInfo().Assembly.GetManifestResourceNames();
|
||||
}
|
||||
|
||||
public Assembly[] GetCurrentAssemblies()
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.ScheduledTasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a task trigger that fires everyday
|
@ -1,11 +1,11 @@
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.ScheduledTasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a task trigger that runs repeatedly on an interval
|
@ -1,20 +1,20 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ScheduledTaskWorker
|
||||
@ -53,6 +53,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// <value>The task manager.</value>
|
||||
private ITaskManager TaskManager { get; set; }
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ISystemEvents _systemEvents;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
|
||||
@ -73,7 +74,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// or
|
||||
/// logger
|
||||
/// </exception>
|
||||
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
|
||||
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem, ISystemEvents systemEvents)
|
||||
{
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
@ -102,6 +103,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
JsonSerializer = jsonSerializer;
|
||||
Logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
_systemEvents = systemEvents;
|
||||
|
||||
InitTriggerEvents();
|
||||
}
|
||||
@ -232,13 +234,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// <summary>
|
||||
/// The _triggers
|
||||
/// </summary>
|
||||
private List<ITaskTrigger> _triggers;
|
||||
private Tuple<TaskTriggerInfo,ITaskTrigger>[] _triggers;
|
||||
/// <summary>
|
||||
/// Gets the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <value>The triggers.</value>
|
||||
/// <exception cref="System.ArgumentNullException">value</exception>
|
||||
public IEnumerable<ITaskTrigger> Triggers
|
||||
private Tuple<TaskTriggerInfo, ITaskTrigger>[] InternalTriggers
|
||||
{
|
||||
get
|
||||
{
|
||||
@ -257,11 +258,33 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
DisposeTriggers();
|
||||
}
|
||||
|
||||
_triggers = value.ToList();
|
||||
_triggers = value.ToArray();
|
||||
|
||||
ReloadTriggerEvents(false);
|
||||
}
|
||||
}
|
||||
|
||||
SaveTriggers(_triggers);
|
||||
/// <summary>
|
||||
/// Gets the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <value>The triggers.</value>
|
||||
/// <exception cref="System.ArgumentNullException">value</exception>
|
||||
public TaskTriggerInfo[] Triggers
|
||||
{
|
||||
get
|
||||
{
|
||||
return InternalTriggers.Select(i => i.Item1).ToArray();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
SaveTriggers(value);
|
||||
|
||||
InternalTriggers = value.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,8 +327,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
|
||||
private void ReloadTriggerEvents(bool isApplicationStartup)
|
||||
{
|
||||
foreach (var trigger in Triggers)
|
||||
foreach (var triggerInfo in InternalTriggers)
|
||||
{
|
||||
var trigger = triggerInfo.Item2;
|
||||
|
||||
trigger.Stop();
|
||||
|
||||
trigger.Triggered -= trigger_Triggered;
|
||||
@ -362,6 +387,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
finally
|
||||
{
|
||||
_currentTask = null;
|
||||
GC.Collect();
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,23 +533,29 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// Loads the triggers.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
private List<ITaskTrigger> LoadTriggers()
|
||||
private Tuple<TaskTriggerInfo, ITaskTrigger>[] LoadTriggers()
|
||||
{
|
||||
var settings = LoadTriggerSettings();
|
||||
|
||||
return settings.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
|
||||
}
|
||||
|
||||
private TaskTriggerInfo[] LoadTriggerSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath())
|
||||
.Select(ScheduledTaskHelpers.GetTrigger)
|
||||
.ToList();
|
||||
.ToArray();
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// File doesn't exist. No biggie. Return defaults.
|
||||
return ScheduledTask.GetDefaultTriggers().ToList();
|
||||
return ScheduledTask.GetDefaultTriggers().ToArray();
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
// File doesn't exist. No biggie. Return defaults.
|
||||
return ScheduledTask.GetDefaultTriggers().ToList();
|
||||
return ScheduledTask.GetDefaultTriggers().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,13 +563,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// Saves the triggers.
|
||||
/// </summary>
|
||||
/// <param name="triggers">The triggers.</param>
|
||||
private void SaveTriggers(IEnumerable<ITaskTrigger> triggers)
|
||||
private void SaveTriggers(TaskTriggerInfo[] triggers)
|
||||
{
|
||||
var path = GetConfigurationFilePath();
|
||||
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
JsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), path);
|
||||
JsonSerializer.SerializeToFile(triggers, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -561,11 +593,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
Id = Id
|
||||
};
|
||||
|
||||
var hasKey = ScheduledTask as IHasKey;
|
||||
if (hasKey != null)
|
||||
{
|
||||
result.Key = hasKey.Key;
|
||||
}
|
||||
result.Key = ScheduledTask.Key;
|
||||
|
||||
if (ex != null)
|
||||
{
|
||||
@ -655,13 +683,98 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a TaskTriggerInfo into a concrete BaseTaskTrigger
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns>BaseTaskTrigger.</returns>
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
/// <exception cref="System.ArgumentException">Invalid trigger type: + info.Type</exception>
|
||||
private ITaskTrigger GetTrigger(TaskTriggerInfo info)
|
||||
{
|
||||
var options = new TaskExecutionOptions
|
||||
{
|
||||
MaxRuntimeMs = info.MaxRuntimeMs
|
||||
};
|
||||
|
||||
if (info.Type.Equals(typeof(DailyTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.TimeOfDayTicks.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new DailyTrigger
|
||||
{
|
||||
TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(WeeklyTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.TimeOfDayTicks.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
if (!info.DayOfWeek.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new WeeklyTrigger
|
||||
{
|
||||
TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
|
||||
DayOfWeek = info.DayOfWeek.Value,
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(IntervalTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.IntervalTicks.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new IntervalTrigger
|
||||
{
|
||||
Interval = TimeSpan.FromTicks(info.IntervalTicks.Value),
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(SystemEventTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.SystemEvent.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new SystemEventTrigger(_systemEvents)
|
||||
{
|
||||
SystemEvent = info.SystemEvent.Value,
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(StartupTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new StartupTrigger();
|
||||
}
|
||||
|
||||
throw new ArgumentException("Unrecognized trigger type: " + info.Type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes each trigger
|
||||
/// </summary>
|
||||
private void DisposeTriggers()
|
||||
{
|
||||
foreach (var trigger in Triggers)
|
||||
foreach (var triggerInfo in InternalTriggers)
|
||||
{
|
||||
var trigger = triggerInfo.Item2;
|
||||
trigger.Triggered -= trigger_Triggered;
|
||||
trigger.Stop();
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.ScheduledTasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class StartupTaskTrigger
|
@ -1,11 +1,11 @@
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.ScheduledTasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class SystemEventTrigger
|
||||
@ -26,6 +26,13 @@ namespace MediaBrowser.Common.ScheduledTasks
|
||||
/// </value>
|
||||
public TaskExecutionOptions TaskOptions { get; set; }
|
||||
|
||||
private readonly ISystemEvents _systemEvents;
|
||||
|
||||
public SystemEventTrigger(ISystemEvents systemEvents)
|
||||
{
|
||||
_systemEvents = systemEvents;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stars waiting for the trigger action
|
||||
/// </summary>
|
||||
@ -36,33 +43,28 @@ namespace MediaBrowser.Common.ScheduledTasks
|
||||
switch (SystemEvent)
|
||||
{
|
||||
case SystemEvent.WakeFromSleep:
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
_systemEvents.Resume += _systemEvents_Resume;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void _systemEvents_Resume(object sender, EventArgs e)
|
||||
{
|
||||
if (SystemEvent == SystemEvent.WakeFromSleep)
|
||||
{
|
||||
// This value is a bit arbitrary, but add a delay to help ensure network connections have been restored before running the task
|
||||
await Task.Delay(10000).ConfigureAwait(false);
|
||||
|
||||
OnTriggered();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops waiting for the trigger action
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the PowerModeChanged event of the SystemEvents control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="PowerModeChangedEventArgs" /> instance containing the event data.</param>
|
||||
async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
{
|
||||
if (e.Mode == PowerModes.Resume && SystemEvent == SystemEvent.WakeFromSleep)
|
||||
{
|
||||
// This value is a bit arbitrary, but add a delay to help ensure network connections have been restored before running the task
|
||||
await Task.Delay(10000).ConfigureAwait(false);
|
||||
|
||||
OnTriggered();
|
||||
}
|
||||
_systemEvents.Resume -= _systemEvents_Resume;
|
||||
}
|
||||
|
||||
/// <summary>
|
@ -1,6 +1,5 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
@ -10,10 +9,10 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using Microsoft.Win32;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.System;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class TaskManager
|
||||
@ -47,6 +46,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// <value>The application paths.</value>
|
||||
private IApplicationPaths ApplicationPaths { get; set; }
|
||||
|
||||
private readonly ISystemEvents _systemEvents;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger.
|
||||
/// </summary>
|
||||
@ -54,25 +55,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
private ILogger Logger { get; set; }
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private bool _suspendTriggers;
|
||||
|
||||
public bool SuspendTriggers
|
||||
{
|
||||
get { return _suspendTriggers; }
|
||||
set
|
||||
{
|
||||
Logger.Info("Setting SuspendTriggers to {0}", value);
|
||||
var executeQueued = _suspendTriggers && !value;
|
||||
|
||||
_suspendTriggers = value;
|
||||
|
||||
if (executeQueued)
|
||||
{
|
||||
ExecuteQueuedTasks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TaskManager" /> class.
|
||||
/// </summary>
|
||||
@ -80,29 +62,23 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// <param name="jsonSerializer">The json serializer.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <exception cref="System.ArgumentException">kernel</exception>
|
||||
public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
|
||||
public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem, ISystemEvents systemEvents)
|
||||
{
|
||||
ApplicationPaths = applicationPaths;
|
||||
JsonSerializer = jsonSerializer;
|
||||
Logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
_systemEvents = systemEvents;
|
||||
|
||||
ScheduledTasks = new IScheduledTaskWorker[] { };
|
||||
}
|
||||
|
||||
private void BindToSystemEvent()
|
||||
{
|
||||
try
|
||||
{
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
_systemEvents.Resume += _systemEvents_Resume;
|
||||
}
|
||||
|
||||
void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
private void _systemEvents_Resume(object sender, EventArgs e)
|
||||
{
|
||||
foreach (var task in ScheduledTasks)
|
||||
{
|
||||
@ -235,7 +211,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
|
||||
lock (_taskQueue)
|
||||
{
|
||||
if (task.State == TaskState.Idle && !SuspendTriggers)
|
||||
if (task.State == TaskState.Idle)
|
||||
{
|
||||
Execute(task, options);
|
||||
return;
|
||||
@ -254,7 +230,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
var myTasks = ScheduledTasks.ToList();
|
||||
|
||||
var list = tasks.ToList();
|
||||
myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, _fileSystem)));
|
||||
myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, _fileSystem, _systemEvents)));
|
||||
|
||||
ScheduledTasks = myTasks.ToArray();
|
||||
|
||||
@ -327,11 +303,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
/// </summary>
|
||||
private void ExecuteQueuedTasks()
|
||||
{
|
||||
if (SuspendTriggers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info("ExecuteQueuedTasks");
|
||||
|
||||
// Execute queued tasks
|
@ -1,15 +1,15 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes old cache files
|
||||
@ -40,13 +40,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
/// Creates the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
public IEnumerable<ITaskTrigger> GetDefaultTriggers()
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
// Until we can vary these default triggers per server and MBT, we need something that makes sense for both
|
||||
return new ITaskTrigger[] {
|
||||
return new[] {
|
||||
|
||||
// Every so often
|
||||
new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
|
||||
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
|
||||
};
|
||||
}
|
||||
|
||||
@ -168,6 +167,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
get { return "Cache file cleanup"; }
|
||||
}
|
||||
|
||||
public string Key
|
||||
{
|
||||
get { return "DeleteCacheFiles"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
@ -202,5 +206,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsLogged
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes old log files
|
||||
@ -36,13 +36,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
/// Creates the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
public IEnumerable<ITaskTrigger> GetDefaultTriggers()
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
// Until we can vary these default triggers per server and MBT, we need something that makes sense for both
|
||||
return new ITaskTrigger[] {
|
||||
return new[] {
|
||||
|
||||
// Every so often
|
||||
new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
|
||||
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
|
||||
};
|
||||
}
|
||||
|
||||
@ -82,6 +81,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public string Key
|
||||
{
|
||||
get { return "CleanLogFiles"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the task
|
||||
/// </summary>
|
||||
@ -125,5 +129,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsLogged
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ReloadLoggerFileTask
|
||||
@ -39,9 +39,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
/// Gets the default triggers.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
public IEnumerable<ITaskTrigger> GetDefaultTriggers()
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(0) }; //12am
|
||||
var trigger = new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = TimeSpan.FromHours(0).Ticks }; //12am
|
||||
|
||||
return new[] { trigger };
|
||||
}
|
||||
@ -74,6 +74,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
get { return "Start new log file"; }
|
||||
}
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
@ -101,5 +103,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsLogged
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.ScheduledTasks
|
||||
namespace Emby.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a task trigger that fires on a weekly basis
|
@ -1,10 +1,10 @@
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Serialization
|
||||
namespace Emby.Common.Implementations.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a wrapper around third party json serialization.
|
||||
@ -60,7 +60,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
||||
throw new ArgumentNullException("file");
|
||||
}
|
||||
|
||||
using (Stream stream = _fileSystem.GetFileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||
using (Stream stream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
|
||||
{
|
||||
SerializeToStream(obj, stream);
|
||||
}
|
@ -4,20 +4,22 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using CommonIO;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Serialization
|
||||
namespace Emby.Common.Implementations.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a wrapper around third party xml serialization.
|
||||
/// </summary>
|
||||
public class XmlSerializer : IXmlSerializer
|
||||
public class MyXmlSerializer : IXmlSerializer
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public XmlSerializer(IFileSystem fileSystem, ILogger logger)
|
||||
public MyXmlSerializer(IFileSystem fileSystem, ILogger logger)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_logger = logger;
|
||||
@ -25,18 +27,18 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
||||
|
||||
// Need to cache these
|
||||
// http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
|
||||
private readonly Dictionary<string, System.Xml.Serialization.XmlSerializer> _serializers =
|
||||
new Dictionary<string, System.Xml.Serialization.XmlSerializer>();
|
||||
private readonly Dictionary<string, XmlSerializer> _serializers =
|
||||
new Dictionary<string, XmlSerializer>();
|
||||
|
||||
private System.Xml.Serialization.XmlSerializer GetSerializer(Type type)
|
||||
private XmlSerializer GetSerializer(Type type)
|
||||
{
|
||||
var key = type.FullName;
|
||||
lock (_serializers)
|
||||
{
|
||||
System.Xml.Serialization.XmlSerializer serializer;
|
||||
XmlSerializer serializer;
|
||||
if (!_serializers.TryGetValue(key, out serializer))
|
||||
{
|
||||
serializer = new System.Xml.Serialization.XmlSerializer(type);
|
||||
serializer = new XmlSerializer(type);
|
||||
_serializers[key] = serializer;
|
||||
}
|
||||
return serializer;
|
||||
@ -48,9 +50,8 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
||||
/// </summary>
|
||||
/// <param name="obj">The obj.</param>
|
||||
/// <param name="writer">The writer.</param>
|
||||
private void SerializeToWriter(object obj, XmlTextWriter writer)
|
||||
private void SerializeToWriter(object obj, XmlWriter writer)
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
var netSerializer = GetSerializer(obj.GetType());
|
||||
netSerializer.Serialize(writer, obj);
|
||||
}
|
||||
@ -63,7 +64,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
||||
/// <returns>System.Object.</returns>
|
||||
public object DeserializeFromStream(Type type, Stream stream)
|
||||
{
|
||||
using (var reader = new XmlTextReader(stream))
|
||||
using (var reader = XmlReader.Create(stream))
|
||||
{
|
||||
var netSerializer = GetSerializer(type);
|
||||
return netSerializer.Deserialize(reader);
|
||||
@ -77,10 +78,18 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
||||
/// <param name="stream">The stream.</param>
|
||||
public void SerializeToStream(object obj, Stream stream)
|
||||
{
|
||||
#if NET46
|
||||
using (var writer = new XmlTextWriter(stream, null))
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
SerializeToWriter(obj, writer);
|
||||
}
|
||||
#else
|
||||
using (var writer = XmlWriter.Create(stream))
|
||||
{
|
||||
SerializeToWriter(obj, writer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
43
Emby.Common.Implementations/TextEncoding/TextEncoding.cs
Normal file
43
Emby.Common.Implementations/TextEncoding/TextEncoding.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System.Text;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Text;
|
||||
|
||||
namespace Emby.Common.Implementations.TextEncoding
|
||||
{
|
||||
public class TextEncoding : ITextEncoding
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public TextEncoding(IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public Encoding GetASCIIEncoding()
|
||||
{
|
||||
return Encoding.ASCII;
|
||||
}
|
||||
|
||||
public Encoding GetFileEncoding(string srcFile)
|
||||
{
|
||||
// *** Detect byte order mark if any - otherwise assume default
|
||||
var buffer = new byte[5];
|
||||
|
||||
using (var file = _fileSystem.OpenRead(srcFile))
|
||||
{
|
||||
file.Read(buffer, 0, 5);
|
||||
}
|
||||
|
||||
if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
|
||||
return Encoding.UTF8;
|
||||
if (buffer[0] == 0xfe && buffer[1] == 0xff)
|
||||
return Encoding.Unicode;
|
||||
if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
|
||||
return Encoding.UTF32;
|
||||
if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
|
||||
return Encoding.UTF7;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
39
Emby.Common.Implementations/Threading/CommonTimer.cs
Normal file
39
Emby.Common.Implementations/Threading/CommonTimer.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace Emby.Common.Implementations.Threading
|
||||
{
|
||||
public class CommonTimer : ITimer
|
||||
{
|
||||
private readonly Timer _timer;
|
||||
|
||||
public CommonTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
|
||||
{
|
||||
_timer = new Timer(new TimerCallback(callback), state, dueTime, period);
|
||||
}
|
||||
|
||||
public CommonTimer(Action<object> callback, object state, int dueTimeMs, int periodMs)
|
||||
{
|
||||
_timer = new Timer(new TimerCallback(callback), state, dueTimeMs, periodMs);
|
||||
}
|
||||
|
||||
public void Change(TimeSpan dueTime, TimeSpan period)
|
||||
{
|
||||
_timer.Change(dueTime, period);
|
||||
}
|
||||
|
||||
public void Change(int dueTimeMs, int periodMs)
|
||||
{
|
||||
_timer.Change(dueTimeMs, periodMs);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_timer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
21
Emby.Common.Implementations/Threading/TimerFactory.cs
Normal file
21
Emby.Common.Implementations/Threading/TimerFactory.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace Emby.Common.Implementations.Threading
|
||||
{
|
||||
public class TimerFactory : ITimerFactory
|
||||
{
|
||||
public ITimer Create(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
|
||||
{
|
||||
return new CommonTimer(callback, state, dueTime, period);
|
||||
}
|
||||
|
||||
public ITimer Create(Action<object> callback, object state, int dueTimeMs, int periodMs)
|
||||
{
|
||||
return new CommonTimer(callback, state, dueTimeMs, periodMs);
|
||||
}
|
||||
}
|
||||
}
|
22
Emby.Common.Implementations/Xml/XmlReaderSettingsFactory.cs
Normal file
22
Emby.Common.Implementations/Xml/XmlReaderSettingsFactory.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Xml;
|
||||
using MediaBrowser.Model.Xml;
|
||||
|
||||
namespace Emby.Common.Implementations.Xml
|
||||
{
|
||||
public class XmlReaderSettingsFactory : IXmlReaderSettingsFactory
|
||||
{
|
||||
public XmlReaderSettings Create(bool enableValidation)
|
||||
{
|
||||
var settings = new XmlReaderSettings();
|
||||
|
||||
if (!enableValidation)
|
||||
{
|
||||
#if NET46
|
||||
settings.ValidationType = ValidationType.None;
|
||||
#endif
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
}
|
71
Emby.Common.Implementations/project.json
Normal file
71
Emby.Common.Implementations/project.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"version": "1.0.0-*",
|
||||
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
"net46": {
|
||||
"frameworkAssemblies": {
|
||||
"System.Collections": "4.0.0.0",
|
||||
"System.IO": "4.0.0.0",
|
||||
"System.Net": "4.0.0.0",
|
||||
"System.Net.Http": "4.0.0.0",
|
||||
"System.Net.Primitives": "4.0.0.0",
|
||||
"System.Net.Http.WebRequest": "4.0.0.0",
|
||||
"System.Reflection": "4.0.0.0",
|
||||
"System.Runtime": "4.0.0.0",
|
||||
"System.Runtime.Extensions": "4.0.0.0",
|
||||
"System.Text.Encoding": "4.0.0.0",
|
||||
"System.Threading": "4.0.0.0",
|
||||
"System.Threading.Tasks": "4.0.0.0",
|
||||
"System.Xml.ReaderWriter": "4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"SimpleInjector": "3.2.4",
|
||||
"ServiceStack.Text": "4.5.4",
|
||||
"NLog": "4.4.0-betaV15",
|
||||
"sharpcompress": "0.14.0",
|
||||
"MediaBrowser.Model": {
|
||||
"target": "project"
|
||||
},
|
||||
"MediaBrowser.Common": {
|
||||
"target": "project"
|
||||
}
|
||||
}
|
||||
},
|
||||
"netstandard1.6": {
|
||||
"imports": "dnxcore50",
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.6.1",
|
||||
"System.IO.FileSystem.DriveInfo": "4.3.0",
|
||||
"System.Diagnostics.Process": "4.3.0",
|
||||
"System.Threading.Timer": "4.3.0",
|
||||
"System.Net.Requests": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0",
|
||||
"System.Xml.XmlSerializer": "4.3.0",
|
||||
"System.Net.Http": "4.3.0",
|
||||
"System.Net.Primitives": "4.3.0",
|
||||
"System.Net.Sockets": "4.3.0",
|
||||
"System.Net.NetworkInformation": "4.3.0",
|
||||
"System.Net.NameResolution": "4.3.0",
|
||||
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime.Loader": "4.3.0",
|
||||
"SimpleInjector": "3.2.4",
|
||||
"ServiceStack.Text.Core": "1.0.27",
|
||||
"NLog": "4.4.0-betaV15",
|
||||
"sharpcompress": "0.14.0",
|
||||
"System.AppDomain": "2.0.11",
|
||||
"MediaBrowser.Model": {
|
||||
"target": "project"
|
||||
},
|
||||
"MediaBrowser.Common": {
|
||||
"target": "project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
|
||||
namespace MediaBrowser.Dlna.Common
|
||||
namespace Emby.Dlna.Common
|
||||
{
|
||||
public class Argument
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
|
||||
namespace MediaBrowser.Dlna.Common
|
||||
namespace Emby.Dlna.Common
|
||||
{
|
||||
public class DeviceIcon
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
|
||||
namespace MediaBrowser.Dlna.Common
|
||||
namespace Emby.Dlna.Common
|
||||
{
|
||||
public class DeviceService
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Dlna.Common
|
||||
namespace Emby.Dlna.Common
|
||||
{
|
||||
public class ServiceAction
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Dlna.Common
|
||||
namespace Emby.Dlna.Common
|
||||
{
|
||||
public class StateVariable
|
||||
{
|
@ -2,7 +2,7 @@
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Dlna
|
||||
namespace Emby.Dlna
|
||||
{
|
||||
public static class ConfigurationExtension
|
||||
{
|
@ -1,24 +1,27 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Dlna.Service;
|
||||
using Emby.Dlna.Service;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Xml;
|
||||
|
||||
namespace MediaBrowser.Dlna.ConnectionManager
|
||||
namespace Emby.Dlna.ConnectionManager
|
||||
{
|
||||
public class ConnectionManager : BaseService, IConnectionManager
|
||||
{
|
||||
private readonly IDlnaManager _dlna;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
|
||||
|
||||
public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient)
|
||||
public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
|
||||
: base(logger, httpClient)
|
||||
{
|
||||
_dlna = dlna;
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
|
||||
}
|
||||
|
||||
public string GetServiceXml(IDictionary<string, string> headers)
|
||||
@ -31,7 +34,7 @@ namespace MediaBrowser.Dlna.ConnectionManager
|
||||
var profile = _dlna.GetProfile(request.Headers) ??
|
||||
_dlna.GetDefaultProfile();
|
||||
|
||||
return new ControlHandler(_logger, profile, _config).ProcessControlRequest(request);
|
||||
return new ControlHandler(_config, _logger, XmlReaderSettingsFactory, profile).ProcessControlRequest(request);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
using MediaBrowser.Dlna.Common;
|
||||
using MediaBrowser.Dlna.Service;
|
||||
using Emby.Dlna.Common;
|
||||
using Emby.Dlna.Service;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Dlna.ConnectionManager
|
||||
namespace Emby.Dlna.ConnectionManager
|
||||
{
|
||||
public class ConnectionManagerXmlBuilder
|
||||
{
|
@ -1,25 +1,20 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Dlna.Server;
|
||||
using MediaBrowser.Dlna.Service;
|
||||
using Emby.Dlna.Server;
|
||||
using Emby.Dlna.Service;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Xml;
|
||||
|
||||
namespace MediaBrowser.Dlna.ConnectionManager
|
||||
namespace Emby.Dlna.ConnectionManager
|
||||
{
|
||||
public class ControlHandler : BaseControlHandler
|
||||
{
|
||||
private readonly DeviceProfile _profile;
|
||||
|
||||
public ControlHandler(ILogger logger, DeviceProfile profile, IServerConfigurationManager config)
|
||||
: base(config, logger)
|
||||
{
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
|
||||
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
|
||||
{
|
||||
if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@ -31,11 +26,16 @@ namespace MediaBrowser.Dlna.ConnectionManager
|
||||
|
||||
private IEnumerable<KeyValuePair<string, string>> HandleGetProtocolInfo()
|
||||
{
|
||||
return new Headers(true)
|
||||
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "Source", _profile.ProtocolInfo },
|
||||
{ "Sink", "" }
|
||||
};
|
||||
}
|
||||
|
||||
public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory, DeviceProfile profile) : base(config, logger, xmlReaderSettingsFactory)
|
||||
{
|
||||
_profile = profile;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using MediaBrowser.Dlna.Common;
|
||||
using Emby.Dlna.Common;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Dlna.ConnectionManager
|
||||
namespace Emby.Dlna.ConnectionManager
|
||||
{
|
||||
public class ServiceActionListBuilder
|
||||
{
|
@ -5,16 +5,17 @@ using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Localization;
|
||||
using MediaBrowser.Dlna.Service;
|
||||
using Emby.Dlna.Service;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Xml;
|
||||
|
||||
namespace MediaBrowser.Dlna.ContentDirectory
|
||||
namespace Emby.Dlna.ContentDirectory
|
||||
{
|
||||
public class ContentDirectory : BaseService, IContentDirectory, IDisposable
|
||||
{
|
||||
@ -29,6 +30,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IUserViewManager _userViewManager;
|
||||
private readonly Func<IMediaEncoder> _mediaEncoder;
|
||||
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
|
||||
|
||||
public ContentDirectory(IDlnaManager dlna,
|
||||
IUserDataManager userDataManager,
|
||||
@ -37,7 +39,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||
IServerConfigurationManager config,
|
||||
IUserManager userManager,
|
||||
ILogger logger,
|
||||
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder)
|
||||
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
|
||||
: base(logger, httpClient)
|
||||
{
|
||||
_dlna = dlna;
|
||||
@ -51,6 +53,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_userViewManager = userViewManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
|
||||
}
|
||||
|
||||
private int SystemUpdateId
|
||||
@ -93,7 +96,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||
_channelManager,
|
||||
_mediaSourceManager,
|
||||
_userViewManager,
|
||||
_mediaEncoder())
|
||||
_mediaEncoder(),
|
||||
XmlReaderSettingsFactory)
|
||||
.ProcessControlRequest(request);
|
||||
}
|
||||
|
||||
@ -122,7 +126,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||
}
|
||||
|
||||
// No configuration so it's going to be pretty arbitrary
|
||||
return _userManager.Users.First();
|
||||
return _userManager.Users.FirstOrDefault(i => i.Policy.IsAdministrator) ??
|
||||
_userManager.Users.First();
|
||||
}
|
||||
|
||||
public void Dispose()
|
@ -10,8 +10,9 @@ using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.Server;
|
||||
|
||||
namespace MediaBrowser.Dlna.ContentDirectory
|
||||
namespace Emby.Dlna.ContentDirectory
|
||||
{
|
||||
public class ContentDirectoryBrowser
|
||||
{
|
||||
@ -90,7 +91,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||
request.ParentId = "1";
|
||||
}
|
||||
|
||||
builder.AppendFormat("<ObjectID>{0}</ObjectID>", SecurityElement.Escape(request.ParentId));
|
||||
builder.AppendFormat("<ObjectID>{0}</ObjectID>", DescriptionXmlBuilder.Escape(request.ParentId));
|
||||
builder.Append("<BrowseFlag>BrowseDirectChildren</BrowseFlag>");
|
||||
|
||||
//builder.Append("<BrowseFlag>BrowseMetadata</BrowseFlag>");
|
||||
@ -98,12 +99,12 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||
builder.Append("<Filter>*</Filter>");
|
||||
|
||||
request.StartIndex = request.StartIndex ?? 0;
|
||||
builder.AppendFormat("<StartingIndex>{0}</StartingIndex>", SecurityElement.Escape(request.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
|
||||
builder.AppendFormat("<StartingIndex>{0}</StartingIndex>", DescriptionXmlBuilder.Escape(request.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
request.Limit = request.Limit ?? 20;
|
||||
if (request.Limit.HasValue)
|
||||
{
|
||||
builder.AppendFormat("<RequestedCount>{0}</RequestedCount>", SecurityElement.Escape(request.Limit.Value.ToString(CultureInfo.InvariantCulture)));
|
||||
builder.AppendFormat("<RequestedCount>{0}</RequestedCount>", DescriptionXmlBuilder.Escape(request.Limit.Value.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
builder.Append("<SortCriteria></SortCriteria>");
|
@ -1,8 +1,8 @@
|
||||
using MediaBrowser.Dlna.Common;
|
||||
using MediaBrowser.Dlna.Service;
|
||||
using Emby.Dlna.Common;
|
||||
using Emby.Dlna.Service;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Dlna.ContentDirectory
|
||||
namespace Emby.Dlna.ContentDirectory
|
||||
{
|
||||
public class ContentDirectoryXmlBuilder
|
||||
{
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user