mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
adjust bitrate limit for HLS audio codecs
This commit is contained in:
parent
0b01acbe91
commit
57e5b59b93
@ -222,7 +222,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
|
|
||||||
EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
|
EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
|
||||||
var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0;
|
var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0;
|
||||||
var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest.AudioBitRate, state.AudioStream) ?? 0;
|
var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
|
||||||
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
|
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
|
||||||
|
|
||||||
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
|
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
|
||||||
|
@ -183,7 +183,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
|
|
||||||
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
|
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
|
||||||
|
|
||||||
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, state.AudioStream);
|
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream);
|
||||||
|
|
||||||
state.OutputAudioCodec = streamingRequest.AudioCodec;
|
state.OutputAudioCodec = streamingRequest.AudioCodec;
|
||||||
|
|
||||||
@ -196,20 +196,41 @@ namespace Jellyfin.Api.Helpers
|
|||||||
|
|
||||||
encodingHelper.TryStreamCopy(state);
|
encodingHelper.TryStreamCopy(state);
|
||||||
|
|
||||||
if (state.OutputVideoBitrate.HasValue && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
|
if (!EncodingHelper.IsCopyCodec(state.OutputVideoCodec) && state.OutputVideoBitrate.HasValue)
|
||||||
{
|
{
|
||||||
var resolution = ResolutionNormalizer.Normalize(
|
var isVideoResolutionNotRequested = !state.VideoRequest.Width.HasValue
|
||||||
state.VideoStream?.BitRate,
|
&& !state.VideoRequest.Height.HasValue
|
||||||
state.VideoStream?.Width,
|
&& !state.VideoRequest.MaxWidth.HasValue
|
||||||
state.VideoStream?.Height,
|
&& !state.VideoRequest.MaxHeight.HasValue;
|
||||||
state.OutputVideoBitrate.Value,
|
|
||||||
state.VideoStream?.Codec,
|
|
||||||
state.OutputVideoCodec,
|
|
||||||
state.VideoRequest.MaxWidth,
|
|
||||||
state.VideoRequest.MaxHeight);
|
|
||||||
|
|
||||||
state.VideoRequest.MaxWidth = resolution.MaxWidth;
|
if (isVideoResolutionNotRequested
|
||||||
state.VideoRequest.MaxHeight = resolution.MaxHeight;
|
&& state.VideoRequest.VideoBitRate.HasValue
|
||||||
|
&& state.VideoStream.BitRate.HasValue
|
||||||
|
&& state.VideoRequest.VideoBitRate.Value >= state.VideoStream.BitRate.Value)
|
||||||
|
{
|
||||||
|
// Don't downscale the resolution if the width/height/MaxWidth/MaxHeight is not requested,
|
||||||
|
// and the requested video bitrate is higher than source video bitrate.
|
||||||
|
if (state.VideoStream.Width.HasValue || state.VideoStream.Height.HasValue)
|
||||||
|
{
|
||||||
|
state.VideoRequest.MaxWidth = state.VideoStream?.Width;
|
||||||
|
state.VideoRequest.MaxHeight = state.VideoStream?.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var resolution = ResolutionNormalizer.Normalize(
|
||||||
|
state.VideoStream?.BitRate,
|
||||||
|
state.VideoStream?.Width,
|
||||||
|
state.VideoStream?.Height,
|
||||||
|
state.OutputVideoBitrate.Value,
|
||||||
|
state.VideoStream?.Codec,
|
||||||
|
state.OutputVideoCodec,
|
||||||
|
state.VideoRequest.MaxWidth,
|
||||||
|
state.VideoRequest.MaxHeight);
|
||||||
|
|
||||||
|
state.VideoRequest.MaxWidth = resolution.MaxWidth;
|
||||||
|
state.VideoRequest.MaxHeight = resolution.MaxHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return .5;
|
return .6;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -1424,36 +1424,48 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|
|
||||||
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
|
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
|
||||||
{
|
{
|
||||||
if (audioStream == null)
|
return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream);
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.AudioBitRate.HasValue)
|
|
||||||
{
|
|
||||||
// Don't encode any higher than this
|
|
||||||
return Math.Min(384000, request.AudioBitRate.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty bitrate area is not allow on iOS
|
|
||||||
// Default audio bitrate to 128K if it is not being requested
|
|
||||||
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
|
||||||
return 128000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int? GetAudioBitrateParam(int? audioBitRate, MediaStream audioStream)
|
public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream)
|
||||||
{
|
{
|
||||||
if (audioStream == null)
|
if (audioStream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioBitRate.HasValue)
|
if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec))
|
||||||
{
|
{
|
||||||
// Don't encode any higher than this
|
|
||||||
return Math.Min(384000, audioBitRate.Value);
|
return Math.Min(384000, audioBitRate.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec))
|
||||||
|
{
|
||||||
|
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if ((audioStream.Channels ?? 0) >= 6)
|
||||||
|
{
|
||||||
|
return Math.Min(640000, audioBitRate.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.Min(384000, audioBitRate.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if ((audioStream.Channels ?? 0) >= 6)
|
||||||
|
{
|
||||||
|
return Math.Min(3584000, audioBitRate.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.Min(1536000, audioBitRate.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Empty bitrate area is not allow on iOS
|
// Empty bitrate area is not allow on iOS
|
||||||
// Default audio bitrate to 128K if it is not being requested
|
// Default audio bitrate to 128K if it is not being requested
|
||||||
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
||||||
|
@ -234,8 +234,8 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
|
|
||||||
var channelsValue = channels.Value;
|
var channelsValue = channels.Value;
|
||||||
|
|
||||||
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) ||
|
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||||
string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (channelsValue <= 2)
|
if (channelsValue <= 2)
|
||||||
{
|
{
|
||||||
@ -248,6 +248,34 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (channelsValue <= 2)
|
||||||
|
{
|
||||||
|
return 192000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channelsValue >= 5)
|
||||||
|
{
|
||||||
|
return 640000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (channelsValue <= 2)
|
||||||
|
{
|
||||||
|
return 960000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channelsValue >= 5)
|
||||||
|
{
|
||||||
|
return 2880000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,6 +802,35 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
stream.BitRate = bitrate;
|
stream.BitRate = bitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract bitrate info from tag "BPS" if possible.
|
||||||
|
if (!stream.BitRate.HasValue
|
||||||
|
&& (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
var bps = GetBPSFromTags(streamInfo);
|
||||||
|
if (bps != null && bps > 0)
|
||||||
|
{
|
||||||
|
stream.BitRate = bps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible.
|
||||||
|
if (!stream.BitRate.HasValue
|
||||||
|
&& (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo);
|
||||||
|
var bytes = GetNumberOfBytesFromTags(streamInfo);
|
||||||
|
if (durationInSeconds != null && bytes != null)
|
||||||
|
{
|
||||||
|
var bps = Convert.ToInt32(bytes * 8 / durationInSeconds);
|
||||||
|
if (bps > 0)
|
||||||
|
{
|
||||||
|
stream.BitRate = bps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var disposition = streamInfo.Disposition;
|
var disposition = streamInfo.Disposition;
|
||||||
if (disposition != null)
|
if (disposition != null)
|
||||||
{
|
{
|
||||||
@ -963,6 +1020,57 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int? GetBPSFromTags(MediaStreamInfo streamInfo)
|
||||||
|
{
|
||||||
|
if (streamInfo != null && streamInfo.Tags != null)
|
||||||
|
{
|
||||||
|
var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS");
|
||||||
|
if (!string.IsNullOrEmpty(bps))
|
||||||
|
{
|
||||||
|
if (int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps))
|
||||||
|
{
|
||||||
|
return parsedBps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double? GetRuntimeSecondsFromTags(MediaStreamInfo streamInfo)
|
||||||
|
{
|
||||||
|
if (streamInfo != null && streamInfo.Tags != null)
|
||||||
|
{
|
||||||
|
var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION");
|
||||||
|
if (!string.IsNullOrEmpty(duration))
|
||||||
|
{
|
||||||
|
if (TimeSpan.TryParse(duration, out var parsedDuration))
|
||||||
|
{
|
||||||
|
return parsedDuration.TotalSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long? GetNumberOfBytesFromTags(MediaStreamInfo streamInfo)
|
||||||
|
{
|
||||||
|
if (streamInfo != null && streamInfo.Tags != null)
|
||||||
|
{
|
||||||
|
var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES");
|
||||||
|
if (!string.IsNullOrEmpty(numberOfBytes))
|
||||||
|
{
|
||||||
|
if (long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes))
|
||||||
|
{
|
||||||
|
return parsedBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void SetSize(InternalMediaInfoResult data, MediaInfo info)
|
private void SetSize(InternalMediaInfoResult data, MediaInfo info)
|
||||||
{
|
{
|
||||||
if (data.Format != null)
|
if (data.Format != null)
|
||||||
|
@ -79,11 +79,11 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
private static double GetVideoBitrateScaleFactor(string codec)
|
private static double GetVideoBitrateScaleFactor(string codec)
|
||||||
{
|
{
|
||||||
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) ||
|
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|
||||||
string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) ||
|
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||||
string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return .5;
|
return .6;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -872,11 +872,34 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
return playlistItem;
|
return playlistItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream)
|
private static int GetDefaultAudioBitrate(string audioCodec, int? audioChannels)
|
||||||
{
|
{
|
||||||
if ((audioStream.Channels ?? 0) >= 6)
|
if (!string.IsNullOrEmpty(audioCodec))
|
||||||
{
|
{
|
||||||
return 384000;
|
// Default to a higher bitrate for stream copy
|
||||||
|
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if ((audioChannels ?? 0) < 2)
|
||||||
|
{
|
||||||
|
return 128000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (audioChannels ?? 0) >= 6 ? 640000 : 384000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if ((audioChannels ?? 0) < 2)
|
||||||
|
{
|
||||||
|
return 768000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (audioChannels ?? 0) >= 6 ? 3584000 : 1536000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 192000;
|
return 192000;
|
||||||
@ -897,14 +920,27 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (targetAudioChannels.HasValue && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
|
if (targetAudioChannels.HasValue
|
||||||
|
&& audioStream.Channels.HasValue
|
||||||
|
&& audioStream.Channels.Value > targetAudioChannels.Value)
|
||||||
{
|
{
|
||||||
// Reduce the bitrate if we're downmixing
|
// Reduce the bitrate if we're downmixing.
|
||||||
defaultBitrate = targetAudioChannels.Value < 2 ? 128000 : 192000;
|
defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels);
|
||||||
|
}
|
||||||
|
else if (targetAudioChannels.HasValue
|
||||||
|
&& audioStream.Channels.HasValue
|
||||||
|
&& audioStream.Channels.Value <= targetAudioChannels.Value
|
||||||
|
&& !string.IsNullOrEmpty(audioStream.Codec)
|
||||||
|
&& targetAudioCodecs != null
|
||||||
|
&& targetAudioCodecs.Length > 0
|
||||||
|
&& !Array.Exists(targetAudioCodecs, elem => string.Equals(audioStream.Codec, elem, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
// Shift the bitrate if we're transcoding to a different audio codec.
|
||||||
|
defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, audioStream.Channels.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
|
defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seeing webm encoding failures when source has 1 audio channel and 22k bitrate.
|
// Seeing webm encoding failures when source has 1 audio channel and 22k bitrate.
|
||||||
@ -938,8 +974,28 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
{
|
{
|
||||||
return 448000;
|
return 448000;
|
||||||
}
|
}
|
||||||
|
else if (totalBitrate <= 4000000)
|
||||||
|
{
|
||||||
|
return 640000;
|
||||||
|
}
|
||||||
|
else if (totalBitrate <= 5000000)
|
||||||
|
{
|
||||||
|
return 768000;
|
||||||
|
}
|
||||||
|
else if (totalBitrate <= 10000000)
|
||||||
|
{
|
||||||
|
return 1536000;
|
||||||
|
}
|
||||||
|
else if (totalBitrate <= 15000000)
|
||||||
|
{
|
||||||
|
return 2304000;
|
||||||
|
}
|
||||||
|
else if (totalBitrate <= 20000000)
|
||||||
|
{
|
||||||
|
return 3584000;
|
||||||
|
}
|
||||||
|
|
||||||
return 640000;
|
return 7168000;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (PlayMethod?, List<TranscodeReason>) GetVideoDirectPlayProfile(
|
private (PlayMethod?, List<TranscodeReason>) GetVideoDirectPlayProfile(
|
||||||
|
@ -787,7 +787,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
public int? GetTargetAudioChannels(string codec)
|
public int? GetTargetAudioChannels(string codec)
|
||||||
{
|
{
|
||||||
var defaultValue = GlobalMaxAudioChannels;
|
var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
|
||||||
|
|
||||||
var value = GetOption(codec, "audiochannels");
|
var value = GetOption(codec, "audiochannels");
|
||||||
if (string.IsNullOrEmpty(value))
|
if (string.IsNullOrEmpty(value))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user