|
|
|
@ -45,6 +45,9 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
|
|
|
|
public string UserId { get; set; }
|
|
|
|
|
|
|
|
|
|
[ApiMember(Name = "MaxStreamingBitrate", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
|
|
|
|
public int? MaxStreamingBitrate { 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; }
|
|
|
|
|
|
|
|
|
@ -58,6 +61,20 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
public string MediaSourceId { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Route("/MediaSources/Open", "POST", Summary = "Opens a media source")]
|
|
|
|
|
public class OpenMediaSource : IReturn<MediaSourceInfo>
|
|
|
|
|
{
|
|
|
|
|
[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")]
|
|
|
|
|
public class CloseMediaSource : IReturnVoid
|
|
|
|
|
{
|
|
|
|
|
[ApiMember(Name = "LiveStreamId", Description = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
|
|
|
public string LiveStreamId { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Authenticated]
|
|
|
|
|
public class MediaInfoService : BaseApiService
|
|
|
|
|
{
|
|
|
|
@ -84,6 +101,18 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
return ToOptimizedResult(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<object> Post(OpenMediaSource request)
|
|
|
|
|
{
|
|
|
|
|
var result = await _mediaSourceManager.OpenLiveStream(request.OpenToken, false, CancellationToken.None).ConfigureAwait(false);
|
|
|
|
|
return ToOptimizedResult(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Post(CloseMediaSource request)
|
|
|
|
|
{
|
|
|
|
|
var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None);
|
|
|
|
|
Task.WaitAll(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<object> Post(GetPostedPlaybackInfo request)
|
|
|
|
|
{
|
|
|
|
|
var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false);
|
|
|
|
@ -102,7 +131,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
if (profile != null)
|
|
|
|
|
{
|
|
|
|
|
var mediaSourceId = request.MediaSourceId;
|
|
|
|
|
SetDeviceSpecificData(request.Id, info, profile, authInfo, null, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex);
|
|
|
|
|
SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ToOptimizedResult(info);
|
|
|
|
@ -158,81 +187,94 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mediaSource.SupportsDirectPlay)
|
|
|
|
|
{
|
|
|
|
|
var supportsDirectStream = mediaSource.SupportsDirectStream;
|
|
|
|
|
SortMediaSources(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dummy this up to fool StreamBuilder
|
|
|
|
|
mediaSource.SupportsDirectStream = true;
|
|
|
|
|
private void SetDeviceSpecificData(BaseItem item,
|
|
|
|
|
MediaSourceInfo mediaSource,
|
|
|
|
|
DeviceProfile profile,
|
|
|
|
|
AuthorizationInfo auth,
|
|
|
|
|
int? maxBitrate,
|
|
|
|
|
long startTimeTicks,
|
|
|
|
|
string mediaSourceId,
|
|
|
|
|
int? audioStreamIndex,
|
|
|
|
|
int? subtitleStreamIndex)
|
|
|
|
|
{
|
|
|
|
|
var streamBuilder = new StreamBuilder();
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
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 (streamInfo == null || !streamInfo.IsDirectStream)
|
|
|
|
|
{
|
|
|
|
|
mediaSource.SupportsDirectPlay = false;
|
|
|
|
|
}
|
|
|
|
|
if (mediaSource.SupportsDirectPlay)
|
|
|
|
|
{
|
|
|
|
|
var supportsDirectStream = mediaSource.SupportsDirectStream;
|
|
|
|
|
|
|
|
|
|
// Set this back to what it was
|
|
|
|
|
mediaSource.SupportsDirectStream = supportsDirectStream;
|
|
|
|
|
}
|
|
|
|
|
// Dummy this up to fool StreamBuilder
|
|
|
|
|
mediaSource.SupportsDirectStream = true;
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
mediaSource.SupportsDirectPlay = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mediaSource.SupportsTranscoding)
|
|
|
|
|
// 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)
|
|
|
|
|
{
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
mediaSource.SupportsDirectStream = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SortMediaSources(result);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SortMediaSources(PlaybackInfoResponse result)
|
|
|
|
|