Browse Source

Disambiguate vpx to vp8 or vp9

Chris Tam 3 năm trước cách đây
mục cha
commit
b205d5a032

+ 1 - 0
CONTRIBUTORS.md

@@ -46,6 +46,7 @@
  - [fruhnow](https://github.com/fruhnow)
  - [geilername](https://github.com/geilername)
  - [gnattu](https://github.com/gnattu)
+ - [GodTamIt](https://github.com/GodTamIt)
  - [grafixeyehero](https://github.com/grafixeyehero)
  - [h1nk](https://github.com/h1nk)
  - [hawken93](https://github.com/hawken93)

+ 2 - 2
Jellyfin.Api/Controllers/AudioController.cs

@@ -76,7 +76,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -241,7 +241,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>

+ 4 - 4
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -150,7 +150,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -316,7 +316,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -482,7 +482,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -813,7 +813,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>

+ 1 - 1
Jellyfin.Api/Controllers/VideoHlsController.cs

@@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>

+ 8 - 8
Jellyfin.Api/Controllers/VideosController.cs

@@ -310,7 +310,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -456,9 +456,9 @@ namespace Jellyfin.Api.Controllers
                 StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
 
                 await new ProgressiveFileCopier(state.DirectStreamProvider, null, _transcodingJobHelper, CancellationToken.None)
-                    {
-                        AllowEndOfFile = false
-                    }.WriteToAsync(Response.Body, CancellationToken.None)
+                {
+                    AllowEndOfFile = false
+                }.WriteToAsync(Response.Body, CancellationToken.None)
                     .ConfigureAwait(false);
 
                 // TODO (moved from MediaBrowser.Api): Don't hardcode contentType
@@ -495,9 +495,9 @@ namespace Jellyfin.Api.Controllers
                 if (state.MediaSource.IsInfiniteStream)
                 {
                     await new ProgressiveFileCopier(state.MediaPath, null, _transcodingJobHelper, CancellationToken.None)
-                        {
-                            AllowEndOfFile = false
-                        }.WriteToAsync(Response.Body, CancellationToken.None)
+                    {
+                        AllowEndOfFile = false
+                    }.WriteToAsync(Response.Body, CancellationToken.None)
                         .ConfigureAwait(false);
 
                     return File(Response.Body, contentType);
@@ -570,7 +570,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
         /// <param name="liveStreamId">The live stream id.</param>
         /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
-        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+        /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
         /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
         /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
         /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>

+ 3 - 1
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -439,7 +439,9 @@ namespace Jellyfin.Api.Helpers
                     return ".ogv";
                 }
 
-                if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
+                if (string.Equals(videoCodec, "vp8", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoCodec, "vp9", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
                 {
                     return ".webm";
                 }

+ 95 - 55
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -206,11 +206,17 @@ namespace MediaBrowser.Controller.MediaEncoding
                     return GetH264Encoder(state, encodingOptions);
                 }
 
-                if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
+                if (string.Equals(codec, "vp8", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
                 {
                     return "libvpx";
                 }
 
+                if (string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
+                {
+                    return "libvpx-vp9";
+                }
+
                 if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase))
                 {
                     return "wmv2";
@@ -442,7 +448,8 @@ namespace MediaBrowser.Controller.MediaEncoding
 
             if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
             {
-                return "vpx";
+                // TODO: this may not always mean VP8, as the codec ages
+                return "vp8";
             }
 
             if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
@@ -558,12 +565,12 @@ namespace MediaBrowser.Controller.MediaEncoding
                     {
                         if (isOpenclTonemappingSupported && !isVppTonemappingSupported)
                         {
-                           arg.Append("-init_hw_device vaapi=va:")
-                                .Append(options.VaapiDevice)
-                                .Append(" -init_hw_device opencl=ocl@va ")
-                                .Append("-hwaccel_device va ")
-                                .Append("-hwaccel_output_format vaapi ")
-                                .Append("-filter_hw_device ocl ");
+                            arg.Append("-init_hw_device vaapi=va:")
+                                 .Append(options.VaapiDevice)
+                                 .Append(" -init_hw_device opencl=ocl@va ")
+                                 .Append("-hwaccel_device va ")
+                                 .Append("-hwaccel_output_format vaapi ")
+                                 .Append("-filter_hw_device ocl ");
                         }
                         else
                         {
@@ -772,49 +779,37 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         public string GetVideoBitrateParam(EncodingJobInfo state, string videoCodec)
         {
-            var bitrate = state.OutputVideoBitrate;
-
-            if (bitrate.HasValue)
+            if (state.OutputVideoBitrate == null)
             {
-                if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
-                {
-                    // When crf is used with vpx, b:v becomes a max rate
-                    // https://trac.ffmpeg.org/wiki/Encode/VP9
-                    return string.Format(
-                        CultureInfo.InvariantCulture,
-                        " -maxrate:v {0} -bufsize:v {1} -b:v {0}",
-                        bitrate.Value,
-                        bitrate.Value * 2);
-                }
+                return string.Empty;
+            }
 
-                if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
-                {
-                    return string.Format(
-                        CultureInfo.InvariantCulture,
-                        " -b:v {0}",
-                        bitrate.Value);
-                }
+            int bitrate = state.OutputVideoBitrate.Value;
 
-                if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase) ||
-                    string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
-                {
-                    // h264
-                    return string.Format(
-                        CultureInfo.InvariantCulture,
-                        " -maxrate {0} -bufsize {1}",
-                        bitrate.Value,
-                        bitrate.Value * 2);
-                }
+            // Currently use the same buffer size for all encoders
+            int bufsize = bitrate * 2;
 
-                // h264
-                return string.Format(
-                    CultureInfo.InvariantCulture,
-                    " -b:v {0} -maxrate {0} -bufsize {1}",
-                    bitrate.Value,
-                    bitrate.Value * 2);
+            if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(videoCodec, "libvpx-vp9", StringComparison.OrdinalIgnoreCase))
+            {
+                // When crf is used with vpx, b:v becomes a max rate
+                // https://trac.ffmpeg.org/wiki/Encode/VP8
+                // https://trac.ffmpeg.org/wiki/Encode/VP9
+                return FormattableString.Invariant($" -maxrate:v {bitrate} -bufsize:v {bufsize} -b:v {bitrate}");
             }
 
-            return string.Empty;
+            if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
+            {
+                return FormattableString.Invariant($" -b:v {bitrate}");
+            }
+
+            if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
+            {
+                return FormattableString.Invariant($" -maxrate {bitrate} -bufsize {bufsize}");
+            }
+
+            return FormattableString.Invariant($" -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
         }
 
         public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level)
@@ -1197,7 +1192,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                     param += " -header_insertion_mode gop -gops_per_idr 1";
                 }
             }
-            else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm
+            else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // vp8
             {
                 // Values 0-3, 0 being highest quality but slower
                 var profileScore = 0;
@@ -1225,6 +1220,55 @@ namespace MediaBrowser.Controller.MediaEncoding
                     qmin,
                     qmax);
             }
+            else if (string.Equals(videoEncoder, "libvpx-vp9", StringComparison.OrdinalIgnoreCase)) // vp9
+            {
+                // When `-deadline` is set to `good` or `best`, `-cpu-used` ranges from 0-5.
+                // When `-deadline` is set to `realtime`, `-cpu-used` ranges from 0-15.
+                // Resources:
+                //   * https://trac.ffmpeg.org/wiki/Encode/VP9
+                //   * https://superuser.com/questions/1586934
+                //   * https://developers.google.com/media/vp9
+                param += encodingOptions.EncoderPreset switch
+                {
+                    "veryslow" => " -deadline best -cpu-used 0",
+                    "slower" => " -deadline best -cpu-used 2",
+                    "slow" => " -deadline best -cpu-used 3",
+                    "medium" => " -deadline good -cpu-used 0",
+                    "fast" => " -deadline good -cpu-used 1",
+                    "faster" => " -deadline good -cpu-used 2",
+                    "veryfast" => " -deadline good -cpu-used 3",
+                    "superfast" => " -deadline good -cpu-used 4",
+                    "ultrafast" => " -deadline good -cpu-used 5",
+                    _ => " -deadline good -cpu-used 1"
+                };
+
+                // TODO: until VP9 gets its own CRF setting, base CRF on H.265.
+                int h265Crf = encodingOptions.H265Crf;
+                int defaultVp9Crf = 31;
+                if (h265Crf >= 0 && h265Crf <= 51)
+                {
+                    // This conversion factor is chosen to match the default CRF for H.265 to the
+                    // recommended 1080p CRF from Google. The factor also maps the logarithmic CRF
+                    // scale of x265 [0, 51] to that of VP9 [0, 63] relatively well.
+
+                    // Resources:
+                    //   * https://developers.google.com/media/vp9/settings/vod
+                    const float H265ToVp9CrfConversionFactor = 1.12F;
+
+                    var vp9Crf = Convert.ToInt32(h265Crf * H265ToVp9CrfConversionFactor);
+
+                    // Encoder allows for CRF values in the range [0, 63].
+                    vp9Crf = Math.Clamp(vp9Crf, 0, 63);
+
+                    param += FormattableString.Invariant($" -crf {vp9Crf}");
+                }
+                else
+                {
+                    param += FormattableString.Invariant($" -crf {defaultVp9Crf}");
+                }
+
+                param += " -row-mt 1 -profile 1";
+            }
             else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase))
             {
                 param += " -mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2";
@@ -3149,20 +3193,16 @@ namespace MediaBrowser.Controller.MediaEncoding
 #nullable enable
         public static int GetNumberOfThreads(EncodingJobInfo? state, EncodingOptions encodingOptions, string? outputVideoCodec)
         {
-            if (string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
-            {
-                // per docs:
-                // -threads    number of threads to use for encoding, can't be 0 [auto] with VP8
-                //             (recommended value : number of real cores - 1)
-                return Math.Max(Environment.ProcessorCount - 1, 1);
-            }
+            // VP8 and VP9 encoders must have their thread counts set.
+            bool mustSetThreadCount = string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(outputVideoCodec, "libvpx-vp9", StringComparison.OrdinalIgnoreCase);
 
             var threads = state?.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount;
 
-            // Automatic
             if (threads <= 0)
             {
-                return 0;
+                // Automatically set thread count
+                return mustSetThreadCount ? Math.Max(Environment.ProcessorCount - 1, 1) : 0;
             }
             else if (threads >= Environment.ProcessorCount)
             {