Extend TranscodingReason reporting

This commit is contained in:
Shadowghost
2026-06-15 09:29:24 +02:00
parent f5c3e2c65a
commit 1dd5a85080
3 changed files with 65 additions and 29 deletions

View File

@@ -25,6 +25,7 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Configuration;
using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
@@ -2611,56 +2612,66 @@ namespace MediaBrowser.Controller.MediaEncoding
}
public bool CanStreamCopyAudio(EncodingJobInfo state, MediaStream audioStream, IEnumerable<string> supportedAudioCodecs)
=> CanStreamCopyAudio(state, audioStream, supportedAudioCodecs, out _);
/// <summary>
/// Determines whether the given audio stream can be stream-copied and, regardless of the outcome,
/// reports the codec/parameter incompatibilities that would force a re-encode via <paramref name="failureReasons"/>.
/// </summary>
/// <param name="state">The encoding job state.</param>
/// <param name="audioStream">The source audio stream.</param>
/// <param name="supportedAudioCodecs">The audio codecs the target supports.</param>
/// <param name="failureReasons">The codec/parameter incompatibilities preventing a copy, or <c>0</c> if the stream is copy-compatible.</param>
/// <returns><c>true</c> if the audio stream can be stream-copied; otherwise, <c>false</c>.</returns>
public bool CanStreamCopyAudio(EncodingJobInfo state, MediaStream audioStream, IEnumerable<string> supportedAudioCodecs, out TranscodeReason failureReasons)
{
var request = state.BaseRequest;
if (!request.AllowAudioStreamCopy)
{
return false;
}
// Policy-independent compatibility check, so the reasons are reported even when a policy gate is what ultimately prevents the copy.
failureReasons = GetAudioStreamCopyFailureReasons(state, audioStream, supportedAudioCodecs);
return request.AllowAudioStreamCopy
&& request.EnableAutoStreamCopy
&& failureReasons == 0;
}
private static TranscodeReason GetAudioStreamCopyFailureReasons(EncodingJobInfo state, MediaStream audioStream, IEnumerable<string> supportedAudioCodecs)
{
var request = state.BaseRequest;
TranscodeReason reasons = 0;
var maxBitDepth = state.GetRequestedAudioBitDepth(audioStream.Codec);
if (maxBitDepth.HasValue
&& audioStream.BitDepth.HasValue
&& audioStream.BitDepth.Value > maxBitDepth.Value)
{
return false;
reasons |= TranscodeReason.AudioBitDepthNotSupported;
}
// Source and target codecs must match
if (string.IsNullOrEmpty(audioStream.Codec)
|| !supportedAudioCodecs.Contains(audioStream.Codec, StringComparison.OrdinalIgnoreCase))
{
return false;
reasons |= TranscodeReason.AudioCodecNotSupported;
}
// Channels must fall within requested value
var channels = state.GetRequestedAudioChannels(audioStream.Codec);
if (channels.HasValue)
if (channels.HasValue
&& (!audioStream.Channels.HasValue
|| audioStream.Channels.Value <= 0
|| audioStream.Channels.Value > channels.Value))
{
if (!audioStream.Channels.HasValue || audioStream.Channels.Value <= 0)
{
return false;
}
if (audioStream.Channels.Value > channels.Value)
{
return false;
}
reasons |= TranscodeReason.AudioChannelsNotSupported;
}
// Sample rate must fall within requested value
if (request.AudioSampleRate.HasValue)
if (request.AudioSampleRate.HasValue
&& (!audioStream.SampleRate.HasValue
|| audioStream.SampleRate.Value <= 0
|| audioStream.SampleRate.Value > request.AudioSampleRate.Value))
{
if (!audioStream.SampleRate.HasValue || audioStream.SampleRate.Value <= 0)
{
return false;
}
if (audioStream.SampleRate.Value > request.AudioSampleRate.Value)
{
return false;
}
reasons |= TranscodeReason.AudioSampleRateNotSupported;
}
// Audio bitrate must fall within requested value
@@ -2668,10 +2679,10 @@ namespace MediaBrowser.Controller.MediaEncoding
&& audioStream.BitRate.HasValue
&& audioStream.BitRate.Value > request.AudioBitRate.Value)
{
return false;
reasons |= TranscodeReason.AudioBitrateNotSupported;
}
return request.EnableAutoStreamCopy;
return reasons;
}
public int GetVideoBitrateParamValue(BaseEncodingJobOptions request, MediaStream videoStream, string outputVideoCodec)
@@ -7217,8 +7228,9 @@ namespace MediaBrowser.Controller.MediaEncoding
&& !IsCopyCodec(state.OutputVideoCodec)
&& options.HlsAudioSeekStrategy is HlsAudioSeekStrategy.TranscodeAudio;
TranscodeReason audioCopyFailureReasons = 0;
if (state.AudioStream is not null
&& CanStreamCopyAudio(state, state.AudioStream, state.SupportedAudioCodecs)
&& CanStreamCopyAudio(state, state.AudioStream, state.SupportedAudioCodecs, out audioCopyFailureReasons)
&& !preventHlsAudioCopy)
{
state.OutputAudioCodec = "copy";
@@ -7232,6 +7244,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
state.OutputAudioCodec = "copy";
}
else if (state.AudioStream is not null && !IsCopyCodec(state.OutputAudioCodec))
{
// Audio is actually being re-encoded although the playback determination may have considered the source copyable.
// Only carry the primary "cannot be passed through" cause - the codec mismatch.
// Bitrate/channels/sample-rate/bit-depth copy refusals are consequences of the chosen transcode target.
state.AddTranscodeReason(audioCopyFailureReasons & TranscodeReason.AudioCodecNotSupported);
}
}
}

View File

@@ -515,6 +515,15 @@ namespace MediaBrowser.Controller.MediaEncoding
public int HlsListSize => 0;
/// <summary>
/// Adds the specified reason(s) to <see cref="TranscodeReasons"/>.
/// </summary>
/// <param name="reason">The transcode reason(s) to add.</param>
public void AddTranscodeReason(TranscodeReason reason)
{
_transcodeReasons = TranscodeReasons | reason;
}
private int? GetMediaStreamCount(MediaStreamType type, int limit)
{
var count = MediaSource.GetStreamCount(type);

View File

@@ -951,6 +951,10 @@ namespace MediaBrowser.Model.Dlna
}
playlistItem.VideoCodecs = videoCodecs;
if (videoStream is not null && !ContainerHelper.ContainsContainer(videoCodecs, false, videoStream.Codec))
{
playlistItem.TranscodeReasons |= TranscodeReason.VideoCodecNotSupported;
}
// Copy video codec options as a starting point, this applies to transcode and direct-stream
playlistItem.MaxFramerate = videoStream?.ReferenceFrameRate;
@@ -999,6 +1003,10 @@ namespace MediaBrowser.Model.Dlna
var directAudioFailures = audioStreamWithSupportedCodec is null ? default : GetCompatibilityAudioCodec(options, item, container ?? string.Empty, audioStreamWithSupportedCodec, null, true, false);
playlistItem.TranscodeReasons |= directAudioFailures;
if (audioStream is not null && audioStreamWithSupportedCodec is null)
{
playlistItem.TranscodeReasons |= TranscodeReason.AudioCodecNotSupported;
}
var directAudioStreamSatisfied = audioStreamWithSupportedCodec is not null && !channelsExceedsLimit
&& directAudioFailures == 0;