Normalize orgPn usage

This commit is contained in:
Luke Pulverenti 2014-04-22 22:47:46 -04:00
parent fdd8c67162
commit 44bfad70d2
13 changed files with 302 additions and 157 deletions

View File

@ -1507,6 +1507,16 @@ namespace MediaBrowser.Api.Playback
}
}
var headers = new Dictionary<string, string>();
foreach (var key in Request.Headers.AllKeys)
{
headers[key] = Request.Headers[key];
}
state.DeviceProfile = string.IsNullOrWhiteSpace(state.Request.DeviceProfileId) ?
DlnaManager.GetProfile(headers) :
DlnaManager.GetProfile(state.Request.DeviceProfileId);
return state;
}
@ -1631,16 +1641,7 @@ namespace MediaBrowser.Api.Playback
private void ApplyDeviceProfileSettings(StreamState state)
{
var headers = new Dictionary<string, string>();
foreach (var key in Request.Headers.AllKeys)
{
headers[key] = Request.Headers[key];
}
var profile = string.IsNullOrWhiteSpace(state.Request.DeviceProfileId) ?
DlnaManager.GetProfile(headers) :
DlnaManager.GetProfile(state.Request.DeviceProfileId);
var profile = state.DeviceProfile;
if (profile == null)
{
@ -1664,13 +1665,12 @@ namespace MediaBrowser.Api.Playback
}
var mediaProfile = state.VideoRequest == null ?
profile.GetAudioMediaProfile(state.OutputContainer, audioCodec, state.AudioStream) :
profile.GetVideoMediaProfile(state.OutputContainer, audioCodec, videoCodec, state.AudioStream, state.VideoStream);
profile.GetAudioMediaProfile(state.OutputContainer, audioCodec) :
profile.GetVideoMediaProfile(state.OutputContainer, audioCodec, videoCodec);
if (mediaProfile != null)
{
state.MimeType = mediaProfile.MimeType;
state.OrgPn = mediaProfile.OrgPn;
}
var transcodingProfile = state.VideoRequest == null ?
@ -1699,45 +1699,65 @@ namespace MediaBrowser.Api.Playback
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
protected void AddDlnaHeaders(StreamState state, IDictionary<string, string> responseHeaders, bool isStaticallyStreamed)
{
var profile = state.DeviceProfile;
if (profile == null)
{
return;
}
var transferMode = GetHeader("transferMode.dlna.org");
responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
// 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
var orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(state.RunTimeTicks.HasValue, isStaticallyStreamed, state.TranscodeSeekInfo);
if (state.RunTimeTicks.HasValue && !isStaticallyStreamed)
{
AddTimeSeekResponseHeaders(state, responseHeaders);
}
// 0 = native, 1 = transcoded
var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
var audioCodec = state.Request.AudioCodec;
var flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.DlnaV15;
if (isStaticallyStreamed)
if (state.VideoRequest == null)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_BYTE_BASED_SEEK;
responseHeaders["contentFeatures.dlna.org"] = new ContentFeatureBuilder(profile)
.BuildAudioHeader(
state.OutputContainer,
audioCodec,
state.OutputAudioBitrate,
state.OutputAudioSampleRate,
state.OutputAudioChannels,
isStaticallyStreamed,
state.RunTimeTicks,
state.TranscodeSeekInfo
);
}
else if (state.RunTimeTicks.HasValue)
else
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_TIME_BASED_SEEK;
}
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
var dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}000000000000000000000000",
Enum.Format(typeof(DlnaFlags), flagValue, "x"));
var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
var orgPn = GetOrgPnValue(state);
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
{
videoCodec = state.VideoStream.Codec;
}
var contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty :
"DLNA.ORG_PN=" + orgPn;
if (!string.IsNullOrEmpty(contentFeatures))
{
responseHeaders["contentFeatures.dlna.org"] = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
responseHeaders["contentFeatures.dlna.org"] = new ContentFeatureBuilder(profile)
.BuildVideoHeader(
state.OutputContainer,
videoCodec,
audioCodec,
state.OutputWidth,
state.OutputHeight,
state.TotalOutputBitrate,
TransportStreamTimestamp.VALID,
isStaticallyStreamed,
state.RunTimeTicks,
state.TranscodeSeekInfo
);
}
foreach (var item in responseHeaders)
@ -1746,50 +1766,6 @@ namespace MediaBrowser.Api.Playback
}
}
private string GetOrgPnValue(StreamState state)
{
if (!string.IsNullOrWhiteSpace(state.OrgPn))
{
return state.OrgPn;
}
if (state.VideoRequest == null)
{
var format = new MediaFormatProfileResolver()
.ResolveAudioFormat(state.OutputContainer,
state.OutputAudioBitrate,
state.OutputAudioSampleRate,
state.OutputAudioChannels);
return format.HasValue ? format.Value.ToString() : null;
}
var audioCodec = state.Request.AudioCodec;
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
{
videoCodec = state.VideoStream.Codec;
}
var videoFormat = new MediaFormatProfileResolver()
.ResolveVideoFormat(state.OutputContainer,
videoCodec,
audioCodec,
state.OutputWidth,
state.OutputHeight,
state.TotalOutputBitrate,
TransportStreamTimestamp.VALID);
return videoFormat.HasValue ? videoFormat.Value.ToString() : null;
}
private void AddTimeSeekResponseHeaders(StreamState state, IDictionary<string, string> responseHeaders)
{
var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(UsCulture);

View File

@ -84,7 +84,6 @@ namespace MediaBrowser.Api.Playback
public string InputAudioCodec { get; set; }
public string MimeType { get; set; }
public string OrgPn { get; set; }
public bool EstimateContentLength { get; set; }
public bool EnableMpegtsM2TsMode { get; set; }
@ -163,6 +162,8 @@ namespace MediaBrowser.Api.Playback
public string OutputContainer { get; set; }
public DeviceProfile DeviceProfile { get; set; }
public int? TotalOutputBitrate
{
get

View File

@ -401,66 +401,38 @@ namespace MediaBrowser.Dlna.PlayTo
private string GetDlnaHeaders(PlaylistItem item)
{
var profile = item.Profile;
var streamInfo = item.StreamInfo;
var orgOp = !streamInfo.IsDirectStream ? ";DLNA.ORG_OP=00" : ";DLNA.ORG_OP=01";
var orgCi = !streamInfo.IsDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
string contentFeatures;
var container = streamInfo.Container.TrimStart('.');
if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
if (streamInfo.MediaType == DlnaProfileType.Audio)
{
contentFeatures = "DLNA.ORG_PN=MP3";
}
else if (string.Equals(container, "wma", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMABASE";
}
else if (string.Equals(container, "wmw", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
else if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
else if (string.Equals(container, "avi", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVI";
}
else if (string.Equals(container, "mkv", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MATROSKA";
}
else if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC";
}
else if (string.Equals(container, "mpeg", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (streamInfo.MediaType == DlnaProfileType.Video)
{
// Default to AVI for video
contentFeatures = "DLNA.ORG_PN=AVI";
}
else
{
// Default to MP3 for audio
contentFeatures = "DLNA.ORG_PN=MP3";
return new ContentFeatureBuilder(profile)
.BuildAudioHeader(streamInfo.Container,
streamInfo.AudioCodec,
streamInfo.TargetAudioBitrate,
streamInfo.TargetAudioSampleRate,
streamInfo.TargetAudioChannels,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo);
}
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
if (streamInfo.MediaType == DlnaProfileType.Video)
{
return new ContentFeatureBuilder(profile)
.BuildVideoHeader(streamInfo.Container,
streamInfo.VideoCodec,
streamInfo.AudioCodec,
streamInfo.TargetWidth,
streamInfo.TargetHeight,
streamInfo.TotalOutputBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo);
}
return null;
}
private PlaylistItem GetPlaylistItem(BaseItem item, List<MediaSourceInfo> mediaSources, DeviceProfile profile, string deviceId)
@ -477,7 +449,9 @@ namespace MediaBrowser.Dlna.PlayTo
MediaSources = mediaSources,
Profile = profile,
DeviceId = deviceId
})
}),
Profile = profile
};
}
@ -493,7 +467,9 @@ namespace MediaBrowser.Dlna.PlayTo
MediaSources = mediaSources,
Profile = profile,
DeviceId = deviceId
})
}),
Profile = profile
};
}

View File

@ -104,10 +104,6 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
public void Stop()
{
}
/// <summary>
/// Creates a socket for the interface and listends for data.
/// </summary>

View File

@ -104,7 +104,6 @@ namespace MediaBrowser.Dlna.PlayTo
{
try
{
_manager.Stop();
_manager.Dispose();
}
catch (Exception ex)

View File

@ -11,5 +11,7 @@ namespace MediaBrowser.Dlna.PlayTo
public string Didl { get; set; }
public StreamInfo StreamInfo { get; set; }
public DeviceProfile Profile { get; set; }
}
}

View File

@ -19,7 +19,9 @@ namespace MediaBrowser.Dlna.PlayTo
{
ItemId = item.Id.ToString("N"),
MediaType = DlnaProfileType.Photo,
}
},
Profile = profile
};
var directPlay = profile.DirectPlayProfiles

View File

@ -632,9 +632,7 @@ namespace MediaBrowser.Dlna.Server
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
streamInfo.AudioCodec,
streamInfo.VideoCodec,
streamInfo.TargetAudioStream,
streamInfo.TargetVideoStream);
streamInfo.VideoCodec);
var formatProfile = mediaProfile == null ? null : mediaProfile.OrgPn;
@ -646,7 +644,7 @@ namespace MediaBrowser.Dlna.Server
targetWidth,
targetHeight,
targetBitrate,
TransportStreamTimestamp.VALID);
streamInfo.TargetTimestamp);
formatProfile = format.HasValue ? format.Value.ToString() : null;
}
@ -731,8 +729,7 @@ namespace MediaBrowser.Dlna.Server
}
var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
streamInfo.AudioCodec,
streamInfo.TargetAudioStream);
streamInfo.AudioCodec);
var formatProfile = mediaProfile == null ? null : mediaProfile.OrgPn;
@ -780,11 +777,11 @@ namespace MediaBrowser.Dlna.Server
{
if (item is MusicAlbum)
{
classType = "object.container.musicAlbum";
classType = "object.container.album.musicAlbum";
}
if (item is MusicArtist)
{
classType = "object.container.musicArtist";
classType = "object.container.person.musicArtist";
}
}

View File

@ -0,0 +1,130 @@
using System;
namespace MediaBrowser.Model.Dlna
{
public class ContentFeatureBuilder
{
private readonly DeviceProfile _profile;
public ContentFeatureBuilder(DeviceProfile profile)
{
_profile = profile;
}
public string BuildAudioHeader(string container,
string audioCodec,
int? audioBitrate,
int? audioSampleRate,
int? audioChannels,
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
var orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo);
// 0 = native, 1 = transcoded
var orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
var flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.DlnaV15;
if (isDirectStream)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_BYTE_BASED_SEEK;
}
else if (runtimeTicks.HasValue)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_TIME_BASED_SEEK;
}
var dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}000000000000000000000000",
Enum.Format(typeof(DlnaFlags), flagValue, "x"));
var mediaProfile = _profile.GetAudioMediaProfile(container, audioCodec);
var orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
if (string.IsNullOrEmpty(orgPn))
{
orgPn = GetAudioOrgPnValue(container, audioBitrate, audioSampleRate, audioChannels);
}
var contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
}
public string BuildVideoHeader(string container,
string videoCodec,
string audioCodec,
int? width,
int? height,
int? bitrate,
TransportStreamTimestamp timestamp,
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
var orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo);
// 0 = native, 1 = transcoded
var orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
var flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.DlnaV15;
if (isDirectStream)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_BYTE_BASED_SEEK;
}
else if (runtimeTicks.HasValue)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_TIME_BASED_SEEK;
}
var dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}000000000000000000000000",
Enum.Format(typeof(DlnaFlags), flagValue, "x"));
var mediaProfile = _profile.GetVideoMediaProfile(container, audioCodec, videoCodec);
var orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
if (string.IsNullOrEmpty(orgPn))
{
orgPn = GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, bitrate, timestamp);
}
var contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
}
private string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
{
var format = new MediaFormatProfileResolver()
.ResolveAudioFormat(container,
audioBitrate,
audioSampleRate,
audioChannels);
return format.HasValue ? format.Value.ToString() : null;
}
private string GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, int? bitrate, TransportStreamTimestamp timestamp)
{
var videoFormat = new MediaFormatProfileResolver()
.ResolveVideoFormat(container,
videoCodec,
audioCodec,
width,
height,
bitrate,
timestamp);
return videoFormat.HasValue ? videoFormat.Value.ToString() : null;
}
}
}

View File

@ -159,7 +159,7 @@ namespace MediaBrowser.Model.Dlna
});
}
public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, MediaStream audioStream)
public ResponseProfile GetAudioMediaProfile(string container, string audioCodec)
{
container = (container ?? string.Empty).TrimStart('.');
@ -186,7 +186,7 @@ namespace MediaBrowser.Model.Dlna
});
}
public ResponseProfile GetVideoMediaProfile(string container, string audioCodec, string videoCodec, MediaStream audioStream, MediaStream videoStream)
public ResponseProfile GetVideoMediaProfile(string container, string audioCodec, string videoCodec)
{
container = (container ?? string.Empty).TrimStart('.');

View File

@ -33,7 +33,7 @@ namespace MediaBrowser.Model.Dlna
string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase))
{
var list = ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, bitrate, timestampType)
var list = ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType)
.ToList();
return list.Count > 0 ? list[0] : (MediaFormatProfile?)null;
@ -54,7 +54,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
private IEnumerable<MediaFormatProfile> ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, int? bitrate, TransportStreamTimestamp timestampType)
private IEnumerable<MediaFormatProfile> ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
{
var suffix = "";

View File

@ -1,4 +1,5 @@
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
@ -56,6 +57,8 @@ namespace MediaBrowser.Model.Dlna
public MediaSourceInfo MediaSource { get; set; }
public TransportStreamTimestamp TargetTimestamp { get; set; }
public string MediaSourceId
{
get
@ -252,6 +255,68 @@ namespace MediaBrowser.Model.Dlna
: stream == null ? null : stream.Channels;
}
}
public int? TotalOutputBitrate
{
get
{
return (TargetAudioBitrate ?? 0) + (VideoBitrate ?? 0);
}
}
public int? TargetWidth
{
get
{
var videoStream = TargetVideoStream;
if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
{
var size = new ImageSize
{
Width = videoStream.Width.Value,
Height = videoStream.Height.Value
};
var newSize = DrawingUtils.Resize(size,
null,
null,
MaxWidth,
MaxHeight);
return Convert.ToInt32(newSize.Width);
}
return MaxWidth;
}
}
public int? TargetHeight
{
get
{
var videoStream = TargetVideoStream;
if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
{
var size = new ImageSize
{
Width = videoStream.Width.Value,
Height = videoStream.Height.Value
};
var newSize = DrawingUtils.Resize(size,
null,
null,
MaxWidth,
MaxHeight);
return Convert.ToInt32(newSize.Height);
}
return MaxHeight;
}
}
}
/// <summary>

View File

@ -68,6 +68,7 @@
<Compile Include="Configuration\ServerConfiguration.cs" />
<Compile Include="Dlna\CodecProfile.cs" />
<Compile Include="Dlna\ContainerProfile.cs" />
<Compile Include="Dlna\ContentFeatureBuilder.cs" />
<Compile Include="Dlna\DeviceIdentification.cs" />
<Compile Include="Dlna\DeviceProfile.cs" />
<Compile Include="Dlna\DeviceProfileInfo.cs" />