mirror of https://github.com/jellyfin/jellyfin.git
Compare commits
8 Commits
36b3823f5f
...
342aae0802
Author | SHA1 | Date |
---|---|---|
Dmitry Lyzo | 342aae0802 | |
Nyanmisaka | e2a22cec0e | |
queeup | 067962ae2a | |
Szilágyi Kristóf | 8a65d239b7 | |
HiPotionQ8 | 518404cd1d | |
Dmitry Lyzo | f205df6426 | |
Dmitry Lyzo | a6a3df62d7 | |
Dmitry Lyzo | 1db916d34a |
|
@ -128,5 +128,7 @@
|
|||
"TaskRefreshTrickplayImages": "توليد صور Trickplay",
|
||||
"TaskRefreshTrickplayImagesDescription": "يُنشئ معاينات Trickplay لمقاطع الفيديو في المكتبات المُمكّنة.",
|
||||
"TaskCleanCollectionsAndPlaylists": "حذف المجموعات وقوائم التشغيل",
|
||||
"TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة."
|
||||
"TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة.",
|
||||
"TaskAudioNormalization": "تطبيع الصوت",
|
||||
"TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت."
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"Albums": "Albumok",
|
||||
"AppDeviceValues": "Program: {0}, eszköz: {1}",
|
||||
"AppDeviceValues": "Program: {0}, Eszköz: {1}",
|
||||
"Application": "Alkalmazás",
|
||||
"Artists": "Előadók",
|
||||
"AuthenticationSucceededWithUserName": "{0} sikeresen hitelesítve",
|
||||
"Books": "Könyvek",
|
||||
"CameraImageUploadedFrom": "Új kamerakép feltöltve innen: {0}",
|
||||
"CameraImageUploadedFrom": "Új kamerakép lett feltöltve innen: {0}",
|
||||
"Channels": "Csatornák",
|
||||
"ChapterNameValue": "{0}. jelenet",
|
||||
"ChapterNameValue": "Jelenet {0}",
|
||||
"Collections": "Gyűjtemények",
|
||||
"DeviceOfflineWithName": "{0} kijelentkezett",
|
||||
"DeviceOnlineWithName": "{0} belépett",
|
||||
|
@ -15,27 +15,27 @@
|
|||
"Favorites": "Kedvencek",
|
||||
"Folders": "Könyvtárak",
|
||||
"Genres": "Műfajok",
|
||||
"HeaderAlbumArtists": "Albumelőadók",
|
||||
"HeaderAlbumArtists": "Album előadók",
|
||||
"HeaderContinueWatching": "Megtekintés folytatása",
|
||||
"HeaderFavoriteAlbums": "Kedvenc albumok",
|
||||
"HeaderFavoriteAlbums": "Kedvenc Albumok",
|
||||
"HeaderFavoriteArtists": "Kedvenc előadók",
|
||||
"HeaderFavoriteEpisodes": "Kedvenc epizódok",
|
||||
"HeaderFavoriteShows": "Kedvenc sorozatok",
|
||||
"HeaderFavoriteSongs": "Kedvenc számok",
|
||||
"HeaderFavoriteSongs": "Kedvenc dalok",
|
||||
"HeaderLiveTV": "Élő TV",
|
||||
"HeaderNextUp": "Következik",
|
||||
"HeaderRecordingGroups": "Felvételi csoportok",
|
||||
"HomeVideos": "Házi videók",
|
||||
"HomeVideos": "Otthoni videók",
|
||||
"Inherit": "Örökölt",
|
||||
"ItemAddedWithName": "{0} hozzáadva a könyvtárhoz",
|
||||
"ItemRemovedWithName": "{0} eltávolítva a könyvtárból",
|
||||
"LabelIpAddressValue": "IP-cím: {0}",
|
||||
"LabelRunningTimeValue": "Lejátszási idő: {0}",
|
||||
"Latest": "Legújabb",
|
||||
"MessageApplicationUpdated": "A Jellyfin kiszolgáló frissítve",
|
||||
"MessageApplicationUpdated": "A Jellyfin kiszolgáló frissítve lett",
|
||||
"MessageApplicationUpdatedTo": "A Jellyfin kiszolgáló frissítve lett a következőre: {0}",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "A kiszolgálókonfigurációs rész frissítve: {0}",
|
||||
"MessageServerConfigurationUpdated": "Kiszolgálókonfiguráció frissítve",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "A kiszolgálókonfigurációs rész frissítve lett: {0}",
|
||||
"MessageServerConfigurationUpdated": "Kiszolgálókonfiguráció frissítve lett",
|
||||
"MixedContent": "Vegyes tartalom",
|
||||
"Movies": "Filmek",
|
||||
"Music": "Zenék",
|
||||
|
@ -46,7 +46,7 @@
|
|||
"NewVersionIsAvailable": "Letölthető a Jellyfin kiszolgáló új verziója.",
|
||||
"NotificationOptionApplicationUpdateAvailable": "Frissítés érhető el az alkalmazáshoz",
|
||||
"NotificationOptionApplicationUpdateInstalled": "Alkalmazásfrissítés telepítve",
|
||||
"NotificationOptionAudioPlayback": "Hanglejátszás elkezdve",
|
||||
"NotificationOptionAudioPlayback": "Hanglejátszás elkezdődött",
|
||||
"NotificationOptionAudioPlaybackStopped": "Hanglejátszás leállítva",
|
||||
"NotificationOptionCameraImageUploaded": "Kamerakép feltöltve",
|
||||
"NotificationOptionInstallationFailed": "Telepítési hiba",
|
||||
|
@ -126,5 +126,9 @@
|
|||
"External": "Külső",
|
||||
"HearingImpaired": "Hallássérült",
|
||||
"TaskRefreshTrickplayImages": "Trickplay képek generálása",
|
||||
"TaskRefreshTrickplayImagesDescription": "Trickplay előnézetet készít az engedélyezett könyvtárakban lévő videókhoz."
|
||||
"TaskRefreshTrickplayImagesDescription": "Trickplay előnézetet készít az engedélyezett könyvtárakban lévő videókhoz.",
|
||||
"TaskAudioNormalization": "Hangerő Normalizáció",
|
||||
"TaskCleanCollectionsAndPlaylistsDescription": "Nem létező elemek törlése a gyűjteményekből és lejátszási listákról.",
|
||||
"TaskAudioNormalizationDescription": "Hangerő normalizációs adatok keresése.",
|
||||
"TaskCleanCollectionsAndPlaylists": "Gyűjtemények és lejátszási listák optimalizálása"
|
||||
}
|
||||
|
|
|
@ -128,5 +128,7 @@
|
|||
"TaskRefreshTrickplayImages": "Trickplay Görselleri Oluştur",
|
||||
"TaskRefreshTrickplayImagesDescription": "Etkin kütüphanelerdeki videolar için trickplay önizlemeleri oluşturur.",
|
||||
"TaskCleanCollectionsAndPlaylistsDescription": "Artık var olmayan koleksiyon ve çalma listelerindeki ögeleri kaldırır.",
|
||||
"TaskCleanCollectionsAndPlaylists": "Koleksiyonları ve çalma listelerini temizleyin"
|
||||
"TaskCleanCollectionsAndPlaylists": "Koleksiyonları ve çalma listelerini temizleyin",
|
||||
"TaskAudioNormalizationDescription": "Ses normalleştirme verileri için dosyaları tarar.",
|
||||
"TaskAudioNormalization": "Ses Normalleştirme"
|
||||
}
|
||||
|
|
|
@ -128,5 +128,7 @@
|
|||
"TaskRefreshTrickplayImages": "生成时间轴缩略图",
|
||||
"TaskRefreshTrickplayImagesDescription": "为启用的媒体库中的视频生成时间轴缩略图。",
|
||||
"TaskCleanCollectionsAndPlaylists": "清理合集和播放列表",
|
||||
"TaskCleanCollectionsAndPlaylistsDescription": "清理合集和播放列表中已不存在的项目。"
|
||||
"TaskCleanCollectionsAndPlaylistsDescription": "清理合集和播放列表中已不存在的项目。",
|
||||
"TaskAudioNormalization": "音频标准化",
|
||||
"TaskAudioNormalizationDescription": "扫描文件以寻找音频标准化数据。"
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
internal const TranscodeReason AudioReasons = TranscodeReason.AudioCodecNotSupported | TranscodeReason.AudioBitrateNotSupported | TranscodeReason.AudioChannelsNotSupported | TranscodeReason.AudioProfileNotSupported | TranscodeReason.AudioSampleRateNotSupported | TranscodeReason.SecondaryAudioNotSupported | TranscodeReason.AudioBitDepthNotSupported | TranscodeReason.AudioIsExternal;
|
||||
internal const TranscodeReason VideoReasons = TranscodeReason.VideoCodecNotSupported | TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported;
|
||||
internal const TranscodeReason DirectStreamReasons = AudioReasons | TranscodeReason.ContainerNotSupported;
|
||||
internal const TranscodeReason GenericReasons = TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported | TranscodeReason.ContainerNotSupported;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly ITranscoderSupport _transcoderSupport;
|
||||
|
@ -72,6 +73,8 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
private StreamInfo? GetOptimalAudioStream(MediaSourceInfo item, MediaOptions options)
|
||||
{
|
||||
CheckCompatibility(MediaType.Audio, item, options.Profile);
|
||||
|
||||
var playlistItem = new StreamInfo
|
||||
{
|
||||
ItemId = options.ItemId,
|
||||
|
@ -203,6 +206,213 @@ namespace MediaBrowser.Model.Dlna
|
|||
return GetOptimalStream(streams, options.GetMaxBitrate(false) ?? 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets compatibility errors (if any) for streams.
|
||||
/// </summary>
|
||||
/// <param name="mediaType">The <see cref="MediaType"/> of object for which to check compatibility.</param>
|
||||
/// <param name="options">The <see cref="MediaOptions"/> object for which to check compatibility.</param>
|
||||
public void CheckCompatibility(MediaType mediaType, MediaOptions options)
|
||||
{
|
||||
foreach (var mediaSource in options.MediaSources)
|
||||
{
|
||||
CheckCompatibility(mediaType, mediaSource, options.Profile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets compatibility errors (if any) for streams.
|
||||
/// </summary>
|
||||
/// <param name="mediaType">The <see cref="MediaType"/> of object for which to check compatibility.</param>
|
||||
/// <param name="mediaSource">The <see cref="MediaSourceInfo"/>.</param>
|
||||
/// <param name="profile">The <see cref="DeviceProfile"/>.</param>
|
||||
public void CheckCompatibility(MediaType mediaType, MediaSourceInfo mediaSource, DeviceProfile profile)
|
||||
{
|
||||
foreach (var stream in mediaSource.MediaStreams)
|
||||
{
|
||||
if (!stream.DirectPlayErrors.HasValue)
|
||||
{
|
||||
stream.DirectPlayErrors = GetCompatibility(mediaType, mediaSource, stream, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets stream compatibility errors, if any.
|
||||
/// </summary>
|
||||
/// <param name="mediaType">The <see cref="MediaType"/> of object for which to check compatibility.</param>
|
||||
/// <param name="mediaSource">The <see cref="MediaSourceInfo"/>.</param>
|
||||
/// <param name="stream">The <see cref="MediaStream"/> for which to check compatibility.</param>
|
||||
/// <param name="profile">The <see cref="DeviceProfile"/>.</param>
|
||||
/// <returns>Stream compatibility errors, if any.</returns>
|
||||
private TranscodeReason GetCompatibility(MediaType mediaType, MediaSourceInfo mediaSource, MediaStream stream, DeviceProfile profile)
|
||||
{
|
||||
switch (stream.Type)
|
||||
{
|
||||
case MediaStreamType.Audio:
|
||||
return GetCompatibilityAudio(mediaSource, stream, profile, mediaType == MediaType.Video);
|
||||
|
||||
case MediaStreamType.Video:
|
||||
return GetCompatibilityVideo(mediaSource, stream, profile);
|
||||
|
||||
case MediaStreamType.Subtitle:
|
||||
var subtitleProfile = GetSubtitleProfile(mediaSource, stream, profile.SubtitleProfiles, PlayMethod.DirectPlay, _transcoderSupport, mediaSource.Container, null);
|
||||
|
||||
if (subtitleProfile.Method != SubtitleDeliveryMethod.Drop
|
||||
&& subtitleProfile.Method != SubtitleDeliveryMethod.External
|
||||
&& subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
|
||||
{
|
||||
return TranscodeReason.SubtitleCodecNotSupported;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets video stream compatibility errors, if any.
|
||||
/// </summary>
|
||||
/// <param name="mediaSource">The <see cref="MediaSourceInfo"/>.</param>
|
||||
/// <param name="videoStream">The <see cref="MediaStream"/> of the video stream.</param>
|
||||
/// <param name="profile">The <see cref="DeviceProfile"/>.</param>
|
||||
/// <returns>Video stream compatibility errors, if any.</returns>
|
||||
private TranscodeReason GetCompatibilityVideo(MediaSourceInfo mediaSource, MediaStream videoStream, DeviceProfile profile)
|
||||
{
|
||||
string container = mediaSource.Container;
|
||||
string videoCodec = videoStream.Codec;
|
||||
|
||||
bool containerSupported = false;
|
||||
bool videoSupported = false;
|
||||
|
||||
foreach (var directPlayProfile in profile.DirectPlayProfiles)
|
||||
{
|
||||
if (directPlayProfile.Type == DlnaProfileType.Video && directPlayProfile.SupportsContainer(container))
|
||||
{
|
||||
containerSupported = true;
|
||||
|
||||
if (directPlayProfile.SupportsVideoCodec(videoCodec))
|
||||
{
|
||||
videoSupported = true;
|
||||
}
|
||||
|
||||
if (containerSupported && videoSupported)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TranscodeReason failures = default;
|
||||
|
||||
if (!containerSupported)
|
||||
{
|
||||
failures |= TranscodeReason.ContainerNotSupported;
|
||||
}
|
||||
|
||||
if (!videoSupported)
|
||||
{
|
||||
return failures | TranscodeReason.VideoCodecNotSupported;
|
||||
}
|
||||
|
||||
// Video
|
||||
int? width = videoStream.Width;
|
||||
int? height = videoStream.Height;
|
||||
int? bitDepth = videoStream.BitDepth;
|
||||
int? videoBitrate = videoStream.BitRate;
|
||||
double? videoLevel = videoStream.Level;
|
||||
string? videoProfile = videoStream.Profile;
|
||||
VideoRangeType? videoRangeType = videoStream.VideoRangeType;
|
||||
float videoFramerate = videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
|
||||
bool? isAnamorphic = videoStream.IsAnamorphic;
|
||||
bool? isInterlaced = videoStream.IsInterlaced;
|
||||
string? videoCodecTag = videoStream.CodecTag;
|
||||
bool? isAvc = videoStream.IsAVC;
|
||||
|
||||
TransportStreamTimestamp? timestamp = mediaSource.Timestamp;
|
||||
int? packetLength = videoStream.PacketLength;
|
||||
int? refFrames = videoStream.RefFrames;
|
||||
|
||||
int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
|
||||
int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
|
||||
|
||||
var checkVideoConditions = (ProfileCondition[] conditions) =>
|
||||
conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc));
|
||||
|
||||
failures |= AggregateFailureConditions(
|
||||
mediaSource,
|
||||
profile,
|
||||
"VideoCodecProfile",
|
||||
profile.CodecProfiles
|
||||
.Where(codecProfile => codecProfile.Type == CodecType.Video &&
|
||||
codecProfile.ContainsAnyCodec(videoCodec, container) &&
|
||||
!checkVideoConditions(codecProfile.ApplyConditions).Any())
|
||||
.SelectMany(codecProfile => checkVideoConditions(codecProfile.Conditions)));
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets audio stream compatibility errors, if any.
|
||||
/// </summary>
|
||||
/// <param name="mediaSource">The <see cref="MediaSourceInfo"/>.</param>
|
||||
/// <param name="audioStream">The <see cref="MediaStream"/> of the audio stream.</param>
|
||||
/// <param name="profile">The <see cref="DeviceProfile"/>.</param>
|
||||
/// <param name="isVideo">True if source is video.</param>
|
||||
/// <returns>Audio stream compatibility errors, if any.</returns>
|
||||
private TranscodeReason GetCompatibilityAudio(MediaSourceInfo mediaSource, MediaStream audioStream, DeviceProfile profile, bool isVideo)
|
||||
{
|
||||
string container = mediaSource.Container;
|
||||
string audioCodec = audioStream.Codec;
|
||||
DlnaProfileType profileType = isVideo ? DlnaProfileType.Video : DlnaProfileType.Audio;
|
||||
|
||||
bool containerSupported = false;
|
||||
bool audioSupported = false;
|
||||
|
||||
foreach (var directPlayProfile in profile.DirectPlayProfiles)
|
||||
{
|
||||
if (directPlayProfile.Type == profileType && directPlayProfile.SupportsContainer(container))
|
||||
{
|
||||
containerSupported = true;
|
||||
|
||||
if (directPlayProfile.SupportsAudioCodec(audioCodec))
|
||||
{
|
||||
audioSupported = true;
|
||||
}
|
||||
|
||||
if (containerSupported && audioSupported)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TranscodeReason failures = default;
|
||||
|
||||
if (!containerSupported)
|
||||
{
|
||||
failures |= TranscodeReason.ContainerNotSupported;
|
||||
}
|
||||
|
||||
if (!audioSupported)
|
||||
{
|
||||
return failures | TranscodeReason.AudioCodecNotSupported;
|
||||
}
|
||||
|
||||
var audioFailureConditions = isVideo
|
||||
? GetProfileConditionsForVideoAudio(profile.CodecProfiles, container, audioCodec, audioStream.Channels, audioStream.BitRate, audioStream.SampleRate, audioStream.BitDepth, audioStream.Profile, mediaSource.IsSecondaryAudio(audioStream))
|
||||
: GetProfileConditionsForAudio(profile.CodecProfiles, container, audioCodec, audioStream.Channels, audioStream.BitRate, audioStream.SampleRate, audioStream.BitDepth, true);
|
||||
|
||||
failures |= AggregateFailureConditions(mediaSource, profile, "AudioCodecProfile", audioFailureConditions);
|
||||
|
||||
if (audioStream.IsExternal)
|
||||
{
|
||||
failures |= TranscodeReason.AudioIsExternal;
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
private static StreamInfo? GetOptimalStream(List<StreamInfo> streams, long maxBitrate)
|
||||
=> SortMediaSources(streams, maxBitrate).FirstOrDefault();
|
||||
|
||||
|
@ -573,6 +783,8 @@ namespace MediaBrowser.Model.Dlna
|
|||
{
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
|
||||
CheckCompatibility(MediaType.Video, item, options.Profile);
|
||||
|
||||
StreamInfo playlistItem = new StreamInfo
|
||||
{
|
||||
ItemId = options.ItemId,
|
||||
|
@ -1162,32 +1374,18 @@ namespace MediaBrowser.Model.Dlna
|
|||
.Where(containerProfile => containerProfile.Type == DlnaProfileType.Video && containerProfile.ContainsContainer(container))
|
||||
.SelectMany(containerProfile => checkVideoConditions(containerProfile.Conditions)));
|
||||
|
||||
// Check video conditions
|
||||
var videoCodecProfileReasons = AggregateFailureConditions(
|
||||
mediaSource,
|
||||
profile,
|
||||
"VideoCodecProfile",
|
||||
profile.CodecProfiles
|
||||
.Where(codecProfile => codecProfile.Type == CodecType.Video &&
|
||||
codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
|
||||
!checkVideoConditions(codecProfile.ApplyConditions).Any())
|
||||
.SelectMany(codecProfile => checkVideoConditions(codecProfile.Conditions)));
|
||||
// FIXME: Throw if CompatibilityErrors has no value?
|
||||
var videoCodecProfileReasons = (videoStream?.DirectPlayErrors ?? 0) & ~GenericReasons;
|
||||
|
||||
// Check audiocandidates profile conditions
|
||||
var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => CheckVideoAudioStreamDirectPlay(options, mediaSource, container, audioStream));
|
||||
// FIXME: Throw if CompatibilityErrors has no value?
|
||||
var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => (audioStream.DirectPlayErrors ?? 0) & ~GenericReasons);
|
||||
|
||||
TranscodeReason subtitleProfileReasons = 0;
|
||||
if (subtitleStream is not null)
|
||||
// FIXME: Throw if CompatibilityErrors has no value?
|
||||
var subtitleProfileReasons = (subtitleStream?.DirectPlayErrors ?? 0) & ~GenericReasons;
|
||||
|
||||
if ((subtitleProfileReasons & TranscodeReason.SubtitleCodecNotSupported) != 0)
|
||||
{
|
||||
var subtitleProfile = GetSubtitleProfile(mediaSource, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.DirectPlay, _transcoderSupport, container, null);
|
||||
|
||||
if (subtitleProfile.Method != SubtitleDeliveryMethod.Drop
|
||||
&& subtitleProfile.Method != SubtitleDeliveryMethod.External
|
||||
&& subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
|
||||
{
|
||||
_logger.LogDebug("Not eligible for {0} due to unsupported subtitles", PlayMethod.DirectPlay);
|
||||
subtitleProfileReasons |= TranscodeReason.SubtitleCodecNotSupported;
|
||||
}
|
||||
_logger.LogDebug("Not eligible for {0} due to unsupported subtitles", PlayMethod.DirectPlay);
|
||||
}
|
||||
|
||||
var rankings = new[] { VideoReasons, AudioReasons, ContainerReasons };
|
||||
|
@ -1303,20 +1501,6 @@ namespace MediaBrowser.Model.Dlna
|
|||
return (Profile: null, PlayMethod: null, AudioStreamIndex: null, TranscodeReasons: failureReasons);
|
||||
}
|
||||
|
||||
private TranscodeReason CheckVideoAudioStreamDirectPlay(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream)
|
||||
{
|
||||
var profile = options.Profile;
|
||||
var audioFailureConditions = GetProfileConditionsForVideoAudio(profile.CodecProfiles, container, audioStream.Codec, audioStream.Channels, audioStream.BitRate, audioStream.SampleRate, audioStream.BitDepth, audioStream.Profile, mediaSource.IsSecondaryAudio(audioStream));
|
||||
|
||||
var audioStreamFailureReasons = AggregateFailureConditions(mediaSource, profile, "VideoAudioCodecProfile", audioFailureConditions);
|
||||
if (audioStream.IsExternal == true)
|
||||
{
|
||||
audioStreamFailureReasons |= TranscodeReason.AudioIsExternal;
|
||||
}
|
||||
|
||||
return audioStreamFailureReasons;
|
||||
}
|
||||
|
||||
private TranscodeReason AggregateFailureConditions(MediaSourceInfo mediaSource, DeviceProfile profile, string type, IEnumerable<ProfileCondition> conditions)
|
||||
{
|
||||
return conditions.Aggregate<ProfileCondition, TranscodeReason>(0, (reasons, i) =>
|
||||
|
|
|
@ -12,6 +12,7 @@ using Jellyfin.Extensions;
|
|||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Session;
|
||||
|
||||
namespace MediaBrowser.Model.Entities
|
||||
{
|
||||
|
@ -615,6 +616,24 @@ namespace MediaBrowser.Model.Entities
|
|||
/// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
|
||||
public bool? IsAnamorphic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets DirectPlay errors.
|
||||
/// </summary>
|
||||
/// <value>DirectPlay errors.</value>
|
||||
public TranscodeReason? DirectPlayErrors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is compatible with the device.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is compatible with the device; otherwise, <c>false</c>.</value>
|
||||
public bool SupportsDirectPlay
|
||||
{
|
||||
get
|
||||
{
|
||||
return DirectPlayErrors == 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetResolutionText()
|
||||
{
|
||||
if (!Width.HasValue || !Height.HasValue)
|
||||
|
|
|
@ -324,6 +324,39 @@ namespace Jellyfin.Model.Tests
|
|||
Assert.Equal(streamInfo?.SubtitleStreamIndex, options.SubtitleStreamIndex);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Chrome
|
||||
[InlineData("Chrome", "mp4-h264-ac3-aac-aac-srt-2600k", new TranscodeReason[] { 0, TranscodeReason.AudioCodecNotSupported, TranscodeReason.SecondaryAudioNotSupported, TranscodeReason.SecondaryAudioNotSupported, 0 })]
|
||||
[InlineData("Chrome", "mp4-h264-dts-srt-2600k", new TranscodeReason[] { 0, TranscodeReason.AudioCodecNotSupported, 0 })]
|
||||
[InlineData("Chrome", "mkv-vp9-aac-srt-2600k", new TranscodeReason[] { TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported, TranscodeReason.ContainerNotSupported | TranscodeReason.AudioCodecNotSupported, 0 })]
|
||||
// Firefox
|
||||
[InlineData("Firefox", "mp4-h264-ac3-aac-aac-srt-2600k", new TranscodeReason[] { 0, TranscodeReason.AudioCodecNotSupported, TranscodeReason.SecondaryAudioNotSupported, TranscodeReason.SecondaryAudioNotSupported, 0 })]
|
||||
[InlineData("Firefox", "mp4-h264-dts-srt-2600k", new TranscodeReason[] { 0, TranscodeReason.AudioCodecNotSupported, 0 })]
|
||||
[InlineData("Firefox", "mkv-vp9-aac-srt-2600k", new TranscodeReason[] { TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported, TranscodeReason.ContainerNotSupported | TranscodeReason.AudioCodecNotSupported, 0 })]
|
||||
// Tizen3-stereo
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-aac-srt-2600k", new TranscodeReason[] { 0, 0, TranscodeReason.SecondaryAudioNotSupported, TranscodeReason.SecondaryAudioNotSupported, 0 })]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-dts-srt-2600k", new TranscodeReason[] { 0, 0, 0 })]
|
||||
[InlineData("Tizen3-stereo", "mkv-vp9-aac-srt-2600k", new TranscodeReason[] { 0, 0, 0 })]
|
||||
// Tizen4-4K-5.1
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-aac-srt-2600k", new TranscodeReason[] { 0, 0, TranscodeReason.SecondaryAudioNotSupported, TranscodeReason.SecondaryAudioNotSupported, 0 })]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-dts-srt-2600k", new TranscodeReason[] { 0, TranscodeReason.AudioCodecNotSupported, 0 })]
|
||||
[InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", new TranscodeReason[] { 0, 0, 0 })]
|
||||
public async Task CheckCompatibility(string deviceName, string mediaSourceName, TranscodeReason[] directPlayErrors)
|
||||
{
|
||||
var options = await GetMediaOptions(deviceName, mediaSourceName);
|
||||
|
||||
var builder = GetStreamBuilder();
|
||||
|
||||
var streamInfo = builder.GetOptimalVideoStream(options);
|
||||
Assert.NotNull(streamInfo);
|
||||
|
||||
var mediaSource = options.MediaSources.First(source => source.Id == streamInfo.MediaSourceId);
|
||||
Assert.NotNull(mediaSource);
|
||||
|
||||
Assert.Equal(directPlayErrors.Length, mediaSource.MediaStreams.Count);
|
||||
Assert.All(mediaSource.MediaStreams, (stream, i) => Assert.Equal(stream.DirectPlayErrors, directPlayErrors[i]));
|
||||
}
|
||||
|
||||
private StreamInfo? BuildVideoItemSimpleTest(MediaOptions options, PlayMethod? playMethod, TranscodeReason why, string transcodeMode, string transcodeProtocol)
|
||||
{
|
||||
if (string.IsNullOrEmpty(transcodeProtocol))
|
||||
|
|
Loading…
Reference in New Issue