aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json7
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json7
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json5
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/uk.json9
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs58
-rw-r--r--Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs86
-rw-r--r--Jellyfin.Api/Models/VideoDtos/DeviceProfileDto.cs15
-rw-r--r--Jellyfin.Server/Program.cs4
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs20
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs169
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs11
-rw-r--r--fedora/jellyfin.spec4
15 files changed, 323 insertions, 106 deletions
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 2de447ad9..f7507e6ba 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var extension = "ts";
var requiresRemux = false;
- var contentType = response.Content.Headers.ContentType.ToString();
+ var contentType = response.Content.Headers.ContentType?.ToString() ?? string.Empty;
if (contentType.IndexOf("matroska", StringComparison.OrdinalIgnoreCase) != -1)
{
requiresRemux = true;
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index ef3ed2580..105ef7be9 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -112,5 +112,10 @@
"TaskRefreshPeople": "Muat ulang Orang",
"TaskCleanLogsDescription": "Menghapus file log yang lebih dari {0} hari.",
"TaskCleanLogs": "Bersihkan Log Direktori",
- "TaskRefreshLibrary": "Pindai Pustaka Media"
+ "TaskRefreshLibrary": "Pindai Pustaka Media",
+ "TaskCleanActivityLogDescription": "Menghapus log aktivitas yang lebih tua dari umur yang dikonfigurasi.",
+ "TaskCleanActivityLog": "Bersihkan Log Aktivitas",
+ "Undefined": "Tidak terdefinisi",
+ "Forced": "Dipaksa",
+ "Default": "Bawaan"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index e1e88cc9b..b6672a554 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -87,7 +87,7 @@
"UserOnlineFromDevice": "{0} heeft verbinding met {1}",
"UserPasswordChangedWithName": "Wachtwoord voor {0} is gewijzigd",
"UserPolicyUpdatedWithName": "Gebruikersbeleid gewijzigd voor {0}",
- "UserStartedPlayingItemWithValues": "{0} heeft afspelen van {1} gestart op {2}",
+ "UserStartedPlayingItemWithValues": "{0} speelt {1} af op {2}",
"UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt op {2}",
"ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
"ValueSpecialEpisodeName": "Speciaal - {0}",
@@ -115,5 +115,8 @@
"TasksLibraryCategory": "Bibliotheek",
"TasksMaintenanceCategory": "Onderhoud",
"TaskCleanActivityLogDescription": "Verwijder activiteiten logs ouder dan de ingestelde tijd.",
- "TaskCleanActivityLog": "Leeg activiteiten logboek"
+ "TaskCleanActivityLog": "Leeg activiteiten logboek",
+ "Undefined": "Niet gedefinieerd",
+ "Forced": "Geforceerd",
+ "Default": "Standaard"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index c0db2cf7f..ca6172fce 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -115,5 +115,8 @@
"TaskRefreshChapterImagesDescription": "Создаются эскизы для видео, которые содержат сцены.",
"TaskCleanCacheDescription": "Удаляются файлы кэша, которые больше не нужны системе.",
"TaskCleanActivityLogDescription": "Удаляет записи журнала активности старше установленного возраста.",
- "TaskCleanActivityLog": "Очистить журнал активности"
+ "TaskCleanActivityLog": "Очистить журнал активности",
+ "Undefined": "Не определено",
+ "Forced": "Форсир-ые",
+ "Default": "По умолчанию"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 8e75b358c..99fbd3954 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -15,13 +15,13 @@
"Favorites": "Obľúbené",
"Folders": "Priečinky",
"Genres": "Žánre",
- "HeaderAlbumArtists": "Umelci albumu",
+ "HeaderAlbumArtists": "Interpreti albumu",
"HeaderContinueWatching": "Pokračovať v pozeraní",
"HeaderFavoriteAlbums": "Obľúbené albumy",
- "HeaderFavoriteArtists": "Obľúbení umelci",
+ "HeaderFavoriteArtists": "Obľúbení interpreti",
"HeaderFavoriteEpisodes": "Obľúbené epizódy",
"HeaderFavoriteShows": "Obľúbené seriály",
- "HeaderFavoriteSongs": "Obľúbené piesne",
+ "HeaderFavoriteSongs": "Obľúbené skladby",
"HeaderLiveTV": "Živá TV",
"HeaderNextUp": "Nasleduje",
"HeaderRecordingGroups": "Skupiny nahrávok",
@@ -33,13 +33,13 @@
"LabelRunningTimeValue": "Dĺžka: {0}",
"Latest": "Najnovšie",
"MessageApplicationUpdated": "Jellyfin Server bol aktualizovaný",
- "MessageApplicationUpdatedTo": "Jellyfin Server bol aktualizový na verziu {0}",
+ "MessageApplicationUpdatedTo": "Jellyfin Server bol aktualizovaný na verziu {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Sekcia {0} konfigurácie servera bola aktualizovaná",
"MessageServerConfigurationUpdated": "Konfigurácia servera bola aktualizovaná",
"MixedContent": "Zmiešaný obsah",
"Movies": "Filmy",
"Music": "Hudba",
- "MusicVideos": "Hudobné videá",
+ "MusicVideos": "Hudobné videoklipy",
"NameInstallFailed": "Inštalácia {0} zlyhala",
"NameSeasonNumber": "Séria {0}",
"NameSeasonUnknown": "Neznáma séria",
@@ -71,7 +71,7 @@
"ScheduledTaskStartedWithName": "{0} zahájených",
"ServerNameNeedsToBeRestarted": "{0} vyžaduje reštart",
"Shows": "Seriály",
- "Songs": "Piesne",
+ "Songs": "Skladby",
"StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Prosím, skúste to o chvíľu znova.",
"SubtitleDownloadFailureForItem": "Sťahovanie titulkov pre {0} zlyhalo",
"SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo",
@@ -89,29 +89,34 @@
"UserPolicyUpdatedWithName": "Používateľské zásady pre {0} boli aktualizované",
"UserStartedPlayingItemWithValues": "{0} spustil prehrávanie {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} ukončil prehrávanie {1} na {2}",
- "ValueHasBeenAddedToLibrary": "{0} bol pridané do vašej knižnice médií",
+ "ValueHasBeenAddedToLibrary": "{0} bol pridaný do vašej knižnice médií",
"ValueSpecialEpisodeName": "Špeciál - {0}",
"VersionNumber": "Verzia {0}",
"TaskDownloadMissingSubtitlesDescription": "Vyhľadá na internete chýbajúce titulky podľa toho, ako sú nakonfigurované metadáta.",
"TaskDownloadMissingSubtitles": "Stiahnuť chýbajúce titulky",
"TaskRefreshChannelsDescription": "Obnoví informácie o internetových kanáloch.",
"TaskRefreshChannels": "Obnoviť kanály",
- "TaskCleanTranscodeDescription": "Vymaže súbory transkódovania, ktoré sú staršie ako jeden deň.",
- "TaskCleanTranscode": "Vyčistiť priečinok pre transkódovanie",
+ "TaskCleanTranscodeDescription": "Vymaže prekódované súbory, ktoré sú staršie ako jeden deň.",
+ "TaskCleanTranscode": "Vyčistiť priečinok pre prekódovanie",
"TaskUpdatePluginsDescription": "Stiahne a nainštaluje aktualizácie pre zásuvné moduly, ktoré sú nastavené tak, aby sa aktualizovali automaticky.",
"TaskUpdatePlugins": "Aktualizovať zásuvné moduly",
"TaskRefreshPeopleDescription": "Aktualizuje metadáta pre hercov a režisérov vo vašej mediálnej knižnici.",
"TaskRefreshPeople": "Obnoviť osoby",
- "TaskCleanLogsDescription": "Vymaže log súbory, ktoré su staršie ako {0} deň/dni/dní.",
+ "TaskCleanLogsDescription": "Vymaže log súbory, ktoré sú staršie ako {0} deň/dni/dní.",
"TaskCleanLogs": "Vyčistiť priečinok s logmi",
"TaskRefreshLibraryDescription": "Hľadá vo vašej mediálnej knižnici nové súbory a obnovuje metadáta.",
"TaskRefreshLibrary": "Prehľadávať knižnicu medií",
"TaskRefreshChapterImagesDescription": "Vytvorí náhľady pre videá, ktoré majú kapitoly.",
"TaskRefreshChapterImages": "Extrahovať obrázky kapitol",
- "TaskCleanCacheDescription": "Vymaže cache súbory, ktoré nie sú už potrebné pre systém.",
- "TaskCleanCache": "Vyčistiť Cache priečinok",
+ "TaskCleanCacheDescription": "Vymaže súbory vyrovnávacej pamäte, ktoré už nie sú potrebné pre systém.",
+ "TaskCleanCache": "Vyčistiť priečinok vyrovnávacej pamäte",
"TasksChannelsCategory": "Internetové kanály",
"TasksApplicationCategory": "Aplikácia",
"TasksLibraryCategory": "Knižnica",
- "TasksMaintenanceCategory": "Údržba"
+ "TasksMaintenanceCategory": "Údržba",
+ "TaskCleanActivityLogDescription": "Vymaže záznamy aktivít v logu, ktoré sú staršie ako zadaná doba.",
+ "TaskCleanActivityLog": "Vyčistiť log aktivít",
+ "Undefined": "Nedefinované",
+ "Forced": "Vynútené",
+ "Default": "Predvolené"
}
diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index 06cc5f633..b6073bf6a 100644
--- a/Emby.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
@@ -27,7 +27,7 @@
"Channels": "Канали",
"CameraImageUploadedFrom": "Нова фотографія завантажена з {0}",
"Books": "Книги",
- "AuthenticationSucceededWithUserName": "{0} успішно авторизований",
+ "AuthenticationSucceededWithUserName": "{0} успішно автентифіковано",
"Artists": "Виконавці",
"Application": "Додаток",
"AppDeviceValues": "Додаток: {0}, Пристрій: {1}",
@@ -112,5 +112,10 @@
"MessageServerConfigurationUpdated": "Конфігурація сервера оновлена",
"MessageNamedServerConfigurationUpdatedWithValue": "Розділ конфігурації сервера {0} оновлено",
"Inherit": "Успадкувати",
- "HeaderRecordingGroups": "Групи запису"
+ "HeaderRecordingGroups": "Групи запису",
+ "Forced": "Примусово",
+ "TaskCleanActivityLogDescription": "Видаляє старші за встановлений термін записи з журналу активності.",
+ "TaskCleanActivityLog": "Очистити журнал активності",
+ "Undefined": "Не визначено",
+ "Default": "За замовчуванням"
}
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index b42e6686e..a76dc057a 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -8,7 +8,6 @@ using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.MediaInfoDtos;
-using Jellyfin.Api.Models.VideoDtos;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
@@ -81,6 +80,9 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Gets live playback media info for an item.
/// </summary>
+ /// <remarks>
+ /// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
+ /// </remarks>
/// <param name="itemId">The item id.</param>
/// <param name="userId">The user id.</param>
/// <param name="maxStreamingBitrate">The maximum streaming bitrate.</param>
@@ -90,13 +92,13 @@ namespace Jellyfin.Api.Controllers
/// <param name="maxAudioChannels">The maximum number of audio channels.</param>
/// <param name="mediaSourceId">The media source id.</param>
/// <param name="liveStreamId">The livestream id.</param>
- /// <param name="deviceProfile">The device profile.</param>
/// <param name="autoOpenLiveStream">Whether to auto open the livestream.</param>
/// <param name="enableDirectPlay">Whether to enable direct play. Default: true.</param>
/// <param name="enableDirectStream">Whether to enable direct stream. Default: true.</param>
/// <param name="enableTranscoding">Whether to enable transcoding. Default: true.</param>
/// <param name="allowVideoStreamCopy">Whether to allow to copy the video stream. Default: true.</param>
/// <param name="allowAudioStreamCopy">Whether to allow to copy the audio stream. Default: true.</param>
+ /// <param name="playbackInfoDto">The playback info.</param>
/// <response code="200">Playback info returned.</response>
/// <returns>A <see cref="Task"/> containing a <see cref="PlaybackInfoResponse"/> with the playback info.</returns>
[HttpPost("Items/{itemId}/PlaybackInfo")]
@@ -111,18 +113,17 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? maxAudioChannels,
[FromQuery] string? mediaSourceId,
[FromQuery] string? liveStreamId,
- [FromBody] DeviceProfileDto? deviceProfile,
- [FromQuery] bool autoOpenLiveStream = false,
- [FromQuery] bool enableDirectPlay = true,
- [FromQuery] bool enableDirectStream = true,
- [FromQuery] bool enableTranscoding = true,
- [FromQuery] bool allowVideoStreamCopy = true,
- [FromQuery] bool allowAudioStreamCopy = true)
+ [FromQuery] bool? autoOpenLiveStream,
+ [FromQuery] bool? enableDirectPlay,
+ [FromQuery] bool? enableDirectStream,
+ [FromQuery] bool? enableTranscoding,
+ [FromQuery] bool? allowVideoStreamCopy,
+ [FromQuery] bool? allowAudioStreamCopy,
+ [FromBody] PlaybackInfoDto? playbackInfoDto)
{
var authInfo = _authContext.GetAuthorizationInfo(Request);
- var profile = deviceProfile?.DeviceProfile;
-
+ var profile = playbackInfoDto?.DeviceProfile;
_logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile);
if (profile == null)
@@ -134,6 +135,23 @@ namespace Jellyfin.Api.Controllers
}
}
+ // Copy params from posted body
+ // TODO clean up when breaking API compatibility.
+ userId ??= playbackInfoDto?.UserId;
+ maxStreamingBitrate ??= playbackInfoDto?.MaxStreamingBitrate;
+ startTimeTicks ??= playbackInfoDto?.StartTimeTicks;
+ audioStreamIndex ??= playbackInfoDto?.AudioStreamIndex;
+ subtitleStreamIndex ??= playbackInfoDto?.SubtitleStreamIndex;
+ maxAudioChannels ??= playbackInfoDto?.MaxAudioChannels;
+ mediaSourceId ??= playbackInfoDto?.MediaSourceId;
+ liveStreamId ??= playbackInfoDto?.LiveStreamId;
+ autoOpenLiveStream ??= playbackInfoDto?.AutoOpenLiveStream ?? false;
+ enableDirectPlay ??= playbackInfoDto?.EnableDirectPlay ?? true;
+ enableDirectStream ??= playbackInfoDto?.EnableDirectStream ?? true;
+ enableTranscoding ??= playbackInfoDto?.EnableTranscoding ?? true;
+ allowVideoStreamCopy ??= playbackInfoDto?.AllowVideoStreamCopy ?? true;
+ allowAudioStreamCopy ??= playbackInfoDto?.AllowAudioStreamCopy ?? true;
+
var info = await _mediaInfoHelper.GetPlaybackInfo(
itemId,
userId,
@@ -161,18 +179,18 @@ namespace Jellyfin.Api.Controllers
maxAudioChannels,
info!.PlaySessionId!,
userId ?? Guid.Empty,
- enableDirectPlay,
- enableDirectStream,
- enableTranscoding,
- allowVideoStreamCopy,
- allowAudioStreamCopy,
+ enableDirectPlay.Value,
+ enableDirectStream.Value,
+ enableTranscoding.Value,
+ allowVideoStreamCopy.Value,
+ allowAudioStreamCopy.Value,
Request.HttpContext.GetNormalizedRemoteIp());
}
_mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate);
}
- if (autoOpenLiveStream)
+ if (autoOpenLiveStream.Value)
{
var mediaSource = string.IsNullOrWhiteSpace(mediaSourceId) ? info.MediaSources[0] : info.MediaSources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.Ordinal));
@@ -183,9 +201,9 @@ namespace Jellyfin.Api.Controllers
new LiveStreamRequest
{
AudioStreamIndex = audioStreamIndex,
- DeviceProfile = deviceProfile?.DeviceProfile,
- EnableDirectPlay = enableDirectPlay,
- EnableDirectStream = enableDirectStream,
+ DeviceProfile = playbackInfoDto?.DeviceProfile,
+ EnableDirectPlay = enableDirectPlay.Value,
+ EnableDirectStream = enableDirectStream.Value,
ItemId = itemId,
MaxAudioChannels = maxAudioChannels,
MaxStreamingBitrate = maxStreamingBitrate,
diff --git a/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs b/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs
new file mode 100644
index 000000000..2cfdba507
--- /dev/null
+++ b/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs
@@ -0,0 +1,86 @@
+using System;
+using MediaBrowser.Model.Dlna;
+
+namespace Jellyfin.Api.Models.MediaInfoDtos
+{
+ /// <summary>
+ /// Plabyback info dto.
+ /// </summary>
+ public class PlaybackInfoDto
+ {
+ /// <summary>
+ /// Gets or sets the playback userId.
+ /// </summary>
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the max streaming bitrate.
+ /// </summary>
+ public int? MaxStreamingBitrate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start time in ticks.
+ /// </summary>
+ public long? StartTimeTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the audio stream index.
+ /// </summary>
+ public int? AudioStreamIndex { get; set; }
+
+ /// <summary>
+ /// Gets or sets the subtitle stream index.
+ /// </summary>
+ public int? SubtitleStreamIndex { get; set; }
+
+ /// <summary>
+ /// Gets or sets the max audio channels.
+ /// </summary>
+ public int? MaxAudioChannels { get; set; }
+
+ /// <summary>
+ /// Gets or sets the media source id.
+ /// </summary>
+ public string? MediaSourceId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the live stream id.
+ /// </summary>
+ public string? LiveStreamId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the device profile.
+ /// </summary>
+ public DeviceProfile? DeviceProfile { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable direct play.
+ /// </summary>
+ public bool? EnableDirectPlay { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable direct stream.
+ /// </summary>
+ public bool? EnableDirectStream { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable transcoding.
+ /// </summary>
+ public bool? EnableTranscoding { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable video stream copy.
+ /// </summary>
+ public bool? AllowVideoStreamCopy { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to allow audio stream copy.
+ /// </summary>
+ public bool? AllowAudioStreamCopy { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to auto open the live stream.
+ /// </summary>
+ public bool? AutoOpenLiveStream { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Jellyfin.Api/Models/VideoDtos/DeviceProfileDto.cs b/Jellyfin.Api/Models/VideoDtos/DeviceProfileDto.cs
deleted file mode 100644
index db55dc34b..000000000
--- a/Jellyfin.Api/Models/VideoDtos/DeviceProfileDto.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using MediaBrowser.Model.Dlna;
-
-namespace Jellyfin.Api.Models.VideoDtos
-{
- /// <summary>
- /// Device profile dto.
- /// </summary>
- public class DeviceProfileDto
- {
- /// <summary>
- /// Gets or sets device profile.
- /// </summary>
- public DeviceProfile? DeviceProfile { get; set; }
- }
-}
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index db67f6470..a1a7a3053 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -106,6 +106,10 @@ namespace Jellyfin.Server
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
+ // Enable cl-va P010 interop for tonemapping on Intel VAAPI
+ Environment.SetEnvironmentVariable("NEOReadDebugKeys", "1");
+ Environment.SetEnvironmentVariable("EnableExtendedVaFormats", "1");
+
await InitLoggingConfigFile(appPaths).ConfigureAwait(false);
// Create an instance of the application configuration to use for application startup
diff --git a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs
new file mode 100644
index 000000000..37e6f64e3
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.Common.Json.Converters
+{
+ /// <summary>
+ /// Converts a Version object or value to/from JSON.
+ /// </summary>
+ public class JsonVersionConverter : JsonConverter<Version>
+ {
+ /// <inheritdoc />
+ public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ => new Version(reader.GetString());
+
+ /// <inheritdoc />
+ public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options)
+ => writer.WriteStringValue(value.ToString());
+ }
+}
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 9a94664ac..1aaf4a43b 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -40,6 +40,7 @@ namespace MediaBrowser.Common.Json
};
options.Converters.Add(new JsonGuidConverter());
+ options.Converters.Add(new JsonVersionConverter());
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new JsonNullableStructConverterFactory());
options.Converters.Add(new JsonDateTimeIso8601Converter());
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 9a6f1231f..3baa59cae 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -112,6 +112,16 @@ namespace MediaBrowser.Controller.MediaEncoding
return _mediaEncoder.SupportsHwaccel("vaapi");
}
+ private bool IsTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
+ {
+ var videoStream = state.VideoStream;
+ return IsColorDepth10(state)
+ && _mediaEncoder.SupportsHwaccel("opencl")
+ && options.EnableTonemapping
+ && !string.IsNullOrEmpty(videoStream.VideoRange)
+ && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase);
+ }
+
/// <summary>
/// Gets the name of the output video codec.
/// </summary>
@@ -468,6 +478,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+ var isTonemappingSupported = IsTonemappingSupported(state, encodingOptions);
if (!IsCopyCodec(outputVideoCodec))
{
@@ -477,10 +488,24 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isVaapiDecoder)
{
- arg.Append("-hwaccel_output_format vaapi ")
- .Append("-vaapi_device ")
- .Append(encodingOptions.VaapiDevice)
- .Append(' ');
+ if (isTonemappingSupported)
+ {
+ arg.Append("-init_hw_device vaapi=va:")
+ .Append(encodingOptions.VaapiDevice)
+ .Append(' ')
+ .Append("-init_hw_device opencl=ocl@va ")
+ .Append("-hwaccel vaapi ")
+ .Append("-hwaccel_device va ")
+ .Append("-hwaccel_output_format vaapi ")
+ .Append("-filter_hw_device ocl ");
+ }
+ else
+ {
+ arg.Append("-hwaccel_output_format vaapi ")
+ .Append("-vaapi_device ")
+ .Append(encodingOptions.VaapiDevice)
+ .Append(' ');
+ }
}
else if (!isVaapiDecoder && isVaapiEncoder)
{
@@ -529,13 +554,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder)
|| (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder))
{
- var isColorDepth10 = IsColorDepth10(state);
-
- if (isColorDepth10
- && _mediaEncoder.SupportsHwaccel("opencl")
- && encodingOptions.EnableTonemapping
- && !string.IsNullOrEmpty(state.VideoStream.VideoRange)
- && state.VideoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ if (isTonemappingSupported)
{
arg.Append("-init_hw_device opencl=ocl:")
.Append(encodingOptions.OpenclDevice)
@@ -1866,6 +1885,19 @@ namespace MediaBrowser.Controller.MediaEncoding
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isVaapiHevcEncoder = outputVideoCodec.IndexOf("hevc_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
+
+ // Tonemapping and burn-in graphical subtitles requires overlay_vaapi.
+ // But it's still in ffmpeg mailing list. Disable it for now.
+ if (isTonemappingSupported && isTonemappingSupportedOnVaapi)
+ {
+ return GetOutputSizeParam(state, options, outputVideoCodec);
+ }
+
// Setup subtitle scaling
if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
{
@@ -1997,6 +2029,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public List<string> GetScalingFilters(
EncodingJobInfo state,
+ EncodingOptions options,
int? videoWidth,
int? videoHeight,
Video3DFormat? threedFormat,
@@ -2035,6 +2068,19 @@ namespace MediaBrowser.Controller.MediaEncoding
|| state.DeInterlace("h265", true)
|| state.DeInterlace("hevc", true);
+ var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && !qsv_or_vaapi;
+
+ var outputPixFmt = string.Empty;
+ if (isTonemappingSupported && isTonemappingSupportedOnVaapi)
+ {
+ outputPixFmt = "format=p010:out_range=limited";
+ }
+ else
+ {
+ outputPixFmt = "format=nv12";
+ }
+
if (!videoWidth.HasValue
|| outputWidth != videoWidth.Value
|| !videoHeight.HasValue
@@ -2045,10 +2091,11 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
- "{0}=w={1}:h={2}:format=nv12{3}",
+ "{0}=w={1}:h={2}{3}{4}",
qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi",
outputWidth,
outputHeight,
+ ":" + outputPixFmt,
(qsv_or_vaapi && isDeintEnabled) ? ":deinterlace=1" : string.Empty));
}
else
@@ -2056,8 +2103,9 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
- "{0}=format=nv12{1}",
+ "{0}={1}{2}",
qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi",
+ outputPixFmt,
(qsv_or_vaapi && isDeintEnabled) ? ":deinterlace=1" : string.Empty));
}
}
@@ -2290,6 +2338,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isSwDecoder = string.IsNullOrEmpty(videoDecoder);
var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiHevcEncoder = outputVideoCodec.IndexOf("hevc_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1;
@@ -2300,6 +2349,10 @@ namespace MediaBrowser.Controller.MediaEncoding
var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1;
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
var isColorDepth10 = IsColorDepth10(state);
+ var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder;
+ var isTonemappingSupportedOnAmf = string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder;
+ var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -2311,18 +2364,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
- if ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder)
- || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder))
+ if (isTonemappingSupportedOnNvenc || isTonemappingSupportedOnAmf || isTonemappingSupportedOnVaapi)
{
// Currently only with the use of NVENC decoder can we get a decent performance.
// Currently only the HEVC/H265 format is supported with NVDEC decoder.
// NVIDIA Pascal and Turing or higher are recommended.
// AMD Polaris and Vega or higher are recommended.
- if (isColorDepth10
- && _mediaEncoder.SupportsHwaccel("opencl")
- && options.EnableTonemapping
- && !string.IsNullOrEmpty(videoStream.VideoRange)
- && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ // Intel Kaby Lake or newer is required.
+ if (isTonemappingSupported)
{
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
@@ -2355,10 +2404,35 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("format=p010");
}
- // Upload the HDR10 or HLG data to the OpenCL device,
- // use tonemap_opencl filter for tone mapping,
- // and then download the SDR data to memory.
- filters.Add("hwupload");
+ if (isNvdecHevcDecoder || isSwDecoder || isD3d11vaDecoder)
+ {
+ // Upload the HDR10 or HLG data to the OpenCL device,
+ // use tonemap_opencl filter for tone mapping,
+ // and then download the SDR data to memory.
+ filters.Add("hwupload");
+ }
+
+ if (isVaapiDecoder)
+ {
+ isScalingInAdvance = true;
+ filters.AddRange(
+ GetScalingFilters(
+ state,
+ options,
+ inputWidth,
+ inputHeight,
+ threeDFormat,
+ videoDecoder,
+ outputVideoCodec,
+ request.Width,
+ request.Height,
+ request.MaxWidth,
+ request.MaxHeight));
+
+ // hwmap the HDR data to opencl device by cl-va p010 interop.
+ filters.Add("hwmap");
+ }
+
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
@@ -2369,33 +2443,46 @@ namespace MediaBrowser.Controller.MediaEncoding
options.TonemappingPeak,
options.TonemappingParam,
options.TonemappingRange));
- filters.Add("hwdownload");
- if (isLibX264Encoder
- || isLibX265Encoder
- || hasGraphicalSubs
- || (isNvdecHevcDecoder && isDeinterlaceHevc)
- || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc))
+ if (isNvdecHevcDecoder || isSwDecoder || isD3d11vaDecoder)
+ {
+ filters.Add("hwdownload");
+ }
+
+ if (isSwDecoder || isD3d11vaDecoder)
+ {
+ if (isLibX264Encoder
+ || isLibX265Encoder
+ || hasGraphicalSubs
+ || (isNvdecHevcDecoder && isDeinterlaceHevc)
+ || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc))
+ {
+ filters.Add("format=nv12");
+ }
+ }
+
+ if (isVaapiDecoder)
{
- filters.Add("format=nv12");
+ // Reverse the data route from opencl to vaapi.
+ filters.Add("hwmap=derive_device=vaapi:reverse=1");
}
}
}
- // When the input may or may not be hardware VAAPI decodable
- if (isVaapiH264Encoder || isVaapiHevcEncoder)
+ // When the input may or may not be hardware VAAPI decodable.
+ if ((isVaapiH264Encoder || isVaapiHevcEncoder) && !isTonemappingSupported && !isTonemappingSupportedOnVaapi)
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
}
- // When burning in graphical subtitles using overlay_qsv, upload videostream to the same qsv context
+ // When burning in graphical subtitles using overlay_qsv, upload videostream to the same qsv context.
else if (isLinux && hasGraphicalSubs && (isQsvH264Encoder || isQsvHevcEncoder))
{
filters.Add("hwupload=extra_hw_frames=64");
}
- // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
+ // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first.
else if (IsVaapiSupported(state) && isVaapiDecoder && (isLibX264Encoder || isLibX265Encoder))
{
var codec = videoStream.Codec.ToLowerInvariant();
@@ -2422,7 +2509,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- // Add hardware deinterlace filter before scaling filter
+ // Add hardware deinterlace filter before scaling filter.
if (isDeinterlaceH264)
{
if (isVaapiH264Encoder)
@@ -2435,7 +2522,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- // Add software deinterlace filter before scaling filter
+ // Add software deinterlace filter before scaling filter.
if ((isDeinterlaceH264 || isDeinterlaceHevc)
&& !isVaapiH264Encoder
&& !isVaapiHevcEncoder
@@ -2467,6 +2554,7 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.AddRange(
GetScalingFilters(
state,
+ options,
inputWidth,
inputHeight,
threeDFormat,
@@ -2483,6 +2571,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasTextSubs)
{
+ // Convert hw context from ocl to va.
+ // For tonemapping and text subs burn-in.
+ if (isTonemappingSupported && isTonemappingSupportedOnVaapi)
+ {
+ filters.Add("scale_vaapi");
+ }
+
// Test passed on Intel and AMD gfx
filters.Add("hwmap=mode=read+write");
filters.Add("format=nv12");
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index 93178d64a..8951cb62e 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -768,16 +768,7 @@ namespace MediaBrowser.Providers.Music
_stopWatchMusicBrainz.Restart();
using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
-
- // MusicBrainz request a contact email address is supplied, as comment, in user agent field:
- // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent .
- request.Headers.UserAgent.ParseAdd(string.Format(
- CultureInfo.InvariantCulture,
- "{0} ( {1} )",
- _appHost.ApplicationUserAgent,
- _appHost.ApplicationUserAgentAddress));
-
- response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(request).ConfigureAwait(false);
+ response = await _httpClientFactory.CreateClient(NamedClient.MusicBrainz).SendAsync(request).ConfigureAwait(false);
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling).
}
diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec
index 0ab1e410a..197126ee5 100644
--- a/fedora/jellyfin.spec
+++ b/fedora/jellyfin.spec
@@ -127,10 +127,6 @@ if [ $1 -gt 1 ] ; then
if [ "${service_state}" = "active" ]; then
systemctl start jellyfin.service
fi
- if [ $1 -eq 1 ]; then
- # On fresh install only, enable the jellyfin.service unit
- systemctl enable --now jellyfin.service
- fi
fi
%systemd_post jellyfin.service