|
@@ -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 (request.Width.HasValue && request.Height.HasValue)
|
|
|
|
|
|
+ if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
{
|
|
- var widthParam = request.Width.Value.ToString(UsCulture);
|
|
|
|
- var heightParam = request.Height.Value.ToString(UsCulture);
|
|
|
|
|
|
+ // Work around vaapi's reduced scaling features
|
|
|
|
+ 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
|
|
|
|
- else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
|
|
|
|
- {
|
|
|
|
- var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
|
|
|
- var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
|
|
|
|
|
|
+ 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));
|
|
|
|
+ }
|
|
|
|
|
|
- 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));
|
|
|
|
- }
|
|
|
|
|
|
+ outputWidth = 2 * Math.Truncate(outputWidth / 2);
|
|
|
|
+ outputHeight = 2 * Math.Truncate(outputHeight / 2);
|
|
|
|
|
|
- // If a fixed width was requested
|
|
|
|
- else if (request.Width.HasValue)
|
|
|
|
|
|
+ if (outputWidth != inputWidth || outputHeight != inputHeight)
|
|
|
|
+ {
|
|
|
|
+ filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- var widthParam = request.Width.Value.ToString(UsCulture);
|
|
|
|
|
|
+ // 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={0}:trunc(ow/a/2)*2", widthParam));
|
|
|
|
- }
|
|
|
|
|
|
+ filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
|
|
|
|
+ }
|
|
|
|
|
|
- // If a fixed height was requested
|
|
|
|
- else if (request.Height.HasValue)
|
|
|
|
- {
|
|
|
|
- var heightParam = request.Height.Value.ToString(UsCulture);
|
|
|
|
|
|
+ // 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(oh*a/2)*2:{0}", heightParam));
|
|
|
|
- }
|
|
|
|
|
|
+ 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 max width was requested
|
|
|
|
- else if (request.MaxWidth.HasValue)
|
|
|
|
- {
|
|
|
|
- var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
|
|
|
|
|
+ // If a fixed width was requested
|
|
|
|
+ else if (request.Width.HasValue)
|
|
|
|
+ {
|
|
|
|
+ var widthParam = request.Width.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={0}:trunc(ow/a/2)*2", widthParam));
|
|
|
|
+ }
|
|
|
|
|
|
- // If a max height was requested
|
|
|
|
- else if (request.MaxHeight.HasValue)
|
|
|
|
- {
|
|
|
|
- var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
|
|
|
|
|
|
+ // 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));
|
|
|
|
|
|
+ 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();
|
|
}
|
|
}
|
|
|
|
|