From a79962b7ebfe59d837970581ac1b5f184b0aa42d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Mar 2015 12:45:16 -0400 Subject: [PATCH] update live stream generation --- .../Playback/BaseStreamingService.cs | 9 ++- .../Playback/Hls/DynamicHlsService.cs | 4 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 45 +++++++++++---- .../Library/IMediaSourceManager.cs | 5 +- .../MediaBrowser.Model.Portable.csproj | 6 ++ .../MediaBrowser.Model.net35.csproj | 6 ++ .../ApiClient/IConnectionManager.cs | 6 ++ MediaBrowser.Model/MediaBrowser.Model.csproj | 2 + .../MediaInfo/LiveStreamRequest.cs | 16 ++++++ .../MediaInfo/LiveStreamResponse.cs | 9 +++ .../Library/MediaSourceManager.cs | 27 ++++++--- .../Session/SessionManager.cs | 57 ++++++++++++------- .../ApplicationHost.cs | 2 +- 13 files changed, 145 insertions(+), 49 deletions(-) create mode 100644 MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs create mode 100644 MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 2e7c9a5a7b..5a4cdaaa93 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -936,10 +936,13 @@ namespace MediaBrowser.Api.Playback if (state.MediaSource.RequiresOpening) { - var mediaSource = await MediaSourceManager.OpenLiveStream(state.MediaSource.OpenToken, false, cancellationTokenSource.Token) - .ConfigureAwait(false); + var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest + { + OpenToken = state.MediaSource.OpenToken + + }, false, cancellationTokenSource.Token).ConfigureAwait(false); - AttachMediaSourceInfo(state, mediaSource, state.VideoRequest, state.RequestedUrl); + AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.VideoRequest, state.RequestedUrl); if (state.VideoRequest != null) { diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index b166bc319f..4f7c0444b2 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -698,7 +698,7 @@ namespace MediaBrowser.Api.Playback.Hls { var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d.ts"; - return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", inputModifier, GetInputArgument(state), threads, @@ -712,7 +712,7 @@ namespace MediaBrowser.Api.Playback.Hls ).Trim(); } - return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index d954c5b197..6f8c2cc505 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -61,14 +61,12 @@ namespace MediaBrowser.Api.Playback public string MediaSourceId { get; set; } } - [Route("/MediaSources/Open", "POST", Summary = "Opens a media source")] - public class OpenMediaSource : IReturn + [Route("/LiveStreams/Open", "POST", Summary = "Opens a media source")] + public class OpenMediaSource : LiveStreamRequest, IReturn { - [ApiMember(Name = "OpenToken", Description = "OpenToken", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string OpenToken { get; set; } } - [Route("/MediaSources/Close", "POST", Summary = "Closes a media source")] + [Route("/LiveStreams/Close", "POST", Summary = "Closes a media source")] public class CloseMediaSource : IReturnVoid { [ApiMember(Name = "LiveStreamId", Description = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] @@ -103,7 +101,32 @@ namespace MediaBrowser.Api.Playback public async Task Post(OpenMediaSource request) { - var result = await _mediaSourceManager.OpenLiveStream(request.OpenToken, false, CancellationToken.None).ConfigureAwait(false); + var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); + + var result = await _mediaSourceManager.OpenLiveStream(request, false, CancellationToken.None).ConfigureAwait(false); + + var profile = request.DeviceProfile; + if (profile == null) + { + var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (caps != null) + { + profile = caps.DeviceProfile; + } + } + + if (profile != null) + { + var item = _libraryManager.GetItemById(request.ItemId); + + SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex, request.SubtitleStreamIndex); + } + + if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl)) + { + result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId; + } + return ToOptimizedResult(result); } @@ -177,11 +200,11 @@ namespace MediaBrowser.Api.Playback return result; } - private void SetDeviceSpecificData(string itemId, - PlaybackInfoResponse result, - DeviceProfile profile, - AuthorizationInfo auth, - int? maxBitrate, + private void SetDeviceSpecificData(string itemId, + PlaybackInfoResponse result, + DeviceProfile profile, + AuthorizationInfo auth, + int? maxBitrate, long startTimeTicks, string mediaSourceId, int? audioStreamIndex, diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 292205c033..9cbbabc8db 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; using System.Threading; @@ -84,11 +85,11 @@ namespace MediaBrowser.Controller.Library /// /// Opens the media source. /// - /// The open token. + /// The request. /// if set to true [enable automatic close]. /// The cancellation token. /// Task<MediaSourceInfo>. - Task OpenLiveStream(string openToken, bool enableAutoClose, CancellationToken cancellationToken); + Task OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken); /// /// Gets the live stream. diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index ab77dc271b..fdedc51a21 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -803,6 +803,12 @@ MediaInfo\IBlurayExaminer.cs + + MediaInfo\LiveStreamRequest.cs + + + MediaInfo\LiveStreamResponse.cs + MediaInfo\MediaProtocol.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index b772e17539..3618aa9728 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -759,6 +759,12 @@ MediaInfo\IBlurayExaminer.cs + + MediaInfo\LiveStreamRequest.cs + + + MediaInfo\LiveStreamResponse.cs + MediaInfo\MediaProtocol.cs diff --git a/MediaBrowser.Model/ApiClient/IConnectionManager.cs b/MediaBrowser.Model/ApiClient/IConnectionManager.cs index 84a815dfc0..f8837f15de 100644 --- a/MediaBrowser.Model/ApiClient/IConnectionManager.cs +++ b/MediaBrowser.Model/ApiClient/IConnectionManager.cs @@ -172,5 +172,11 @@ namespace MediaBrowser.Model.ApiClient /// if set to true [remember credentials]. /// Task. Task AuthenticateOffline(UserDto user, string password, bool rememberCredentials); + + /// + /// Gets the offline users. + /// + /// Task<List<UserDto>>. + Task> GetOfflineUsers(); } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index fe67038df9..0673095125 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -141,6 +141,8 @@ + + diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs new file mode 100644 index 0000000000..8078219d82 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -0,0 +1,16 @@ +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamRequest + { + public string OpenToken { get; set; } + public string UserId { get; set; } + public int? MaxStreamingBitrate { get; set; } + public long? StartTimeTicks { get; set; } + public int? AudioStreamIndex { get; set; } + public int? SubtitleStreamIndex { get; set; } + public string ItemId { get; set; } + public DeviceProfile DeviceProfile { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs new file mode 100644 index 0000000000..e79e37a717 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs @@ -0,0 +1,9 @@ +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamResponse + { + public MediaSourceInfo MediaSource { get; set; } + } +} diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 3dbcf4aad4..64b1f2c89a 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -8,13 +7,14 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Serialization; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Server.Implementations.LiveTv; namespace MediaBrowser.Server.Implementations.Library { @@ -23,16 +23,18 @@ namespace MediaBrowser.Server.Implementations.Library private readonly IItemRepository _itemRepo; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; + private readonly IJsonSerializer _jsonSerializer; private IMediaSourceProvider[] _providers; private readonly ILogger _logger; - public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger) + public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer) { _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; _logger = logger; + _jsonSerializer = jsonSerializer; } public void AddParts(IEnumerable providers) @@ -317,13 +319,13 @@ namespace MediaBrowser.Server.Implementations.Library private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); - public async Task OpenLiveStream(string openToken, bool enableAutoClose, CancellationToken cancellationToken) + public async Task OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken) { await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { - var tuple = GetProvider(openToken); + var tuple = GetProvider(request.OpenToken); var provider = tuple.Item1; var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); @@ -344,12 +346,19 @@ namespace MediaBrowser.Server.Implementations.Library StartCloseTimer(); } - if (!string.IsNullOrWhiteSpace(mediaSource.TranscodingUrl)) + var json = _jsonSerializer.SerializeToString(mediaSource); + var clone = _jsonSerializer.DeserializeFromString(json); + + if (!string.IsNullOrWhiteSpace(request.UserId)) { - mediaSource.TranscodingUrl += "&LiveStreamId=" + mediaSource.LiveStreamId; + var user = _userManager.GetUserById(request.UserId); + SetUserProperties(clone, user); } - return mediaSource; + return new LiveStreamResponse + { + MediaSource = clone + }; } finally { diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index ec93ae8766..16fc420639 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Devices; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Library; @@ -304,13 +305,21 @@ namespace MediaBrowser.Server.Implementations.Session } } + private async Task GetMediaSource(BaseItem item, string mediaSourceId) + { + var sources = await _mediaSourceManager.GetPlayackMediaSources(item.Id.ToString("N"), false, CancellationToken.None) + .ConfigureAwait(false); + + return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + } + /// /// Updates the now playing item id. /// /// The session. /// The information. /// The library item. - private void UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem) + private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem) { if (string.IsNullOrWhiteSpace(info.MediaSourceId)) { @@ -319,29 +328,27 @@ namespace MediaBrowser.Server.Implementations.Session if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null) { - var runtimeTicks = libraryItem.RunTimeTicks; + var current = session.NowPlayingItem; - if (!string.Equals(info.ItemId, info.MediaSourceId) && - !string.IsNullOrWhiteSpace(info.MediaSourceId)) + if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) { - var runtimeItem = _libraryManager.GetItemById(new Guid(info.MediaSourceId)) ?? - _libraryManager.GetItemById(info.ItemId); + var runtimeTicks = libraryItem.RunTimeTicks; - runtimeTicks = runtimeItem.RunTimeTicks; - } + var mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId).ConfigureAwait(false); - var current = session.NowPlayingItem; + if (mediaSource != null) + { + runtimeTicks = mediaSource.RunTimeTicks; + } - if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) - { - info.Item = GetItemInfo(libraryItem, libraryItem, info.MediaSourceId); + info.Item = GetItemInfo(libraryItem, libraryItem, mediaSource); + + info.Item.RunTimeTicks = runtimeTicks; } else { info.Item = current; } - - info.Item.RunTimeTicks = runtimeTicks; } session.NowPlayingItem = info.Item; @@ -432,6 +439,12 @@ namespace MediaBrowser.Server.Implementations.Session device = device ?? _deviceManager.GetDevice(deviceId); + if (device == null) + { + var userIdString = userId.HasValue ? userId.Value.ToString("N") : null; + device = await _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString).ConfigureAwait(false); + } + if (device != null) { if (!string.IsNullOrEmpty(device.CustomName)) @@ -570,7 +583,7 @@ namespace MediaBrowser.Server.Implementations.Session ? null : _libraryManager.GetItemById(new Guid(info.ItemId)); - UpdateNowPlayingItem(session, info, libraryItem); + await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false); if (!string.IsNullOrEmpty(session.DeviceId) && info.PlayMethod != PlayMethod.Transcode) { @@ -652,7 +665,7 @@ namespace MediaBrowser.Server.Implementations.Session ? null : _libraryManager.GetItemById(new Guid(info.ItemId)); - UpdateNowPlayingItem(session, info, libraryItem); + await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false); var users = GetUsers(session); @@ -731,7 +744,9 @@ namespace MediaBrowser.Server.Implementations.Session if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) { - info.Item = GetItemInfo(libraryItem, libraryItem, info.MediaSourceId); + var mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId).ConfigureAwait(false); + + info.Item = GetItemInfo(libraryItem, libraryItem, mediaSource); } else { @@ -1439,10 +1454,10 @@ namespace MediaBrowser.Server.Implementations.Session /// /// The item. /// The chapter owner. - /// The media source identifier. + /// The media source. /// BaseItemInfo. /// item - private BaseItemInfo GetItemInfo(BaseItem item, BaseItem chapterOwner, string mediaSourceId) + private BaseItemInfo GetItemInfo(BaseItem item, BaseItem chapterOwner, MediaSourceInfo mediaSource) { if (item == null) { @@ -1593,9 +1608,9 @@ namespace MediaBrowser.Server.Implementations.Session info.Chapters = _dtoService.GetChapterInfoDtos(chapterOwner).ToList(); } - if (!string.IsNullOrWhiteSpace(mediaSourceId)) + if (mediaSource != null) { - info.MediaStreams = _mediaSourceManager.GetMediaStreams(mediaSourceId).ToList(); + info.MediaStreams = mediaSource.MediaStreams; } return info; diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 039c5edf38..aad88d022f 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -472,7 +472,7 @@ namespace MediaBrowser.Server.Startup.Common ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient); RegisterSingleInstance(ChannelManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager")); + MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer); RegisterSingleInstance(MediaSourceManager); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager);