mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-20 17:14:42 +01:00
Merge changes
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
26
MediaBrowser.Controller/LiveTv/IGuideManager.cs
Normal file
26
MediaBrowser.Controller/LiveTv/IGuideManager.cs
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
@@ -175,12 +174,6 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// <returns>Task.</returns>
|
||||
Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the guide information.
|
||||
/// </summary>
|
||||
/// <returns>GuideInfo.</returns>
|
||||
GuideInfo GetGuideInfo();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recommended programs.
|
||||
/// </summary>
|
||||
@@ -253,14 +246,6 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// <returns>Task.</returns>
|
||||
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>
|
||||
@@ -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);
|
||||
|
||||
@@ -140,14 +140,6 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// <returns>Task.</returns>
|
||||
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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,6 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// <returns>Task<IEnumerable<ChannelInfo>>.</returns>
|
||||
Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tuner infos.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task<List<LiveTvTunerInfo>>.</returns>
|
||||
Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the channel stream.
|
||||
/// </summary>
|
||||
|
||||
46
MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
Normal file
46
MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
var subtitleWidth = state.SubtitleStream?.Width;
|
||||
var subtitleHeight = state.SubtitleStream?.Height;
|
||||
|
||||
// setup a relative small canvas_size for overlay_qsv/vaapi to reduce transfer overhead
|
||||
var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, 1080);
|
||||
|
||||
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)
|
||||
@@ -4898,6 +5006,237 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return (newfilters, swFilterChain.SubFilters, swFilterChain.OverlayFilters);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,8 +374,7 @@ namespace MediaBrowser.Controller.Session
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
@@ -386,30 +385,17 @@ namespace MediaBrowser.Controller.Session
|
||||
|
||||
foreach (var controller in controllers)
|
||||
{
|
||||
if (controller is IDisposable disposable)
|
||||
if (controller is IAsyncDisposable disposableAsync)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
StopAutomaticProgress();
|
||||
|
||||
var controllers = SessionControllers.ToList();
|
||||
|
||||
foreach (var controller in controllers)
|
||||
{
|
||||
if (controller is IAsyncDisposable disposableAsync)
|
||||
{
|
||||
_logger.LogDebug("Disposing session controller asynchronously {TypeName}", disposableAsync.GetType().Name);
|
||||
await disposableAsync.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user