aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.ServerApplication/FFMpeg
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-11-09 13:24:57 -0500
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-11-09 13:24:57 -0500
commit1a80362a0f04c3cc571456af64f9de19c0c30d2a (patch)
tree31916eea422d8ac861aa36a47cae832eee168ad8 /MediaBrowser.ServerApplication/FFMpeg
parent40897bac1494791e1ec6abcfe85cda27d4664a32 (diff)
created common startup project for mono & windows
Diffstat (limited to 'MediaBrowser.ServerApplication/FFMpeg')
-rw-r--r--MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs252
-rw-r--r--MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs416
-rw-r--r--MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs24
3 files changed, 0 insertions, 692 deletions
diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs
deleted file mode 100644
index 2ca9107861..0000000000
--- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs
+++ /dev/null
@@ -1,252 +0,0 @@
-using Mono.Unix.Native;
-using System;
-using System.IO;
-using System.Text.RegularExpressions;
-
-namespace MediaBrowser.ServerApplication.FFMpeg
-{
- public static class FFMpegDownloadInfo
- {
- // Windows builds: http://ffmpeg.zeranoe.com/builds/
- // Linux builds: http://ffmpeg.gusari.org/static/
- // OS X builds: http://ffmpegmac.net/
- // OS X x64: http://www.evermeet.cx/ffmpeg/
-
- public static string Version = getFfmpegValue("Version");
-
- public static string FFMpegFilename = getFfmpegValue("FFMpegFilename");
- public static string FFProbeFilename = getFfmpegValue("FFProbeFilename");
-
- public static string ArchiveType = getFfmpegValue("ArchiveType");
-
- private static string getFfmpegValue(string arg)
- {
- OperatingSystem os = Environment.OSVersion;
- PlatformID pid = os.Platform;
- switch (pid)
- {
- case PlatformID.Win32NT:
- switch (arg)
- {
- case "Version":
- return "20141005";
- case "FFMpegFilename":
- return "ffmpeg.exe";
- case "FFProbeFilename":
- return "ffprobe.exe";
- case "ArchiveType":
- return "7z";
- }
- break;
-
- case PlatformID.Unix:
- if (PlatformDetection.IsMac)
- {
- if (PlatformDetection.IsX86_64)
- {
- switch (arg)
- {
- case "Version":
- return "20140923";
- case "FFMpegFilename":
- return "ffmpeg";
- case "FFProbeFilename":
- return "ffprobe";
- case "ArchiveType":
- return "7z";
- }
- }
- if (PlatformDetection.IsX86)
- {
- switch (arg)
- {
- case "Version":
- return "20140910";
- case "FFMpegFilename":
- return "ffmpeg";
- case "FFProbeFilename":
- return "ffprobe";
- case "ArchiveType":
- return "7z";
- }
- }
- }
- else if (PlatformDetection.IsLinux)
- {
- if (PlatformDetection.IsX86)
- {
- switch (arg)
- {
- case "Version":
- return "20140716";
- case "FFMpegFilename":
- return "ffmpeg";
- case "FFProbeFilename":
- return "ffprobe";
- case "ArchiveType":
- return "gz";
- }
- }
-
- else if (PlatformDetection.IsX86_64)
- {
- // Linux on x86 or x86_64
- switch (arg)
- {
- case "Version":
- return "20140716";
- case "FFMpegFilename":
- return "ffmpeg";
- case "FFProbeFilename":
- return "ffprobe";
- case "ArchiveType":
- return "gz";
- }
- }
- }
-
- break;
- }
-
- switch (arg)
- {
- case "Version":
- return "path";
- case "FFMpegFilename":
- return "ffmpeg";
- case "FFProbeFilename":
- return "ffprobe";
- case "ArchiveType":
- return "";
- default:
- return string.Empty;
- }
- }
-
- public static string[] GetDownloadUrls()
- {
- var pid = Environment.OSVersion.Platform;
-
- switch (pid)
- {
- case PlatformID.Win32NT:
- if (PlatformDetection.IsX86_64)
- {
- return new[]
- {
- "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20141005-git-e079d43-win64-static.7z",
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/windows/ffmpeg-20141005-git-e079d43-win64-static.7z"
- };
- }
-
- return new[]
- {
- "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20141005-git-e079d43-win32-static.7z",
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/windows/ffmpeg-20141005-git-e079d43-win32-static.7z"
- };
-
- case PlatformID.Unix:
- if (PlatformDetection.IsMac && PlatformDetection.IsX86)
- {
- return new[]
- {
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/osx/ffmpeg-x86-2.4.2.7z"
- };
- }
-
- if (PlatformDetection.IsMac && PlatformDetection.IsX86_64)
- {
- return new[]
- {
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.4.1.7z"
- };
- }
-
- if (PlatformDetection.IsLinux)
- {
- if (PlatformDetection.IsX86)
- {
- return new[]
- {
- "http://ffmpeg.gusari.org/static/32bit/ffmpeg.static.32bit.latest.tar.gz",
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/linux/ffmpeg.static.32bit.2014-07-16.tar.gz"
- };
- }
-
- if (PlatformDetection.IsX86_64)
- {
- return new[]
- {
- "http://ffmpeg.gusari.org/static/64bit/ffmpeg.static.64bit.latest.tar.gz",
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/linux/ffmpeg.static.64bit.2014-07-16.tar.gz"
- };
- }
-
- }
-
- // No Unix version available
- return new string[] { };
-
- default:
- throw new ApplicationException("No ffmpeg download available for " + pid);
- }
- }
- }
-
- public static class PlatformDetection
- {
- public readonly static bool IsWindows;
- public readonly static bool IsMac;
- public readonly static bool IsLinux;
- public readonly static bool IsX86;
- public readonly static bool IsX86_64;
- public readonly static bool IsArm;
-
- static PlatformDetection()
- {
- IsWindows = Path.DirectorySeparatorChar == '\\';
-
- // Don't call uname on windows
- if (!IsWindows)
- {
- var uname = GetUnixName();
-
- var sysName = uname.sysname ?? string.Empty;
-
- IsMac = string.Equals(sysName, "Darwin", StringComparison.OrdinalIgnoreCase);
- IsLinux = string.Equals(sysName, "Linux", StringComparison.OrdinalIgnoreCase);
-
- var archX86 = new Regex("(i|I)[3-6]86");
- IsX86 = archX86.IsMatch(uname.machine);
- IsX86_64 = !IsX86 && uname.machine == "x86_64";
- IsArm = !IsX86 && !IsX86_64 && uname.machine.StartsWith("arm");
- }
- else
- {
- if (Environment.Is64BitOperatingSystem)
- IsX86_64 = true;
- else
- IsX86 = true;
- }
- }
-
- private static Uname GetUnixName()
- {
- var uname = new Uname();
- Utsname utsname;
- var callResult = Syscall.uname(out utsname);
- if (callResult == 0)
- {
- uname.sysname = utsname.sysname;
- uname.machine = utsname.machine;
- }
- return uname;
- }
- }
-
- public class Uname
- {
- public string sysname = string.Empty;
- public string machine = string.Empty;
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
deleted file mode 100644
index 7af8546ab0..0000000000
--- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
+++ /dev/null
@@ -1,416 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.ServerApplication.IO;
-using Mono.Unix.Native;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.ServerApplication.FFMpeg
-{
- public class FFMpegDownloader
- {
- private readonly IHttpClient _httpClient;
- private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
- private readonly IZipClient _zipClient;
- private readonly IFileSystem _fileSystem;
-
- private readonly string[] _fontUrls =
- {
- "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z"
- };
-
- public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem)
- {
- _logger = logger;
- _appPaths = appPaths;
- _httpClient = httpClient;
- _zipClient = zipClient;
- _fileSystem = fileSystem;
- }
-
- public async Task<FFMpegInfo> GetFFMpegInfo(StartupOptions options, IProgress<double> progress)
- {
- var customffMpegPath = options.GetOption("-ffmpeg");
- var customffProbePath = options.GetOption("-ffprobe");
-
- if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath))
- {
- return new FFMpegInfo
- {
- ProbePath = customffProbePath,
- EncoderPath = customffMpegPath,
- Version = "custom"
- };
- }
-
- var version = FFMpegDownloadInfo.Version;
-
- if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase))
- {
- return new FFMpegInfo
- {
- ProbePath = FFMpegDownloadInfo.FFProbeFilename,
- EncoderPath = FFMpegDownloadInfo.FFMpegFilename,
- Version = version
- };
- }
-
- var rootEncoderPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg");
- var versionedDirectoryPath = Path.Combine(rootEncoderPath, version);
-
- var info = new FFMpegInfo
- {
- ProbePath = Path.Combine(versionedDirectoryPath, FFMpegDownloadInfo.FFProbeFilename),
- EncoderPath = Path.Combine(versionedDirectoryPath, FFMpegDownloadInfo.FFMpegFilename),
- Version = version
- };
-
- Directory.CreateDirectory(versionedDirectoryPath);
-
- var excludeFromDeletions = new List<string> { versionedDirectoryPath };
-
- if (!File.Exists(info.ProbePath) || !File.Exists(info.EncoderPath))
- {
- // ffmpeg not present. See if there's an older version we can start with
- var existingVersion = GetExistingVersion(info, rootEncoderPath);
-
- // No older version. Need to download and block until complete
- if (existingVersion == null)
- {
- await DownloadFFMpeg(versionedDirectoryPath, progress).ConfigureAwait(false);
- }
- else
- {
- // Older version found.
- // Start with that. Download new version in the background.
- var newPath = versionedDirectoryPath;
- Task.Run(() => DownloadFFMpegInBackground(newPath));
-
- info = existingVersion;
- versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath);
-
- excludeFromDeletions.Add(versionedDirectoryPath);
- }
- }
-
- await DownloadFonts(versionedDirectoryPath).ConfigureAwait(false);
-
- DeleteOlderFolders(Path.GetDirectoryName(versionedDirectoryPath), excludeFromDeletions);
-
- return info;
- }
-
- private void DeleteOlderFolders(string path, IEnumerable<string> excludeFolders )
- {
- var folders = Directory.GetDirectories(path)
- .Where(i => !excludeFolders.Contains(i, StringComparer.OrdinalIgnoreCase))
- .ToList();
-
- foreach (var folder in folders)
- {
- DeleteFolder(folder);
- }
- }
-
- private void DeleteFolder(string path)
- {
- try
- {
- Directory.Delete(path, true);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting {0}", ex, path);
- }
- }
-
- private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath)
- {
- var encoderFilename = Path.GetFileName(info.EncoderPath);
- var probeFilename = Path.GetFileName(info.ProbePath);
-
- foreach (var directory in Directory.EnumerateDirectories(rootEncoderPath, "*", SearchOption.TopDirectoryOnly)
- .ToList())
- {
- var allFiles = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).ToList();
-
- var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase));
- var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase));
-
- if (!string.IsNullOrWhiteSpace(encoder) &&
- !string.IsNullOrWhiteSpace(probe))
- {
- return new FFMpegInfo
- {
- EncoderPath = encoder,
- ProbePath = probe,
- Version = Path.GetFileName(Path.GetDirectoryName(probe))
- };
- }
- }
-
- return null;
- }
-
- private async void DownloadFFMpegInBackground(string directory)
- {
- try
- {
- await DownloadFFMpeg(directory, new Progress<double>()).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error downloading ffmpeg", ex);
- }
- }
-
- private async Task DownloadFFMpeg(string directory, IProgress<double> progress)
- {
- foreach (var url in FFMpegDownloadInfo.GetDownloadUrls())
- {
- progress.Report(0);
-
- try
- {
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- Progress = progress
-
- }).ConfigureAwait(false);
-
- ExtractFFMpeg(tempFile, directory);
- return;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error downloading {0}", ex, url);
- }
- }
-
- throw new ApplicationException("Unable to download required components. Please try again later.");
- }
-
- private void ExtractFFMpeg(string tempFile, string targetFolder)
- {
- _logger.Info("Extracting ffmpeg from {0}", tempFile);
-
- var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString());
-
- Directory.CreateDirectory(tempFolder);
-
- try
- {
- ExtractArchive(tempFile, tempFolder);
-
- var files = Directory.EnumerateFiles(tempFolder, "*", SearchOption.AllDirectories).ToList();
-
- foreach (var file in files.Where(i =>
- {
- var filename = Path.GetFileName(i);
-
- return
- string.Equals(filename, FFMpegDownloadInfo.FFProbeFilename, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(filename, FFMpegDownloadInfo.FFMpegFilename, StringComparison.OrdinalIgnoreCase);
- }))
- {
- File.Copy(file, Path.Combine(targetFolder, Path.GetFileName(file)), true);
-
- SetFilePermissions(targetFolder, file);
- }
- }
- finally
- {
- DeleteFile(tempFile);
- }
- }
-
- private void SetFilePermissions(string targetFolder, string file)
- {
- // Linux: File permission to 666, and user's execute bit
- if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
- {
- Syscall.chmod(Path.Combine(targetFolder, Path.GetFileName(file)), FilePermissions.DEFFILEMODE | FilePermissions.S_IXUSR);
- }
- }
-
- private void ExtractArchive(string archivePath, string targetPath)
- {
- _logger.Info("Extracting {0} to {1}", archivePath, targetPath);
-
- if (string.Equals(FFMpegDownloadInfo.ArchiveType, "7z", StringComparison.OrdinalIgnoreCase))
- {
- _zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
- }
- else if (string.Equals(FFMpegDownloadInfo.ArchiveType, "gz", StringComparison.OrdinalIgnoreCase))
- {
- _zipClient.ExtractAllFromTar(archivePath, targetPath, true);
- }
- }
- private void Extract7zArchive(string archivePath, string targetPath)
- {
- _logger.Info("Extracting {0} to {1}", archivePath, targetPath);
-
- _zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
- }
-
- private void DeleteFile(string path)
- {
- try
- {
- File.Delete(path);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting temp file {0}", ex, path);
- }
- }
-
- /// <summary>
- /// Extracts the fonts.
- /// </summary>
- /// <param name="targetPath">The target path.</param>
- /// <returns>Task.</returns>
- private async Task DownloadFonts(string targetPath)
- {
- try
- {
- var fontsDirectory = Path.Combine(targetPath, "fonts");
-
- Directory.CreateDirectory(fontsDirectory);
-
- const string fontFilename = "ARIALUNI.TTF";
-
- var fontFile = Path.Combine(fontsDirectory, fontFilename);
-
- if (File.Exists(fontFile))
- {
- await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
- }
- else
- {
- // Kick this off, but no need to wait on it
- Task.Run(async () =>
- {
- await DownloadFontFile(fontsDirectory, fontFilename, new Progress<double>()).ConfigureAwait(false);
-
- await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
- });
- }
- }
- catch (HttpException ex)
- {
- // Don't let the server crash because of this
- _logger.ErrorException("Error downloading ffmpeg font files", ex);
- }
- catch (Exception ex)
- {
- // Don't let the server crash because of this
- _logger.ErrorException("Error writing ffmpeg font files", ex);
- }
- }
-
- /// <summary>
- /// Downloads the font file.
- /// </summary>
- /// <param name="fontsDirectory">The fonts directory.</param>
- /// <param name="fontFilename">The font filename.</param>
- /// <returns>Task.</returns>
- private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress)
- {
- var existingFile = Directory
- .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories)
- .FirstOrDefault();
-
- if (existingFile != null)
- {
- try
- {
- File.Copy(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
- return;
- }
- catch (IOException ex)
- {
- // Log this, but don't let it fail the operation
- _logger.ErrorException("Error copying file", ex);
- }
- }
-
- string tempFile = null;
-
- foreach (var url in _fontUrls)
- {
- progress.Report(0);
-
- try
- {
- tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = url,
- Progress = progress
-
- }).ConfigureAwait(false);
-
- break;
- }
- catch (Exception ex)
- {
- // The core can function without the font file, so handle this
- _logger.ErrorException("Failed to download ffmpeg font file from {0}", ex, url);
- }
- }
-
- if (string.IsNullOrEmpty(tempFile))
- {
- return;
- }
-
- Extract7zArchive(tempFile, fontsDirectory);
-
- try
- {
- File.Delete(tempFile);
- }
- catch (IOException ex)
- {
- // Log this, but don't let it fail the operation
- _logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
- }
- }
-
- /// <summary>
- /// Writes the font config file.
- /// </summary>
- /// <param name="fontsDirectory">The fonts directory.</param>
- /// <returns>Task.</returns>
- private async Task WriteFontConfigFile(string fontsDirectory)
- {
- const string fontConfigFilename = "fonts.conf";
- var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
-
- if (!File.Exists(fontConfigFile))
- {
- var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
-
- var bytes = Encoding.UTF8.GetBytes(contents);
-
- using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
- FileShare.Read, true))
- {
- await fileStream.WriteAsync(bytes, 0, bytes.Length);
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs
deleted file mode 100644
index 1361277aac..0000000000
--- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace MediaBrowser.ServerApplication.FFMpeg
-{
- /// <summary>
- /// Class FFMpegInfo
- /// </summary>
- public class FFMpegInfo
- {
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string EncoderPath { get; set; }
- /// <summary>
- /// Gets or sets the probe path.
- /// </summary>
- /// <value>The probe path.</value>
- public string ProbePath { get; set; }
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
- }
-} \ No newline at end of file