123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- using MediaBrowser.Controller.Devices;
- using MediaBrowser.Controller.Entities;
- using MediaBrowser.Controller.Library;
- using MediaBrowser.Controller.Net;
- using MediaBrowser.Model.Dlna;
- using MediaBrowser.Model.Dto;
- using MediaBrowser.Model.Entities;
- using MediaBrowser.Model.MediaInfo;
- using MediaBrowser.Model.Session;
- using ServiceStack;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- namespace MediaBrowser.Api.Playback
- {
- [Route("/Items/{Id}/MediaInfo", "GET", Summary = "Gets live playback media info for an item")]
- public class GetLiveMediaInfo : IReturn<PlaybackInfoResponse>
- {
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
- }
- [Route("/Items/{Id}/PlaybackInfo", "GET", Summary = "Gets live playback media info for an item")]
- public class GetPlaybackInfo : IReturn<PlaybackInfoResponse>
- {
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
- }
- [Route("/Items/{Id}/PlaybackInfo", "POST", Summary = "Gets live playback media info for an item")]
- public class GetPostedPlaybackInfo : PlaybackInfoRequest, IReturn<PlaybackInfoResponse>
- {
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string Id { get; set; }
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string UserId { get; set; }
- [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public long? StartTimeTicks { get; set; }
- [ApiMember(Name = "AudioStreamIndex", Description = "Optional. The index of the audio stream to use. If omitted the first audio stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? AudioStreamIndex { get; set; }
- [ApiMember(Name = "SubtitleStreamIndex", Description = "Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
- public int? SubtitleStreamIndex { get; set; }
- [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string MediaSourceId { get; set; }
- }
- [Authenticated]
- public class MediaInfoService : BaseApiService
- {
- private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IDeviceManager _deviceManager;
- private readonly ILibraryManager _libraryManager;
- public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager)
- {
- _mediaSourceManager = mediaSourceManager;
- _deviceManager = deviceManager;
- _libraryManager = libraryManager;
- }
- public async Task<object> Get(GetPlaybackInfo request)
- {
- var result = await GetPlaybackInfo(request.Id, request.UserId).ConfigureAwait(false);
- return ToOptimizedResult(result);
- }
- public async Task<object> Get(GetLiveMediaInfo request)
- {
- var result = await GetPlaybackInfo(request.Id, request.UserId).ConfigureAwait(false);
- return ToOptimizedResult(result);
- }
- public async Task<object> Post(GetPostedPlaybackInfo request)
- {
- var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false);
- var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
- var profile = request.DeviceProfile;
- if (profile == null)
- {
- var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
- if (caps != null)
- {
- profile = caps.DeviceProfile;
- }
- }
- if (profile != null)
- {
- var mediaSourceId = request.MediaSourceId;
- SetDeviceSpecificData(request.Id, info, profile, authInfo, null, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex);
- }
- return ToOptimizedResult(info);
- }
- private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string mediaSourceId = null)
- {
- var result = new PlaybackInfoResponse();
- IEnumerable<MediaSourceInfo> mediaSources;
- try
- {
- mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false);
- }
- catch (PlaybackException ex)
- {
- mediaSources = new List<MediaSourceInfo>();
- result.ErrorCode = ex.ErrorCode;
- }
- result.MediaSources = mediaSources.ToList();
- if (!string.IsNullOrWhiteSpace(mediaSourceId))
- {
- result.MediaSources = result.MediaSources
- .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
- if (result.MediaSources.Count == 0)
- {
- if (!result.ErrorCode.HasValue)
- {
- result.ErrorCode = PlaybackErrorCode.NoCompatibleStream;
- }
- }
- else
- {
- result.StreamId = Guid.NewGuid().ToString("N");
- }
- return result;
- }
- private void SetDeviceSpecificData(string itemId,
- PlaybackInfoResponse result,
- DeviceProfile profile,
- AuthorizationInfo auth,
- int? maxBitrate,
- long startTimeTicks,
- string mediaSourceId,
- int? audioStreamIndex,
- int? subtitleStreamIndex)
- {
- var streamBuilder = new StreamBuilder();
- var item = _libraryManager.GetItemById(itemId);
- foreach (var mediaSource in result.MediaSources)
- {
- var options = new VideoOptions
- {
- MediaSources = new List<MediaSourceInfo> { mediaSource },
- Context = EncodingContext.Streaming,
- DeviceId = auth.DeviceId,
- ItemId = item.Id.ToString("N"),
- Profile = profile,
- MaxBitrate = maxBitrate
- };
- if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
- {
- options.MediaSourceId = mediaSourceId;
- options.AudioStreamIndex = audioStreamIndex;
- options.SubtitleStreamIndex = subtitleStreamIndex;
- }
- if (mediaSource.SupportsDirectPlay)
- {
- var supportsDirectStream = mediaSource.SupportsDirectStream;
- // Dummy this up to fool StreamBuilder
- mediaSource.SupportsDirectStream = true;
- // The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectPlay = false;
- }
- // Set this back to what it was
- mediaSource.SupportsDirectStream = supportsDirectStream;
- }
- if (mediaSource.SupportsDirectStream)
- {
- // The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectStream = false;
- }
- }
- if (mediaSource.SupportsTranscoding)
- {
- // The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
- if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode)
- {
- streamInfo.StartPositionTicks = startTimeTicks;
- mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).Substring(1);
- mediaSource.TranscodingContainer = streamInfo.Container;
- mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
- }
- }
- }
- SortMediaSources(result);
- }
- private void SortMediaSources(PlaybackInfoResponse result)
- {
- var originalList = result.MediaSources.ToList();
- result.MediaSources = result.MediaSources.OrderBy(i =>
- {
- // Nothing beats direct playing a file
- if (i.SupportsDirectPlay && i.Protocol == MediaProtocol.File)
- {
- return 0;
- }
- return 1;
- }).ThenBy(i =>
- {
- // Let's assume direct streaming a file is just as desirable as direct playing a remote url
- if (i.SupportsDirectPlay || i.SupportsDirectStream)
- {
- return 0;
- }
- return 1;
- }).ThenBy(i =>
- {
- switch (i.Protocol)
- {
- case MediaProtocol.File:
- return 0;
- default:
- return 1;
- }
- }).ThenBy(originalList.IndexOf)
- .ToList();
- }
- }
- }
|