aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Channels/IChannelManager.cs7
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs16
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs9
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs11
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs8
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/IGuideManager.cs26
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs21
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvService.cs13
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHost.cs7
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHostManager.cs46
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs54
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs77
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingInfo.cs210
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs16
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs586
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs3
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs28
22 files changed, 639 insertions, 513 deletions
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index 8eb27888a..c8b432ecb 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -95,12 +95,5 @@ namespace MediaBrowser.Controller.Channels
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The item media sources.</returns>
IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken);
-
- /// <summary>
- /// Whether the item supports media probe.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>Whether media probe should be enabled.</returns>
- bool EnableMediaProbe(BaseItem item);
}
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index d789033f1..b225f22df 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -184,7 +185,7 @@ namespace MediaBrowser.Controller.Entities
/// <exception cref="ArgumentNullException">The id is empty.</exception>
public BaseItem FindVirtualChild(Guid id)
{
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 18d948a62..11cdf8444 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo<ArtistInfo>
{
[JsonIgnore]
- public bool IsAccessedByName => ParentId.Equals(default);
+ public bool IsAccessedByName => ParentId.IsEmpty();
[JsonIgnore]
public override bool IsFolder => !IsAccessedByName;
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index fdbceac96..ddcc994a0 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -240,7 +240,7 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- if (!ChannelId.Equals(default))
+ if (!ChannelId.IsEmpty())
{
return SourceType.Channel;
}
@@ -530,7 +530,7 @@ namespace MediaBrowser.Controller.Entities
get
{
var id = DisplayParentId;
- if (id.Equals(default))
+ if (id.IsEmpty())
{
return null;
}
@@ -746,7 +746,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool StopRefreshIfLocalMetadataFound => true;
[JsonIgnore]
- protected virtual bool SupportsOwnedItems => !ParentId.Equals(default) && IsFileProtocol;
+ protected virtual bool SupportsOwnedItems => !ParentId.IsEmpty() && IsFileProtocol;
[JsonIgnore]
public virtual bool SupportsPeople => false;
@@ -823,7 +823,7 @@ namespace MediaBrowser.Controller.Entities
public BaseItem GetOwner()
{
var ownerId = OwnerId;
- return ownerId.Equals(default) ? null : LibraryManager.GetItemById(ownerId);
+ return ownerId.IsEmpty() ? null : LibraryManager.GetItemById(ownerId);
}
public bool CanDelete(User user, List<Folder> allCollectionFolders)
@@ -968,7 +968,7 @@ namespace MediaBrowser.Controller.Entities
public BaseItem GetParent()
{
var parentId = ParentId;
- if (parentId.Equals(default))
+ if (parentId.IsEmpty())
{
return null;
}
@@ -1361,7 +1361,7 @@ namespace MediaBrowser.Controller.Entities
var tasks = extras.Select(i =>
{
var subOptions = new MetadataRefreshOptions(options);
- if (!i.OwnerId.Equals(ownerId) || !i.ParentId.Equals(default))
+ if (!i.OwnerId.Equals(ownerId) || !i.ParentId.IsEmpty())
{
i.OwnerId = ownerId;
i.ParentId = Guid.Empty;
@@ -1673,7 +1673,7 @@ namespace MediaBrowser.Controller.Entities
// First get using the cached Id
if (info.ItemId.HasValue)
{
- if (info.ItemId.Value.Equals(default))
+ if (info.ItemId.Value.IsEmpty())
{
return null;
}
@@ -2439,7 +2439,7 @@ namespace MediaBrowser.Controller.Entities
return Task.FromResult(true);
}
- if (video.OwnerId.Equals(default))
+ if (video.OwnerId.IsEmpty())
{
video.OwnerId = this.Id;
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index e707eedbf..74eb089de 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Collections;
@@ -198,7 +199,7 @@ namespace MediaBrowser.Controller.Entities
{
item.SetParent(this);
- if (item.Id.Equals(default))
+ if (item.Id.IsEmpty())
{
item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType());
}
@@ -697,7 +698,7 @@ namespace MediaBrowser.Controller.Entities
if (this is not UserRootFolder
&& this is not AggregateFolder
- && query.ParentId.Equals(default))
+ && query.ParentId.IsEmpty())
{
query.Parent = this;
}
@@ -840,7 +841,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default))
+ if (!query.AdjacentTo.IsNullOrEmpty())
{
Logger.LogDebug("Query requires post-filtering due to AdjacentTo");
return true;
@@ -987,7 +988,7 @@ namespace MediaBrowser.Controller.Entities
#pragma warning restore CA1309
// This must be the last filter
- if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default))
+ if (!query.AdjacentTo.IsNullOrEmpty())
{
items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value);
}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index bf31508c1..37e241414 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -8,6 +8,7 @@ using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -74,12 +75,12 @@ namespace MediaBrowser.Controller.Entities.TV
get
{
var seriesId = SeriesId;
- if (seriesId.Equals(default))
+ if (seriesId.IsEmpty())
{
seriesId = FindSeriesId();
}
- return seriesId.Equals(default) ? null : (LibraryManager.GetItemById(seriesId) as Series);
+ return seriesId.IsEmpty() ? null : (LibraryManager.GetItemById(seriesId) as Series);
}
}
@@ -89,12 +90,12 @@ namespace MediaBrowser.Controller.Entities.TV
get
{
var seasonId = SeasonId;
- if (seasonId.Equals(default))
+ if (seasonId.IsEmpty())
{
seasonId = FindSeasonId();
}
- return seasonId.Equals(default) ? null : (LibraryManager.GetItemById(seasonId) as Season);
+ return seasonId.IsEmpty() ? null : (LibraryManager.GetItemById(seasonId) as Season);
}
}
@@ -271,7 +272,7 @@ namespace MediaBrowser.Controller.Entities.TV
var seasonId = SeasonId;
- if (!seasonId.Equals(default) && !list.Contains(seasonId))
+ if (!seasonId.IsEmpty() && !list.Contains(seasonId))
{
list.Add(seasonId);
}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 0a040a3c2..c29cefc15 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Querying;
@@ -48,12 +49,12 @@ namespace MediaBrowser.Controller.Entities.TV
get
{
var seriesId = SeriesId;
- if (seriesId.Equals(default))
+ if (seriesId.IsEmpty())
{
seriesId = FindSeriesId();
}
- return seriesId.Equals(default) ? null : (LibraryManager.GetItemById(seriesId) as Series);
+ return seriesId.IsEmpty() ? null : (LibraryManager.GetItemById(seriesId) as Series);
}
}
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index eb026deb4..c93488a85 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -70,11 +70,11 @@ namespace MediaBrowser.Controller.Entities
/// <inheritdoc />
public override IEnumerable<Guid> GetIdsForAncestorQuery()
{
- if (!DisplayParentId.Equals(default))
+ if (!DisplayParentId.IsEmpty())
{
yield return DisplayParentId;
}
- else if (!ParentId.Equals(default))
+ else if (!ParentId.IsEmpty())
{
yield return ParentId;
}
@@ -95,11 +95,11 @@ namespace MediaBrowser.Controller.Entities
{
var parent = this as Folder;
- if (!DisplayParentId.Equals(default))
+ if (!DisplayParentId.IsEmpty())
{
parent = LibraryManager.GetItemById(DisplayParentId) as Folder ?? parent;
}
- else if (!ParentId.Equals(default))
+ else if (!ParentId.IsEmpty())
{
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
}
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index a3525c862..4af000557 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -433,7 +433,7 @@ namespace MediaBrowser.Controller.Entities
var user = query.User;
// This must be the last filter
- if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default))
+ if (!query.AdjacentTo.IsNullOrEmpty())
{
items = FilterForAdjacency(items.ToList(), query.AdjacentTo.Value);
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index be2eb4d28..5adadec39 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -456,7 +456,7 @@ namespace MediaBrowser.Controller.Entities
foreach (var child in LinkedAlternateVersions)
{
// Reset the cached value
- if (child.ItemId.HasValue && child.ItemId.Value.Equals(default))
+ if (child.ItemId.IsNullOrEmpty())
{
child.ItemId = null;
}
diff --git a/MediaBrowser.Controller/LiveTv/IGuideManager.cs b/MediaBrowser.Controller/LiveTv/IGuideManager.cs
new file mode 100644
index 000000000..9883b9283
--- /dev/null
+++ b/MediaBrowser.Controller/LiveTv/IGuideManager.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.LiveTv;
+
+namespace MediaBrowser.Controller.LiveTv;
+
+/// <summary>
+/// Service responsible for managing the Live TV guide.
+/// </summary>
+public interface IGuideManager
+{
+ /// <summary>
+ /// Gets the guide information.
+ /// </summary>
+ /// <returns>The <see cref="GuideInfo"/>.</returns>
+ GuideInfo GetGuideInfo();
+
+ /// <summary>
+ /// Refresh the guide.
+ /// </summary>
+ /// <param name="progress">The <see cref="IProgress{T}"/> to use.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
+ /// <returns>Task representing the refresh operation.</returns>
+ Task RefreshGuide(IProgress<double> progress, CancellationToken cancellationToken);
+}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 4206159e7..2dbc2cf82 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -71,9 +71,8 @@ namespace MediaBrowser.Controller.LiveTv
/// Adds the parts.
/// </summary>
/// <param name="services">The services.</param>
- /// <param name="tunerHosts">The tuner hosts.</param>
/// <param name="listingProviders">The listing providers.</param>
- void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders);
+ void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<IListingsProvider> listingProviders);
/// <summary>
/// Gets the timer.
@@ -176,12 +175,6 @@ namespace MediaBrowser.Controller.LiveTv
Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
/// <summary>
- /// Gets the guide information.
- /// </summary>
- /// <returns>GuideInfo.</returns>
- GuideInfo GetGuideInfo();
-
- /// <summary>
/// Gets the recommended programs.
/// </summary>
/// <param name="query">The query.</param>
@@ -254,14 +247,6 @@ namespace MediaBrowser.Controller.LiveTv
Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem Item, BaseItemDto ItemDto)> programs, IReadOnlyList<ItemFields> fields, User user = null);
/// <summary>
- /// Saves the tuner host.
- /// </summary>
- /// <param name="info">Turner host to save.</param>
- /// <param name="dataSourceChanged">Option to specify that data source has changed.</param>
- /// <returns>Tuner host information wrapped in a task.</returns>
- Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
-
- /// <summary>
/// Saves the listing provider.
/// </summary>
/// <param name="info">The information.</param>
@@ -298,10 +283,6 @@ namespace MediaBrowser.Controller.LiveTv
Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
- List<NameIdPair> GetTunerHostTypes();
-
- Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
-
string GetEmbyTvActiveRecordingPath(string id);
ActiveRecordingInfo GetActiveRecordingInfo(string path);
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
index ce34954e3..52fb15648 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
@@ -141,14 +141,6 @@ namespace MediaBrowser.Controller.LiveTv
Task CloseLiveStream(string id, CancellationToken cancellationToken);
/// <summary>
- /// Records the live stream.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task RecordLiveStream(string id, CancellationToken cancellationToken);
-
- /// <summary>
/// Resets the tuner.
/// </summary>
/// <param name="id">The identifier.</param>
@@ -180,9 +172,4 @@ namespace MediaBrowser.Controller.LiveTv
{
Task<ILiveStream> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
}
-
- public interface ISupportsUpdatingDefaults
- {
- Task UpdateTimerDefaults(SeriesTimerInfo info, CancellationToken cancellationToken);
- }
}
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
index b98309158..3689a2adf 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -36,13 +36,6 @@ namespace MediaBrowser.Controller.LiveTv
Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
/// <summary>
- /// Gets the tuner infos.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;List&lt;LiveTvTunerInfo&gt;&gt;.</returns>
- Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken);
-
- /// <summary>
/// Gets the channel stream.
/// </summary>
/// <param name="channelId">The channel identifier.</param>
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
new file mode 100644
index 000000000..3df6066f6
--- /dev/null
+++ b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+
+namespace MediaBrowser.Controller.LiveTv;
+
+/// <summary>
+/// Service responsible for managing the <see cref="ITunerHost"/>s.
+/// </summary>
+public interface ITunerHostManager
+{
+ /// <summary>
+ /// Gets the available <see cref="ITunerHost"/>s.
+ /// </summary>
+ IReadOnlyList<ITunerHost> TunerHosts { get; }
+
+ /// <summary>
+ /// Gets the <see cref="NameIdPair"/>s for the available <see cref="ITunerHost"/>s.
+ /// </summary>
+ /// <returns>The <see cref="NameIdPair"/>s.</returns>
+ IEnumerable<NameIdPair> GetTunerHostTypes();
+
+ /// <summary>
+ /// Saves the tuner host.
+ /// </summary>
+ /// <param name="info">Turner host to save.</param>
+ /// <param name="dataSourceChanged">Option to specify that data source has changed.</param>
+ /// <returns>Tuner host information wrapped in a task.</returns>
+ Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
+
+ /// <summary>
+ /// Discovers the available tuners.
+ /// </summary>
+ /// <param name="newDevicesOnly">A value indicating whether to only return new devices.</param>
+ /// <returns>The <see cref="TunerHostInfo"/>s.</returns>
+ IAsyncEnumerable<TunerHostInfo> DiscoverTuners(bool newDevicesOnly);
+
+ /// <summary>
+ /// Scans for tuner devices that have changed URLs.
+ /// </summary>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
+ /// <returns>A task that represents the scanning operation.</returns>
+ Task ScanForTunerDeviceChanges(CancellationToken cancellationToken);
+}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
deleted file mode 100644
index eb3babc18..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvServiceStatusInfo
- {
- public LiveTvServiceStatusInfo()
- {
- Tuners = new List<LiveTvTunerInfo>();
- IsVisible = true;
- }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public LiveTvServiceStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the status message.
- /// </summary>
- /// <value>The status message.</value>
- public string StatusMessage { get; set; }
-
- /// <summary>
- /// Gets or sets the version.
- /// </summary>
- /// <value>The version.</value>
- public string Version { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has update available.
- /// </summary>
- /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
- public bool HasUpdateAvailable { get; set; }
-
- /// <summary>
- /// Gets or sets the tuners.
- /// </summary>
- /// <value>The tuners.</value>
- public List<LiveTvTunerInfo> Tuners { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is visible.
- /// </summary>
- /// <value><c>true</c> if this instance is visible; otherwise, <c>false</c>.</value>
- public bool IsVisible { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
deleted file mode 100644
index aa5eb59d1..000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvTunerInfo
- {
- public LiveTvTunerInfo()
- {
- Clients = new List<string>();
- }
-
- /// <summary>
- /// Gets or sets the type of the source.
- /// </summary>
- /// <value>The type of the source.</value>
- public string SourceType { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public LiveTvTunerStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the channel identifier.
- /// </summary>
- /// <value>The channel identifier.</value>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the recording identifier.
- /// </summary>
- /// <value>The recording identifier.</value>
- public string RecordingId { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the program.
- /// </summary>
- /// <value>The name of the program.</value>
- public string ProgramName { get; set; }
-
- /// <summary>
- /// Gets or sets the clients.
- /// </summary>
- /// <value>The clients.</value>
- public List<string> Clients { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance can reset.
- /// </summary>
- /// <value><c>true</c> if this instance can reset; otherwise, <c>false</c>.</value>
- public bool CanReset { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
deleted file mode 100644
index 1dcf7a58f..000000000
--- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class RecordingInfo
- {
- public RecordingInfo()
- {
- Genres = new List<string>();
- }
-
- /// <summary>
- /// Gets or sets the id of the recording.
- /// </summary>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the series timer identifier.
- /// </summary>
- /// <value>The series timer identifier.</value>
- public string SeriesTimerId { get; set; }
-
- /// <summary>
- /// Gets or sets the timer identifier.
- /// </summary>
- /// <value>The timer identifier.</value>
- public string TimerId { get; set; }
-
- /// <summary>
- /// Gets or sets the channelId of the recording.
- /// </summary>
- public string ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the channel.
- /// </summary>
- /// <value>The type of the channel.</value>
- public ChannelType ChannelType { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the recording.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- public string Path { get; set; }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- /// <summary>
- /// Gets or sets the overview.
- /// </summary>
- /// <value>The overview.</value>
- public string Overview { get; set; }
-
- /// <summary>
- /// Gets or sets the start date of the recording, in UTC.
- /// </summary>
- public DateTime StartDate { get; set; }
-
- /// <summary>
- /// Gets or sets the end date of the recording, in UTC.
- /// </summary>
- public DateTime EndDate { get; set; }
-
- /// <summary>
- /// Gets or sets the program identifier.
- /// </summary>
- /// <value>The program identifier.</value>
- public string ProgramId { get; set; }
-
- /// <summary>
- /// Gets or sets the status.
- /// </summary>
- /// <value>The status.</value>
- public RecordingStatus Status { get; set; }
-
- /// <summary>
- /// Gets or sets the genre of the program.
- /// </summary>
- public List<string> Genres { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is repeat.
- /// </summary>
- /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
- public bool IsRepeat { get; set; }
-
- /// <summary>
- /// Gets or sets the episode title.
- /// </summary>
- /// <value>The episode title.</value>
- public string EpisodeTitle { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is hd.
- /// </summary>
- /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
- public bool? IsHD { get; set; }
-
- /// <summary>
- /// Gets or sets the audio.
- /// </summary>
- /// <value>The audio.</value>
- public ProgramAudio? Audio { get; set; }
-
- /// <summary>
- /// Gets or sets the original air date.
- /// </summary>
- /// <value>The original air date.</value>
- public DateTime? OriginalAirDate { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is movie.
- /// </summary>
- /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
- public bool IsMovie { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is sports.
- /// </summary>
- /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
- public bool IsSports { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is series.
- /// </summary>
- /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
- public bool IsSeries { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is live.
- /// </summary>
- /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
- public bool IsLive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is news.
- /// </summary>
- /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
- public bool IsNews { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is kids.
- /// </summary>
- /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
- public bool IsKids { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is premiere.
- /// </summary>
- /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
- public bool IsPremiere { get; set; }
-
- /// <summary>
- /// Gets or sets the official rating.
- /// </summary>
- /// <value>The official rating.</value>
- public string OfficialRating { get; set; }
-
- /// <summary>
- /// Gets or sets the community rating.
- /// </summary>
- /// <value>The community rating.</value>
- public float? CommunityRating { get; set; }
-
- /// <summary>
- /// Gets or sets the image path if it can be accessed directly from the file system.
- /// </summary>
- /// <value>The image path.</value>
- public string ImagePath { get; set; }
-
- /// <summary>
- /// Gets or sets the image url if it can be downloaded.
- /// </summary>
- /// <value>The image URL.</value>
- public string ImageUrl { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has image.
- /// </summary>
- /// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
- public bool? HasImage { get; set; }
-
- /// <summary>
- /// Gets or sets the show identifier.
- /// </summary>
- /// <value>The show identifier.</value>
- public string ShowId { get; set; }
-
- /// <summary>
- /// Gets or sets the date last updated.
- /// </summary>
- /// <value>The date last updated.</value>
- public DateTime DateLastUpdated { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
deleted file mode 100644
index 0b943c939..000000000
--- a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class RecordingStatusChangedEventArgs : EventArgs
- {
- public string RecordingId { get; set; }
-
- public RecordingStatus NewStatus { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 400e7f40f..1c95192f1 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.MediaEncoding
private const string VaapiAlias = "va";
private const string D3d11vaAlias = "dx11";
private const string VideotoolboxAlias = "vt";
+ private const string RkmppAlias = "rk";
private const string OpenclAlias = "ocl";
private const string CudaAlias = "cu";
private const string DrmAlias = "dr";
@@ -161,6 +162,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{ "vaapi", hwEncoder + "_vaapi" },
{ "videotoolbox", hwEncoder + "_videotoolbox" },
{ "v4l2m2m", hwEncoder + "_v4l2m2m" },
+ { "rkmpp", hwEncoder + "_rkmpp" },
};
if (!string.IsNullOrEmpty(hwType)
@@ -217,6 +219,14 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("hwupload_vaapi");
}
+ private bool IsRkmppFullSupported()
+ {
+ return _mediaEncoder.SupportsHwaccel("rkmpp")
+ && _mediaEncoder.SupportsFilter("scale_rkrga")
+ && _mediaEncoder.SupportsFilter("vpp_rkrga")
+ && _mediaEncoder.SupportsFilter("overlay_rkrga");
+ }
+
private bool IsOpenclFullSupported()
{
return _mediaEncoder.SupportsHwaccel("opencl")
@@ -696,6 +706,14 @@ namespace MediaBrowser.Controller.MediaEncoding
return codec.ToLowerInvariant();
}
+ private string GetRkmppDeviceArgs(string alias)
+ {
+ alias ??= RkmppAlias;
+
+ // device selection in rk is not supported.
+ return " -init_hw_device rkmpp=" + alias;
+ }
+
private string GetVideoToolboxDeviceArgs(string alias)
{
alias ??= VideotoolboxAlias;
@@ -835,30 +853,25 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetGraphicalSubCanvasSize(EncodingJobInfo state)
{
- // DVBSUB and DVDSUB use the fixed canvas size 720x576
+ // DVBSUB uses the fixed canvas size 720x576
if (state.SubtitleStream is not null
&& state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
&& !state.SubtitleStream.IsTextSubtitleStream
- && !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase)
- && !string.Equals(state.SubtitleStream.Codec, "DVDSUB", StringComparison.OrdinalIgnoreCase))
+ && !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase))
{
- var inW = state.VideoStream?.Width;
- var inH = state.VideoStream?.Height;
- var reqW = state.BaseRequest.Width;
- var reqH = state.BaseRequest.Height;
- var reqMaxW = state.BaseRequest.MaxWidth;
- var reqMaxH = state.BaseRequest.MaxHeight;
-
- // setup a relative small canvas_size for overlay_qsv/vaapi to reduce transfer overhead
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, 1080);
+ var subtitleWidth = state.SubtitleStream?.Width;
+ var subtitleHeight = state.SubtitleStream?.Height;
- if (overlayW.HasValue && overlayH.HasValue)
+ if (subtitleWidth.HasValue
+ && subtitleHeight.HasValue
+ && subtitleWidth.Value > 0
+ && subtitleHeight.Value > 0)
{
return string.Format(
CultureInfo.InvariantCulture,
" -canvas_size {0}x{1}",
- overlayW.Value,
- overlayH.Value);
+ subtitleWidth.Value,
+ subtitleHeight.Value);
}
}
@@ -1061,6 +1074,33 @@ namespace MediaBrowser.Controller.MediaEncoding
// no videotoolbox hw filter.
args.Append(GetVideoToolboxDeviceArgs(VideotoolboxAlias));
}
+ else if (string.Equals(optHwaccelType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ {
+ if (!isLinux || !_mediaEncoder.SupportsHwaccel("rkmpp"))
+ {
+ return string.Empty;
+ }
+
+ var isRkmppDecoder = vidDecoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
+ var isRkmppEncoder = vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
+ if (!isRkmppDecoder && !isRkmppEncoder)
+ {
+ return string.Empty;
+ }
+
+ args.Append(GetRkmppDeviceArgs(RkmppAlias));
+
+ var filterDevArgs = string.Empty;
+ var doOclTonemap = isHwTonemapAvailable && IsOpenclFullSupported();
+
+ if (doOclTonemap && !isRkmppDecoder)
+ {
+ args.Append(GetOpenclDeviceArgs(0, null, RkmppAlias, OpenclAlias));
+ filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
+ }
+
+ args.Append(filterDevArgs);
+ }
if (!string.IsNullOrEmpty(vidDecoder))
{
@@ -1477,8 +1517,10 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(codec, "h264_rkmpp", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(codec, "hevc_rkmpp", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "av1_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "av1_nvenc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "av1_amf", StringComparison.OrdinalIgnoreCase)
@@ -1918,20 +1960,22 @@ namespace MediaBrowser.Controller.MediaEncoding
profile = "constrained_baseline";
}
- // libx264, h264_qsv and h264_nvenc does not support Constrained Baseline profile, force Baseline in this case.
+ // libx264, h264_{qsv,nvenc,rkmpp} does not support Constrained Baseline profile, force Baseline in this case.
if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoEncoder, "h264_rkmpp", StringComparison.OrdinalIgnoreCase))
&& profile.Contains("baseline", StringComparison.OrdinalIgnoreCase))
{
profile = "baseline";
}
- // libx264, h264_qsv, h264_nvenc and h264_vaapi does not support Constrained High profile, force High in this case.
+ // libx264, h264_{qsv,nvenc,vaapi,rkmpp} does not support Constrained High profile, force High in this case.
if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+ || string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoEncoder, "h264_rkmpp", StringComparison.OrdinalIgnoreCase))
&& profile.Contains("high", StringComparison.OrdinalIgnoreCase))
{
profile = "high";
@@ -2015,6 +2059,11 @@ namespace MediaBrowser.Controller.MediaEncoding
param += " -level " + level;
}
}
+ else if (string.Equals(videoEncoder, "h264_rkmpp", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoEncoder, "hevc_rkmpp", StringComparison.OrdinalIgnoreCase))
+ {
+ param += " -level " + level;
+ }
else if (!string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
{
param += " -level " + level;
@@ -2833,6 +2882,48 @@ namespace MediaBrowser.Controller.MediaEncoding
return (outputWidth, outputHeight);
}
+ public static bool IsScaleRatioSupported(
+ int? videoWidth,
+ int? videoHeight,
+ int? requestedWidth,
+ int? requestedHeight,
+ int? requestedMaxWidth,
+ int? requestedMaxHeight,
+ double? maxScaleRatio)
+ {
+ var (outWidth, outHeight) = GetFixedOutputSize(
+ videoWidth,
+ videoHeight,
+ requestedWidth,
+ requestedHeight,
+ requestedMaxWidth,
+ requestedMaxHeight);
+
+ if (!videoWidth.HasValue
+ || !videoHeight.HasValue
+ || !outWidth.HasValue
+ || !outHeight.HasValue
+ || !maxScaleRatio.HasValue
+ || (maxScaleRatio.Value < 1.0f))
+ {
+ return false;
+ }
+
+ var minScaleRatio = 1.0f / maxScaleRatio;
+ var scaleRatioW = (double)outWidth / (double)videoWidth;
+ var scaleRatioH = (double)outHeight / (double)videoHeight;
+
+ if (scaleRatioW < minScaleRatio
+ || scaleRatioW > maxScaleRatio
+ || scaleRatioH < minScaleRatio
+ || scaleRatioH > maxScaleRatio)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
public static string GetHwScaleFilter(
string hwScaleSuffix,
string videoFormat,
@@ -2877,7 +2968,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- public static string GetCustomSwScaleFilter(
+ public static string GetGraphicalSubPreProcessFilters(
int? videoWidth,
int? videoHeight,
int? requestedWidth,
@@ -2897,7 +2988,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return string.Format(
CultureInfo.InvariantCulture,
- "scale=s={0}x{1}:flags=fast_bilinear",
+ @"scale=-1:{1}:fast_bilinear,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}",
outWidth.Value,
outHeight.Value);
}
@@ -2913,7 +3004,7 @@ namespace MediaBrowser.Controller.MediaEncoding
int? requestedHeight,
int? requestedMaxWidth,
int? requestedMaxHeight,
- int? framerate)
+ float? framerate)
{
var reqTicks = state.BaseRequest.StartTimeTicks ?? 0;
var startTime = TimeSpan.FromTicks(reqTicks).ToString(@"hh\\\:mm\\\:ss\\\.fff", CultureInfo.InvariantCulture);
@@ -2932,7 +3023,7 @@ namespace MediaBrowser.Controller.MediaEncoding
"alphasrc=s={0}x{1}:r={2}:start='{3}'",
outWidth.Value,
outHeight.Value,
- framerate ?? 10,
+ framerate ?? 25,
reqTicks > 0 ? startTime : 0);
}
@@ -3340,9 +3431,8 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (hasGraphicalSubs)
{
- // [0:s]scale=s=1280x720
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3504,15 +3594,17 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- // scale=s=1280x720,format=yuva420p,hwupload
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
else if (hasTextSubs)
{
+ var framerate = state.VideoStream?.RealFrameRate;
+ var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
+
// alphasrc=s=1280x720:r=10:start=0,format=yuva420p,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, hasAssSubs ? 10 : 5);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=yuva420p");
@@ -3527,8 +3619,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
}
@@ -3702,15 +3794,17 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- // scale=s=1280x720,format=yuva420p,hwupload
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
else if (hasTextSubs)
{
+ var framerate = state.VideoStream?.RealFrameRate;
+ var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
+
// alphasrc=s=1280x720:r=10:start=0,format=yuva420p,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, hasAssSubs ? 10 : 5);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=yuva420p");
@@ -3727,8 +3821,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
}
@@ -3938,16 +4032,18 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- // scale,format=bgra,hwupload
- // overlay_qsv can handle overlay scaling,
- // add a dummy scale filter to pair with -canvas_size.
- subFilters.Add("scale=flags=fast_bilinear");
+ // overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
else if (hasTextSubs)
{
+ var framerate = state.VideoStream?.RealFrameRate;
+ var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
+
// alphasrc=s=1280x720:r=10:start=0,format=bgra,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, hasAssSubs ? 10 : 5);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -3973,8 +4069,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
}
@@ -4158,12 +4254,17 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- subFilters.Add("scale=flags=fast_bilinear");
+ // overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
else if (hasTextSubs)
{
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, hasAssSubs ? 10 : 5);
+ var framerate = state.VideoStream?.RealFrameRate;
+ var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
+
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4189,8 +4290,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
}
@@ -4425,12 +4526,17 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- subFilters.Add("scale=flags=fast_bilinear");
+ // overlay_vaapi can handle overlay scaling, setup a smaller height to reduce transfer overhead
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
else if (hasTextSubs)
{
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, hasAssSubs ? 10 : 5);
+ var framerate = state.VideoStream?.RealFrameRate;
+ var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
+
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4454,8 +4560,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
if (isVaapiEncoder)
@@ -4599,14 +4705,16 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- // scale=s=1280x720,format=bgra,hwupload
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
else if (hasTextSubs)
{
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, hasAssSubs ? 10 : 5);
+ var framerate = state.VideoStream?.RealFrameRate;
+ var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
+
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4815,8 +4923,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- subFilters.Add(subSwScaleFilter);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
if (isVaapiEncoder)
@@ -4899,6 +5007,237 @@ namespace MediaBrowser.Controller.MediaEncoding
}
/// <summary>
+ /// Gets the parameter of Rockchip RKMPP/RKRGA filter chain.
+ /// </summary>
+ /// <param name="state">Encoding state.</param>
+ /// <param name="options">Encoding options.</param>
+ /// <param name="vidEncoder">Video encoder to use.</param>
+ /// <returns>The tuple contains three lists: main, sub and overlay filters.</returns>
+ public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetRkmppVidFilterChain(
+ EncodingJobInfo state,
+ EncodingOptions options,
+ string vidEncoder)
+ {
+ if (!string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ {
+ return (null, null, null);
+ }
+
+ var isLinux = OperatingSystem.IsLinux();
+ var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
+ var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
+ var isSwEncoder = !vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
+ var isRkmppOclSupported = isLinux && IsRkmppFullSupported() && IsOpenclFullSupported();
+
+ if ((isSwDecoder && isSwEncoder)
+ || !isRkmppOclSupported
+ || !_mediaEncoder.SupportsFilter("alphasrc"))
+ {
+ return GetSwVidFilterChain(state, options, vidEncoder);
+ }
+
+ // prefered rkmpp + rkrga + opencl filters pipeline
+ if (isRkmppOclSupported)
+ {
+ return GetRkmppVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
+ }
+
+ return (null, null, null);
+ }
+
+ public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetRkmppVidFiltersPrefered(
+ EncodingJobInfo state,
+ EncodingOptions options,
+ string vidDecoder,
+ string vidEncoder)
+ {
+ var inW = state.VideoStream?.Width;
+ var inH = state.VideoStream?.Height;
+ var reqW = state.BaseRequest.Width;
+ var reqH = state.BaseRequest.Height;
+ var reqMaxW = state.BaseRequest.MaxWidth;
+ var reqMaxH = state.BaseRequest.MaxHeight;
+ var threeDFormat = state.MediaSource.Video3DFormat;
+
+ var isRkmppDecoder = vidDecoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
+ var isRkmppEncoder = vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
+ var isSwDecoder = !isRkmppDecoder;
+ var isSwEncoder = !isRkmppEncoder;
+ var isDrmInDrmOut = isRkmppDecoder && isRkmppEncoder;
+
+ var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
+ var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
+ var doDeintH2645 = doDeintH264 || doDeintHevc;
+ var doOclTonemap = IsHwTonemapAvailable(state, options);
+
+ var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
+ var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
+ var hasAssSubs = hasSubs
+ && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+
+ /* Make main filters for video stream */
+ var mainFilters = new List<string>();
+
+ mainFilters.Add(GetOverwriteColorPropertiesParam(state, doOclTonemap));
+
+ if (isSwDecoder)
+ {
+ // INPUT sw surface(memory)
+ // sw deint
+ if (doDeintH2645)
+ {
+ var swDeintFilter = GetSwDeinterlaceFilter(state, options);
+ mainFilters.Add(swDeintFilter);
+ }
+
+ var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ if (!string.IsNullOrEmpty(swScaleFilter))
+ {
+ swScaleFilter += ":flags=fast_bilinear";
+ }
+
+ // sw scale
+ mainFilters.Add(swScaleFilter);
+ mainFilters.Add("format=" + outFormat);
+
+ // keep video at memory except ocl tonemap,
+ // since the overhead caused by hwupload >>> using sw filter.
+ // sw => hw
+ if (doOclTonemap)
+ {
+ mainFilters.Add("hwupload=derive_device=opencl");
+ }
+ }
+ else if (isRkmppDecoder)
+ {
+ // INPUT rkmpp/drm surface(gem/dma-heap)
+
+ var isFullAfbcPipeline = isDrmInDrmOut && !doOclTonemap;
+ var outFormat = doOclTonemap ? "p010" : "nv12";
+ var hwScaleFilter = GetHwScaleFilter("rkrga", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter2 = GetHwScaleFilter("rkrga", string.Empty, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+
+ if (!hasSubs
+ || !isFullAfbcPipeline
+ || !string.IsNullOrEmpty(hwScaleFilter2))
+ {
+ // try enabling AFBC to save DDR bandwidth
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isFullAfbcPipeline)
+ {
+ hwScaleFilter += ":afbc=1";
+ }
+
+ // hw scale
+ mainFilters.Add(hwScaleFilter);
+ }
+ }
+
+ if (doOclTonemap && isRkmppDecoder)
+ {
+ // map from rkmpp/drm to opencl via drm-opencl interop.
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
+ }
+
+ // ocl tonemap
+ if (doOclTonemap)
+ {
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
+ // enable tradeoffs for performance
+ if (!string.IsNullOrEmpty(tonemapFilter))
+ {
+ tonemapFilter += ":tradeoff=1";
+ }
+
+ mainFilters.Add(tonemapFilter);
+ }
+
+ var memoryOutput = false;
+ var isUploadForOclTonemap = isSwDecoder && doOclTonemap;
+ if ((isRkmppDecoder && isSwEncoder) || isUploadForOclTonemap)
+ {
+ memoryOutput = true;
+
+ // OUTPUT nv12 surface(memory)
+ mainFilters.Add("hwdownload");
+ mainFilters.Add("format=nv12");
+ }
+
+ // OUTPUT nv12 surface(memory)
+ if (isSwDecoder && isRkmppEncoder)
+ {
+ memoryOutput = true;
+ }
+
+ if (memoryOutput)
+ {
+ // text subtitles
+ if (hasTextSubs)
+ {
+ var textSubtitlesFilter = GetTextSubtitlesFilter(state, false, false);
+ mainFilters.Add(textSubtitlesFilter);
+ }
+ }
+
+ if (isDrmInDrmOut)
+ {
+ if (doOclTonemap)
+ {
+ // OUTPUT drm(nv12) surface(gem/dma-heap)
+ // reverse-mapping via drm-opencl interop.
+ mainFilters.Add("hwmap=derive_device=rkmpp:mode=write:reverse=1");
+ mainFilters.Add("format=drm_prime");
+ }
+ }
+
+ /* Make sub and overlay filters for subtitle stream */
+ var subFilters = new List<string>();
+ var overlayFilters = new List<string>();
+ if (isDrmInDrmOut)
+ {
+ if (hasSubs)
+ {
+ if (hasGraphicalSubs)
+ {
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
+ subFilters.Add("format=bgra");
+ }
+ else if (hasTextSubs)
+ {
+ var framerate = state.VideoStream?.RealFrameRate;
+ var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
+
+ // alphasrc=s=1280x720:r=10:start=0,format=bgra,subtitles,hwupload
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
+ subFilters.Add(alphaSrcFilter);
+ subFilters.Add("format=bgra");
+ subFilters.Add(subTextSubtitlesFilter);
+ }
+
+ subFilters.Add("hwupload=derive_device=rkmpp");
+
+ // try enabling AFBC to save DDR bandwidth
+ overlayFilters.Add("overlay_rkrga=eof_action=pass:repeatlast=0:format=nv12:afbc=1");
+ }
+ }
+ else if (memoryOutput)
+ {
+ if (hasGraphicalSubs)
+ {
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subPreProcFilters);
+ overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
+ }
+ }
+
+ return (mainFilters, subFilters, overlayFilters);
+ }
+
+ /// <summary>
/// Gets the parameter of video processing filters.
/// </summary>
/// <param name="state">Encoding state.</param>
@@ -4944,6 +5283,10 @@ namespace MediaBrowser.Controller.MediaEncoding
{
(mainFilters, subFilters, overlayFilters) = GetAppleVidFilterChain(state, options, outputVideoCodec);
}
+ else if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ {
+ (mainFilters, subFilters, overlayFilters) = GetRkmppVidFilterChain(state, options, outputVideoCodec);
+ }
else
{
(mainFilters, subFilters, overlayFilters) = GetSwVidFilterChain(state, options, outputVideoCodec);
@@ -5075,18 +5418,21 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv422p", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase))
{
return 8;
}
if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv422p10le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase))
{
return 10;
}
if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv422p12le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase))
{
return 12;
@@ -5139,7 +5485,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.Codec, "av1", StringComparison.OrdinalIgnoreCase)))
{
- return null;
+ // One exception is that RKMPP decoder can handle H.264 High 10.
+ if (!(string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)
+ && string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase)))
+ {
+ return null;
+ }
}
if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
@@ -5166,6 +5517,11 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth);
}
+
+ if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetRkmppVidDecoder(state, options, videoStream, bitDepth);
+ }
}
var whichCodec = videoStream.Codec;
@@ -5231,6 +5587,11 @@ namespace MediaBrowser.Controller.MediaEncoding
return null;
}
+ if (string.Equals(decoderSuffix, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
return isCodecAvailable ? (" -c:v " + decoderName) : null;
}
@@ -5253,6 +5614,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isCudaSupported = (isLinux || isWindows) && IsCudaFullSupported();
var isQsvSupported = (isLinux || isWindows) && _mediaEncoder.SupportsHwaccel("qsv");
var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox");
+ var isRkmppSupported = isLinux && IsRkmppFullSupported();
var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase);
var ffmpegVersion = _mediaEncoder.EncoderVersion;
@@ -5355,6 +5717,14 @@ namespace MediaBrowser.Controller.MediaEncoding
return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_output_format videotoolbox_vld" : string.Empty);
}
+ // Rockchip rkmpp
+ if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)
+ && isRkmppSupported
+ && isCodecAvailable)
+ {
+ return " -hwaccel rkmpp" + (outputHwSurface ? " -hwaccel_output_format drm_prime" : string.Empty);
+ }
+
return null;
}
@@ -5661,6 +6031,102 @@ namespace MediaBrowser.Controller.MediaEncoding
return null;
}
+ public string GetRkmppVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth)
+ {
+ var isLinux = OperatingSystem.IsLinux();
+
+ if (!isLinux
+ || !string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ var inW = state.VideoStream?.Width;
+ var inH = state.VideoStream?.Height;
+ var reqW = state.BaseRequest.Width;
+ var reqH = state.BaseRequest.Height;
+ var reqMaxW = state.BaseRequest.MaxWidth;
+ var reqMaxH = state.BaseRequest.MaxHeight;
+
+ // rkrga RGA2e supports range from 1/16 to 16
+ if (!IsScaleRatioSupported(inW, inH, reqW, reqH, reqMaxW, reqMaxH, 16.0f))
+ {
+ return null;
+ }
+
+ var isRkmppOclSupported = IsRkmppFullSupported() && IsOpenclFullSupported();
+ var hwSurface = isRkmppOclSupported
+ && _mediaEncoder.SupportsFilter("alphasrc");
+
+ // rkrga RGA3 supports range from 1/8 to 8
+ var isAfbcSupported = hwSurface && IsScaleRatioSupported(inW, inH, reqW, reqH, reqMaxW, reqMaxH, 8.0f);
+
+ // TODO: add more 8/10bit and 4:2:2 formats for Rkmpp after finishing the ffcheck tool
+ var is8bitSwFormatsRkmpp = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
+ var is10bitSwFormatsRkmpp = string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
+ var is8_10bitSwFormatsRkmpp = is8bitSwFormatsRkmpp || is10bitSwFormatsRkmpp;
+
+ // nv15 and nv20 are bit-stream only formats
+ if (is10bitSwFormatsRkmpp && !hwSurface)
+ {
+ return null;
+ }
+
+ if (is8bitSwFormatsRkmpp)
+ {
+ if (string.Equals(videoStream.Codec, "mpeg1video", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "mpeg1video", bitDepth, hwSurface);
+ }
+
+ if (string.Equals(videoStream.Codec, "mpeg2video", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "mpeg2video", bitDepth, hwSurface);
+ }
+
+ if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "mpeg4", bitDepth, hwSurface);
+ }
+
+ if (string.Equals(videoStream.Codec, "vp8", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "vp8", bitDepth, hwSurface);
+ }
+ }
+
+ if (is8_10bitSwFormatsRkmpp)
+ {
+ if (string.Equals(videoStream.Codec, "avc", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase))
+ {
+ var accelType = GetHwaccelType(state, options, "h264", bitDepth, hwSurface);
+ return accelType + ((!string.IsNullOrEmpty(accelType) && isAfbcSupported) ? " -afbc rga" : string.Empty);
+ }
+
+ if (string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase))
+ {
+ var accelType = GetHwaccelType(state, options, "hevc", bitDepth, hwSurface);
+ return accelType + ((!string.IsNullOrEmpty(accelType) && isAfbcSupported) ? " -afbc rga" : string.Empty);
+ }
+
+ if (string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase))
+ {
+ var accelType = GetHwaccelType(state, options, "vp9", bitDepth, hwSurface);
+ return accelType + ((!string.IsNullOrEmpty(accelType) && isAfbcSupported) ? " -afbc rga" : string.Empty);
+ }
+
+ if (string.Equals(videoStream.Codec, "av1", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "av1", bitDepth, hwSurface);
+ }
+ }
+
+ return null;
+ }
+
/// <summary>
/// Gets the number of threads.
/// </summary>
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 53df7133b..5a47236f9 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -111,7 +111,8 @@ namespace MediaBrowser.Controller.Session
/// Reports the session ended.
/// </summary>
/// <param name="sessionId">The session identifier.</param>
- void ReportSessionEnded(string sessionId);
+ /// <returns>Task.</returns>
+ ValueTask ReportSessionEnded(string sessionId);
/// <summary>
/// Sends the general command.
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 3e30c8dc4..3a12a56f1 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.Controller.Session
/// <summary>
/// Class SessionInfo.
/// </summary>
- public sealed class SessionInfo : IAsyncDisposable, IDisposable
+ public sealed class SessionInfo : IAsyncDisposable
{
// 1 second
private const long ProgressIncrement = 10000000;
@@ -374,26 +374,6 @@ namespace MediaBrowser.Controller.Session
}
}
- /// <inheritdoc />
- public void Dispose()
- {
- _disposed = true;
-
- StopAutomaticProgress();
-
- var controllers = SessionControllers.ToList();
- SessionControllers = Array.Empty<ISessionController>();
-
- foreach (var controller in controllers)
- {
- if (controller is IDisposable disposable)
- {
- _logger.LogDebug("Disposing session controller synchronously {TypeName}", disposable.GetType().Name);
- disposable.Dispose();
- }
- }
- }
-
public async ValueTask DisposeAsync()
{
_disposed = true;
@@ -401,6 +381,7 @@ namespace MediaBrowser.Controller.Session
StopAutomaticProgress();
var controllers = SessionControllers.ToList();
+ SessionControllers = Array.Empty<ISessionController>();
foreach (var controller in controllers)
{
@@ -409,6 +390,11 @@ namespace MediaBrowser.Controller.Session
_logger.LogDebug("Disposing session controller asynchronously {TypeName}", disposableAsync.GetType().Name);
await disposableAsync.DisposeAsync().ConfigureAwait(false);
}
+ else if (controller is IDisposable disposable)
+ {
+ _logger.LogDebug("Disposing session controller synchronously {TypeName}", disposable.GetType().Name);
+ disposable.Dispose();
+ }
}
}
}