add vaapi support

This commit is contained in:
Luke Pulverenti 2016-08-23 12:31:16 -04:00
parent f57a0e6b81
commit 2e65c32ede
5 changed files with 186 additions and 117 deletions

View File

@ -314,6 +314,10 @@ namespace MediaBrowser.Api.Playback
{ {
return GetAvailableEncoder("h264_omx", defaultEncoder); return GetAvailableEncoder("h264_omx", defaultEncoder);
} }
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
return GetAvailableEncoder("h264_vaapi", defaultEncoder);
}
} }
return defaultEncoder; return defaultEncoder;
@ -427,7 +431,8 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.VideoRequest.Profile)) if (!string.IsNullOrEmpty(state.VideoRequest.Profile))
{ {
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase)) if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
// not supported by h264_omx // not supported by h264_omx
param += " -profile:v " + state.VideoRequest.Profile; param += " -profile:v " + state.VideoRequest.Profile;
@ -482,7 +487,8 @@ namespace MediaBrowser.Api.Playback
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
param = "-pix_fmt yuv420p " + param; param = "-pix_fmt yuv420p " + param;
} }
@ -548,59 +554,97 @@ namespace MediaBrowser.Api.Playback
var filters = new List<string>(); var filters = new List<string>();
if (state.DeInterlace) if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
}
else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
filters.Add("yadif=0:-1:0"); filters.Add("yadif=0:-1:0");
} }
// If fixed dimensions were supplied if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
if (request.Width.HasValue && request.Height.HasValue)
{ {
var widthParam = request.Width.Value.ToString(UsCulture); // Work around vaapi's reduced scaling features
var heightParam = request.Height.Value.ToString(UsCulture); var scaler = "scale_vaapi";
filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam)); // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
// (outputWidth, outputHeight). The user may request precise output dimensions or maximum
// output dimensions. Output dimensions are guaranteed to be even.
decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
if (outputWidth > maximumWidth || outputHeight > maximumHeight)
{
var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
}
outputWidth = 2 * Math.Truncate(outputWidth / 2);
outputHeight = 2 * Math.Truncate(outputHeight / 2);
if (outputWidth != inputWidth || outputHeight != inputHeight)
{
filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
}
} }
else
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{ {
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); // If fixed dimensions were supplied
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); if (request.Width.HasValue && request.Height.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam)); filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
} }
// If a fixed width was requested // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
else if (request.Width.HasValue) else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{ {
var widthParam = request.Width.Value.ToString(UsCulture); var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam)); filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
} }
// If a fixed height was requested // If a fixed width was requested
else if (request.Height.HasValue) else if (request.Width.HasValue)
{ {
var heightParam = request.Height.Value.ToString(UsCulture); var widthParam = request.Width.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam)); filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
} }
// If a max width was requested // If a fixed height was requested
else if (request.MaxWidth.HasValue) else if (request.Height.HasValue)
{ {
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam)); filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
} }
// If a max height was requested // If a max width was requested
else if (request.MaxHeight.HasValue) else if (request.MaxWidth.HasValue)
{ {
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam)); filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
}
// If a max height was requested
else if (request.MaxHeight.HasValue)
{
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
}
} }
var output = string.Empty; var output = string.Empty;
@ -935,6 +979,15 @@ namespace MediaBrowser.Api.Playback
} }
} }
if (state.VideoRequest != null)
{
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
{
arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
}
}
return arg.Trim(); return arg.Trim();
} }

View File

@ -680,7 +680,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrEmpty(state.Options.Profile)) if (!string.IsNullOrEmpty(state.Options.Profile))
{ {
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase)) if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
// not supported by h264_omx // not supported by h264_omx
param += " -profile:v " + state.Options.Profile; param += " -profile:v " + state.Options.Profile;
@ -737,7 +738,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
param = "-pix_fmt yuv420p " + param; param = "-pix_fmt yuv420p " + param;
} }
@ -887,66 +889,96 @@ namespace MediaBrowser.MediaEncoding.Encoder
var filters = new List<string>(); var filters = new List<string>();
if (state.DeInterlace) if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
}
else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
filters.Add("yadif=0:-1:0"); filters.Add("yadif=0:-1:0");
} }
// If fixed dimensions were supplied if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
if (request.Width.HasValue && request.Height.HasValue)
{ {
var widthParam = request.Width.Value.ToString(UsCulture); // Work around vaapi's reduced scaling features
var heightParam = request.Height.Value.ToString(UsCulture); var scaler = "scale_vaapi";
filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam)); // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
} // (outputWidth, outputHeight). The user may request precise output dimensions or maximum
// output dimensions. Output dimensions are guaranteed to be even.
decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size if (outputWidth > maximumWidth || outputHeight > maximumHeight)
else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
}
// If a fixed width was requested
else if (request.Width.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
}
// If a fixed height was requested
else if (request.Height.HasValue)
{
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
}
// If a max width was requested
else if (request.MaxWidth.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
}
// If a max height was requested
else if (request.MaxHeight.HasValue)
{
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
}
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
if (filters.Count > 1)
{ {
//filters[filters.Count - 1] += ":flags=fast_bilinear"; var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
}
outputWidth = 2 * Math.Truncate(outputWidth / 2);
outputHeight = 2 * Math.Truncate(outputHeight / 2);
if (outputWidth != inputWidth || outputHeight != inputHeight)
{
filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
}
}
else
{
// If fixed dimensions were supplied
if (request.Width.HasValue && request.Height.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
}
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
}
// If a fixed width was requested
else if (request.Width.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
}
// If a fixed height was requested
else if (request.Height.HasValue)
{
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
}
// If a max width was requested
else if (request.MaxWidth.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
}
// If a max height was requested
else if (request.MaxHeight.HasValue)
{
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
} }
} }

View File

@ -586,6 +586,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ {
return GetAvailableEncoder(mediaEncoder, "h264_omx", defaultEncoder); return GetAvailableEncoder(mediaEncoder, "h264_omx", defaultEncoder);
} }
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder);
}
} }
return defaultEncoder; return defaultEncoder;

View File

@ -10,6 +10,7 @@ namespace MediaBrowser.Model.Configuration
public int ThrottleDelaySeconds { get; set; } public int ThrottleDelaySeconds { get; set; }
public string HardwareAccelerationType { get; set; } public string HardwareAccelerationType { get; set; }
public string EncoderAppPath { get; set; } public string EncoderAppPath { get; set; }
public string VaapiDevice { get; set; }
public EncodingOptions() public EncodingOptions()
{ {

View File

@ -132,7 +132,7 @@ namespace MediaBrowser.Server.Mono.Native
{ {
get get
{ {
return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx; return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx;
} }
} }
@ -187,7 +187,7 @@ namespace MediaBrowser.Server.Mono.Native
{ {
info.SystemArchitecture = Architecture.X64; info.SystemArchitecture = Architecture.X64;
} }
else else
{ {
info.SystemArchitecture = Architecture.X86; info.SystemArchitecture = Architecture.X86;
} }
@ -273,32 +273,11 @@ namespace MediaBrowser.Server.Mono.Native
break; break;
} }
info.DownloadUrls = GetDownloadUrls(environment); // No version available - user requirement
info.DownloadUrls = new string[] { };
return info; return info;
} }
private static string[] GetDownloadUrls(NativeEnvironment environment)
{
switch (environment.OperatingSystem)
{
case OperatingSystem.Linux:
switch (environment.SystemArchitecture)
{
case Architecture.X64:
return new[]
{
"https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
};
}
break;
}
// No version available
return new string[] { };
}
} }
public class NullPowerManagement : IPowerManagement public class NullPowerManagement : IPowerManagement