Integrated MediaInfo wrapper to be able to properly handle Unicode on Linux.

This commit is contained in:
Taloth Saldono 2015-05-12 23:58:46 +02:00
parent 96578ca59b
commit 5cd2d71e6f
9 changed files with 351 additions and 34 deletions

View File

@ -114,8 +114,8 @@ Function PackageMono()
get-childitem $outputFolderMono -File -Filter sqlite3.* -Recurse | foreach ($_) {remove-item $_.fullname}
get-childitem $outputFolderMono -File -Filter MediaInfo.* -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host "Adding MediaInfoDotNet.dll.config (for dllmap)"
Copy-Item "$sourceFolder\MediaInfoDotNet.dll.config" $outputFolderMono
Write-Host "Adding NzbDrone.Core.dll.config (for dllmap)"
Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" $outputFolderMono
Write-Host Renaming NzbDrone.Console.exe to NzbDrone.exe
Get-ChildItem $outputFolderMono -File -Filter "NzbDrone.exe*" -Recurse | foreach ($_) {remove-item $_.fullname}
@ -212,8 +212,8 @@ Function PackageTests()
CleanFolder $testPackageFolder $true
Write-Host "Adding MediaInfoDotNet.dll.config (for dllmap)"
Copy-Item "$sourceFolder\MediaInfoDotNet.dll.config" -Destination $testPackageFolder -Force
Write-Host "Adding NzbDrone.Core.dll.config (for dllmap)"
Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" -Destination $testPackageFolder -Force
Write-Host "##teamcity[progressFinish 'Creating Test Package']"
}

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<dllmap os="osx" dll="MediaInfo.dll" target="libmediainfo.0.dylib"/>
<dllmap os="linux" dll="MediaInfo.dll" target="libmediainfo.so.0" />
<dllmap os="freebsd" dll="MediaInfo.dll" target="libmediainfo.so.0" />
<dllmap os="solaris" dll="MediaInfo.dll" target="libmediainfo.so.0.0.0" />
</configuration>

View File

@ -19,6 +19,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(It.IsAny<string>()))
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.OpenReadStream(It.IsAny<string>()))
.Returns<string>(s => new FileStream(s, FileMode.Open, FileAccess.Read));
}
[Test]
@ -56,7 +60,6 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
}
[Test]
[Ignore]
public void get_info_unicode()
{
var srcPath = Path.Combine(Directory.GetCurrentDirectory(), "Files", "Media", "H264_sample.mp4");

View File

@ -1,6 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using MediaInfoLib;
using NzbDrone.Core.MediaFiles.MediaInfo;
namespace NzbDrone.Core.HealthCheck.Checks
{

View File

@ -0,0 +1,328 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Core.MediaFiles.MediaInfo
{
public enum StreamKind
{
General,
Video,
Audio,
Text,
Other,
Image,
Menu,
}
public enum InfoKind
{
Name,
Text,
Measure,
Options,
NameText,
MeasureText,
Info,
HowTo
}
public enum InfoOptions
{
ShowInInform,
Support,
ShowInSupported,
TypeOfValue
}
public enum InfoFileOptions
{
FileOption_Nothing = 0x00,
FileOption_NoRecursive = 0x01,
FileOption_CloseAll = 0x02,
FileOption_Max = 0x04
};
public class MediaInfo : IDisposable
{
private IntPtr _handle;
public bool MustUseAnsi { get; set; }
public Encoding Encoding { get; set; }
public MediaInfo()
{
_handle = MediaInfo_New();
InitializeEncoding();
}
~MediaInfo()
{
MediaInfo_Delete(_handle);
}
public void Dispose()
{
MediaInfo_Delete(_handle);
GC.SuppressFinalize(this);
}
private void InitializeEncoding()
{
if (Environment.OSVersion.ToString().IndexOf("Windows") != -1)
{
// Windows guaranteed UCS-2
MustUseAnsi = false;
Encoding = Encoding.Unicode;
}
else
{
// Linux normally UCS-4. As fallback we try UCS-2 and plain Ansi.
MustUseAnsi = false;
Encoding = Encoding.UTF32;
if (Option("Info_Version", "").StartsWith("MediaInfoLib"))
{
return;
}
Encoding = Encoding.Unicode;
if (Option("Info_Version", "").StartsWith("MediaInfoLib"))
{
return;
}
MustUseAnsi = true;
Encoding = Encoding.Default;
if (Option("Info_Version", "").StartsWith("MediaInfoLib"))
{
return;
}
throw new NotSupportedException("Unsupported MediaInfoLib encoding");
}
}
private IntPtr MakeStringParameter(string value)
{
var buffer = Encoding.GetBytes(value);
Array.Resize(ref buffer, buffer.Length + 4);
var buf = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, buf, buffer.Length);
return buf;
}
private string MakeStringResult(IntPtr value)
{
if (Encoding == Encoding.Unicode)
{
return Marshal.PtrToStringUni(value);
}
else if (Encoding == Encoding.UTF32)
{
int i = 0;
for (; i < 1024; i += 4)
{
var data = Marshal.ReadInt32(value, i);
if (data == 0)
{
break;
}
}
var buffer = new byte[i];
Marshal.Copy(value, buffer, 0, i);
return Encoding.GetString(buffer, 0, i);
}
else
{
return Marshal.PtrToStringAnsi(value);
}
}
public int Open(string fileName)
{
var pFileName = MakeStringParameter(fileName);
try
{
if (MustUseAnsi)
{
return (int)MediaInfoA_Open(_handle, pFileName);
}
else
{
return (int)MediaInfo_Open(_handle, pFileName);
}
}
finally
{
Marshal.FreeHGlobal(pFileName);
}
}
public int Open(Stream stream)
{
var buffer = new byte[64 * 1024];
var isValid = (int)MediaInfo_Open_Buffer_Init(_handle, stream.Length, 0);
if (isValid == 1)
{
int bufferRead;
do
{
bufferRead = stream.Read(buffer, 0, buffer.Length);
if (MediaInfo_Open_Buffer_Continue(_handle, buffer, (IntPtr)bufferRead) == (IntPtr)0)
{
break;
}
var seekPos = MediaInfo_Open_Buffer_Continue_GoTo_Get(_handle);
if (seekPos != -1)
{
seekPos = stream.Seek(seekPos, SeekOrigin.Begin);
MediaInfo_Open_Buffer_Init(_handle, stream.Length, seekPos);
}
} while (bufferRead > 0);
MediaInfo_Open_Buffer_Finalize(_handle);
}
return isValid;
}
public void Close()
{
MediaInfo_Close(_handle);
}
public string Get(StreamKind streamKind, int streamNumber, string parameter, InfoKind infoKind = InfoKind.Text, InfoKind searchKind = InfoKind.Name)
{
var pParameter = MakeStringParameter(parameter);
try
{
if (MustUseAnsi)
{
return MakeStringResult(MediaInfoA_Get(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, pParameter, (IntPtr)infoKind, (IntPtr)searchKind));
}
else
{
return MakeStringResult(MediaInfo_Get(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, pParameter, (IntPtr)infoKind, (IntPtr)searchKind));
}
}
finally
{
Marshal.FreeHGlobal(pParameter);
}
}
public string Get(StreamKind streamKind, int streamNumber, int parameter, InfoKind infoKind)
{
if (MustUseAnsi)
{
return MakeStringResult(MediaInfoA_GetI(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, (IntPtr)parameter, (IntPtr)infoKind));
}
else
{
return MakeStringResult(MediaInfo_GetI(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, (IntPtr)parameter, (IntPtr)infoKind));
}
}
public String Option(String option, String value)
{
var pOption = MakeStringParameter(option);
var pValue = MakeStringParameter(value);
try
{
if (MustUseAnsi)
{
return MakeStringResult(MediaInfoA_Option(_handle, pOption, pValue));
}
else
{
return MakeStringResult(MediaInfo_Option(_handle, pOption, pValue));
}
}
finally
{
Marshal.FreeHGlobal(pOption);
Marshal.FreeHGlobal(pValue);
}
}
public int State_Get()
{
return (int)MediaInfo_State_Get(_handle);
}
public int Count_Get(StreamKind streamKind, int streamNumber = -1)
{
return (int)MediaInfo_Count_Get(_handle, (IntPtr)streamKind, (IntPtr)streamNumber);
}
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_New();
[DllImport("MediaInfo.dll")]
private static extern void MediaInfo_Delete(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open(IntPtr handle, IntPtr fileName);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open_Buffer_Init(IntPtr handle, Int64 fileSize, Int64 fileOffset);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize);
[DllImport("MediaInfo.dll")]
private static extern Int64 MediaInfo_Open_Buffer_Continue_GoTo_Get(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open_Buffer_Finalize(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern void MediaInfo_Close(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Option(IntPtr handle, IntPtr option, IntPtr value);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_State_Get(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Count_Get(IntPtr handle, IntPtr StreamKind, IntPtr streamNumber);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_New();
[DllImport("MediaInfo.dll")]
private static extern void MediaInfoA_Delete(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open(IntPtr handle, IntPtr fileName);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open_Buffer_Init(IntPtr handle, Int64 fileSize, Int64 fileOffset);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize);
[DllImport("MediaInfo.dll")]
private static extern Int64 MediaInfoA_Open_Buffer_Continue_GoTo_Get(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open_Buffer_Finalize(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern void MediaInfoA_Close(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Option(IntPtr handle, IntPtr option, IntPtr value);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_State_Get(IntPtr handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Count_Get(IntPtr handle, IntPtr StreamKind, IntPtr streamNumber);
}
}

View File

@ -2,7 +2,6 @@
using System.Globalization;
using System.IO;
using System.Text;
using MediaInfoLib;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
@ -33,29 +32,16 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
if (!_diskProvider.FileExists(filename))
throw new FileNotFoundException("Media file does not exist: " + filename);
MediaInfoLib.MediaInfo mediaInfo = null;
MediaInfo mediaInfo = null;
try
{
mediaInfo = new MediaInfoLib.MediaInfo();
mediaInfo = new MediaInfo();
_logger.Debug("Getting media info from {0}", filename);
mediaInfo.Option("ParseSpeed", "0.2");
int open;
if (OsInfo.IsWindows)
{
open = mediaInfo.Open(filename);
}
else
{
mediaInfo.Option("CharSet", "UTF-8");
// On non-Windows the wrapper uses the ansi library methods, which libmediainfo converts internally to unicode from multibyte (utf8).
// To avoid building MediaInfoDotNet ourselves we simply trick the wrapper to send utf8 strings instead of ansi.
var utf8filename = Encoding.Default.GetString(Encoding.UTF8.GetBytes(filename));
open = mediaInfo.Open(utf8filename);
}
int open = mediaInfo.Open(_diskProvider.OpenReadStream(filename));
if (open != 0)
{

View File

@ -93,9 +93,6 @@
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="MediaInfoDotNet">
<HintPath>..\packages\MediaInfoNet.0.3\lib\MediaInfoDotNet.dll</HintPath>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
@ -606,6 +603,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="MediaFiles\MediaFileTableCleanupService.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoLib.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoModel.cs" />
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoService.cs" />
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
@ -940,6 +938,9 @@
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="NzbDrone.Core.dll.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
<None Include="Properties\AnalysisRules.ruleset" />
</ItemGroup>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<dllmap os="osx" dll="MediaInfo.dll" target="libmediainfo.0.dylib"/>
<dllmap os="linux" dll="MediaInfo.dll" target="libmediainfo.so.0" />
<dllmap os="freebsd" dll="MediaInfo.dll" target="libmediainfo.so.0" />
<dllmap os="solaris" dll="MediaInfo.dll" target="libmediainfo.so.0.0.0" />
</configuration>

View File

@ -4,7 +4,6 @@
<package id="FluentMigrator.Runner" version="1.3.1.0" targetFramework="net40" />
<package id="FluentValidation" version="5.5.0.0" targetFramework="net40" />
<package id="ImageResizer" version="3.4.3" targetFramework="net40" />
<package id="MediaInfoNet" version="0.3" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="Prowlin" version="0.9.4456.26422" targetFramework="net40" />