You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Sonarr/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoLib.cs

370 lines
12 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Core.MediaFiles.MediaInfo
{
[Flags]
public enum BufferStatus
{
Accepted = 1,
Filled = 2,
Updated = 4,
Finalized = 8
}
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 static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(MediaInfo));
private IntPtr _handle;
public bool MustUseAnsi { get; set; }
public Encoding Encoding { get; set; }
public MediaInfo()
{
_handle = MediaInfo_New();
InitializeEncoding();
}
~MediaInfo()
{
if (_handle != IntPtr.Zero)
{
MediaInfo_Delete(_handle);
}
}
public void Dispose()
{
if (_handle != IntPtr.Zero)
{
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
{
var responses = new List<string>();
// Linux normally UCS-4. As fallback we try UCS-2 and plain Ansi.
MustUseAnsi = false;
Encoding = Encoding.UTF32;
var version = Option("Info_Version", "");
responses.Add(version);
if (version.StartsWith("MediaInfoLib"))
{
return;
}
Encoding = Encoding.Unicode;
version = Option("Info_Version", "");
responses.Add(version);
if (version.StartsWith("MediaInfoLib"))
{
return;
}
MustUseAnsi = true;
Encoding = Encoding.Default;
version = Option("Info_Version", "");
responses.Add(version);
if (version.StartsWith("MediaInfoLib"))
{
return;
}
throw new NotSupportedException("Unsupported MediaInfoLib encoding, version check responses (may be gibberish, show it to the Sonarr devs): " + responses.Join(", ") );
}
}
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)
{
if (stream.Length < 1024)
{
return 0;
}
var isValid = (int)MediaInfo_Open_Buffer_Init(_handle, stream.Length, 0);
if (isValid == 1)
{
var buffer = new byte[16 * 1024];
long seekStart = 0;
long totalRead = 0;
int bufferRead;
do
{
bufferRead = stream.Read(buffer, 0, buffer.Length);
totalRead += bufferRead;
var status = (BufferStatus)MediaInfo_Open_Buffer_Continue(_handle, buffer, (IntPtr)bufferRead);
if (status.HasFlag(BufferStatus.Finalized) || status <= 0 || bufferRead == 0)
{
Logger.Trace("Read file offset {0}-{1} ({2} bytes)", seekStart, stream.Position, stream.Position - seekStart);
break;
}
var seekPos = MediaInfo_Open_Buffer_Continue_GoTo_Get(_handle);
if (seekPos != -1)
{
Logger.Trace("Read file offset {0}-{1} ({2} bytes)", seekStart, stream.Position, stream.Position - seekStart);
seekPos = stream.Seek(seekPos, SeekOrigin.Begin);
seekStart = seekPos;
MediaInfo_Open_Buffer_Init(_handle, stream.Length, seekPos);
}
} while (bufferRead > 0);
MediaInfo_Open_Buffer_Finalize(_handle);
Logger.Trace("Read a total of {0} bytes ({1:0.0}%)", totalRead, totalRead * 100.0 / stream.Length);
}
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.ToLowerInvariant());
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")]
private static extern IntPtr MediaInfo_New();
[DllImport("mediainfo")]
private static extern void MediaInfo_Delete(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_Open(IntPtr handle, IntPtr fileName);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_Open_Buffer_Init(IntPtr handle, long fileSize, long fileOffset);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize);
[DllImport("mediainfo")]
private static extern long MediaInfo_Open_Buffer_Continue_GoTo_Get(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_Open_Buffer_Finalize(IntPtr handle);
[DllImport("mediainfo")]
private static extern void MediaInfo_Close(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_Option(IntPtr handle, IntPtr option, IntPtr value);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_State_Get(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfo_Count_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_New();
[DllImport("mediainfo")]
private static extern void MediaInfoA_Delete(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_Open(IntPtr handle, IntPtr fileName);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_Open_Buffer_Init(IntPtr handle, long fileSize, long fileOffset);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize);
[DllImport("mediainfo")]
private static extern long MediaInfoA_Open_Buffer_Continue_GoTo_Get(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_Open_Buffer_Finalize(IntPtr handle);
[DllImport("mediainfo")]
private static extern void MediaInfoA_Close(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_Option(IntPtr handle, IntPtr option, IntPtr value);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_State_Get(IntPtr handle);
[DllImport("mediainfo")]
private static extern IntPtr MediaInfoA_Count_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber);
}
}