mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-11 11:33:02 +03:00
Compare commits
81 Commits
v10.8.9
...
release-10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e93d03d8cb | ||
|
|
a656799dc8 | ||
|
|
83d2c69516 | ||
|
|
204fdeb035 | ||
|
|
c4cdcb73fc | ||
|
|
1e0bd32358 | ||
|
|
bf5f00a383 | ||
|
|
0430ffecb6 | ||
|
|
85cfd080f1 | ||
|
|
a173d01139 | ||
|
|
3e7fad55de | ||
|
|
8cd685a4e9 | ||
|
|
9d565bbb83 | ||
|
|
ab855af95e | ||
|
|
0bac1eab98 | ||
|
|
97c2ba0115 | ||
|
|
4df1003029 | ||
|
|
3269ce56ca | ||
|
|
ce8eddd484 | ||
|
|
5e2872509a | ||
|
|
d3f4dcf6f6 | ||
|
|
be5e10ac37 | ||
|
|
b85a0288a7 | ||
|
|
f8fd851961 | ||
|
|
757f88b1a2 | ||
|
|
fa732bf4a1 | ||
|
|
4f6edd9c3c | ||
|
|
768497d0ff | ||
|
|
f1dc7d3a66 | ||
|
|
a732a28229 | ||
|
|
17626b8e48 | ||
|
|
98c6c34fbb | ||
|
|
bec8d7b3f5 | ||
|
|
2acae258b8 | ||
|
|
643df48707 | ||
|
|
2b98ce052e | ||
|
|
702347df50 | ||
|
|
9e5aa3e87e | ||
|
|
2cd29d1cfd | ||
|
|
eba95cc7f0 | ||
|
|
82ad2633fd | ||
|
|
d9f5619c9a | ||
|
|
d5a8419bc5 | ||
|
|
c448a4f6a5 | ||
|
|
faac37bcf9 | ||
|
|
5921379a29 | ||
|
|
79bb7560dc | ||
|
|
bf37db7f42 | ||
|
|
0ad70bb699 | ||
|
|
e6313d01eb | ||
|
|
876a6b9aec | ||
|
|
e0344353cd | ||
|
|
9799136daf | ||
|
|
3a5503be5f | ||
|
|
2cc0869144 | ||
|
|
3d735e242a | ||
|
|
31712e5da9 | ||
|
|
060097703b | ||
|
|
233e079e58 | ||
|
|
eafd785eb6 | ||
|
|
9908dad045 | ||
|
|
2b4bf81575 | ||
|
|
0c7ceb1545 | ||
|
|
173a963dbf | ||
|
|
6821a2ab35 | ||
|
|
efc79295de | ||
|
|
4d1a583297 | ||
|
|
c94a99fced | ||
|
|
5fdea32dca | ||
|
|
d1c668e230 | ||
|
|
6d662b6587 | ||
|
|
22a8283a9e | ||
|
|
0d9d2e0690 | ||
|
|
edaba7dbe5 | ||
|
|
e8b0ae07af | ||
|
|
c807712246 | ||
|
|
9a14a624a8 | ||
|
|
037eeed746 | ||
|
|
8ecb9558e2 | ||
|
|
8d04c98e35 | ||
|
|
09f1c7f535 |
@@ -160,6 +160,8 @@
|
||||
- [vgambier](https://github.com/vgambier)
|
||||
- [MinecraftPlaye](https://github.com/MinecraftPlaye)
|
||||
- [RealGreenDragon](https://github.com/RealGreenDragon)
|
||||
- [TheTyrius](https://github.com/TheTyrius)
|
||||
- [Çağrı Sakaoğlu](https://github.com/ilovepilav)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
@@ -228,3 +230,6 @@
|
||||
- [gnuyent](https://github.com/gnuyent)
|
||||
- [Matthew Jones](https://github.com/matthew-jones-uk)
|
||||
- [Jakob Kukla](https://github.com/jakobkukla)
|
||||
- [Utku Özdemir](https://github.com/utkuozdemir)
|
||||
- [JPUC1143](https://github.com/Jpuc1143/)
|
||||
- [0x25CBFC4F](https://github.com/0x25CBFC4F)
|
||||
|
||||
@@ -250,7 +250,6 @@ namespace Emby.Naming.Common
|
||||
".sfx",
|
||||
".shn",
|
||||
".sid",
|
||||
".spc",
|
||||
".stm",
|
||||
".strm",
|
||||
".ult",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<PackageId>Jellyfin.Naming</PackageId>
|
||||
<VersionPrefix>10.8.9</VersionPrefix>
|
||||
<VersionPrefix>10.8.13</VersionPrefix>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -183,6 +183,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
private static void SetProviderIdFromPath(Series item, string path)
|
||||
{
|
||||
var justName = Path.GetFileName(path.AsSpan());
|
||||
|
||||
var imdbId = justName.GetAttributeValue("imdbid");
|
||||
if (!string.IsNullOrEmpty(imdbId))
|
||||
{
|
||||
item.SetProviderId(MetadataProvider.Imdb, imdbId);
|
||||
}
|
||||
|
||||
var tvdbId = justName.GetAttributeValue("tvdbid");
|
||||
if (!string.IsNullOrEmpty(tvdbId))
|
||||
|
||||
@@ -324,9 +324,15 @@ namespace Emby.Server.Implementations.Updates
|
||||
}
|
||||
|
||||
_completedInstallationsInternal.Add(package);
|
||||
await _eventManager.PublishAsync(isUpdate
|
||||
? (GenericEventArgs<InstallationInfo>)new PluginUpdatedEventArgs(package)
|
||||
: new PluginInstalledEventArgs(package)).ConfigureAwait(false);
|
||||
|
||||
if (isUpdate)
|
||||
{
|
||||
await _eventManager.PublishAsync(new PluginUpdatedEventArgs(package)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _eventManager.PublishAsync(new PluginInstalledEventArgs(package)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_applicationHost.NotifyPendingRestart();
|
||||
}
|
||||
|
||||
@@ -91,18 +91,18 @@ namespace Jellyfin.Api.Controllers
|
||||
[ProducesAudioFile]
|
||||
public async Task<ActionResult> GetAudioStream(
|
||||
[FromRoute, Required] Guid itemId,
|
||||
[FromQuery] string? container,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? container,
|
||||
[FromQuery] bool? @static,
|
||||
[FromQuery] string? @params,
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -132,8 +132,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -262,12 +262,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -297,8 +297,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
|
||||
@@ -125,12 +125,14 @@ namespace Jellyfin.Api.Controllers
|
||||
/// <param name="mediaEncoderPath">Media encoder path form body.</param>
|
||||
/// <response code="204">Media encoder path updated.</response>
|
||||
/// <returns>Status.</returns>
|
||||
[Obsolete("This endpoint is obsolete.")]
|
||||
[HttpPost("MediaEncoder/Path")]
|
||||
[Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public ActionResult UpdateMediaEncoderPath([FromBody, Required] MediaEncoderPathDto mediaEncoderPath)
|
||||
{
|
||||
_mediaEncoder.UpdateEncoderPath(mediaEncoderPath.Path, mediaEncoderPath.PathType);
|
||||
// API ENDPOINT DISABLED (NOOP) FOR SECURITY PURPOSES
|
||||
//_mediaEncoder.UpdateEncoderPath(mediaEncoderPath.Path, mediaEncoderPath.PathType);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Jellyfin.Api.Constants;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.Models.PlaybackDtos;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.MediaEncoding.Hls.Playlist;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
@@ -21,6 +22,7 @@ using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.MediaEncoding.Encoder;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.IO;
|
||||
@@ -172,18 +174,18 @@ namespace Jellyfin.Api.Controllers
|
||||
[ProducesPlaylistFile]
|
||||
public async Task<ActionResult> GetLiveHlsStream(
|
||||
[FromRoute, Required] Guid itemId,
|
||||
[FromQuery] string? container,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? container,
|
||||
[FromQuery] bool? @static,
|
||||
[FromQuery] string? @params,
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -213,8 +215,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -424,12 +426,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery, Required] string mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -461,8 +463,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -594,12 +596,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery, Required] string mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -630,8 +632,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -760,12 +762,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -797,8 +799,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -928,12 +930,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -964,8 +966,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -1105,12 +1107,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -1142,8 +1144,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -1286,12 +1288,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -1322,8 +1324,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -1662,8 +1664,8 @@ namespace Jellyfin.Api.Controllers
|
||||
startNumber.ToString(CultureInfo.InvariantCulture),
|
||||
baseUrlParam,
|
||||
isEventPlaylist ? "event" : "vod",
|
||||
outputTsArg,
|
||||
outputPath).Trim();
|
||||
EncodingUtils.NormalizePath(outputTsArg),
|
||||
EncodingUtils.NormalizePath(outputPath)).Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1693,7 +1695,7 @@ namespace Jellyfin.Api.Controllers
|
||||
|
||||
audioTranscodeParams += "-acodec " + audioCodec;
|
||||
|
||||
if (state.OutputAudioBitrate.HasValue)
|
||||
if (state.OutputAudioBitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(state.ActualOutputAudioCodec, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
@@ -1714,11 +1716,11 @@ namespace Jellyfin.Api.Controllers
|
||||
|
||||
// dts, flac, opus and truehd are experimental in mp4 muxer
|
||||
var strictArgs = string.Empty;
|
||||
|
||||
if (string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(state.ActualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(state.ActualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase))
|
||||
var actualOutputAudioCodec = state.ActualOutputAudioCodec;
|
||||
if (string.Equals(actualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(actualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(actualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(actualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
strictArgs = " -strict -2";
|
||||
}
|
||||
@@ -1747,8 +1749,7 @@ namespace Jellyfin.Api.Controllers
|
||||
}
|
||||
|
||||
var bitrate = state.OutputAudioBitrate;
|
||||
|
||||
if (bitrate.HasValue)
|
||||
if (bitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(actualOutputAudioCodec, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
@@ -1843,7 +1844,11 @@ namespace Jellyfin.Api.Controllers
|
||||
// args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
|
||||
|
||||
// video processing filters.
|
||||
args += _encodingHelper.GetVideoProcessingFilterParam(state, _encodingOptions, codec);
|
||||
var videoProcessParam = _encodingHelper.GetVideoProcessingFilterParam(state, _encodingOptions, codec);
|
||||
|
||||
var negativeMapArgs = _encodingHelper.GetNegativeMapArgsByFilters(state, videoProcessParam);
|
||||
|
||||
args = negativeMapArgs + args + videoProcessParam;
|
||||
|
||||
// -start_at_zero is necessary to use with -ss when seeking,
|
||||
// otherwise the target position cannot be determined.
|
||||
|
||||
@@ -102,13 +102,13 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] Guid? userId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] int? maxAudioChannels,
|
||||
[FromQuery] int? transcodingAudioChannels,
|
||||
[FromQuery] int? maxStreamingBitrate,
|
||||
[FromQuery] int? audioBitRate,
|
||||
[FromQuery] long? startTimeTicks,
|
||||
[FromQuery] string? transcodingContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? transcodingContainer,
|
||||
[FromQuery] string? transcodingProtocol,
|
||||
[FromQuery] int? maxAudioSampleRate,
|
||||
[FromQuery] int? maxAudioBitDepth,
|
||||
|
||||
@@ -318,18 +318,18 @@ namespace Jellyfin.Api.Controllers
|
||||
[ProducesVideoFile]
|
||||
public async Task<ActionResult> GetVideoStream(
|
||||
[FromRoute, Required] Guid itemId,
|
||||
[FromQuery] string? container,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? container,
|
||||
[FromQuery] bool? @static,
|
||||
[FromQuery] string? @params,
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -361,8 +361,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
@@ -578,12 +578,12 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? deviceProfileId,
|
||||
[FromQuery] string? playSessionId,
|
||||
[FromQuery] string? segmentContainer,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer,
|
||||
[FromQuery] int? segmentLength,
|
||||
[FromQuery] int? minSegments,
|
||||
[FromQuery] string? mediaSourceId,
|
||||
[FromQuery] string? deviceId,
|
||||
[FromQuery] string? audioCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec,
|
||||
[FromQuery] bool? enableAutoStreamCopy,
|
||||
[FromQuery] bool? allowVideoStreamCopy,
|
||||
[FromQuery] bool? allowAudioStreamCopy,
|
||||
@@ -615,8 +615,8 @@ namespace Jellyfin.Api.Controllers
|
||||
[FromQuery] int? cpuCoreLimit,
|
||||
[FromQuery] string? liveStreamId,
|
||||
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||
[FromQuery] string? videoCodec,
|
||||
[FromQuery] string? subtitleCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec,
|
||||
[FromQuery][RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec,
|
||||
[FromQuery] string? transcodeReasons,
|
||||
[FromQuery] int? audioStreamIndex,
|
||||
[FromQuery] int? videoStreamIndex,
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
@@ -203,6 +204,13 @@ namespace Jellyfin.Api.Helpers
|
||||
|
||||
if (state.VideoStream != null && state.VideoRequest != null)
|
||||
{
|
||||
// Provide a workaround for the case issue between flac and fLaC.
|
||||
var flacWaPlaylist = ApplyFlacCaseWorkaround(state, basicPlaylist.ToString());
|
||||
if (!string.IsNullOrEmpty(flacWaPlaylist))
|
||||
{
|
||||
builder.Append(flacWaPlaylist);
|
||||
}
|
||||
|
||||
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
||||
|
||||
// Provide SDR HEVC entrance for backward compatibility.
|
||||
@@ -221,10 +229,25 @@ namespace Jellyfin.Api.Helpers
|
||||
sdrVideoUrl += "&AllowVideoStreamCopy=false";
|
||||
|
||||
var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
|
||||
var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
|
||||
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
|
||||
var sdrOutputAudioBitrate = 0;
|
||||
if (EncodingHelper.LosslessAudioCodecs.Contains(state.VideoRequest.AudioCodec, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sdrOutputAudioBitrate = state.AudioStream.BitRate ?? 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream, state.OutputAudioChannels) ?? 0;
|
||||
}
|
||||
|
||||
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
|
||||
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
|
||||
var sdrPlaylist = AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
|
||||
|
||||
// Provide a workaround for the case issue between flac and fLaC.
|
||||
flacWaPlaylist = ApplyFlacCaseWorkaround(state, sdrPlaylist.ToString());
|
||||
if (!string.IsNullOrEmpty(flacWaPlaylist))
|
||||
{
|
||||
builder.Append(flacWaPlaylist);
|
||||
}
|
||||
|
||||
// Restore the video codec
|
||||
state.OutputVideoCodec = "copy";
|
||||
@@ -254,6 +277,13 @@ namespace Jellyfin.Api.Helpers
|
||||
state.VideoStream.Level = originalLevel;
|
||||
var newPlaylist = ReplacePlaylistCodecsField(basicPlaylist, playlistCodecsField, newPlaylistCodecsField);
|
||||
builder.Append(newPlaylist);
|
||||
|
||||
// Provide a workaround for the case issue between flac and fLaC.
|
||||
flacWaPlaylist = ApplyFlacCaseWorkaround(state, newPlaylist);
|
||||
if (!string.IsNullOrEmpty(flacWaPlaylist))
|
||||
{
|
||||
builder.Append(flacWaPlaylist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,6 +642,11 @@ namespace Jellyfin.Api.Helpers
|
||||
return HlsCodecStringHelpers.GetALACString();
|
||||
}
|
||||
|
||||
if (string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return HlsCodecStringHelpers.GetOPUSString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
@@ -710,7 +745,19 @@ namespace Jellyfin.Api.Helpers
|
||||
return oldPlaylist.Replace(
|
||||
oldValue.ToString(),
|
||||
newValue.ToString(),
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private string ApplyFlacCaseWorkaround(StreamState state, string srcPlaylist)
|
||||
{
|
||||
if (!string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var newPlaylist = srcPlaylist.Replace(",flac\"", ",fLaC\"", StringComparison.Ordinal);
|
||||
|
||||
return newPlaylist.Contains(",fLaC\"", StringComparison.Ordinal) ? newPlaylist : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,18 @@ namespace Jellyfin.Api.Helpers
|
||||
/// <summary>
|
||||
/// Codec name for FLAC.
|
||||
/// </summary>
|
||||
public const string FLAC = "fLaC";
|
||||
public const string FLAC = "flac";
|
||||
|
||||
/// <summary>
|
||||
/// Codec name for ALAC.
|
||||
/// </summary>
|
||||
public const string ALAC = "alac";
|
||||
|
||||
/// <summary>
|
||||
/// Codec name for OPUS.
|
||||
/// </summary>
|
||||
public const string OPUS = "opus";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a MP3 codec string.
|
||||
/// </summary>
|
||||
@@ -101,6 +106,15 @@ namespace Jellyfin.Api.Helpers
|
||||
return ALAC;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an OPUS codec string.
|
||||
/// </summary>
|
||||
/// <returns>OPUS codec string.</returns>
|
||||
public static string GetOPUSString()
|
||||
{
|
||||
return OPUS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a H.264 codec string.
|
||||
/// </summary>
|
||||
|
||||
@@ -182,12 +182,18 @@ namespace Jellyfin.Api.Helpers
|
||||
: GetOutputFileExtension(state, mediaSource);
|
||||
}
|
||||
|
||||
var outputAudioCodec = streamingRequest.AudioCodec;
|
||||
if (EncodingHelper.LosslessAudioCodecs.Contains(outputAudioCodec))
|
||||
{
|
||||
state.OutputAudioBitrate = state.AudioStream.BitRate ?? 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream, state.OutputAudioChannels) ?? 0;
|
||||
}
|
||||
|
||||
state.OutputAudioCodec = outputAudioCodec;
|
||||
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
|
||||
|
||||
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream);
|
||||
|
||||
state.OutputAudioCodec = streamingRequest.AudioCodec;
|
||||
|
||||
state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
|
||||
|
||||
if (state.VideoRequest != null)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj" />
|
||||
<ProjectReference Include="..\src\Jellyfin.MediaEncoding.Hls\Jellyfin.MediaEncoding.Hls.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<PackageId>Jellyfin.Data</PackageId>
|
||||
<VersionPrefix>10.8.9</VersionPrefix>
|
||||
<VersionPrefix>10.8.13</VersionPrefix>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<PackageId>Jellyfin.Common</PackageId>
|
||||
<VersionPrefix>10.8.9</VersionPrefix>
|
||||
<VersionPrefix>10.8.13</VersionPrefix>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -170,6 +170,11 @@ namespace MediaBrowser.Common.Net
|
||||
address = address.MapToIPv4();
|
||||
}
|
||||
|
||||
if (address.AddressFamily != AddressFamily)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var (altAddress, altPrefix) = NetworkAddressOf(address, PrefixLength);
|
||||
return NetworkAddress.Address.Equals(altAddress) && NetworkAddress.PrefixLength >= altPrefix;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@ namespace MediaBrowser.Controller.ClientEvent
|
||||
{
|
||||
var fileName = $"upload_{clientName}_{clientVersion}_{DateTime.UtcNow:yyyyMMddHHmmss}_{Guid.NewGuid():N}.log";
|
||||
var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName);
|
||||
if (!Path.GetFullPath(logFilePath).StartsWith(_applicationPaths.LogDirectoryPath, StringComparison.Ordinal))
|
||||
{
|
||||
throw new ArgumentException("Path resolved to filename not in log directory");
|
||||
}
|
||||
|
||||
await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
|
||||
await fileContents.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
return fileName;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// The supported image extensions.
|
||||
/// </summary>
|
||||
public static readonly string[] SupportedImageExtensions
|
||||
= new[] { ".png", ".jpg", ".jpeg", ".tbn", ".gif" };
|
||||
= new[] { ".png", ".jpg", ".jpeg", ".webp", ".tbn", ".gif" };
|
||||
|
||||
private static readonly List<string> _supportedExtensions = new List<string>(SupportedImageExtensions)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<PackageId>Jellyfin.Controller</PackageId>
|
||||
<VersionPrefix>10.8.9</VersionPrefix>
|
||||
<VersionPrefix>10.8.13</VersionPrefix>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -25,6 +25,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class EncodingHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// The codec validation regex.
|
||||
/// </summary>
|
||||
public const string ValidationRegex = @"^[a-zA-Z0-9\-\._,|]{0,40}$";
|
||||
|
||||
private const string QsvAlias = "qs";
|
||||
private const string VaapiAlias = "va";
|
||||
private const string D3d11vaAlias = "dx11";
|
||||
@@ -36,11 +41,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
private readonly ISubtitleEncoder _subtitleEncoder;
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled);
|
||||
|
||||
// i915 hang was fixed by linux 6.2 (3f882f2)
|
||||
private readonly Version _minKerneli915Hang = new Version(5, 18);
|
||||
private readonly Version _maxKerneli915Hang = new Version(6, 1, 3);
|
||||
private readonly Version _minFixedKernel60i915Hang = new Version(6, 0, 18);
|
||||
|
||||
private readonly Version _minFFmpegImplictHwaccel = new Version(6, 0);
|
||||
private readonly Version _minFFmpegHwaUnsafeOutput = new Version(6, 0);
|
||||
private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, 3);
|
||||
|
||||
private static readonly string[] _videoProfilesH264 = new[]
|
||||
{
|
||||
"ConstrainedBaseline",
|
||||
@@ -59,6 +70,16 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
"Main10"
|
||||
};
|
||||
|
||||
public static readonly string[] LosslessAudioCodecs = new string[]
|
||||
{
|
||||
"alac",
|
||||
"ape",
|
||||
"flac",
|
||||
"mlp",
|
||||
"truehd",
|
||||
"wavpack"
|
||||
};
|
||||
|
||||
public EncodingHelper(
|
||||
IApplicationPaths appPaths,
|
||||
IMediaEncoder mediaEncoder,
|
||||
@@ -239,7 +260,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return "libtheora";
|
||||
}
|
||||
|
||||
return codec.ToLowerInvariant();
|
||||
if (_validationRegex.IsMatch(codec))
|
||||
{
|
||||
return codec.ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
return "copy";
|
||||
@@ -262,7 +286,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
public static string GetInputFormat(string container)
|
||||
{
|
||||
if (string.IsNullOrEmpty(container))
|
||||
if (string.IsNullOrEmpty(container) || !_validationRegex.IsMatch(container))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -509,6 +533,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var codec = state.OutputAudioCodec;
|
||||
|
||||
if (!_validationRegex.IsMatch(codec))
|
||||
{
|
||||
codec = "aac";
|
||||
}
|
||||
|
||||
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Use libfdk_aac for better audio quality if using custom build of FFmpeg which has fdk_aac support
|
||||
@@ -545,6 +574,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return "flac";
|
||||
}
|
||||
|
||||
if (string.Equals(codec, "dts", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "dca";
|
||||
}
|
||||
|
||||
return codec.ToLowerInvariant();
|
||||
}
|
||||
|
||||
@@ -608,14 +642,20 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
private string GetVaapiDeviceArgs(string renderNodePath, string driver, string kernelDriver, string alias)
|
||||
{
|
||||
alias ??= VaapiAlias;
|
||||
renderNodePath = renderNodePath ?? "/dev/dri/renderD128";
|
||||
var options = string.IsNullOrEmpty(driver)
|
||||
? renderNodePath
|
||||
: ",driver=" + driver + (string.IsNullOrEmpty(kernelDriver) ? string.Empty : ",kernel_driver=" + kernelDriver);
|
||||
|
||||
// 'renderNodePath' has higher priority than 'kernelDriver'
|
||||
var driverOpts = string.IsNullOrEmpty(renderNodePath)
|
||||
? (string.IsNullOrEmpty(kernelDriver) ? string.Empty : ",kernel_driver=" + kernelDriver)
|
||||
: renderNodePath;
|
||||
|
||||
// 'driver' behaves similarly to env LIBVA_DRIVER_NAME
|
||||
driverOpts += string.IsNullOrEmpty(driver) ? string.Empty : ",driver=" + driver;
|
||||
|
||||
var options = string.IsNullOrEmpty(driverOpts) ? string.Empty : ":" + driverOpts;
|
||||
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
" -init_hw_device vaapi={0}:{1}",
|
||||
" -init_hw_device vaapi={0}{1}",
|
||||
alias,
|
||||
options);
|
||||
}
|
||||
@@ -647,9 +687,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
public string GetGraphicalSubCanvasSize(EncodingJobInfo state)
|
||||
{
|
||||
// DVBSUB and DVDSUB use the fixed canvas size 720x576
|
||||
if (state.SubtitleStream != null
|
||||
&& state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
|
||||
&& !state.SubtitleStream.IsTextSubtitleStream)
|
||||
&& !state.SubtitleStream.IsTextSubtitleStream
|
||||
&& !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(state.SubtitleStream.Codec, "DVDSUB", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var inW = state.VideoStream?.Width;
|
||||
var inH = state.VideoStream?.Height;
|
||||
@@ -717,14 +760,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (_mediaEncoder.IsVaapiDeviceInteliHD)
|
||||
{
|
||||
args.Append(GetVaapiDeviceArgs(null, "iHD", null, VaapiAlias));
|
||||
args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "iHD", null, VaapiAlias));
|
||||
}
|
||||
else if (_mediaEncoder.IsVaapiDeviceInteli965)
|
||||
{
|
||||
// Only override i965 since it has lower priority than iHD in libva lookup.
|
||||
Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME", "i965");
|
||||
Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME_JELLYFIN", "i965");
|
||||
args.Append(GetVaapiDeviceArgs(null, "i965", null, VaapiAlias));
|
||||
args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "i965", null, VaapiAlias));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1291,6 +1334,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
args += keyFrameArg + gopArg;
|
||||
}
|
||||
|
||||
// global_header produced by AMD VA-API encoder causes non-playable fMP4 on iOS
|
||||
if (codec.Contains("vaapi", StringComparison.OrdinalIgnoreCase)
|
||||
&& _mediaEncoder.IsVaapiDeviceAmd)
|
||||
{
|
||||
args += " -flags:v -global_header";
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -1441,11 +1491,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
param += " -preset p7";
|
||||
break;
|
||||
|
||||
case "slow":
|
||||
case "slower":
|
||||
param += " -preset p6";
|
||||
break;
|
||||
|
||||
case "slower":
|
||||
case "slow":
|
||||
param += " -preset p5";
|
||||
break;
|
||||
|
||||
@@ -1478,8 +1528,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
switch (encodingOptions.EncoderPreset)
|
||||
{
|
||||
case "veryslow":
|
||||
case "slow":
|
||||
case "slower":
|
||||
case "slow":
|
||||
param += " -quality quality";
|
||||
break;
|
||||
|
||||
@@ -1952,9 +2002,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
}
|
||||
|
||||
// Video bitrate must fall within requested value
|
||||
// Audio bitrate must fall within requested value
|
||||
if (request.AudioBitRate.HasValue
|
||||
&& audioStream.BitDepth.HasValue
|
||||
&& audioStream.BitRate.HasValue
|
||||
&& audioStream.BitRate.Value > request.AudioBitRate.Value)
|
||||
{
|
||||
return false;
|
||||
@@ -2018,14 +2068,20 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
private static double GetVideoBitrateScaleFactor(string codec)
|
||||
{
|
||||
// hevc & vp9 - 40% more efficient than h.264
|
||||
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCase))
|
||||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return .6;
|
||||
}
|
||||
|
||||
// av1 - 50% more efficient than h.264
|
||||
if (string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return .5;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2033,7 +2089,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec);
|
||||
var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
|
||||
var scaleFactor = outputScaleFactor / inputScaleFactor;
|
||||
|
||||
// Don't scale the real bitrate lower than the requested bitrate
|
||||
var scaleFactor = Math.Max(outputScaleFactor / inputScaleFactor, 1);
|
||||
|
||||
if (bitrate <= 500000)
|
||||
{
|
||||
@@ -2055,56 +2113,55 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return Convert.ToInt32(scaleFactor * bitrate);
|
||||
}
|
||||
|
||||
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
|
||||
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream, int? outputAudioChannels)
|
||||
{
|
||||
return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream);
|
||||
return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream, outputAudioChannels);
|
||||
}
|
||||
|
||||
public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream)
|
||||
public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream, int? outputAudioChannels)
|
||||
{
|
||||
if (audioStream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec))
|
||||
var inputChannels = audioStream.Channels ?? 0;
|
||||
var outputChannels = outputAudioChannels ?? 0;
|
||||
var bitrate = audioBitRate ?? int.MaxValue;
|
||||
|
||||
if (string.IsNullOrEmpty(audioCodec)
|
||||
|| string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Math.Min(384000, audioBitRate.Value);
|
||||
return (inputChannels, outputChannels) switch
|
||||
{
|
||||
(>= 6, >= 6 or 0) => Math.Min(640000, bitrate),
|
||||
(> 0, > 0) => Math.Min(outputChannels * 128000, bitrate),
|
||||
(> 0, _) => Math.Min(inputChannels * 128000, bitrate),
|
||||
(_, _) => Math.Min(384000, bitrate)
|
||||
};
|
||||
}
|
||||
|
||||
if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec))
|
||||
if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "dca", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||
return (inputChannels, outputChannels) switch
|
||||
{
|
||||
if ((audioStream.Channels ?? 0) >= 6)
|
||||
{
|
||||
return Math.Min(640000, audioBitRate.Value);
|
||||
}
|
||||
|
||||
return Math.Min(384000, audioBitRate.Value);
|
||||
}
|
||||
|
||||
if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if ((audioStream.Channels ?? 0) >= 6)
|
||||
{
|
||||
return Math.Min(3584000, audioBitRate.Value);
|
||||
}
|
||||
|
||||
return Math.Min(1536000, audioBitRate.Value);
|
||||
}
|
||||
(>= 6, >= 6 or 0) => Math.Min(768000, bitrate),
|
||||
(> 0, > 0) => Math.Min(outputChannels * 136000, bitrate),
|
||||
(> 0, _) => Math.Min(inputChannels * 136000, bitrate),
|
||||
(_, _) => Math.Min(672000, bitrate)
|
||||
};
|
||||
}
|
||||
|
||||
// Empty bitrate area is not allow on iOS
|
||||
// Default audio bitrate to 128K if it is not being requested
|
||||
// Default audio bitrate to 128K per channel if we don't have codec specific defaults
|
||||
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
||||
return 128000;
|
||||
return 128000 * (outputAudioChannels ?? audioStream.Channels ?? 2);
|
||||
}
|
||||
|
||||
public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions)
|
||||
@@ -2397,6 +2454,30 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the negative map args by filters.
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="videoProcessFilters">The videoProcessFilters.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
public string GetNegativeMapArgsByFilters(EncodingJobInfo state, string videoProcessFilters)
|
||||
{
|
||||
string args = string.Empty;
|
||||
|
||||
// http://ffmpeg.org/ffmpeg-all.html#toc-Complex-filtergraphs-1
|
||||
if (state.VideoStream != null && videoProcessFilters.Contains("-filter_complex", StringComparison.Ordinal))
|
||||
{
|
||||
int videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.VideoStream);
|
||||
|
||||
args += string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"-map -0:{0} ",
|
||||
videoStreamIndex);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines which stream will be used for playback.
|
||||
/// </summary>
|
||||
@@ -2767,7 +2848,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat)
|
||||
public string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hwTonemapSuffix))
|
||||
{
|
||||
@@ -2778,7 +2859,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (hwTonemapSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args += ",procamp_vaapi=b={2}:c={3}:extra_hw_frames=16";
|
||||
args = "procamp_vaapi=b={2}:c={3}," + args + ":extra_hw_frames=32";
|
||||
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
args,
|
||||
@@ -2791,14 +2873,24 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
args += ":tonemap={2}:peak={3}:desat={4}";
|
||||
|
||||
if (options.TonemappingParam != 0)
|
||||
if (string.Equals(options.TonemappingMode, "max", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(options.TonemappingMode, "rgb", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args += ":param={5}";
|
||||
if (_mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode)
|
||||
{
|
||||
args += ":tonemap_mode={5}";
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
|
||||
if (options.TonemappingParam != 0)
|
||||
{
|
||||
args += ":range={6}";
|
||||
args += ":param={6}";
|
||||
}
|
||||
|
||||
if (string.Equals(options.TonemappingRange, "tv", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(options.TonemappingRange, "pc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args += ":range={7}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2810,6 +2902,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
options.TonemappingAlgorithm,
|
||||
options.TonemappingPeak,
|
||||
options.TonemappingDesat,
|
||||
options.TonemappingMode,
|
||||
options.TonemappingParam,
|
||||
options.TonemappingRange);
|
||||
}
|
||||
@@ -3214,7 +3307,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
// OUTPUT nv12 surface(memory)
|
||||
// prefer hwmap to hwdownload on opencl.
|
||||
var hwTransferFilter = hasGraphicalSubs ? "hwdownload" : "hwmap";
|
||||
var hwTransferFilter = hasGraphicalSubs ? "hwdownload" : "hwmap=mode=read";
|
||||
mainFilters.Add(hwTransferFilter);
|
||||
mainFilters.Add("format=nv12");
|
||||
}
|
||||
@@ -3415,12 +3508,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// map from d3d11va to qsv.
|
||||
mainFilters.Add("hwmap=derive_device=qsv");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert a qsv scaler to sync the decoder surface,
|
||||
// msdk will passthrough this internally.
|
||||
mainFilters.Add("hwmap=derive_device=qsv,scale_qsv");
|
||||
}
|
||||
}
|
||||
|
||||
// hw deint
|
||||
@@ -3457,7 +3544,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// OUTPUT nv12 surface(memory)
|
||||
// prefer hwmap to hwdownload on opencl.
|
||||
// qsv hwmap is not fully implemented for the time being.
|
||||
mainFilters.Add(isHwmapUsable ? "hwmap" : "hwdownload");
|
||||
mainFilters.Add(isHwmapUsable ? "hwmap=mode=read" : "hwdownload");
|
||||
mainFilters.Add("format=nv12");
|
||||
}
|
||||
|
||||
@@ -3615,6 +3702,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
var outFormat = doTonemap ? string.Empty : "nv12";
|
||||
var hwScaleFilter = GetHwScaleFilter(isVaapiDecoder ? "vaapi" : "qsv", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
||||
|
||||
// allocate extra pool sizes for vaapi vpp
|
||||
if (!string.IsNullOrEmpty(hwScaleFilter) && isVaapiDecoder)
|
||||
{
|
||||
hwScaleFilter += ":extra_hw_frames=24";
|
||||
}
|
||||
|
||||
// hw scale
|
||||
mainFilters.Add(hwScaleFilter);
|
||||
}
|
||||
@@ -3661,7 +3755,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// OUTPUT nv12 surface(memory)
|
||||
// prefer hwmap to hwdownload on opencl/vaapi.
|
||||
// qsv hwmap is not fully implemented for the time being.
|
||||
mainFilters.Add(isHwmapUsable ? "hwmap" : "hwdownload");
|
||||
mainFilters.Add(isHwmapUsable ? "hwmap=mode=read" : "hwdownload");
|
||||
mainFilters.Add("format=nv12");
|
||||
}
|
||||
|
||||
@@ -3878,6 +3972,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
var outFormat = doTonemap ? string.Empty : "nv12";
|
||||
var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
||||
|
||||
// allocate extra pool sizes for vaapi vpp
|
||||
if (!string.IsNullOrEmpty(hwScaleFilter))
|
||||
{
|
||||
hwScaleFilter += ":extra_hw_frames=24";
|
||||
}
|
||||
|
||||
// hw scale
|
||||
mainFilters.Add(hwScaleFilter);
|
||||
}
|
||||
@@ -3919,7 +4020,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
// OUTPUT nv12 surface(memory)
|
||||
// prefer hwmap to hwdownload on opencl/vaapi.
|
||||
mainFilters.Add(isHwmapNotUsable ? "hwdownload" : "hwmap");
|
||||
mainFilters.Add(isHwmapNotUsable ? "hwdownload" : "hwmap=mode=read");
|
||||
mainFilters.Add("format=nv12");
|
||||
}
|
||||
|
||||
@@ -4072,6 +4173,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
outFormat = doOclTonemap ? string.Empty : "nv12";
|
||||
var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
||||
|
||||
// allocate extra pool sizes for vaapi vpp
|
||||
if (!string.IsNullOrEmpty(hwScaleFilter))
|
||||
{
|
||||
hwScaleFilter += ":extra_hw_frames=24";
|
||||
}
|
||||
|
||||
// hw scale
|
||||
mainFilters.Add(hwScaleFilter);
|
||||
}
|
||||
@@ -4376,7 +4484,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
|
||||
// HWA decoders can handle both video files and video folders.
|
||||
var videoType = mediaSource.VideoType;
|
||||
var videoType = state.VideoType;
|
||||
if (videoType != VideoType.VideoFile
|
||||
&& videoType != VideoType.Iso
|
||||
&& videoType != VideoType.Dvd
|
||||
@@ -4517,8 +4625,18 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox");
|
||||
var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var ffmpegVersion = _mediaEncoder.EncoderVersion;
|
||||
|
||||
// Set the av1 codec explicitly to trigger hw accelerator, otherwise libdav1d will be used.
|
||||
var isAv1 = string.Equals(videoCodec, "av1", StringComparison.OrdinalIgnoreCase);
|
||||
var isAv1 = ffmpegVersion < _minFFmpegImplictHwaccel
|
||||
&& string.Equals(videoCodec, "av1", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// Allow profile mismatch if decoding H.264 baseline with d3d11va and vaapi hwaccels.
|
||||
var profileMismatch = string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(state.VideoStream?.Profile, "baseline", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// Disable the extra internal copy in nvdec. We already handle it in filter chain.
|
||||
var nvdecNoInternalCopy = ffmpegVersion >= _minFFmpegHwaUnsafeOutput;
|
||||
|
||||
if (bitDepth == 10 && isCodecAvailable)
|
||||
{
|
||||
@@ -4544,14 +4662,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
if (isVaapiSupported && isCodecAvailable)
|
||||
{
|
||||
return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty)
|
||||
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
}
|
||||
|
||||
if (isD3d11Supported && isCodecAvailable)
|
||||
{
|
||||
// set -threads 3 to intel d3d11va decoder explicitly. Lower threads may result in dead lock.
|
||||
// on newer devices such as Xe, the larger the init_pool_size, the longer the initialization time for opencl to derive from d3d11.
|
||||
return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty) + " -threads 3" + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty)
|
||||
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + " -threads 2" + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -4571,7 +4689,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
if (options.EnableEnhancedNvdecDecoder)
|
||||
{
|
||||
// set -threads 1 to nvdec decoder explicitly since it doesn't implement threading support.
|
||||
return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty)
|
||||
+ (nvdecNoInternalCopy ? " -hwaccel_flags +unsafe_output" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4586,7 +4705,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
if (isD3d11Supported && isCodecAvailable)
|
||||
{
|
||||
return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty)
|
||||
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4595,9 +4715,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
&& isVaapiSupported
|
||||
&& isCodecAvailable)
|
||||
{
|
||||
return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty)
|
||||
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
|
||||
}
|
||||
|
||||
// Apple videotoolbox
|
||||
if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)
|
||||
&& isVideotoolboxSupported
|
||||
&& isCodecAvailable)
|
||||
@@ -5213,15 +5335,23 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return;
|
||||
}
|
||||
|
||||
var inputChannels = audioStream == null ? 6 : audioStream.Channels ?? 6;
|
||||
var inputChannels = audioStream is null ? 6 : audioStream.Channels ?? 6;
|
||||
var shiftAudioCodecs = new List<string>();
|
||||
if (inputChannels >= 6)
|
||||
{
|
||||
return;
|
||||
// DTS and TrueHD are not supported by HLS
|
||||
// Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used
|
||||
shiftAudioCodecs.Add("dca");
|
||||
shiftAudioCodecs.Add("truehd");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Transcoding to 2ch ac3 or eac3 almost always causes a playback failure
|
||||
// Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used
|
||||
shiftAudioCodecs.Add("ac3");
|
||||
shiftAudioCodecs.Add("eac3");
|
||||
}
|
||||
|
||||
// Transcoding to 2ch ac3 almost always causes a playback failure
|
||||
// Keep it in the supported codecs list, but shift it to the end of the list so that if transcoding happens, another codec is used
|
||||
var shiftAudioCodecs = new[] { "ac3", "eac3" };
|
||||
if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return;
|
||||
@@ -5398,7 +5528,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// video processing filters.
|
||||
var videoProcessParam = GetVideoProcessingFilterParam(state, encodingOptions, videoCodec);
|
||||
|
||||
args += videoProcessParam;
|
||||
var negativeMapArgs = GetNegativeMapArgsByFilters(state, videoProcessParam);
|
||||
|
||||
args = negativeMapArgs + args + videoProcessParam;
|
||||
|
||||
hasCopyTs = videoProcessParam.Contains("copyts", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
@@ -5463,7 +5595,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
var bitrate = state.OutputAudioBitrate;
|
||||
|
||||
if (bitrate.HasValue)
|
||||
if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
@@ -5483,8 +5615,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var audioTranscodeParams = new List<string>();
|
||||
|
||||
var bitrate = state.OutputAudioBitrate;
|
||||
var channels = state.OutputAudioChannels;
|
||||
var outputCodec = state.OutputAudioCodec;
|
||||
|
||||
if (bitrate.HasValue)
|
||||
if (bitrate.HasValue && !LosslessAudioCodecs.Contains(outputCodec, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
@@ -5494,7 +5628,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrEmpty(outputCodec))
|
||||
{
|
||||
audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state));
|
||||
}
|
||||
|
||||
if (!string.Equals(outputCodec, "opus", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// opus only supports specific sampling rates
|
||||
var sampleRate = state.OutputAudioSampleRate;
|
||||
|
||||
@@ -14,6 +14,7 @@ using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.MediaEncoding.Encoder;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
@@ -317,10 +318,10 @@ namespace MediaBrowser.MediaEncoding.Attachments
|
||||
|
||||
var processArgs = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"-dump_attachment:{1} {2} -i {0} -t 0 -f null null",
|
||||
"-dump_attachment:{1} \"{2}\" -i {0} -t 0 -f null null",
|
||||
inputPath,
|
||||
attachmentStreamIndex,
|
||||
outputPath);
|
||||
EncodingUtils.NormalizePath(outputPath));
|
||||
|
||||
int exitCode;
|
||||
|
||||
|
||||
@@ -25,11 +25,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
"mpeg2video",
|
||||
"mpeg4",
|
||||
"msmpeg4",
|
||||
"dts",
|
||||
"dca",
|
||||
"ac3",
|
||||
"aac",
|
||||
"mp3",
|
||||
"flac",
|
||||
"truehd",
|
||||
"h264_qsv",
|
||||
"hevc_qsv",
|
||||
"mpeg2_qsv",
|
||||
@@ -58,10 +59,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
"aac",
|
||||
"libfdk_aac",
|
||||
"ac3",
|
||||
"dca",
|
||||
"libmp3lame",
|
||||
"libopus",
|
||||
"libvorbis",
|
||||
"flac",
|
||||
"truehd",
|
||||
"srt",
|
||||
"h264_amf",
|
||||
"hevc_amf",
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private static string NormalizePath(string path)
|
||||
public static string NormalizePath(string path)
|
||||
{
|
||||
// Quotes are valid path characters in linux and they need to be escaped here with a leading \
|
||||
return path.Replace("\"", "\\\"", StringComparison.Ordinal);
|
||||
|
||||
@@ -751,9 +751,11 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
}
|
||||
|
||||
if (isAudio
|
||||
|| string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
|
||||
&& (string.Equals(stream.Codec, "bmp", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "webp", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
stream.Type = MediaStreamType.EmbeddedImage;
|
||||
}
|
||||
|
||||
@@ -21,20 +21,21 @@ namespace MediaBrowser.Model.Configuration
|
||||
EnableTonemapping = false;
|
||||
EnableVppTonemapping = false;
|
||||
TonemappingAlgorithm = "bt2390";
|
||||
TonemappingMode = "auto";
|
||||
TonemappingRange = "auto";
|
||||
TonemappingDesat = 0;
|
||||
TonemappingThreshold = 0.8;
|
||||
TonemappingPeak = 100;
|
||||
TonemappingParam = 0;
|
||||
VppTonemappingBrightness = 0;
|
||||
VppTonemappingContrast = 1.2;
|
||||
VppTonemappingBrightness = 16;
|
||||
VppTonemappingContrast = 1;
|
||||
H264Crf = 23;
|
||||
H265Crf = 28;
|
||||
DeinterlaceDoubleRate = false;
|
||||
DeinterlaceMethod = "yadif";
|
||||
EnableDecodingColorDepth10Hevc = true;
|
||||
EnableDecodingColorDepth10Vp9 = true;
|
||||
EnableEnhancedNvdecDecoder = false;
|
||||
// Enhanced Nvdec or system native decoder is required for DoVi to SDR tone-mapping.
|
||||
EnableEnhancedNvdecDecoder = true;
|
||||
PreferSystemNativeHwDecoder = true;
|
||||
EnableIntelLowPowerH264HwEncoder = false;
|
||||
EnableIntelLowPowerHevcHwEncoder = false;
|
||||
@@ -81,12 +82,12 @@ namespace MediaBrowser.Model.Configuration
|
||||
|
||||
public string TonemappingAlgorithm { get; set; }
|
||||
|
||||
public string TonemappingMode { get; set; }
|
||||
|
||||
public string TonemappingRange { get; set; }
|
||||
|
||||
public double TonemappingDesat { get; set; }
|
||||
|
||||
public double TonemappingThreshold { get; set; }
|
||||
|
||||
public double TonemappingPeak { get; set; }
|
||||
|
||||
public double TonemappingParam { get; set; }
|
||||
|
||||
@@ -136,12 +136,26 @@ namespace MediaBrowser.Model.Dlna
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
if (int.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var expected))
|
||||
var conditionType = condition.Condition;
|
||||
if (condition.Condition == ProfileConditionType.EqualsAny)
|
||||
{
|
||||
switch (condition.Condition)
|
||||
foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
|
||||
{
|
||||
if (int.TryParse(singleConditionString, NumberStyles.Integer, CultureInfo.InvariantCulture, out int conditionValue)
|
||||
&& conditionValue.Equals(currentValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (int.TryParse(condition.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var expected))
|
||||
{
|
||||
switch (conditionType)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
case ProfileConditionType.EqualsAny:
|
||||
return currentValue.Value.Equals(expected);
|
||||
case ProfileConditionType.GreaterThanEqual:
|
||||
return currentValue.Value >= expected;
|
||||
@@ -212,9 +226,24 @@ namespace MediaBrowser.Model.Dlna
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
if (double.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var expected))
|
||||
var conditionType = condition.Condition;
|
||||
if (condition.Condition == ProfileConditionType.EqualsAny)
|
||||
{
|
||||
switch (condition.Condition)
|
||||
foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
|
||||
{
|
||||
if (double.TryParse(singleConditionString, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double conditionValue)
|
||||
&& conditionValue.Equals(currentValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (double.TryParse(condition.Value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var expected))
|
||||
{
|
||||
switch (conditionType)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
return currentValue.Value.Equals(expected);
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly ITranscoderSupport _transcoderSupport;
|
||||
private static readonly string[] _supportedHlsVideoCodecs = new string[] { "h264", "hevc" };
|
||||
private static readonly string[] _supportedHlsAudioCodecsTs = new string[] { "aac", "ac3", "eac3", "mp3" };
|
||||
private static readonly string[] _supportedHlsAudioCodecsMp4 = new string[] { "aac", "ac3", "eac3", "mp3", "alac", "flac", "opus", "dca", "truehd" };
|
||||
|
||||
public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger)
|
||||
{
|
||||
@@ -732,7 +735,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
if (options.AllowVideoStreamCopy)
|
||||
{
|
||||
// prefer direct copy profile
|
||||
float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
|
||||
float videoFramerate = videoStream?.AverageFrameRate ?? videoStream?.RealFrameRate ?? 0;
|
||||
TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
|
||||
int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
|
||||
int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
|
||||
@@ -743,7 +746,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
if (ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream?.Codec))
|
||||
{
|
||||
var videoCodec = transcodingProfile.VideoCodec;
|
||||
var videoCodec = videoStream?.Codec;
|
||||
var container = transcodingProfile.Container;
|
||||
var appliedVideoConditions = options.Profile.CodecProfiles
|
||||
.Where(i => i.Type == CodecType.Video &&
|
||||
@@ -770,6 +773,13 @@ namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
// Prefer matching video codecs
|
||||
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
|
||||
|
||||
// Enforce HLS video codec restrictions
|
||||
if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
videoCodecs = videoCodecs.Where(codec => _supportedHlsVideoCodecs.Contains(codec)).ToArray();
|
||||
}
|
||||
|
||||
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
|
||||
if (directVideoCodec != null)
|
||||
{
|
||||
@@ -805,6 +815,20 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
// Prefer matching audio codecs, could do better here
|
||||
var audioCodecs = ContainerProfile.SplitValue(audioCodec);
|
||||
|
||||
// Enforce HLS audio codec restrictions
|
||||
if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.Equals(playlistItem.Container, "mp4", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsMp4.Contains(codec)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsTs.Contains(codec)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
|
||||
playlistItem.AudioCodecs = audioCodecs;
|
||||
if (directAudioStream != null)
|
||||
@@ -850,7 +874,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
var appliedVideoConditions = options.Profile.CodecProfiles
|
||||
.Where(i => i.Type == CodecType.Video &&
|
||||
i.ContainsAnyCodec(videoCodec, container) &&
|
||||
i.ContainsAnyCodec(videoStream?.Codec, container) &&
|
||||
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)));
|
||||
var isFirstAppliedCodecProfile = true;
|
||||
foreach (var i in appliedVideoConditions)
|
||||
@@ -882,7 +906,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
var appliedAudioConditions = options.Profile.CodecProfiles
|
||||
.Where(i => i.Type == CodecType.VideoAudio &&
|
||||
i.ContainsAnyCodec(audioCodec, container) &&
|
||||
i.ContainsAnyCodec(audioStream?.Codec, container) &&
|
||||
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
|
||||
isFirstAppliedCodecProfile = true;
|
||||
foreach (var i in appliedAudioConditions)
|
||||
@@ -1114,7 +1138,8 @@ namespace MediaBrowser.Model.Dlna
|
||||
profile,
|
||||
"VideoCodecProfile",
|
||||
profile.CodecProfiles
|
||||
.Where(codecProfile => codecProfile.Type == CodecType.Video && codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
|
||||
.Where(codecProfile => codecProfile.Type == CodecType.Video &&
|
||||
codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
|
||||
!checkVideoConditions(codecProfile.ApplyConditions).Any())
|
||||
.SelectMany(codecProfile => checkVideoConditions(codecProfile.Conditions)));
|
||||
|
||||
@@ -1535,7 +1560,8 @@ namespace MediaBrowser.Model.Dlna
|
||||
bool? isSecondaryAudio)
|
||||
{
|
||||
return codecProfiles
|
||||
.Where(profile => profile.Type == CodecType.VideoAudio && profile.ContainsAnyCodec(codec, container) &&
|
||||
.Where(profile => profile.Type == CodecType.VideoAudio &&
|
||||
profile.ContainsAnyCodec(codec, container) &&
|
||||
profile.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio)))
|
||||
.SelectMany(profile => profile.Conditions)
|
||||
.Where(condition => !ConditionProcessor.IsVideoAudioConditionSatisfied(condition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio));
|
||||
@@ -1552,7 +1578,8 @@ namespace MediaBrowser.Model.Dlna
|
||||
bool checkConditions)
|
||||
{
|
||||
var conditions = codecProfiles
|
||||
.Where(profile => profile.Type == CodecType.Audio && profile.ContainsAnyCodec(codec, container) &&
|
||||
.Where(profile => profile.Type == CodecType.Audio &&
|
||||
profile.ContainsAnyCodec(codec, container) &&
|
||||
profile.ApplyConditions.All(applyCondition => ConditionProcessor.IsAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth)))
|
||||
.SelectMany(profile => profile.Conditions);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<PackageId>Jellyfin.Model</PackageId>
|
||||
<VersionPrefix>10.8.9</VersionPrefix>
|
||||
<VersionPrefix>10.8.13</VersionPrefix>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -181,6 +181,10 @@ namespace MediaBrowser.Providers.Manager
|
||||
{
|
||||
contentType = "image/png";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpRequestException("Invalid image received: contentType not set.", null, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
// thetvdb will sometimes serve a rubbish 404 html page with a 200 OK code, because reasons...
|
||||
|
||||
@@ -176,9 +176,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
|
||||
var format = imageStream.Codec switch
|
||||
{
|
||||
"bmp" => ImageFormat.Bmp,
|
||||
"gif" => ImageFormat.Gif,
|
||||
"mjpeg" => ImageFormat.Jpg,
|
||||
"png" => ImageFormat.Png,
|
||||
"gif" => ImageFormat.Gif,
|
||||
"webp" => ImageFormat.Webp,
|
||||
_ => ImageFormat.Jpg
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("10.8.9")]
|
||||
[assembly: AssemblyFileVersion("10.8.9")]
|
||||
[assembly: AssemblyVersion("10.8.13")]
|
||||
[assembly: AssemblyFileVersion("10.8.13")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
# We just wrap `build` so this is really it
|
||||
name: "jellyfin"
|
||||
version: "10.8.9"
|
||||
version: "10.8.13"
|
||||
packages:
|
||||
- debian.amd64
|
||||
- debian.arm64
|
||||
|
||||
24
debian/changelog
vendored
24
debian/changelog
vendored
@@ -1,3 +1,27 @@
|
||||
jellyfin-server (10.8.13-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.8.13; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.13
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Tue, 28 Nov 2023 22:21:55 -0500
|
||||
|
||||
jellyfin-server (10.8.12-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.8.12; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.12
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 04 Nov 2023 14:42:37 -0400
|
||||
|
||||
jellyfin-server (10.8.11-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.8.11; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.11
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 23 Sep 2023 21:40:37 -0400
|
||||
|
||||
jellyfin-server (10.8.10-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.8.10; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.10
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 23 Apr 2023 11:02:05 -0400
|
||||
|
||||
jellyfin-server (10.8.9-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.8.9; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.9
|
||||
|
||||
3
debian/conf/jellyfin
vendored
3
debian/conf/jellyfin
vendored
@@ -27,6 +27,9 @@ JELLYFIN_RESTART_OPT="--restartpath=/usr/lib/jellyfin/restart.sh"
|
||||
# ffmpeg binary paths, overriding the system values
|
||||
JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg"
|
||||
|
||||
# Disable glibc dynamic heap adjustment
|
||||
MALLOC_TRIM_THRESHOLD_=131072
|
||||
|
||||
# [OPTIONAL] run Jellyfin as a headless service
|
||||
#JELLYFIN_SERVICE_OPT="--service"
|
||||
|
||||
|
||||
2
debian/metapackage/jellyfin
vendored
2
debian/metapackage/jellyfin
vendored
@@ -5,7 +5,7 @@ Homepage: https://jellyfin.org
|
||||
Standards-Version: 3.9.2
|
||||
|
||||
Package: jellyfin
|
||||
Version: 10.8.9
|
||||
Version: 10.8.13
|
||||
Maintainer: Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
Depends: jellyfin-server, jellyfin-web
|
||||
Description: Provides the Jellyfin Free Software Media System
|
||||
|
||||
10
debian/postinst
vendored
10
debian/postinst
vendored
@@ -10,6 +10,8 @@ if [[ -f $DEFAULT_FILE ]]; then
|
||||
fi
|
||||
|
||||
JELLYFIN_USER=${JELLYFIN_USER:-jellyfin}
|
||||
RENDER_GROUP=${RENDER_GROUP:-render}
|
||||
VIDEO_GROUP=${VIDEO_GROUP:-video}
|
||||
|
||||
# Data directories for program data (cache, db), configs, and logs
|
||||
PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
|
||||
@@ -28,6 +30,14 @@ case "$1" in
|
||||
adduser --system --ingroup ${JELLYFIN_USER} --shell /bin/false ${JELLYFIN_USER} --no-create-home --home ${PROGRAMDATA} \
|
||||
--gecos "Jellyfin default user" > /dev/null 2>&1
|
||||
fi
|
||||
# Add jellyfin to the render group for hwa
|
||||
if [[ ! -z "$(getent group ${RENDER_GROUP})" ]]; then
|
||||
usermod -aG ${RENDER_GROUP} ${JELLYFIN_USER} > /dev/null 2>&1
|
||||
fi
|
||||
# Add jellyfin to the video group for hwa
|
||||
if [[ ! -z "$(getent group ${VIDEO_GROUP})" ]]; then
|
||||
usermod -aG ${VIDEO_GROUP} ${JELLYFIN_USER} > /dev/null 2>&1
|
||||
fi
|
||||
# ensure $PROGRAMDATA exists
|
||||
if [[ ! -d $PROGRAMDATA ]]; then
|
||||
mkdir $PROGRAMDATA
|
||||
|
||||
@@ -26,6 +26,9 @@ JELLYFIN_CACHE_DIR="/var/cache/jellyfin"
|
||||
# In-App service control
|
||||
JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh"
|
||||
|
||||
# Disable glibc dynamic heap adjustment
|
||||
MALLOC_TRIM_THRESHOLD_=131072
|
||||
|
||||
# [OPTIONAL] ffmpeg binary paths, overriding the UI-configured values
|
||||
#JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/bin/ffmpeg"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
%endif
|
||||
|
||||
Name: jellyfin
|
||||
Version: 10.8.9
|
||||
Version: 10.8.13
|
||||
Release: 1%{?dist}
|
||||
Summary: The Free Software Media System
|
||||
License: GPLv2
|
||||
@@ -139,6 +139,9 @@ getent group jellyfin >/dev/null || groupadd -r jellyfin
|
||||
getent passwd jellyfin >/dev/null || \
|
||||
useradd -r -g jellyfin -d %{_sharedstatedir}/jellyfin -s /sbin/nologin \
|
||||
-c "Jellyfin default user" jellyfin
|
||||
# Add jellyfin to the render and video groups for hwa.
|
||||
[ ! -z "$(getent group render)" ] && usermod -aG render jellyfin >/dev/null 2>&1
|
||||
[ ! -z "$(getent group video)" ] && usermod -aG video jellyfin >/dev/null 2>&1
|
||||
exit 0
|
||||
|
||||
%post server
|
||||
@@ -176,6 +179,14 @@ fi
|
||||
%systemd_postun_with_restart jellyfin.service
|
||||
|
||||
%changelog
|
||||
* Tue Nov 28 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.8.13; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.13
|
||||
* Sat Nov 04 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.8.12; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.12
|
||||
* Sat Sep 23 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.8.11; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.11
|
||||
* Sun Apr 23 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.8.10; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.10
|
||||
* Sun Jan 22 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.8.9; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.9
|
||||
* Tue Nov 29 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<PackageId>Jellyfin.Extensions</PackageId>
|
||||
<VersionPrefix>10.8.9</VersionPrefix>
|
||||
<VersionPrefix>10.8.13</VersionPrefix>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV60Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV512Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV432Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)]
|
||||
@@ -36,6 +38,8 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
{
|
||||
public GetFFmpegVersionTestData()
|
||||
{
|
||||
Add(EncoderValidatorTestsData.FFmpegV60Output, new Version(6, 0));
|
||||
Add(EncoderValidatorTestsData.FFmpegV512Output, new Version(5, 1, 2));
|
||||
Add(EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4));
|
||||
Add(EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2));
|
||||
Add(EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1));
|
||||
|
||||
@@ -2,6 +2,30 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
{
|
||||
internal static class EncoderValidatorTestsData
|
||||
{
|
||||
public const string FFmpegV60Output = @"ffmpeg version 6.0-Jellyfin Copyright (c) 2000-2023 the FFmpeg developers
|
||||
built with gcc 12.2.0 (crosstool-NG 1.25.0.90_cf9beb1)
|
||||
configuration: --prefix=/ffbuild/prefix --pkg-config=pkg-config --pkg-config-flags=--static --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --extra-version=Jellyfin --extra-cflags= --extra-cxxflags= --extra-ldflags= --extra-ldexeflags= --extra-libs= --enable-gpl --enable-version3 --enable-lto --disable-ffplay --disable-debug --disable-doc --disable-ptx-compression --disable-sdl2 --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --enable-amf --enable-chromaprint --enable-libdav1d --enable-dxva2 --enable-d3d11va --enable-libfdk-aac --enable-ffnvcodec --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc --enable-libass --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvpx --enable-libwebp --enable-libvpl --enable-schannel --enable-libsrt --enable-libsvtav1 --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libx264 --enable-libx265 --enable-libzimg --enable-libzvbi
|
||||
libavutil 58. 2.100 / 58. 2.100
|
||||
libavcodec 60. 3.100 / 60. 3.100
|
||||
libavformat 60. 3.100 / 60. 3.100
|
||||
libavdevice 60. 1.100 / 60. 1.100
|
||||
libavfilter 9. 3.100 / 9. 3.100
|
||||
libswscale 7. 1.100 / 7. 1.100
|
||||
libswresample 4. 10.100 / 4. 10.100
|
||||
libpostproc 57. 1.100 / 57. 1.100";
|
||||
|
||||
public const string FFmpegV512Output = @"ffmpeg version 5.1.2-Jellyfin Copyright (c) 2000-2022 the FFmpeg developers
|
||||
built with gcc 10-win32 (GCC) 20220324
|
||||
configuration: --prefix=/opt/ffmpeg --arch=x86_64 --target-os=mingw32 --cross-prefix=x86_64-w64-mingw32- --pkg-config=pkg-config --pkg-config-flags=--static --extra-libs='-lfftw3f -lstdc++' --extra-cflags=-DCHROMAPRINT_NODLL --extra-version=Jellyfin --disable-ffplay --disable-debug --disable-doc --disable-sdl2 --disable-ptx-compression --disable-w32threads --enable-pthreads --enable-shared --enable-lto --enable-gpl --enable-version3 --enable-schannel --enable-iconv --enable-libxml2 --enable-zlib --enable-lzma --enable-gmp --enable-chromaprint --enable-libfreetype --enable-libfribidi --enable-libfontconfig --enable-libass --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libzimg --enable-libx264 --enable-libx265 --enable-libsvtav1 --enable-libdav1d --enable-libfdk-aac --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-ffnvcodec --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc
|
||||
libavutil 57. 28.100 / 57. 28.100
|
||||
libavcodec 59. 37.100 / 59. 37.100
|
||||
libavformat 59. 27.100 / 59. 27.100
|
||||
libavdevice 59. 7.100 / 59. 7.100
|
||||
libavfilter 8. 44.100 / 8. 44.100
|
||||
libswscale 6. 7.100 / 6. 7.100
|
||||
libswresample 4. 7.100 / 4. 7.100
|
||||
libpostproc 56. 6.100 / 56. 6.100";
|
||||
|
||||
public const string FFmpegV44Output = @"ffmpeg version 4.4-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers
|
||||
built with gcc 10.3.0 (Rev5, Built by MSYS2 project)
|
||||
configuration: --disable-static --enable-shared --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls
|
||||
|
||||
@@ -98,9 +98,11 @@ namespace Jellyfin.Providers.Tests.MediaInfo
|
||||
[InlineData(null, null, 1, ImageType.Primary, ImageFormat.Jpg)] // no label, finds primary
|
||||
[InlineData("backdrop", null, 2, ImageType.Backdrop, ImageFormat.Jpg)] // uses label to find index 2, not just pulling first stream
|
||||
[InlineData("cover", null, 2, ImageType.Primary, ImageFormat.Jpg)] // uses label to find index 2, not just pulling first stream
|
||||
[InlineData(null, "bmp", 1, ImageType.Primary, ImageFormat.Bmp)]
|
||||
[InlineData(null, "gif", 1, ImageType.Primary, ImageFormat.Gif)]
|
||||
[InlineData(null, "mjpeg", 1, ImageType.Primary, ImageFormat.Jpg)]
|
||||
[InlineData(null, "png", 1, ImageType.Primary, ImageFormat.Png)]
|
||||
[InlineData(null, "gif", 1, ImageType.Primary, ImageFormat.Gif)]
|
||||
[InlineData(null, "webp", 1, ImageType.Primary, ImageFormat.Webp)]
|
||||
public async void GetImage_Embedded_ReturnsCorrectSelection(string label, string? codec, int targetIndex, ImageType type, ImageFormat? expectedFormat)
|
||||
{
|
||||
var streams = new List<MediaStream>();
|
||||
|
||||
Reference in New Issue
Block a user