add basic dlna server browsing

This commit is contained in:
Luke Pulverenti 2014-04-18 01:03:01 -04:00
parent 818d799091
commit 7f320ce063
38 changed files with 1363 additions and 855 deletions

View File

@ -1,5 +1,7 @@
using MediaBrowser.Controller.Dlna;
using System;
using MediaBrowser.Controller.Dlna;
using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System.Collections.Generic;
using System.IO;
@ -21,9 +23,12 @@ namespace MediaBrowser.Api.Dlna
{
}
[Route("/Dlna/control", "POST", Summary = "Processes a control request")]
[Route("/Dlna/{UuId}/control", "POST", Summary = "Processes a control request")]
public class ProcessControlRequest : IRequiresRequestStream
{
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UuId { get; set; }
public Stream RequestStream { get; set; }
}
@ -66,12 +71,16 @@ namespace MediaBrowser.Api.Dlna
private async Task<ControlResponse> PostAsync(ProcessControlRequest request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(1);
using (var reader = new StreamReader(request.RequestStream))
{
return _dlnaManager.ProcessControlRequest(new ControlRequest
{
Headers = GetRequestHeaders(),
InputXml = await reader.ReadToEndAsync().ConfigureAwait(false)
InputXml = await reader.ReadToEndAsync().ConfigureAwait(false),
TargetServerUuId = id
});
}
}

View File

@ -300,7 +300,7 @@ namespace MediaBrowser.Api.Playback
case EncodingQuality.HighSpeed:
return 2;
case EncodingQuality.HighQuality:
return isWebm ? Math.Max((int)((Environment.ProcessorCount -1) / 2) , 2) : 0;
return 2;
case EncodingQuality.MaxQuality:
return isWebm ? Math.Max(Environment.ProcessorCount - 1, 2) : 0;
default:
@ -373,7 +373,6 @@ namespace MediaBrowser.Api.Playback
break;
case EncodingQuality.MaxQuality:
crf = "4";
//profilescore aready set to 0
break;
default:
throw new ArgumentException("Unrecognized quality setting");
@ -381,7 +380,9 @@ namespace MediaBrowser.Api.Playback
if (isVc1)
{
profileScore = 1;
profileScore ++;
// Max of 2
profileScore = Math.Min(profileScore, 2);
}
// http://www.webmproject.org/docs/encoder-parameters/
@ -1713,33 +1714,19 @@ namespace MediaBrowser.Api.Playback
var extension = GetOutputFileExtension(state);
// 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=";
var orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(state.RunTimeTicks.HasValue, isStaticallyStreamed, state.TranscodeSeekInfo);
if (state.RunTimeTicks.HasValue)
if (state.RunTimeTicks.HasValue && !isStaticallyStreamed)
{
// Time-based seeking currently only possible when transcoding
orgOp += isStaticallyStreamed ? "0" : "1";
// Byte-based seeking only possible when not transcoding
orgOp += isStaticallyStreamed || state.TranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0";
if (!isStaticallyStreamed)
{
AddTimeSeekResponseHeaders(state, responseHeaders);
}
}
else
{
// No seeking is available if we don't know the content runtime
orgOp += "00";
AddTimeSeekResponseHeaders(state, responseHeaders);
}
// 0 = native, 1 = transcoded
var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
var flagValue = DlnaFlags.DLNA_ORG_FLAG_STREAMING_TRANSFER_MODE |
DlnaFlags.DLNA_ORG_FLAG_BACKGROUND_TRANSFERT_MODE |
DlnaFlags.DLNA_ORG_FLAG_DLNA_V15;
var flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.DlnaV15;
if (isStaticallyStreamed)
{
@ -1801,23 +1788,6 @@ namespace MediaBrowser.Api.Playback
}
}
[Flags]
private enum DlnaFlags
{
DLNA_ORG_FLAG_SENDER_PACED = (1 << 31),
DLNA_ORG_FLAG_TIME_BASED_SEEK = (1 << 30),
DLNA_ORG_FLAG_BYTE_BASED_SEEK = (1 << 29),
DLNA_ORG_FLAG_PLAY_CONTAINER = (1 << 28),
DLNA_ORG_FLAG_S0_INCREASE = (1 << 27),
DLNA_ORG_FLAG_SN_INCREASE = (1 << 26),
DLNA_ORG_FLAG_RTSP_PAUSE = (1 << 25),
DLNA_ORG_FLAG_STREAMING_TRANSFER_MODE = (1 << 24),
DLNA_ORG_FLAG_INTERACTIVE_TRANSFERT_MODE = (1 << 23),
DLNA_ORG_FLAG_BACKGROUND_TRANSFERT_MODE = (1 << 22),
DLNA_ORG_FLAG_CONNECTION_STALL = (1 << 21),
DLNA_ORG_FLAG_DLNA_V15 = (1 << 20),
};
private void AddTimeSeekResponseHeaders(StreamState state, IDictionary<string, string> responseHeaders)
{
var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(UsCulture);

View File

@ -8,6 +8,8 @@ namespace MediaBrowser.Controller.Dlna
public string InputXml { get; set; }
public string TargetServerUuId { get; set; }
public ControlRequest()
{
Headers = new Dictionary<string, string>();
@ -20,6 +22,8 @@ namespace MediaBrowser.Controller.Dlna
public string Xml { get; set; }
public bool IsSuccessful { get; set; }
public ControlResponse()
{
Headers = new Dictionary<string, string>();

View File

@ -12,6 +12,10 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<SongInfo>
{
public string FormatName { get; set; }
public long? Size { get; set; }
public string Container { get; set; }
public Audio()
{
Artists = new List<string>();

View File

@ -26,6 +26,10 @@ namespace MediaBrowser.Controller.Entities
public List<Guid> AdditionalPartIds { get; set; }
public List<Guid> LocalAlternateVersionIds { get; set; }
public string FormatName { get; set; }
public long? Size { get; set; }
public string Container { get; set; }
public Video()
{
PlayableStreamFileNames = new List<string>();

View File

@ -3,6 +3,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Dlna.Profiles;
using MediaBrowser.Dlna.Server;
@ -27,8 +28,11 @@ namespace MediaBrowser.Dlna
private readonly IJsonSerializer _jsonSerializer;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IUserDataManager _userDataManager;
public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager)
public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
@ -37,6 +41,9 @@ namespace MediaBrowser.Dlna
_jsonSerializer = jsonSerializer;
_userManager = userManager;
_libraryManager = libraryManager;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
//DumpProfiles();
}
@ -502,7 +509,14 @@ namespace MediaBrowser.Dlna
public ControlResponse ProcessControlRequest(ControlRequest request)
{
return new ControlHandler(_logger, _userManager, _libraryManager)
var profile = GetProfile(request.Headers)
?? GetDefaultProfile();
var device = DlnaServerEntryPoint.Instance.GetServerUpnpDevice(request.TargetServerUuId);
var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
return new ControlHandler(_logger, _userManager, _libraryManager, profile, serverAddress, _dtoService, _imageProcessor, _userDataManager)
.ProcessControlRequest(request);
}

View File

@ -81,7 +81,6 @@
<Compile Include="Ssdp\SsdpHelper.cs" />
<Compile Include="PlayTo\SsdpHttpClient.cs" />
<Compile Include="Common\StateVariable.cs" />
<Compile Include="PlayTo\StreamHelper.cs" />
<Compile Include="PlayTo\TransportCommands.cs" />
<Compile Include="PlayTo\TransportStateEventArgs.cs" />
<Compile Include="PlayTo\uBaseObject.cs" />

View File

@ -39,6 +39,7 @@ namespace MediaBrowser.Dlna.PlayTo
/// <param name="serverAddress">The server address.</param>
/// <param name="streamUrl">The stream URL.</param>
/// <param name="streams">The streams.</param>
/// <param name="includeImageRes">if set to <c>true</c> [include image resource].</param>
/// <returns>System.String.</returns>
public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable<MediaStream> streams, bool includeImageRes)
{

View File

@ -1,12 +1,14 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
@ -32,6 +34,7 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IDlnaManager _dlnaManager;
private readonly IUserManager _userManager;
private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService;
private bool _playbackStarted;
private const int UpdateTimerIntervalMs = 1000;
@ -52,7 +55,7 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager, IUserManager userManager, IServerApplicationHost appHost)
public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager, IUserManager userManager, IServerApplicationHost appHost, IDtoService dtoService)
{
_session = session;
_itemRepository = itemRepository;
@ -62,6 +65,7 @@ namespace MediaBrowser.Dlna.PlayTo
_dlnaManager = dlnaManager;
_userManager = userManager;
_appHost = appHost;
_dtoService = dtoService;
_logger = logger;
}
@ -172,20 +176,23 @@ namespace MediaBrowser.Dlna.PlayTo
if (playlistItem != null)
{
var streamInfo = playlistItem.StreamInfo;
if (!_playbackStarted)
{
await _sessionManager.OnPlaybackStart(new PlaybackStartInfo
{
ItemId = _currentItem.Id.ToString("N"),
SessionId = _session.Id,
CanSeek = true,
CanSeek = streamInfo.RunTimeTicks.HasValue,
QueueableMediaTypes = new List<string> { _currentItem.MediaType },
MediaSourceId = playlistItem.MediaSourceId,
AudioStreamIndex = playlistItem.AudioStreamIndex,
SubtitleStreamIndex = playlistItem.SubtitleStreamIndex,
MediaSourceId = streamInfo.MediaSourceId,
AudioStreamIndex = streamInfo.AudioStreamIndex,
SubtitleStreamIndex = streamInfo.SubtitleStreamIndex,
IsMuted = _device.IsMuted,
IsPaused = _device.IsPaused,
VolumeLevel = _device.Volume
VolumeLevel = _device.Volume,
PlayMethod = streamInfo.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
}).ConfigureAwait(false);
@ -196,9 +203,9 @@ namespace MediaBrowser.Dlna.PlayTo
{
var ticks = _device.Position.Ticks;
if (playlistItem.Transcode)
if (!streamInfo.IsDirectStream)
{
ticks += playlistItem.StartPositionTicks;
ticks += streamInfo.StartPositionTicks;
}
await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
@ -208,11 +215,12 @@ namespace MediaBrowser.Dlna.PlayTo
PositionTicks = ticks,
IsMuted = _device.IsMuted,
IsPaused = _device.IsPaused,
MediaSourceId = playlistItem.MediaSourceId,
AudioStreamIndex = playlistItem.AudioStreamIndex,
SubtitleStreamIndex = playlistItem.SubtitleStreamIndex,
MediaSourceId = streamInfo.MediaSourceId,
AudioStreamIndex = streamInfo.AudioStreamIndex,
SubtitleStreamIndex = streamInfo.SubtitleStreamIndex,
VolumeLevel = _device.Volume,
CanSeek = true
CanSeek = streamInfo.RunTimeTicks.HasValue,
PlayMethod = streamInfo.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
}).ConfigureAwait(false);
}
@ -411,46 +419,43 @@ namespace MediaBrowser.Dlna.PlayTo
private PlaylistItem CreatePlaylistItem(BaseItem item, long startPostionTicks, string serverAddress)
{
var streams = _itemRepository.GetMediaStreams(new MediaStreamQuery
{
ItemId = item.Id
}).ToList();
var deviceInfo = _device.Properties;
var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ??
_dlnaManager.GetDefaultProfile();
var playlistItem = GetPlaylistItem(item, streams, profile);
playlistItem.StartPositionTicks = startPostionTicks;
playlistItem.DeviceProfileId = profile.Id;
var mediaSources = item is Audio || item is Video
? _dtoService.GetMediaSources(item)
: new List<MediaSourceInfo>();
if (playlistItem.MediaType == DlnaProfileType.Audio)
{
playlistItem.StreamUrl = StreamHelper.GetAudioUrl(deviceInfo, playlistItem, streams, serverAddress);
}
else
{
playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress);
}
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId);
playlistItem.StreamInfo.StartPositionTicks = startPostionTicks;
playlistItem.Didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams, profile.EnableAlbumArtInDidl);
playlistItem.StreamUrl = playlistItem.StreamInfo.ToUrl(serverAddress);
var mediaStreams = mediaSources
.Where(i => string.Equals(i.Id, playlistItem.StreamInfo.MediaSourceId))
.SelectMany(i => i.MediaStreams)
.ToList();
playlistItem.Didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, mediaStreams, profile.EnableAlbumArtInDidl);
return playlistItem;
}
private string GetDlnaHeaders(PlaylistItem item)
{
var orgOp = item.Transcode ? ";DLNA.ORG_OP=00" : ";DLNA.ORG_OP=01";
var streamInfo = item.StreamInfo;
var orgCi = item.Transcode ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
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 = item.Container.TrimStart('.');
var container = streamInfo.Container.TrimStart('.');
if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
{
@ -488,7 +493,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (item.MediaType == DlnaProfileType.Video)
else if (streamInfo.MediaType == DlnaProfileType.Video)
{
// Default to AVI for video
contentFeatures = "DLNA.ORG_PN=AVI";
@ -502,20 +507,38 @@ namespace MediaBrowser.Dlna.PlayTo
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
}
private PlaylistItem GetPlaylistItem(BaseItem item, List<MediaStream> mediaStreams, DeviceProfile profile)
private PlaylistItem GetPlaylistItem(BaseItem item, List<MediaSourceInfo> mediaSources, DeviceProfile profile, string deviceId)
{
var video = item as Video;
if (video != null)
{
return new PlaylistItemFactory().Create(video, mediaStreams, profile);
return new PlaylistItem
{
StreamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
{
ItemId = item.Id.ToString("N"),
MediaSources = mediaSources,
Profile = profile,
DeviceId = deviceId
})
};
}
var audio = item as Audio;
if (audio != null)
{
return new PlaylistItemFactory().Create(audio, mediaStreams, profile);
return new PlaylistItem
{
StreamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions
{
ItemId = item.Id.ToString("N"),
MediaSources = mediaSources,
Profile = profile,
DeviceId = deviceId
})
};
}
var photo = item as Photo;
@ -578,8 +601,9 @@ namespace MediaBrowser.Dlna.PlayTo
await _device.SetAvTransport(nextTrack.StreamUrl, dlnaheaders, nextTrack.Didl);
if (nextTrack.StartPositionTicks > 0 && !nextTrack.Transcode)
await _device.Seek(TimeSpan.FromTicks(nextTrack.StartPositionTicks));
var streamInfo = nextTrack.StreamInfo;
if (streamInfo.StartPositionTicks > 0 && streamInfo.IsDirectStream)
await _device.Seek(TimeSpan.FromTicks(streamInfo.StartPositionTicks));
return true;
}
@ -602,7 +626,7 @@ namespace MediaBrowser.Dlna.PlayTo
return Task.FromResult(false);
prevTrack.PlayState = 1;
return _device.SetAvTransport(prevTrack.StreamUrl, GetDlnaHeaders(prevTrack), prevTrack.Didl);
return _device.SetAvTransport(prevTrack.StreamInfo.ToDlnaUrl(GetServerAddress()), GetDlnaHeaders(prevTrack), prevTrack.Didl);
}
#endregion

View File

@ -2,11 +2,11 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session;
using MediaBrowser.Dlna.Ssdp;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
@ -37,8 +37,9 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IDlnaManager _dlnaManager;
private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService;
public PlayToManager(ILogger logger, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepository, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost)
public PlayToManager(ILogger logger, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepository, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService)
{
_locations = new ConcurrentDictionary<string, DateTime>();
_tokenSource = new CancellationTokenSource();
@ -52,6 +53,7 @@ namespace MediaBrowser.Dlna.PlayTo
_userManager = userManager;
_dlnaManager = dlnaManager;
_appHost = appHost;
_dtoService = dtoService;
_config = config;
}
@ -248,7 +250,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (controller == null)
{
sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost);
sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost, _dtoService);
controller.Init(device);

View File

@ -2,6 +2,7 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Plugins;
@ -24,8 +25,9 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IUserManager _userManager;
private readonly IDlnaManager _dlnaManager;
private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService;
public PlayToServerEntryPoint(ILogManager logManager, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost)
public PlayToServerEntryPoint(ILogManager logManager, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService)
{
_config = config;
_sessionManager = sessionManager;
@ -36,6 +38,7 @@ namespace MediaBrowser.Dlna.PlayTo
_userManager = userManager;
_dlnaManager = dlnaManager;
_appHost = appHost;
_dtoService = dtoService;
_logger = logManager.GetLogger("PlayTo");
}
@ -72,7 +75,18 @@ namespace MediaBrowser.Dlna.PlayTo
{
try
{
_manager = new PlayToManager(_logger, _config, _sessionManager, _httpClient, _itemRepo, _libraryManager, _networkManager, _userManager, _dlnaManager, _appHost);
_manager = new PlayToManager(_logger,
_config,
_sessionManager,
_httpClient,
_itemRepo,
_libraryManager,
_networkManager,
_userManager,
_dlnaManager,
_appHost,
_dtoService);
_manager.Start();
}
catch (Exception ex)

View File

@ -4,45 +4,12 @@ namespace MediaBrowser.Dlna.PlayTo
{
public class PlaylistItem
{
public string ItemId { get; set; }
public string MediaSourceId { get; set; }
public bool Transcode { get; set; }
public DlnaProfileType MediaType { get; set; }
public string Container { get; set; }
public int PlayState { get; set; }
public string StreamUrl { get; set; }
public string Didl { get; set; }
public long StartPositionTicks { get; set; }
public string VideoCodec { get; set; }
public string AudioCodec { get; set; }
public int? AudioStreamIndex { get; set; }
public int? SubtitleStreamIndex { get; set; }
public int? MaxAudioChannels { get; set; }
public int? AudioBitrate { get; set; }
public int? VideoBitrate { get; set; }
public int? VideoLevel { get; set; }
public int? MaxWidth { get; set; }
public int? MaxHeight { get; set; }
public int? MaxFramerate { get; set; }
public string DeviceProfileId { get; set; }
public StreamInfo StreamInfo { get; set; }
}
}

View File

@ -1,10 +1,6 @@
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@ -15,235 +11,40 @@ namespace MediaBrowser.Dlna.PlayTo
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public PlaylistItem Create(Audio item, List<MediaStream> mediaStreams, DeviceProfile profile)
{
var playlistItem = new PlaylistItem
{
ItemId = item.Id.ToString("N"),
MediaType = DlnaProfileType.Audio
};
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
var directPlay = profile.DirectPlayProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
if (directPlay != null)
{
var audioCodec = audioStream == null ? null : audioStream.Codec;
// Make sure audio codec profiles are satisfied
if (!string.IsNullOrEmpty(audioCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.Audio && i.ContainsCodec(audioCodec))
.All(i => AreConditionsSatisfied(i.Conditions, item.Path, null, audioStream)))
{
playlistItem.Transcode = false;
playlistItem.Container = Path.GetExtension(item.Path);
return playlistItem;
}
}
var transcodingProfile = profile.TranscodingProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
if (transcodingProfile != null)
{
playlistItem.Transcode = true;
playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
playlistItem.AudioCodec = transcodingProfile.AudioCodec;
var audioTranscodingConditions = profile.CodecProfiles
.Where(i => i.Type == CodecType.Audio && i.ContainsCodec(transcodingProfile.AudioCodec))
.Take(1)
.SelectMany(i => i.Conditions);
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
}
return playlistItem;
}
public PlaylistItem Create(Photo item, DeviceProfile profile)
{
var playlistItem = new PlaylistItem
{
ItemId = item.Id.ToString("N"),
MediaType = DlnaProfileType.Photo
StreamInfo = new StreamInfo
{
ItemId = item.Id.ToString("N"),
MediaType = DlnaProfileType.Photo,
}
};
var directPlay = profile.DirectPlayProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item));
.FirstOrDefault(i => i.Type == DlnaProfileType.Photo && IsSupported(i, item));
if (directPlay != null)
{
playlistItem.Transcode = false;
playlistItem.Container = Path.GetExtension(item.Path);
playlistItem.StreamInfo.IsDirectStream = true;
playlistItem.StreamInfo.Container = Path.GetExtension(item.Path);
return playlistItem;
}
var transcodingProfile = profile.TranscodingProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
.FirstOrDefault(i => i.Type == DlnaProfileType.Photo);
if (transcodingProfile != null)
{
playlistItem.Transcode = true;
playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
playlistItem.StreamInfo.IsDirectStream = true;
playlistItem.StreamInfo.Container = "." + transcodingProfile.Container.TrimStart('.');
}
return playlistItem;
}
public PlaylistItem Create(Video item, List<MediaStream> mediaStreams, DeviceProfile profile)
{
var playlistItem = new PlaylistItem
{
ItemId = item.Id.ToString("N"),
MediaType = DlnaProfileType.Video
};
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
var directPlay = profile.DirectPlayProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
if (directPlay != null)
{
var videoCodec = videoStream == null ? null : videoStream.Codec;
// Make sure video codec profiles are satisfied
if (!string.IsNullOrEmpty(videoCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.Video && i.ContainsCodec(videoCodec))
.All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream)))
{
var audioCodec = audioStream == null ? null : audioStream.Codec;
// Make sure audio codec profiles are satisfied
if (string.IsNullOrEmpty(audioCodec) || profile.CodecProfiles.Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(audioCodec))
.All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream)))
{
playlistItem.Transcode = false;
playlistItem.Container = Path.GetExtension(item.Path);
return playlistItem;
}
}
}
var transcodingProfile = profile.TranscodingProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
if (transcodingProfile != null)
{
playlistItem.Transcode = true;
playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault();
playlistItem.VideoCodec = transcodingProfile.VideoCodec;
var videoTranscodingConditions = profile.CodecProfiles
.Where(i => i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec))
.Take(1)
.SelectMany(i => i.Conditions);
ApplyTranscodingConditions(playlistItem, videoTranscodingConditions);
var audioTranscodingConditions = profile.CodecProfiles
.Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(transcodingProfile.AudioCodec))
.Take(1)
.SelectMany(i => i.Conditions);
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
}
return playlistItem;
}
private void ApplyTranscodingConditions(PlaylistItem item, IEnumerable<ProfileCondition> conditions)
{
foreach (var condition in conditions
.Where(i => !string.IsNullOrEmpty(i.Value)))
{
var value = condition.Value;
switch (condition.Property)
{
case ProfileConditionValue.AudioBitrate:
{
int num;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.AudioBitrate = num;
}
break;
}
case ProfileConditionValue.AudioChannels:
{
int num;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.MaxAudioChannels = num;
}
break;
}
case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.VideoBitDepth:
case ProfileConditionValue.VideoProfile:
{
// Not supported yet
break;
}
case ProfileConditionValue.Height:
{
int num;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.MaxHeight = num;
}
break;
}
case ProfileConditionValue.VideoBitrate:
{
int num;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.VideoBitrate = num;
}
break;
}
case ProfileConditionValue.VideoFramerate:
{
int num;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.MaxFramerate = num;
}
break;
}
case ProfileConditionValue.VideoLevel:
{
int num;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.VideoLevel = num;
}
break;
}
case ProfileConditionValue.Width:
{
int num;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.MaxWidth = num;
}
break;
}
default:
throw new ArgumentException("Unrecognized ProfileConditionValue");
}
}
}
private bool IsSupported(DirectPlayProfile profile, Photo item)
{
var mediaPath = item.Path;
@ -260,223 +61,5 @@ namespace MediaBrowser.Dlna.PlayTo
return true;
}
private bool IsSupported(DirectPlayProfile profile, Audio item, MediaStream audioStream)
{
var mediaPath = item.Path;
if (profile.Container.Length > 0)
{
// Check container type
var mediaContainer = Path.GetExtension(mediaPath);
if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
return true;
}
private bool IsSupported(DirectPlayProfile profile, Video item, MediaStream videoStream, MediaStream audioStream)
{
if (item.VideoType != VideoType.VideoFile)
{
return false;
}
var mediaPath = item.Path;
if (profile.Container.Length > 0)
{
// Check container type
var mediaContainer = Path.GetExtension(mediaPath);
if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
// Check video codec
var videoCodecs = profile.GetVideoCodecs();
if (videoCodecs.Count > 0)
{
var videoCodec = videoStream == null ? null : videoStream.Codec;
if (string.IsNullOrWhiteSpace(videoCodec) || !videoCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
var audioCodecs = profile.GetAudioCodecs();
if (audioCodecs.Count > 0)
{
// Check audio codecs
var audioCodec = audioStream == null ? null : audioStream.Codec;
if (string.IsNullOrWhiteSpace(audioCodec) || !audioCodecs.Contains(audioCodec, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
return true;
}
private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Audio item)
{
// Placeholder for future conditions
return true;
}
private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Photo item)
{
// Placeholder for future conditions
return true;
}
private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Video item)
{
// Placeholder for future conditions
return true;
}
private bool AreConditionsSatisfied(IEnumerable<ProfileCondition> conditions, string mediaPath, MediaStream videoStream, MediaStream audioStream)
{
return conditions.All(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream));
}
/// <summary>
/// Determines whether [is condition satisfied] [the specified condition].
/// </summary>
/// <param name="condition">The condition.</param>
/// <param name="mediaPath">The media path.</param>
/// <param name="videoStream">The video stream.</param>
/// <param name="audioStream">The audio stream.</param>
/// <returns><c>true</c> if [is condition satisfied] [the specified condition]; otherwise, <c>false</c>.</returns>
/// <exception cref="System.InvalidOperationException">Unexpected ProfileConditionType</exception>
private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
{
if (condition.Property == ProfileConditionValue.Has64BitOffsets)
{
// TODO: Determine how to evaluate this
}
if (condition.Property == ProfileConditionValue.VideoProfile)
{
var profile = videoStream == null ? null : videoStream.Profile;
if (!string.IsNullOrWhiteSpace(profile))
{
switch (condition.Condition)
{
case ProfileConditionType.Equals:
return string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
case ProfileConditionType.NotEquals:
return !string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
default:
throw new InvalidOperationException("Unexpected ProfileConditionType");
}
}
}
else if (condition.Property == ProfileConditionValue.AudioProfile)
{
var profile = audioStream == null ? null : audioStream.Profile;
if (!string.IsNullOrWhiteSpace(profile))
{
switch (condition.Condition)
{
case ProfileConditionType.Equals:
return string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
case ProfileConditionType.NotEquals:
return !string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
default:
throw new InvalidOperationException("Unexpected ProfileConditionType");
}
}
}
else
{
var actualValue = GetConditionValue(condition, mediaPath, videoStream, audioStream);
if (actualValue.HasValue)
{
long expected;
if (long.TryParse(condition.Value, NumberStyles.Any, _usCulture, out expected))
{
switch (condition.Condition)
{
case ProfileConditionType.Equals:
return actualValue.Value == expected;
case ProfileConditionType.GreaterThanEqual:
return actualValue.Value >= expected;
case ProfileConditionType.LessThanEqual:
return actualValue.Value <= expected;
case ProfileConditionType.NotEquals:
return actualValue.Value != expected;
default:
throw new InvalidOperationException("Unexpected ProfileConditionType");
}
}
}
}
// Value doesn't exist in metadata. Fail it if required.
return !condition.IsRequired;
}
/// <summary>
/// Gets the condition value.
/// </summary>
/// <param name="condition">The condition.</param>
/// <param name="mediaPath">The media path.</param>
/// <param name="videoStream">The video stream.</param>
/// <param name="audioStream">The audio stream.</param>
/// <returns>System.Nullable{System.Int64}.</returns>
/// <exception cref="System.InvalidOperationException">Unexpected Property</exception>
private long? GetConditionValue(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
{
switch (condition.Property)
{
case ProfileConditionValue.AudioBitrate:
return audioStream == null ? null : audioStream.BitRate;
case ProfileConditionValue.AudioChannels:
return audioStream == null ? null : audioStream.Channels;
case ProfileConditionValue.VideoBitrate:
return videoStream == null ? null : videoStream.BitRate;
case ProfileConditionValue.VideoFramerate:
return videoStream == null ? null : (ConvertToLong(videoStream.AverageFrameRate ?? videoStream.RealFrameRate));
case ProfileConditionValue.Height:
return videoStream == null ? null : videoStream.Height;
case ProfileConditionValue.Width:
return videoStream == null ? null : videoStream.Width;
case ProfileConditionValue.VideoLevel:
return videoStream == null ? null : ConvertToLong(videoStream.Level);
default:
throw new InvalidOperationException("Unexpected Property");
}
}
/// <summary>
/// Converts to long.
/// </summary>
/// <param name="val">The value.</param>
/// <returns>System.Nullable{System.Int64}.</returns>
private long? ConvertToLong(float? val)
{
return val.HasValue ? Convert.ToInt64(val.Value) : (long?)null;
}
/// <summary>
/// Converts to long.
/// </summary>
/// <param name="val">The value.</param>
/// <returns>System.Nullable{System.Int64}.</returns>
private long? ConvertToLong(double? val)
{
return val.HasValue ? Convert.ToInt64(val.Value) : (long?)null;
}
}
}

View File

@ -1,69 +0,0 @@
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
using System.Globalization;
namespace MediaBrowser.Dlna.PlayTo
{
class StreamHelper
{
/// <summary>
/// Gets the audio URL.
/// </summary>
/// <param name="deviceProperties">The device properties.</param>
/// <param name="item">The item.</param>
/// <param name="streams">The streams.</param>
/// <param name="serverAddress">The server address.</param>
/// <returns>System.String.</returns>
internal static string GetAudioUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
{
var dlnaCommand = BuildDlnaUrl(deviceProperties, item);
return string.Format("{0}/audio/{1}/stream{2}?{3}", serverAddress, item.ItemId, "." + item.Container.TrimStart('.'), dlnaCommand);
}
/// <summary>
/// Gets the video URL.
/// </summary>
/// <param name="deviceProperties">The device properties.</param>
/// <param name="item">The item.</param>
/// <param name="streams">The streams.</param>
/// <param name="serverAddress">The server address.</param>
/// <returns>The url to send to the device</returns>
internal static string GetVideoUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
{
var dlnaCommand = BuildDlnaUrl(deviceProperties, item);
return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand);
}
/// <summary>
/// Builds the dlna URL.
/// </summary>
private static string BuildDlnaUrl(DeviceInfo deviceProperties, PlaylistItem item)
{
var usCulture = new CultureInfo("en-US");
var list = new List<string>
{
item.DeviceProfileId ?? string.Empty,
deviceProperties.UUID ?? string.Empty,
item.MediaSourceId ?? string.Empty,
(!item.Transcode).ToString().ToLower(),
item.VideoCodec ?? string.Empty,
item.AudioCodec ?? string.Empty,
item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(usCulture) : string.Empty,
item.SubtitleStreamIndex.HasValue ? item.SubtitleStreamIndex.Value.ToString(usCulture) : string.Empty,
item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(usCulture) : string.Empty,
item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(usCulture) : string.Empty,
item.MaxAudioChannels.HasValue ? item.MaxAudioChannels.Value.ToString(usCulture) : string.Empty,
item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(usCulture) : string.Empty,
item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(usCulture) : string.Empty,
item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(usCulture) : string.Empty,
item.StartPositionTicks.ToString(usCulture),
item.VideoLevel.HasValue ? item.VideoLevel.Value.ToString(usCulture) : string.Empty
};
return string.Format("Params={0}", string.Join(";", list.ToArray()));
}
}
}

View File

@ -1,15 +1,23 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;
namespace MediaBrowser.Dlna.Server
@ -19,7 +27,12 @@ namespace MediaBrowser.Dlna.Server
private readonly ILogger _logger;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private DeviceProfile _profile;
private readonly DeviceProfile _profile;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IUserDataManager _userDataManager;
private readonly string _serverAddress;
private const string NS_DC = "http://purl.org/dc/elements/1.1/";
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
@ -31,11 +44,16 @@ namespace MediaBrowser.Dlna.Server
private int systemID = 0;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public ControlHandler(ILogger logger, IUserManager userManager, ILibraryManager libraryManager)
public ControlHandler(ILogger logger, IUserManager userManager, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager)
{
_logger = logger;
_userManager = userManager;
_libraryManager = libraryManager;
_profile = profile;
_serverAddress = serverAddress;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
}
public ControlResponse ProcessControlRequest(ControlRequest request)
@ -46,6 +64,8 @@ namespace MediaBrowser.Dlna.Server
}
catch (Exception ex)
{
_logger.ErrorException("Error processing control request", ex);
return GetErrorResponse(ex);
}
}
@ -120,7 +140,8 @@ namespace MediaBrowser.Dlna.Server
var controlResponse = new ControlResponse
{
Xml = env.OuterXml
Xml = env.OuterXml,
IsSuccessful = true
};
controlResponse.Headers.Add("EXT", string.Empty);
@ -153,7 +174,8 @@ namespace MediaBrowser.Dlna.Server
return new ControlResponse
{
Xml = env.OuterXml
Xml = env.OuterXml,
IsSuccessful = false
};
}
@ -161,7 +183,16 @@ namespace MediaBrowser.Dlna.Server
{
var id = sparams["ObjectID"];
var newbookmark = long.Parse(sparams["PosSecond"]);
var item = _libraryManager.GetItemById(new Guid(id));
var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
_userDataManager.SaveUserData(user.Id, item, userdata, UserDataSaveReason.TogglePlayed,
CancellationToken.None);
return new Headers();
}
@ -209,13 +240,13 @@ namespace MediaBrowser.Dlna.Server
var id = sparams["ObjectID"];
var flag = sparams["BrowseFlag"];
int requested = 20;
var provided = 0;
int requested = 0;
int start = 0;
if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
{
requested = 20;
requested = 0;
}
if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0)
{
@ -232,11 +263,11 @@ namespace MediaBrowser.Dlna.Server
didl.SetAttribute("xmlns:sec", NS_SEC);
result.AppendChild(didl);
var folder = string.IsNullOrWhiteSpace(id)
var folder = string.IsNullOrWhiteSpace(id) || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
? user.RootFolder
: (Folder)_libraryManager.GetItemById(new Guid(id));
var children = folder.GetChildren(user, true).ToList();
var children = GetChildrenSorted(folder, user).ToList();
if (string.Equals(flag, "BrowseMetadata"))
{
@ -245,40 +276,29 @@ namespace MediaBrowser.Dlna.Server
}
else
{
foreach (var i in children.OfType<Folder>())
if (start > 0)
{
if (start > 0)
{
start--;
continue;
}
var childCount = i.GetChildren(user, true).Count();
Browse_AddFolder(result, i, childCount);
if (++provided == requested)
{
break;
}
children = children.Skip(start).ToList();
}
if (requested > 0)
{
children = children.Take(requested).ToList();
}
if (provided != requested)
provided = children.Count;
foreach (var i in children)
{
foreach (var i in children.Where(i => !i.IsFolder))
if (i.IsFolder)
{
if (start > 0)
{
start--;
continue;
}
var f = (Folder)i;
var childCount = GetChildrenSorted(f, user).Count();
Browse_AddFolder(result, f, childCount);
}
else
{
Browse_AddItem(result, i, user);
if (++provided == requested)
{
break;
}
}
}
}
@ -294,10 +314,23 @@ namespace MediaBrowser.Dlna.Server
};
}
private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user)
{
var children = folder.GetChildren(user, true).Where(i => i.LocationType != LocationType.Virtual);
if (folder is Series || folder is Season || folder is BoxSet)
{
return children;
}
return _libraryManager.Sort(children, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
}
private void Browse_AddFolder(XmlDocument result, Folder f, int childCount)
{
var container = result.CreateElement(string.Empty, "container", NS_DIDL);
container.SetAttribute("restricted", "0");
container.SetAttribute("searchable", "1");
container.SetAttribute("childCount", childCount.ToString(_usCulture));
container.SetAttribute("id", f.Id.ToString("N"));
@ -311,20 +344,28 @@ namespace MediaBrowser.Dlna.Server
container.SetAttribute("parentID", parent.Id.ToString("N"));
}
var title = result.CreateElement("dc", "title", NS_DC);
title.InnerText = f.Name;
container.AppendChild(title);
AddCommonFields(f, container);
var date = result.CreateElement("dc", "date", NS_DC);
date.InnerText = f.DateModified.ToString("o");
container.AppendChild(date);
AddCover(f, container);
var objectClass = result.CreateElement("upnp", "class", NS_UPNP);
objectClass.InnerText = "object.container.storageFolder";
container.AppendChild(objectClass);
container.AppendChild(CreateObjectClass(result, f));
result.DocumentElement.AppendChild(container);
}
private void AddValue(XmlElement elem, string prefix, string name, string value, string namespaceUri)
{
try
{
var date = elem.OwnerDocument.CreateElement(prefix, name, namespaceUri);
date.InnerText = value;
elem.AppendChild(date);
}
catch (XmlException)
{
//_logger.Error("Error adding xml value: " + value);
}
}
private void Browse_AddItem(XmlDocument result, BaseItem item, User user)
{
var element = result.CreateElement(string.Empty, "item", NS_DIDL);
@ -342,52 +383,217 @@ namespace MediaBrowser.Dlna.Server
AddGeneralProperties(item, element);
AddActors(item, element);
// refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
var title = result.CreateElement("dc", "title", NS_DC);
title.InnerText = item.Name;
element.AppendChild(title);
var audio = item as Audio;
if (audio != null)
{
AddAudioResource(element, audio);
}
var res = result.CreateElement(string.Empty, "res", NS_DIDL);
//res.InnerText = String.Format(
// "http://{0}:{1}{2}file/{3}",
// request.LocalEndPoint.Address,
// request.LocalEndPoint.Port,
// prefix,
// resource.Id
// );
//if (props.TryGetValue("SizeRaw", out prop))
//{
// res.SetAttribute("size", prop);
//}
//if (props.TryGetValue("Resolution", out prop))
//{
// res.SetAttribute("resolution", prop);
//}
//if (props.TryGetValue("Duration", out prop))
//{
// res.SetAttribute("duration", prop);
//}
//res.SetAttribute("protocolInfo", String.Format(
// "http-get:*:{1}:{0};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
// resource.PN, DlnaMaps.Mime[resource.Type], DlnaMaps.DefaultStreaming
// ));
element.AppendChild(res);
var video = item as Video;
if (video != null)
{
AddVideoResource(element, video);
}
AddCover(item, element);
result.DocumentElement.AppendChild(element);
}
private string GetDeviceId()
{
return "erer";
}
private void AddVideoResource(XmlElement container, Video video)
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
var sources = _dtoService.GetMediaSources(video);
int? maxBitrateSetting = null;
var streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
{
ItemId = video.Id.ToString("N"),
MediaSources = sources,
Profile = _profile,
DeviceId = GetDeviceId(),
MaxBitrate = maxBitrateSetting
});
var url = streamInfo.ToDlnaUrl(_serverAddress);
res.InnerText = url;
var mediaSource = sources.First(i => string.Equals(i.Id, streamInfo.MediaSourceId));
if (mediaSource.RunTimeTicks.HasValue)
{
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (streamInfo.IsDirectStream && mediaSource.Size.HasValue)
{
res.SetAttribute("size", mediaSource.Size.Value.ToString(_usCulture));
}
var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video && !string.Equals(i.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase));
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
var targetAudioBitrate = streamInfo.AudioBitrate ?? (audioStream == null ? null : audioStream.BitRate);
var targetSampleRate = audioStream == null ? null : audioStream.SampleRate;
var targetChannels = streamInfo.MaxAudioChannels ?? (audioStream == null ? null : audioStream.Channels);
var targetWidth = streamInfo.MaxWidth ?? (videoStream == null ? null : videoStream.Width);
var targetHeight = streamInfo.MaxHeight ?? (videoStream == null ? null : videoStream.Height);
var targetVideoCodec = streamInfo.IsDirectStream
? (videoStream == null ? null : videoStream.Codec)
: streamInfo.VideoCodec;
var targetAudioCodec = streamInfo.IsDirectStream
? (audioStream == null ? null : audioStream.Codec)
: streamInfo.AudioCodec;
var targetBitrate = maxBitrateSetting ?? mediaSource.Bitrate;
if (targetChannels.HasValue)
{
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (targetSampleRate.HasValue)
{
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (targetAudioBitrate.HasValue)
{
res.SetAttribute("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
}
var formatProfile = new MediaFormatProfileResolver().ResolveVideoFormat(streamInfo.Container,
targetVideoCodec,
targetAudioCodec,
targetWidth,
targetHeight,
targetBitrate,
TransportStreamTimestamp.NONE);
var filename = url.Substring(0, url.IndexOf('?'));
var orgOpValue = DlnaMaps.GetOrgOpValue(mediaSource.RunTimeTicks.HasValue, streamInfo.IsDirectStream, streamInfo.TranscodeSeekInfo);
var orgCi = streamInfo.IsDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:DLNA.ORG_PN={1};DLNA.ORG_OP={2};DLNA.ORG_CI={3};DLNA.ORG_FLAGS={4}",
MimeTypes.GetMimeType(filename),
formatProfile,
orgOpValue,
orgCi,
DlnaMaps.DefaultStreaming
));
container.AppendChild(res);
}
private void AddAudioResource(XmlElement container, Audio audio)
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
var sources = _dtoService.GetMediaSources(audio);
var streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions
{
ItemId = audio.Id.ToString("N"),
MediaSources = sources,
Profile = _profile,
DeviceId = GetDeviceId()
});
var url = streamInfo.ToDlnaUrl(_serverAddress);
res.InnerText = url;
var mediaSource = sources.First(i => string.Equals(i.Id, streamInfo.MediaSourceId));
if (mediaSource.RunTimeTicks.HasValue)
{
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (streamInfo.IsDirectStream && mediaSource.Size.HasValue)
{
res.SetAttribute("size", mediaSource.Size.Value.ToString(_usCulture));
}
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
var targetAudioBitrate = streamInfo.AudioBitrate ?? (audioStream == null ? null : audioStream.BitRate);
var targetSampleRate = audioStream == null ? null : audioStream.SampleRate;
var targetChannels = streamInfo.MaxAudioChannels ?? (audioStream == null ? null : audioStream.Channels);
if (targetChannels.HasValue)
{
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (targetSampleRate.HasValue)
{
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (targetAudioBitrate.HasValue)
{
res.SetAttribute("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
}
var formatProfile = new MediaFormatProfileResolver().ResolveAudioFormat(streamInfo.Container, targetAudioBitrate, targetSampleRate, targetChannels);
var filename = url.Substring(0, url.IndexOf('?'));
var orgOpValue = DlnaMaps.GetOrgOpValue(mediaSource.RunTimeTicks.HasValue, streamInfo.IsDirectStream, streamInfo.TranscodeSeekInfo);
var orgCi = streamInfo.IsDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:DLNA.ORG_PN={1};DLNA.ORG_OP={2};DLNA.ORG_CI={3};DLNA.ORG_FLAGS={4}",
MimeTypes.GetMimeType(filename),
formatProfile,
orgOpValue,
orgCi,
DlnaMaps.DefaultStreaming
));
container.AppendChild(res);
}
private XmlElement CreateObjectClass(XmlDocument result, BaseItem item)
{
var objectClass = result.CreateElement("upnp", "class", NS_UPNP);
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
if (item.IsFolder)
{
string classType = null;
if (!_profile.RequiresPlainFolders)
{
if (item is MusicAlbum)
{
classType = "object.container.musicAlbum";
}
if (item is MusicArtist)
{
classType = "object.container.musicArtist";
}
}
objectClass.InnerText = classType ?? "object.container.storageFolder";
}
else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
objectClass.InnerText = "object.item.audioItem.musicTrack";
}
@ -397,7 +603,14 @@ namespace MediaBrowser.Dlna.Server
}
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
objectClass.InnerText = "object.item.videoItem.movie";
if (!_profile.RequiresPlainVideoItems && item is Movie)
{
objectClass.InnerText = "object.item.videoItem.movie";
}
else
{
objectClass.InnerText = "object.item.videoItem";
}
}
else
{
@ -407,152 +620,259 @@ namespace MediaBrowser.Dlna.Server
return objectClass;
}
private void AddActors(BaseItem item, XmlElement element)
private void AddPeople(BaseItem item, XmlElement element)
{
foreach (var actor in item.People)
{
var e = element.OwnerDocument.CreateElement("upnp", "actor", NS_UPNP);
e.InnerText = actor.Name;
element.AppendChild(e);
AddValue(element, "upnp", (actor.Type ?? PersonType.Actor).ToLower(), actor.Name, NS_UPNP);
}
}
private void AddBookmarkInfo(BaseItem item, User user, XmlElement element)
{
//var bookmark = bookmarkable.Bookmark;
//if (bookmark.HasValue)
//{
// var dcmInfo = item.OwnerDocument.CreateElement("sec", "dcmInfo", NS_SEC);
// dcmInfo.InnerText = string.Format("BM={0}", bookmark.Value);
// item.AppendChild(dcmInfo);
//}
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());
if (userdata.PlaybackPositionTicks > 0)
{
var dcmInfo = element.OwnerDocument.CreateElement("sec", "dcmInfo", NS_SEC);
dcmInfo.InnerText = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds).ToString(_usCulture));
element.AppendChild(dcmInfo);
}
}
private void AddGeneralProperties(BaseItem item, XmlElement element)
/// <summary>
/// Adds fields used by both items and folders
/// </summary>
/// <param name="item"></param>
/// <param name="element"></param>
private void AddCommonFields(BaseItem item, XmlElement element)
{
//var prop = string.Empty;
//if (props.TryGetValue("DateO", out prop))
//{
// var e = item.OwnerDocument.CreateElement("dc", "date", NS_DC);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Genre", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "genre", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
if (item.PremiereDate.HasValue)
{
AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
}
if (item.Genres.Count > 0)
{
AddValue(element, "upnp", "genre", item.Genres[0], NS_UPNP);
}
if (item.Studios.Count > 0)
{
AddValue(element, "upnp", "publisher", item.Studios[0], NS_UPNP);
}
AddValue(element, "dc", "title", item.Name, NS_DC);
if (!string.IsNullOrWhiteSpace(item.Overview))
{
var e = element.OwnerDocument.CreateElement("dc", "description", NS_DC);
e.InnerText = item.Overview;
element.AppendChild(e);
AddValue(element, "dc", "description", item.Overview, NS_DC);
}
//if (props.TryGetValue("Artist", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "artist", NS_UPNP);
// e.SetAttribute("role", "AlbumArtist");
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Performer", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "artist", NS_UPNP);
// e.SetAttribute("role", "Performer");
// e.InnerText = prop;
// item.AppendChild(e);
// e = item.OwnerDocument.CreateElement("dc", "creator", NS_DC);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Album", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "album", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Track", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "originalTrackNumber", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Creator", out prop))
//{
// var e = item.OwnerDocument.CreateElement("dc", "creator", NS_DC);
// e.InnerText = prop;
// item.AppendChild(e);
//}
if (!string.IsNullOrEmpty(item.OfficialRating))
{
AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
}
//if (props.TryGetValue("Director", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "director", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
AddPeople(item, element);
}
private void AddGeneralProperties(BaseItem item, XmlElement element)
{
AddCommonFields(item, element);
var audio = item as Audio;
if (audio != null)
{
if (audio.Artists.Count > 0)
{
AddValue(element, "upnp", "artist", audio.Artists[0], NS_UPNP);
}
if (!string.IsNullOrEmpty(audio.Album))
{
AddValue(element, "upnp", "album", audio.Album, NS_UPNP);
}
if (!string.IsNullOrEmpty(audio.AlbumArtist))
{
AddValue(element, "upnp", "albumArtist", audio.AlbumArtist, NS_UPNP);
}
}
var album = item as MusicAlbum;
if (album != null)
{
if (!string.IsNullOrEmpty(album.AlbumArtist))
{
AddValue(element, "upnp", "artist", album.AlbumArtist, NS_UPNP);
AddValue(element, "upnp", "albumArtist", album.AlbumArtist, NS_UPNP);
}
}
var musicVideo = item as MusicVideo;
if (musicVideo != null)
{
if (!string.IsNullOrEmpty(musicVideo.Artist))
{
AddValue(element, "upnp", "artist", musicVideo.Artist, NS_UPNP);
}
if (!string.IsNullOrEmpty(musicVideo.Album))
{
AddValue(element, "upnp", "album", musicVideo.Album, NS_UPNP);
}
}
if (item.IndexNumber.HasValue)
{
AddValue(element, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
}
}
private void AddCover(BaseItem item, XmlElement element)
{
//var result = item.OwnerDocument;
//var cover = resource as IMediaCover;
//if (cover == null)
//{
// return;
//}
//try
//{
// var c = cover.Cover;
// var curl = String.Format(
// "http://{0}:{1}{2}cover/{3}",
// request.LocalEndPoint.Address,
// request.LocalEndPoint.Port,
// prefix,
// resource.Id
// );
// var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
// var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
// profile.InnerText = "JPEG_TN";
// icon.SetAttributeNode(profile);
// icon.InnerText = curl;
// item.AppendChild(icon);
// icon = result.CreateElement("upnp", "icon", NS_UPNP);
// profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
// profile.InnerText = "JPEG_TN";
// icon.SetAttributeNode(profile);
// icon.InnerText = curl;
// item.AppendChild(icon);
var imageInfo = GetImageInfo(item);
// var res = result.CreateElement(string.Empty, "res", NS_DIDL);
// res.InnerText = curl;
if (imageInfo == null)
{
return;
}
// res.SetAttribute("protocolInfo", string.Format(
// "http-get:*:{1}:{0};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
// c.PN, DlnaMaps.Mime[c.Type], DlnaMaps.DefaultStreaming
// ));
// var width = c.MetaWidth;
// var height = c.MetaHeight;
// if (width.HasValue && height.HasValue)
// {
// res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value));
// }
// else
// {
// res.SetAttribute("resolution", "200x200");
// }
// res.SetAttribute("protocolInfo", string.Format(
// "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=01;DLNA.ORG_CI=1;DLNA.ORG_FLAGS={0}",
// DlnaMaps.DefaultInteractive
// ));
// item.AppendChild(res);
//}
//catch (Exception)
//{
// return;
//}
var result = element.OwnerDocument;
var curl = GetImageUrl(imageInfo);
var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = "JPEG_TN";
icon.SetAttributeNode(profile);
icon.InnerText = curl;
element.AppendChild(icon);
icon = result.CreateElement("upnp", "icon", NS_UPNP);
profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = "JPEG_TN";
icon.SetAttributeNode(profile);
icon.InnerText = curl;
element.AppendChild(icon);
if (!_profile.EnableAlbumArtInDidl)
{
return;
}
var res = result.CreateElement(string.Empty, "res", NS_DIDL);
res.InnerText = curl;
int? width = imageInfo.Width;
int? height = imageInfo.Height;
var mediaProfile = new MediaFormatProfileResolver().ResolveImageFormat("jpg", width, height);
res.SetAttribute("protocolInfo", string.Format(
"http-get:*:{1}DLNA.ORG_PN=:{0};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
mediaProfile, "image/jpeg", DlnaMaps.DefaultStreaming
));
if (width.HasValue && height.HasValue)
{
res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value));
}
else
{
// TODO: Devices need to see something here?
res.SetAttribute("resolution", "200x200");
}
element.AppendChild(res);
}
private ImageDownloadInfo GetImageInfo(BaseItem item)
{
if (item.HasImage(ImageType.Primary))
{
return GetImageInfo(item, ImageType.Primary);
}
if (item.HasImage(ImageType.Thumb))
{
return GetImageInfo(item, ImageType.Thumb);
}
if (item is Audio || item is Episode)
{
item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
if (item != null)
{
return GetImageInfo(item, ImageType.Primary);
}
}
return null;
}
private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
{
var imageInfo = item.GetImageInfo(type, 0);
string tag = null;
try
{
var guid = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
tag = guid.HasValue ? guid.Value.ToString("N") : null;
}
catch
{
}
int? width = null;
int? height = null;
try
{
var size = _imageProcessor.GetImageSize(imageInfo.Path, imageInfo.DateModified);
width = Convert.ToInt32(size.Width);
height = Convert.ToInt32(size.Height);
}
catch
{
}
return new ImageDownloadInfo
{
ItemId = item.Id.ToString("N"),
Type = ImageType.Primary,
ImageTag = tag,
Width = width,
Height = height
};
}
class ImageDownloadInfo
{
internal string ItemId;
internal string ImageTag;
internal ImageType Type;
internal int? Width;
internal int? Height;
}
private string GetImageUrl(ImageDownloadInfo info)
{
return string.Format("{0}/Items/{1}/Images/{2}?tag={3}&format=jpg",
_serverAddress,
info.ItemId,
info.Type,
info.ImageTag);
}
}
}

View File

@ -176,7 +176,7 @@ namespace MediaBrowser.Dlna.Server
ServiceType = "urn:schemas-upnp-org:service:ContentDirectory:1",
ServiceId = "urn:upnp-org:serviceId:ContentDirectory",
ScpdUrl = "/mediabrowser/dlna/contentdirectory.xml",
ControlUrl = "/mediabrowser/dlna/control"
ControlUrl = "/mediabrowser/dlna/" + _serverUdn + "/control"
});
return list;

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common;
using System.Linq;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@ -18,8 +19,12 @@ namespace MediaBrowser.Dlna.Server
private readonly IApplicationHost _appHost;
private readonly INetworkManager _network;
public static DlnaServerEntryPoint Instance;
public DlnaServerEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost, INetworkManager network)
{
Instance = this;
_config = config;
_appHost = appHost;
_network = network;
@ -86,6 +91,11 @@ namespace MediaBrowser.Dlna.Server
}
}
public UpnpDevice GetServerUpnpDevice(string uuid)
{
return _ssdpHandler.Devices.FirstOrDefault(i => string.Equals(uuid, i.Uuid.ToString("N"), StringComparison.OrdinalIgnoreCase));
}
private void DisposeServer()
{
lock (_syncLock)

View File

@ -44,7 +44,7 @@ namespace MediaBrowser.Dlna.Server
Start();
}
private IEnumerable<UpnpDevice> Devices
public IEnumerable<UpnpDevice> Devices
{
get
{

View File

@ -224,7 +224,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
case EncodingQuality.HighSpeed:
return 2;
case EncodingQuality.HighQuality:
return isWebm ? Math.Max((int)((Environment.ProcessorCount -1) / 2) , 2) : 0;
return 2;
case EncodingQuality.MaxQuality:
return isWebm ? Math.Max(Environment.ProcessorCount - 1, 2) : 0;
default:

View File

@ -768,7 +768,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=40\" -f image2 \"{1}\"", inputPath, "-", vf) :
var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) :
string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf);
var probeSize = GetProbeSizeArgument(type);

View File

@ -119,6 +119,15 @@
<Compile Include="..\MediaBrowser.Model\Dlna\DirectPlayProfile.cs">
<Link>Dlna\DirectPlayProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
<Link>Dlna\DlnaMaps.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
<Link>Dlna\MediaFormatProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfileResolver.cs">
<Link>Dlna\MediaFormatProfileResolver.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
<Link>Dlna\ResponseProfile.cs</Link>
</Compile>

View File

@ -106,6 +106,15 @@
<Compile Include="..\MediaBrowser.Model\Dlna\DirectPlayProfile.cs">
<Link>Dlna\DirectPlayProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
<Link>Dlna\DlnaMaps.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
<Link>Dlna\MediaFormatProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfileResolver.cs">
<Link>Dlna\MediaFormatProfileResolver.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
<Link>Dlna\ResponseProfile.cs</Link>
</Compile>

View File

@ -0,0 +1,62 @@
using System;
namespace MediaBrowser.Model.Dlna
{
public class DlnaMaps
{
public static readonly string DefaultStreaming =
FlagsToString(DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.ConnectionStall |
DlnaFlags.ByteBasedSeek |
DlnaFlags.DlnaV15);
public static readonly string DefaultInteractive =
FlagsToString(DlnaFlags.InteractiveTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.ConnectionStall |
DlnaFlags.ByteBasedSeek |
DlnaFlags.DlnaV15);
public static string FlagsToString(DlnaFlags flags)
{
return string.Format("{0:X8}{1:D24}", (ulong)flags, 0);
}
public static string GetOrgOpValue(bool hasKnownRuntime, bool isDirectStream, TranscodeSeekInfo profileTranscodeSeekInfo)
{
if (hasKnownRuntime)
{
var orgOp = string.Empty;
// Time-based seeking currently only possible when transcoding
orgOp += isDirectStream ? "0" : "1";
// Byte-based seeking only possible when not transcoding
orgOp += isDirectStream || profileTranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0";
return orgOp;
}
// No seeking is available if we don't know the content runtime
return "00";
}
}
[Flags]
public enum DlnaFlags : ulong
{
BackgroundTransferMode = (1 << 22),
ByteBasedSeek = (1 << 29),
ConnectionStall = (1 << 21),
DlnaV15 = (1 << 20),
InteractiveTransferMode = (1 << 23),
PlayContainer = (1 << 28),
RtspPause = (1 << 25),
S0Increase = (1 << 27),
SenderPaced = (1L << 31),
SnIncrease = (1 << 26),
StreamingTransferMode = (1 << 24),
TimeBasedSeek = (1 << 30)
}
}

View File

@ -0,0 +1,122 @@

using System;
namespace MediaBrowser.Model.Dlna
{
public enum MediaFormatProfile
{
MP3,
WMA_BASE,
WMA_FULL,
LPCM16_44_MONO,
LPCM16_44_STEREO,
LPCM16_48_MONO,
LPCM16_48_STEREO,
AAC_ISO,
AAC_ISO_320,
AAC_ADTS,
AAC_ADTS_320,
FLAC,
OGG,
JPEG_SM,
JPEG_MED,
JPEG_LRG,
JPEG_TN,
PNG_LRG,
PNG_TN,
GIF_LRG,
RAW,
MPEG1,
MPEG_PS_PAL,
MPEG_PS_NTSC,
MPEG_TS_SD_EU,
MPEG_TS_SD_EU_ISO,
MPEG_TS_SD_EU_T,
MPEG_TS_SD_NA,
MPEG_TS_SD_NA_ISO,
MPEG_TS_SD_NA_T,
MPEG_TS_SD_KO,
MPEG_TS_SD_KO_ISO,
MPEG_TS_SD_KO_T,
MPEG_TS_JP_T,
AVI,
MATROSKA,
FLV,
DVR_MS,
WTV,
OGV,
AVC_MP4_MP_SD_AAC_MULT5,
AVC_MP4_MP_SD_MPEG1_L3,
AVC_MP4_MP_SD_AC3,
AVC_MP4_MP_HD_720p_AAC,
AVC_MP4_MP_HD_1080i_AAC,
AVC_MP4_HP_HD_AAC,
AVC_TS_MP_HD_AAC_MULT5,
AVC_TS_MP_HD_AAC_MULT5_T,
AVC_TS_MP_HD_AAC_MULT5_ISO,
AVC_TS_MP_HD_MPEG1_L3,
AVC_TS_MP_HD_MPEG1_L3_T,
AVC_TS_MP_HD_MPEG1_L3_ISO,
AVC_TS_MP_HD_AC3,
AVC_TS_MP_HD_AC3_T,
AVC_TS_MP_HD_AC3_ISO,
AVC_TS_HP_HD_MPEG1_L2_T,
AVC_TS_HP_HD_MPEG1_L2_ISO,
AVC_TS_MP_SD_AAC_MULT5,
AVC_TS_MP_SD_AAC_MULT5_T,
AVC_TS_MP_SD_AAC_MULT5_ISO,
AVC_TS_MP_SD_MPEG1_L3,
AVC_TS_MP_SD_MPEG1_L3_T,
AVC_TS_MP_SD_MPEG1_L3_ISO,
AVC_TS_HP_SD_MPEG1_L2_T,
AVC_TS_HP_SD_MPEG1_L2_ISO,
AVC_TS_MP_SD_AC3,
AVC_TS_MP_SD_AC3_T,
AVC_TS_MP_SD_AC3_ISO,
AVC_TS_HD_DTS_T,
AVC_TS_HD_DTS_ISO,
WMVMED_BASE,
WMVMED_FULL,
WMVMED_PRO,
WMVHIGH_FULL,
WMVHIGH_PRO,
VC1_ASF_AP_L1_WMA,
VC1_ASF_AP_L2_WMA,
VC1_ASF_AP_L3_WMA,
VC1_TS_AP_L1_AC3_ISO,
VC1_TS_AP_L2_AC3_ISO,
VC1_TS_HD_DTS_ISO,
VC1_TS_HD_DTS_T,
MPEG4_P2_MP4_ASP_AAC,
MPEG4_P2_MP4_SP_L6_AAC,
MPEG4_P2_MP4_NDSD,
MPEG4_P2_TS_ASP_AAC,
MPEG4_P2_TS_ASP_AAC_T,
MPEG4_P2_TS_ASP_AAC_ISO,
MPEG4_P2_TS_ASP_MPEG1_L3,
MPEG4_P2_TS_ASP_MPEG1_L3_T,
MPEG4_P2_TS_ASP_MPEG1_L3_ISO,
MPEG4_P2_TS_ASP_MPEG2_L2,
MPEG4_P2_TS_ASP_MPEG2_L2_T,
MPEG4_P2_TS_ASP_MPEG2_L2_ISO,
MPEG4_P2_TS_ASP_AC3,
MPEG4_P2_TS_ASP_AC3_T,
MPEG4_P2_TS_ASP_AC3_ISO,
AVC_TS_HD_50_LPCM_T,
AVC_MP4_LPCM,
MPEG4_P2_3GPP_SP_L0B_AAC,
MPEG4_P2_3GPP_SP_L0B_AMR,
AVC_3GPP_BL_QCIF15_AAC,
MPEG4_H263_3GPP_P0_L10_AMR,
MPEG4_H263_MP4_P0_L10_AAC
}
public enum TransportStreamTimestamp
{
NONE,
ZERO,
VALID
}
}

View File

@ -0,0 +1,342 @@
using System;
namespace MediaBrowser.Model.Dlna
{
public class MediaFormatProfileResolver
{
public MediaFormatProfile ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, int? bitrate, TransportStreamTimestamp timestampType)
{
if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
return ResolveVideoASFFormat(videoCodec, audioCodec, width, height, bitrate);
if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase))
return ResolveVideoMP4Format(videoCodec, audioCodec, width, height, bitrate);
if (string.Equals(container, "avi", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVI;
if (string.Equals(container, "mkv", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MATROSKA;
if (string.Equals(container, "mpeg2ps", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
// MediaFormatProfile.MPEG_PS_PAL, MediaFormatProfile.MPEG_PS_NTSC
return MediaFormatProfile.MPEG_PS_NTSC;
if (string.Equals(container, "mpeg1video", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MPEG1;
if (string.Equals(container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "mpegts", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase))
return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, bitrate, timestampType);
if (string.Equals(container, "flv", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.FLV;
if (string.Equals(container, "wtv", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.WTV;
if (string.Equals(container, "3gp", StringComparison.OrdinalIgnoreCase))
return ResolveVideo3GPFormat(videoCodec, audioCodec, width, height, bitrate);
if (string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.OGV;
throw new ArgumentException("Unsupported container: " + container);
}
private MediaFormatProfile ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, int? bitrate, TransportStreamTimestamp timestampType)
{
// String suffix = "";
// if (isNoTimestamp(timestampType))
// suffix = "_ISO";
// else if (timestampType == TransportStreamTimestamp.VALID) {
// suffix = "_T";
// }
// String resolution = "S";
// if ((width.intValue() > 720) || (height.intValue() > 576)) {
// resolution = "H";
// }
// if (videoCodec == VideoCodec.MPEG2)
// {
// List!(MediaFormatProfile) profiles = Arrays.asList(cast(MediaFormatProfile[])[ MediaFormatProfile.valueOf("MPEG_TS_SD_EU" + suffix), MediaFormatProfile.valueOf("MPEG_TS_SD_NA" + suffix), MediaFormatProfile.valueOf("MPEG_TS_SD_KO" + suffix) ]);
// if ((timestampType == TransportStreamTimestamp.VALID) && (audioCodec == AudioCodec.AAC)) {
// profiles.add(MediaFormatProfile.MPEG_TS_JP_T);
// }
// return profiles;
// }if (videoCodec == VideoCodec.H264)
// {
// if (audioCodec == AudioCodec.LPCM)
// return Collections.singletonList(MediaFormatProfile.AVC_TS_HD_50_LPCM_T);
// if (audioCodec == AudioCodec.DTS) {
// if (isNoTimestamp(timestampType)) {
// return Collections.singletonList(MediaFormatProfile.AVC_TS_HD_DTS_ISO);
// }
// return Collections.singletonList(MediaFormatProfile.AVC_TS_HD_DTS_T);
// }
// if (audioCodec == AudioCodec.MP2) {
// if (isNoTimestamp(timestampType)) {
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_HP_%sD_MPEG1_L2_ISO", cast(Object[])[ resolution ])));
// }
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_HP_%sD_MPEG1_L2_T", cast(Object[])[ resolution ])));
// }
// if (audioCodec == AudioCodec.AAC)
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_AAC_MULT5%s", cast(Object[])[ resolution, suffix ])));
// if (audioCodec == AudioCodec.MP3)
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_MPEG1_L3%s", cast(Object[])[ resolution, suffix ])));
// if ((audioCodec is null) || (audioCodec == AudioCodec.AC3)) {
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_AC3%s", cast(Object[])[ resolution, suffix ])));
// }
// }
// else if (videoCodec == VideoCodec.VC1) {
// if ((audioCodec is null) || (audioCodec == AudioCodec.AC3))
// {
// if ((width.intValue() > 720) || (height.intValue() > 576)) {
// return Collections.singletonList(MediaFormatProfile.VC1_TS_AP_L2_AC3_ISO);
// }
// return Collections.singletonList(MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO);
// }
// if (audioCodec == AudioCodec.DTS) {
// suffix = suffix.equals("_ISO") ? suffix : "_T";
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("VC1_TS_HD_DTS%s", cast(Object[])[ suffix ])));
// }
// } else if ((videoCodec == VideoCodec.MPEG4) || (videoCodec == VideoCodec.MSMPEG4)) {
// if (audioCodec == AudioCodec.AAC)
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_AAC%s", cast(Object[])[ suffix ])));
// if (audioCodec == AudioCodec.MP3)
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_MPEG1_L3%s", cast(Object[])[ suffix ])));
// if (audioCodec == AudioCodec.MP2)
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_MPEG2_L2%s", cast(Object[])[ suffix ])));
// if ((audioCodec is null) || (audioCodec == AudioCodec.AC3)) {
// return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_AC3%s", cast(Object[])[ suffix ])));
// }
// }
throw new ArgumentException("Mpeg video file does not match any supported DLNA profile");
}
private MediaFormatProfile ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height, int? bitrate)
{
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(audioCodec, "lpcm", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_MP4_LPCM;
if (string.IsNullOrEmpty(audioCodec) ||
string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.AVC_MP4_MP_SD_AC3;
}
if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.AVC_MP4_MP_SD_MPEG1_L3;
}
if (width.HasValue && height.HasValue)
{
if ((width.Value <= 720) && (height.Value <= 576))
{
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_MP4_MP_SD_AAC_MULT5;
}
else if ((width.Value <= 1280) && (height.Value <= 720))
{
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_MP4_MP_HD_720p_AAC;
}
else if ((width.Value <= 1920) && (height.Value <= 1080))
{
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.AVC_MP4_MP_HD_1080i_AAC;
}
}
}
}
else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
{
if (width.HasValue && height.HasValue && width.Value <= 720 && height.Value <= 576)
{
if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MPEG4_P2_MP4_ASP_AAC;
if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_P2_MP4_NDSD;
}
}
else if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_P2_MP4_SP_L6_AAC;
}
}
else if (string.Equals(videoCodec, "h263", StringComparison.OrdinalIgnoreCase) && string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_H263_MP4_P0_L10_AAC;
}
throw new ArgumentException("MP4 video file does not match any supported DLNA profile");
}
private MediaFormatProfile ResolveVideo3GPFormat(string videoCodec, string audioCodec, int? width, int? height, int? bitrate)
{
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_3GPP_BL_QCIF15_AAC;
}
else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AAC;
if (string.Equals(audioCodec, "amrnb", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AMR;
}
else if (string.Equals(videoCodec, "h263", StringComparison.OrdinalIgnoreCase) && string.Equals(audioCodec, "amrnb", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_H263_3GPP_P0_L10_AMR;
}
throw new ArgumentException("3GP video file does not match any supported DLNA profile");
}
private MediaFormatProfile ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height, int? bitrate)
{
if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase) &&
(string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "wmapro", StringComparison.OrdinalIgnoreCase)))
{
if (width.HasValue && height.HasValue)
{
if ((width.Value <= 720) && (height.Value <= 576))
{
if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.WMVMED_FULL;
}
return MediaFormatProfile.WMVMED_PRO;
}
}
if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.WMVHIGH_FULL;
}
return MediaFormatProfile.WMVHIGH_PRO;
}
if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase))
{
if (width.HasValue && height.HasValue)
{
if ((width.Value <= 720) && (height.Value <= 576))
return MediaFormatProfile.VC1_ASF_AP_L1_WMA;
if ((width.Value <= 1280) && (height.Value <= 720))
return MediaFormatProfile.VC1_ASF_AP_L2_WMA;
if ((width.Value <= 1920) && (height.Value <= 1080))
return MediaFormatProfile.VC1_ASF_AP_L3_WMA;
}
}
else if (string.Equals(videoCodec, "mpeg2video", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.DVR_MS;
}
throw new ArgumentException("ASF video file does not match any supported DLNA profile");
}
public MediaFormatProfile ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels)
{
if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
return ResolveAudioASFFormat(bitrate, frequency, channels);
if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MP3;
if (string.Equals(container, "lpcm", StringComparison.OrdinalIgnoreCase))
return ResolveAudioLPCMFormat(bitrate, frequency, channels);
if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase))
return ResolveAudioMP4Format(bitrate, frequency, channels);
if (string.Equals(container, "adts", StringComparison.OrdinalIgnoreCase))
return ResolveAudioADTSFormat(bitrate, frequency, channels);
if (string.Equals(container, "flac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.FLAC;
if (string.Equals(container, "oga", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.OGG;
throw new ArgumentException("Unsupported container: " + container);
}
private MediaFormatProfile ResolveAudioASFFormat(int? bitrate, int? frequency, int? channels)
{
if (bitrate.HasValue && bitrate.Value <= 193)
{
return MediaFormatProfile.WMA_BASE;
}
return MediaFormatProfile.WMA_FULL;
}
private MediaFormatProfile ResolveAudioLPCMFormat(int? bitrate, int? frequency, int? channels)
{
if (frequency.HasValue && channels.HasValue)
{
if (frequency.Value == 44100 && channels.Value == 1)
{
return MediaFormatProfile.LPCM16_44_MONO;
}
if (frequency.Value == 44100 && channels.Value == 2)
{
return MediaFormatProfile.LPCM16_44_STEREO;
}
if (frequency.Value == 48000 && channels.Value == 1)
{
return MediaFormatProfile.LPCM16_48_MONO;
}
if (frequency.Value == 48000 && channels.Value == 1)
{
return MediaFormatProfile.LPCM16_48_STEREO;
}
throw new ArgumentException("Unsupported LPCM format of file %s. Only 44100 / 48000 Hz and Mono / Stereo files are allowed.");
}
return MediaFormatProfile.LPCM16_48_STEREO;
}
private MediaFormatProfile ResolveAudioMP4Format(int? bitrate, int? frequency, int? channels)
{
if (bitrate.HasValue && bitrate.Value <= 320)
{
return MediaFormatProfile.AAC_ISO_320;
}
return MediaFormatProfile.AAC_ISO;
}
private MediaFormatProfile ResolveAudioADTSFormat(int? bitrate, int? frequency, int? channels)
{
if (bitrate.HasValue && bitrate.Value <= 320)
{
return MediaFormatProfile.AAC_ADTS_320;
}
return MediaFormatProfile.AAC_ADTS;
}
public MediaFormatProfile ResolveImageFormat(string container, int? width, int? height)
{
if (string.Equals(container, "jpeg", StringComparison.OrdinalIgnoreCase) ||
string.Equals(container, "jpg", StringComparison.OrdinalIgnoreCase))
return ResolveImageJPGFormat(width, height);
if (string.Equals(container, "png", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.PNG_LRG;
if (string.Equals(container, "gif", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.GIF_LRG;
if (string.Equals(container, "raw", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.RAW;
throw new ArgumentException("Unsupported container: " + container);
}
private MediaFormatProfile ResolveImageJPGFormat(int? width, int? height)
{
if (width.HasValue && height.HasValue)
{
if ((width.Value <= 640) && (height.Value <= 480))
return MediaFormatProfile.JPEG_SM;
if ((width.Value <= 1024) && (height.Value <= 768))
{
return MediaFormatProfile.JPEG_MED;
}
}
return MediaFormatProfile.JPEG_LRG;
}
}
}

View File

@ -81,7 +81,8 @@ namespace MediaBrowser.Model.Dlna
{
ItemId = options.ItemId,
MediaType = DlnaProfileType.Audio,
MediaSourceId = item.Id
MediaSourceId = item.Id,
RunTimeTicks = item.RunTimeTicks
};
var audioStream = item.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
@ -114,6 +115,7 @@ namespace MediaBrowser.Model.Dlna
if (transcodingProfile != null)
{
playlistItem.IsDirectStream = false;
playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
playlistItem.Container = transcodingProfile.Container;
playlistItem.AudioCodec = transcodingProfile.AudioCodec;
@ -150,7 +152,8 @@ namespace MediaBrowser.Model.Dlna
{
ItemId = options.ItemId,
MediaType = DlnaProfileType.Video,
MediaSourceId = item.Id
MediaSourceId = item.Id,
RunTimeTicks = item.RunTimeTicks
};
var audioStream = item.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
@ -193,6 +196,7 @@ namespace MediaBrowser.Model.Dlna
{
playlistItem.IsDirectStream = false;
playlistItem.Container = transcodingProfile.Container;
playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault();
playlistItem.VideoCodec = transcodingProfile.VideoCodec;

View File

@ -46,6 +46,10 @@ namespace MediaBrowser.Model.Dlna
public string DeviceProfileId { get; set; }
public string DeviceId { get; set; }
public long? RunTimeTicks { get; set; }
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
public string ToUrl(string baseUrl)
{
return ToDlnaUrl(baseUrl);

View File

@ -10,6 +10,7 @@ namespace MediaBrowser.Model.Dto
public string Path { get; set; }
public string Container { get; set; }
public long? Size { get; set; }
public LocationType LocationType { get; set; }
@ -25,6 +26,14 @@ namespace MediaBrowser.Model.Dto
public List<MediaStream> MediaStreams { get; set; }
public List<string> Formats { get; set; }
public int? Bitrate { get; set; }
public MediaSourceInfo()
{
Formats = new List<string>();
MediaStreams = new List<MediaStream>();
}
}
}

View File

@ -52,6 +52,18 @@ namespace MediaBrowser.Model.Entities
/// </summary>
/// <value>The primary image item identifier.</value>
public string PrimaryImageItemId { get; set; }
/// <summary>
/// Gets or sets the logo image tag.
/// </summary>
/// <value>The logo image tag.</value>
public Guid? LogoImageTag { get; set; }
/// <summary>
/// Gets or sets the logo item identifier.
/// </summary>
/// <value>The logo item identifier.</value>
public string LogoItemId { get; set; }
/// <summary>
/// Gets or sets the thumb image tag.

View File

@ -72,6 +72,9 @@
<Compile Include="Dlna\DeviceProfile.cs" />
<Compile Include="Dlna\DeviceProfileInfo.cs" />
<Compile Include="Dlna\DirectPlayProfile.cs" />
<Compile Include="Dlna\DlnaMaps.cs" />
<Compile Include="Dlna\MediaFormatProfile.cs" />
<Compile Include="Dlna\MediaFormatProfileResolver.cs" />
<Compile Include="Dlna\ResponseProfile.cs" />
<Compile Include="Dlna\StreamBuilder.cs" />
<Compile Include="Dlna\StreamInfo.cs" />

View File

@ -90,6 +90,19 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The volume level.</value>
public int? VolumeLevel { get; set; }
/// <summary>
/// Gets or sets the play method.
/// </summary>
/// <value>The play method.</value>
public PlayMethod PlayMethod { get; set; }
}
public enum PlayMethod
{
Transcode = 0,
DirectStream = 1,
DirectPlay = 2
}
/// <summary>

View File

@ -233,5 +233,11 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The now playing media version identifier.</value>
public string MediaSourceId { get; set; }
/// <summary>
/// Gets or sets the play method.
/// </summary>
/// <value>The play method.</value>
public PlayMethod? PlayMethod { get; set; }
}
}

View File

@ -121,9 +121,27 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
if (data.format.tags != null)
if (data.format != null)
{
FetchDataFromTags(audio, data.format.tags);
audio.FormatName = data.format.format_name;
var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.');
audio.Container = extension;
if (!string.IsNullOrEmpty(data.format.size))
{
audio.Size = long.Parse(data.format.size , _usCulture);
}
else
{
audio.Size = null;
}
if (data.format.tags != null)
{
FetchDataFromTags(audio, data.format.tags);
}
}
return _itemRepo.SaveMediaStreams(audio.Id, mediaStreams, cancellationToken);

View File

@ -18,6 +18,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
namespace MediaBrowser.Providers.MediaInfo
{
@ -159,6 +160,29 @@ namespace MediaBrowser.Providers.MediaInfo
{
video.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks;
}
video.FormatName = (data.format.format_name ?? string.Empty)
.Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
if (video.VideoType == VideoType.VideoFile)
{
var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.');
video.Container = extension;
}
else
{
video.Container = null;
}
if (!string.IsNullOrEmpty(data.format.size))
{
video.Size = long.Parse(data.format.size, _usCulture);
}
else
{
video.Size = null;
}
}
var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;

View File

@ -1234,15 +1234,21 @@ namespace MediaBrowser.Server.Implementations.Dto
Path = GetMappedPath(i),
RunTimeTicks = i.RunTimeTicks,
Video3DFormat = i.Video3DFormat,
VideoType = i.VideoType
VideoType = i.VideoType,
Container = i.Container,
Size = i.Size,
Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList()
};
if (i.VideoType == VideoType.VideoFile || i.VideoType == VideoType.Iso)
if (string.IsNullOrEmpty(info.Container))
{
var locationType = i.LocationType;
if (!string.IsNullOrWhiteSpace(i.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
if (i.VideoType == VideoType.VideoFile || i.VideoType == VideoType.Iso)
{
info.Container = Path.GetExtension(i.Path).TrimStart('.');
var locationType = i.LocationType;
if (!string.IsNullOrWhiteSpace(i.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
{
info.Container = Path.GetExtension(i.Path).TrimStart('.');
}
}
}
@ -1265,13 +1271,19 @@ namespace MediaBrowser.Server.Implementations.Dto
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
Name = i.Name,
Path = GetMappedPath(i),
RunTimeTicks = i.RunTimeTicks
RunTimeTicks = i.RunTimeTicks,
Container = i.Container,
Size = i.Size,
Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList()
};
var locationType = i.LocationType;
if (!string.IsNullOrWhiteSpace(i.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
if (string.IsNullOrEmpty(info.Container))
{
info.Container = Path.GetExtension(i.Path).TrimStart('.');
var locationType = i.LocationType;
if (!string.IsNullOrWhiteSpace(i.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
{
info.Container = Path.GetExtension(i.Path).TrimStart('.');
}
}
var bitrate = info.MediaStreams.Where(m => m.Type == MediaStreamType.Audio).Select(m => m.BitRate ?? 0).Sum();

View File

@ -158,15 +158,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
continue;
}
if (video.VideoType == VideoType.BluRay)
{
// Can only extract reliably on single file blurays
if (video.PlayableStreamFileNames == null || video.PlayableStreamFileNames.Count != 1)
{
continue;
}
}
// Add some time for the first chapter to make sure we don't end up with a black image
var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);

View File

@ -294,6 +294,7 @@ namespace MediaBrowser.Server.Implementations.Session
session.PlayState.VolumeLevel = info.VolumeLevel;
session.PlayState.AudioStreamIndex = info.AudioStreamIndex;
session.PlayState.SubtitleStreamIndex = info.SubtitleStreamIndex;
session.PlayState.PlayMethod = info.PlayMethod;
}
/// <summary>
@ -1253,8 +1254,8 @@ namespace MediaBrowser.Server.Implementations.Session
}
var backropItem = item.HasImage(ImageType.Backdrop) ? item : null;
var thumbItem = item.HasImage(ImageType.Thumb) ? item : null;
var logoItem = item.HasImage(ImageType.Logo) ? item : null;
if (thumbItem == null)
{
@ -1292,6 +1293,11 @@ namespace MediaBrowser.Server.Implementations.Session
thumbItem = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Thumb));
}
if (logoItem == null)
{
logoItem = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Logo));
}
if (thumbItem != null)
{
info.ThumbImageTag = GetImageCacheTag(thumbItem, ImageType.Thumb);
@ -1304,6 +1310,12 @@ namespace MediaBrowser.Server.Implementations.Session
info.BackdropItemId = GetDtoId(backropItem);
}
if (logoItem != null)
{
info.LogoImageTag = GetImageCacheTag(logoItem, ImageType.Logo);
info.LogoItemId = GetDtoId(logoItem);
}
return info;
}

View File

@ -506,7 +506,7 @@ namespace MediaBrowser.ServerApplication
var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
RegisterSingleInstance<IAppThemeManager>(appThemeManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"), JsonSerializer, UserManager, LibraryManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"), JsonSerializer, UserManager, LibraryManager, DtoService, ImageProcessor, UserDataManager);
RegisterSingleInstance<IDlnaManager>(dlnaManager);
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);