You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jellyfin/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs

260 lines
9.2 KiB

#nullable disable
#pragma warning disable CS1591
using System;
6 years ago
using System.Collections.Generic;
using System.Globalization;
using Jellyfin.Data.Enums;
using MediaBrowser.Model.MediaInfo;
6 years ago
namespace MediaBrowser.Model.Dlna
{
public static class ContentFeatureBuilder
6 years ago
{
public static string BuildImageHeader(
DeviceProfile profile,
string container,
6 years ago
int? width,
int? height,
bool isDirectStream,
string orgPn = null)
{
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetImageOrgOpValue();
// 0 = native, 1 = transcoded
var orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
var flagValue = DlnaFlags.BackgroundTransferMode |
6 years ago
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
6 years ago
string dlnaflags = string.Format(
CultureInfo.InvariantCulture,
";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
6 years ago
if (string.IsNullOrEmpty(orgPn))
{
ResponseProfile mediaProfile = profile.GetImageMediaProfile(
container,
width,
height);
orgPn = mediaProfile?.OrgPn;
if (string.IsNullOrEmpty(orgPn))
{
orgPn = GetImageOrgPnValue(container, width, height);
}
6 years ago
}
if (string.IsNullOrEmpty(orgPn))
{
return orgOp.TrimStart(';') + orgCi + dlnaflags;
6 years ago
}
return "DLNA.ORG_PN=" + orgPn + orgOp + orgCi + dlnaflags;
6 years ago
}
public static string BuildAudioHeader(
DeviceProfile profile,
string container,
6 years ago
string audioCodec,
int? audioBitrate,
int? audioSampleRate,
int? audioChannels,
int? audioBitDepth,
bool isDirectStream,
long? runtimeTicks,
TranscodeSeekInfo transcodeSeekInfo)
{
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks > 0, isDirectStream, transcodeSeekInfo);
// 0 = native, 1 = transcoded
string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
var flagValue = DlnaFlags.StreamingTransferMode |
6 years ago
DlnaFlags.BackgroundTransferMode |
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
4 years ago
// if (isDirectStream)
// {
// flagValue = flagValue | DlnaFlags.ByteBasedSeek;
// }
// else if (runtimeTicks.HasValue)
// {
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
// }
6 years ago
string dlnaflags = string.Format(
CultureInfo.InvariantCulture,
";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
6 years ago
ResponseProfile mediaProfile = profile.GetAudioMediaProfile(
container,
6 years ago
audioCodec,
audioChannels,
audioBitrate,
audioSampleRate,
audioBitDepth);
string orgPn = mediaProfile?.OrgPn;
6 years ago
if (string.IsNullOrEmpty(orgPn))
{
orgPn = GetAudioOrgPnValue(container, audioBitrate, audioSampleRate, audioChannels);
}
if (string.IsNullOrEmpty(orgPn))
{
return orgOp.TrimStart(';') + orgCi + dlnaflags;
}
6 years ago
return "DLNA.ORG_PN=" + orgPn + orgOp + orgCi + dlnaflags;
6 years ago
}
public static IEnumerable<string> BuildVideoHeader(
DeviceProfile profile,
string container,
6 years ago
string videoCodec,
string audioCodec,
int? width,
int? height,
int? bitDepth,
int? videoBitrate,
TransportStreamTimestamp timestamp,
bool isDirectStream,
long? runtimeTicks,
string videoProfile,
VideoRangeType videoRangeType,
6 years ago
double? videoLevel,
float? videoFramerate,
int? packetLength,
TranscodeSeekInfo transcodeSeekInfo,
bool? isAnamorphic,
bool? isInterlaced,
int? refFrames,
int? numVideoStreams,
int? numAudioStreams,
string videoCodecTag,
bool? isAvc)
{
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks > 0, isDirectStream, transcodeSeekInfo);
// 0 = native, 1 = transcoded
string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
var flagValue = DlnaFlags.StreamingTransferMode |
6 years ago
DlnaFlags.BackgroundTransferMode |
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
if (isDirectStream)
{
flagValue |= DlnaFlags.ByteBasedSeek;
}
// Time based seek is currently disabled when streaming. On LG CX3 adding DlnaFlags.TimeBasedSeek and orgPn causes the DLNA playback to fail (format not supported). Further investigations are needed before enabling the remaining code paths.
// else if (runtimeTicks.HasValue)
// {
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
// }
string dlnaflags = string.Format(
CultureInfo.InvariantCulture,
";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
6 years ago
ResponseProfile mediaProfile = profile.GetVideoMediaProfile(
container,
6 years ago
audioCodec,
videoCodec,
width,
height,
bitDepth,
videoBitrate,
videoProfile,
videoRangeType,
6 years ago
videoLevel,
videoFramerate,
packetLength,
timestamp,
isAnamorphic,
isInterlaced,
refFrames,
numVideoStreams,
numAudioStreams,
videoCodecTag,
isAvc);
var orgPnValues = new List<string>();
6 years ago
if (mediaProfile is not null && !string.IsNullOrEmpty(mediaProfile.OrgPn))
6 years ago
{
orgPnValues.AddRange(mediaProfile.OrgPn.Split(',', StringSplitOptions.RemoveEmptyEntries));
6 years ago
}
else
{
foreach (var s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp))
6 years ago
{
orgPnValues.Add(s.ToString());
6 years ago
break;
}
}
var contentFeatureList = new List<string>();
6 years ago
foreach (string orgPn in orgPnValues)
{
if (string.IsNullOrEmpty(orgPn))
{
contentFeatureList.Add(orgOp.TrimStart(';') + orgCi + dlnaflags);
}
else if (isDirectStream)
{
// orgOp should be added all the time once the time based seek is resolved for transcoded streams
contentFeatureList.Add("DLNA.ORG_PN=" + orgPn + orgOp + orgCi + dlnaflags);
}
else
{
contentFeatureList.Add("DLNA.ORG_PN=" + orgPn + orgCi + dlnaflags);
}
6 years ago
}
if (orgPnValues.Count == 0)
{
contentFeatureList.Add(orgOp.TrimStart(';') + orgCi + dlnaflags);
6 years ago
}
return contentFeatureList;
}
private static string GetImageOrgPnValue(string container, int? width, int? height)
6 years ago
{
MediaFormatProfile? format = MediaFormatProfileResolver.ResolveImageFormat(container, width, height);
6 years ago
return format.HasValue ? format.Value.ToString() : null;
}
private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
6 years ago
{
MediaFormatProfile? format = MediaFormatProfileResolver.ResolveAudioFormat(
container,
6 years ago
audioBitrate,
audioSampleRate,
audioChannels);
return format.HasValue ? format.Value.ToString() : null;
}
private static MediaFormatProfile[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp)
6 years ago
{
return MediaFormatProfileResolver.ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp);
6 years ago
}
}
}