mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-24 02:02:29 -04:00
Merge pull request #11399 from jellyfin/audio-remux
This commit is contained in:
commit
4239de1ee7
@ -17,6 +17,7 @@ using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -137,6 +138,8 @@ public class UniversalAudioController : BaseJellyfinApiController
|
||||
// set device specific data
|
||||
foreach (var sourceInfo in info.MediaSources)
|
||||
{
|
||||
sourceInfo.TranscodingContainer = transcodingContainer;
|
||||
sourceInfo.TranscodingSubProtocol = transcodingProtocol ?? sourceInfo.TranscodingSubProtocol;
|
||||
_mediaInfoHelper.SetDeviceSpecificData(
|
||||
item,
|
||||
sourceInfo,
|
||||
@ -171,6 +174,8 @@ public class UniversalAudioController : BaseJellyfinApiController
|
||||
return Redirect(mediaSource.Path);
|
||||
}
|
||||
|
||||
// This one is currently very misleading as the SupportsDirectStream actually means "can direct play"
|
||||
// The definition of DirectStream also seems changed during development
|
||||
var isStatic = mediaSource.SupportsDirectStream;
|
||||
if (!isStatic && mediaSource.TranscodingSubProtocol == MediaStreamProtocol.hls)
|
||||
{
|
||||
@ -178,20 +183,25 @@ public class UniversalAudioController : BaseJellyfinApiController
|
||||
// ffmpeg option -> file extension
|
||||
// mpegts -> ts
|
||||
// fmp4 -> mp4
|
||||
// TODO: remove this when we switch back to the segment muxer
|
||||
var supportedHlsContainers = new[] { "ts", "mp4" };
|
||||
|
||||
// fallback to mpegts if device reports some weird value unsupported by hls
|
||||
var requestedSegmentContainer = Array.Exists(
|
||||
supportedHlsContainers,
|
||||
element => string.Equals(element, transcodingContainer, StringComparison.OrdinalIgnoreCase)) ? transcodingContainer : "ts";
|
||||
var segmentContainer = Array.Exists(
|
||||
supportedHlsContainers,
|
||||
element => string.Equals(element, mediaSource.TranscodingContainer, StringComparison.OrdinalIgnoreCase)) ? mediaSource.TranscodingContainer : requestedSegmentContainer;
|
||||
var dynamicHlsRequestDto = new HlsAudioRequestDto
|
||||
{
|
||||
Id = itemId,
|
||||
Container = ".m3u8",
|
||||
Static = isStatic,
|
||||
PlaySessionId = info.PlaySessionId,
|
||||
// fallback to mpegts if device reports some weird value unsupported by hls
|
||||
SegmentContainer = Array.Exists(supportedHlsContainers, element => element == transcodingContainer) ? transcodingContainer : "ts",
|
||||
SegmentContainer = segmentContainer,
|
||||
MediaSourceId = mediaSourceId,
|
||||
DeviceId = deviceId,
|
||||
AudioCodec = audioCodec,
|
||||
AudioCodec = mediaSource.TranscodeReasons == TranscodeReason.ContainerNotSupported ? "copy" : audioCodec,
|
||||
EnableAutoStreamCopy = true,
|
||||
AllowAudioStreamCopy = true,
|
||||
AllowVideoStreamCopy = true,
|
||||
|
@ -151,6 +151,14 @@ public class DynamicHlsHelper
|
||||
|
||||
var queryString = _httpContextAccessor.HttpContext.Request.QueryString.ToString();
|
||||
|
||||
// from universal audio service, need to override the AudioCodec when the actual request differs from original query
|
||||
if (!string.Equals(state.OutputAudioCodec, _httpContextAccessor.HttpContext.Request.Query["AudioCodec"].ToString(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var newQuery = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(_httpContextAccessor.HttpContext.Request.QueryString.ToString());
|
||||
newQuery["AudioCodec"] = state.OutputAudioCodec;
|
||||
queryString = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(string.Empty, newQuery);
|
||||
}
|
||||
|
||||
// from universal audio service
|
||||
if (!string.IsNullOrWhiteSpace(state.Request.SegmentContainer)
|
||||
&& !queryString.Contains("SegmentContainer", StringComparison.OrdinalIgnoreCase))
|
||||
|
@ -470,6 +470,11 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable
|
||||
: "FFmpeg.DirectStream-";
|
||||
}
|
||||
|
||||
if (state.VideoRequest is null && EncodingHelper.IsCopyCodec(state.OutputAudioCodec))
|
||||
{
|
||||
logFilePrefix = "FFmpeg.Remux-";
|
||||
}
|
||||
|
||||
var logFilePath = Path.Combine(
|
||||
_serverConfigurationManager.ApplicationPaths.LogDirectoryPath,
|
||||
$"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
|
||||
|
@ -108,7 +108,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
var inputAudioSampleRate = audioStream?.SampleRate;
|
||||
var inputAudioBitDepth = audioStream?.BitDepth;
|
||||
|
||||
if (directPlayMethod.HasValue)
|
||||
if (directPlayMethod is PlayMethod.DirectPlay)
|
||||
{
|
||||
var profile = options.Profile;
|
||||
var audioFailureConditions = GetProfileConditionsForAudio(profile.CodecProfiles, item.Container, audioStream?.Codec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, true);
|
||||
@ -124,6 +124,46 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
if (directPlayMethod is PlayMethod.DirectStream)
|
||||
{
|
||||
var remuxContainer = item.TranscodingContainer ?? "ts";
|
||||
var supportedHlsContainers = new[] { "ts", "mp4" };
|
||||
// If the container specified for the profile is an HLS supported container, use that container instead, overriding the preference
|
||||
// The client should be responsible to ensure this container is compatible
|
||||
remuxContainer = Array.Exists(supportedHlsContainers, element => string.Equals(element, directPlayInfo.Profile?.Container, StringComparison.OrdinalIgnoreCase)) ? directPlayInfo.Profile?.Container : remuxContainer;
|
||||
bool codeIsSupported;
|
||||
if (item.TranscodingSubProtocol == MediaStreamProtocol.hls)
|
||||
{
|
||||
// Enforce HLS audio codec restrictions
|
||||
if (string.Equals(remuxContainer, "mp4", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
codeIsSupported = _supportedHlsAudioCodecsMp4.Contains(directPlayInfo.Profile?.AudioCodec ?? directPlayInfo.Profile?.Container);
|
||||
}
|
||||
else
|
||||
{
|
||||
codeIsSupported = _supportedHlsAudioCodecsTs.Contains(directPlayInfo.Profile?.AudioCodec ?? directPlayInfo.Profile?.Container);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's assume the client has given a correct container for http
|
||||
codeIsSupported = true;
|
||||
}
|
||||
|
||||
if (codeIsSupported)
|
||||
{
|
||||
playlistItem.PlayMethod = directPlayMethod.Value;
|
||||
playlistItem.Container = remuxContainer;
|
||||
playlistItem.TranscodeReasons = transcodeReasons;
|
||||
playlistItem.SubProtocol = item.TranscodingSubProtocol;
|
||||
item.TranscodingContainer = remuxContainer;
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
transcodeReasons |= TranscodeReason.AudioCodecNotSupported;
|
||||
playlistItem.TranscodeReasons = transcodeReasons;
|
||||
}
|
||||
|
||||
TranscodingProfile? transcodingProfile = null;
|
||||
foreach (var tcProfile in options.Profile.TranscodingProfiles)
|
||||
{
|
||||
@ -379,6 +419,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
var directPlayProfile = options.Profile.DirectPlayProfiles
|
||||
.FirstOrDefault(x => x.Type == DlnaProfileType.Audio && IsAudioDirectPlaySupported(x, item, audioStream));
|
||||
|
||||
TranscodeReason transcodeReasons = 0;
|
||||
if (directPlayProfile is null)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
@ -387,14 +428,25 @@ namespace MediaBrowser.Model.Dlna
|
||||
item.Path ?? "Unknown path",
|
||||
audioStream.Codec ?? "Unknown codec");
|
||||
|
||||
return (null, null, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles));
|
||||
}
|
||||
var directStreamProfile = options.Profile.DirectPlayProfiles
|
||||
.FirstOrDefault(x => x.Type == DlnaProfileType.Audio && IsAudioDirectStreamSupported(x, item, audioStream));
|
||||
|
||||
TranscodeReason transcodeReasons = 0;
|
||||
if (directStreamProfile is not null)
|
||||
{
|
||||
directPlayProfile = directStreamProfile;
|
||||
transcodeReasons |= TranscodeReason.ContainerNotSupported;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (null, null, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles));
|
||||
}
|
||||
}
|
||||
|
||||
// The profile describes what the device supports
|
||||
// If device requirements are satisfied then allow both direct stream and direct play
|
||||
if (item.SupportsDirectPlay)
|
||||
// Note: As of 10.10 codebase, SupportsDirectPlay is always true because the MediaSourceInfo initializes this key as true
|
||||
// Need to check additionally for current transcode reasons
|
||||
if (item.SupportsDirectPlay && transcodeReasons == 0)
|
||||
{
|
||||
if (!IsBitrateLimitExceeded(item, options.GetMaxBitrate(true) ?? 0))
|
||||
{
|
||||
@ -414,7 +466,10 @@ namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
if (!IsBitrateLimitExceeded(item, options.GetMaxBitrate(true) ?? 0))
|
||||
{
|
||||
if (options.EnableDirectStream)
|
||||
// Note: as of 10.10 codebase, the options.EnableDirectStream is always false due to
|
||||
// "direct-stream http streaming is currently broken"
|
||||
// Don't check that option for audio as we always assume that is supported
|
||||
if (transcodeReasons == TranscodeReason.ContainerNotSupported)
|
||||
{
|
||||
return (directPlayProfile, PlayMethod.DirectStream, transcodeReasons);
|
||||
}
|
||||
@ -2129,5 +2184,24 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsAudioDirectStreamSupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream audioStream)
|
||||
{
|
||||
// Check container type, this should NOT be supported
|
||||
// If the container is supported, the file should be directly played
|
||||
if (!profile.SupportsContainer(item.Container))
|
||||
{
|
||||
// Check audio codec, we cannot use the SupportsAudioCodec here
|
||||
// Because that one assumes empty container supports all codec, which is just useless
|
||||
string? audioCodec = audioStream?.Codec;
|
||||
if (string.Equals(profile.AudioCodec, audioCodec, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(profile.Container, audioCodec, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user