implement modular media sources

This commit is contained in:
Luke Pulverenti 2015-03-28 16:22:27 -04:00
parent 3add1872c8
commit bd2ea703e3
42 changed files with 702 additions and 371 deletions

View File

@ -1,6 +1,5 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
@ -65,7 +64,6 @@ namespace MediaBrowser.Api.Playback
protected IFileSystem FileSystem { get; private set; }
protected ILiveTvManager LiveTvManager { get; private set; }
protected IDlnaManager DlnaManager { get; private set; }
protected IDeviceManager DeviceManager { get; private set; }
protected ISubtitleEncoder SubtitleEncoder { get; private set; }
@ -75,14 +73,13 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </summary>
protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient)
protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient)
{
ZipClient = zipClient;
MediaSourceManager = mediaSourceManager;
DeviceManager = deviceManager;
SubtitleEncoder = subtitleEncoder;
DlnaManager = dlnaManager;
LiveTvManager = liveTvManager;
FileSystem = fileSystem;
ServerConfigurationManager = serverConfig;
UserManager = userManager;
@ -95,11 +92,10 @@ namespace MediaBrowser.Api.Playback
/// Gets the command line arguments.
/// </summary>
/// <param name="outputPath">The output path.</param>
/// <param name="transcodingJobId">The transcoding job identifier.</param>
/// <param name="state">The state.</param>
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
/// <returns>System.String.</returns>
protected abstract string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding);
protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding);
/// <summary>
/// Gets the type of the transcoding job.
@ -128,7 +124,7 @@ namespace MediaBrowser.Api.Playback
var outputFileExtension = GetOutputFileExtension(state);
var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false);
var data = GetCommandLineArguments("dummy\\dummy", state, false);
data += "-" + (state.Request.DeviceId ?? string.Empty);
data += "-" + (state.Request.StreamId ?? string.Empty);
@ -719,8 +715,10 @@ namespace MediaBrowser.Api.Playback
seconds.ToString(UsCulture));
}
var mediaPath = state.MediaPath ?? string.Empty;
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
mediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
state.InternalSubtitleStreamOffset.ToString(UsCulture),
seconds.ToString(UsCulture));
}
@ -895,12 +893,11 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="transcodingJobId">The transcoding job identifier.</param>
/// <param name="state">The state.</param>
/// <returns>System.String.</returns>
protected string GetInputArgument(string transcodingJobId, StreamState state)
protected string GetInputArgument(StreamState state)
{
var arg = "-i " + GetInputPathArgument(transcodingJobId, state);
var arg = "-i " + GetInputPathArgument(state);
if (state.SubtitleStream != null)
{
@ -913,27 +910,18 @@ namespace MediaBrowser.Api.Playback
return arg;
}
private string GetInputPathArgument(string transcodingJobId, StreamState state)
private string GetInputPathArgument(StreamState state)
{
//if (state.InputProtocol == MediaProtocol.File &&
// state.RunTimeTicks.HasValue &&
// state.VideoType == VideoType.VideoFile &&
// !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
//{
// if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo)
// {
// }
//}
var protocol = state.InputProtocol;
var mediaPath = state.MediaPath ?? string.Empty;
var inputPath = new[] { state.MediaPath };
var inputPath = new[] { mediaPath };
if (state.IsInputVideo)
{
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
{
inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
inputPath = MediaEncoderHelpers.GetInputArgument(mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
}
}
@ -947,55 +935,20 @@ namespace MediaBrowser.Api.Playback
state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
if (string.IsNullOrEmpty(state.MediaPath))
if (state.MediaSource.RequiresOpening)
{
var checkCodecs = false;
var mediaSource = await MediaSourceManager.OpenMediaSource(state.MediaSource.OpenKey, cancellationTokenSource.Token)
.ConfigureAwait(false);
if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name))
AttachMediaSourceInfo(state, mediaSource, state.VideoRequest, state.RequestedUrl);
if (state.VideoRequest != null)
{
var streamInfo = await LiveTvManager.GetChannelStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
state.MediaPath = streamInfo.Path;
state.InputProtocol = streamInfo.Protocol;
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
checkCodecs = true;
TryStreamCopy(state, state.VideoRequest);
}
else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) ||
string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name))
{
var streamInfo = await LiveTvManager.GetRecordingStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
state.MediaPath = streamInfo.Path;
state.InputProtocol = streamInfo.Protocol;
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
checkCodecs = true;
}
var videoRequest = state.VideoRequest;
if (videoRequest != null && checkCodecs)
{
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
}
// TODO: This is only needed for live tv
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
@ -1017,7 +970,7 @@ namespace MediaBrowser.Api.Playback
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
var transcodingId = Guid.NewGuid().ToString("N");
var commandLineArgs = GetCommandLineArguments(outputPath, transcodingId, state, true);
var commandLineArgs = GetCommandLineArguments(outputPath, state, true);
if (ApiEntryPoint.Instance.GetEncodingOptions().EnableDebugLogging)
{
@ -1644,7 +1597,7 @@ namespace MediaBrowser.Api.Playback
request.AudioCodec = InferAudioCodec(url);
}
var state = new StreamState(LiveTvManager, Logger)
var state = new StreamState(MediaSourceManager, Logger)
{
Request = request,
RequestedUrl = url
@ -1658,109 +1611,20 @@ namespace MediaBrowser.Api.Playback
var item = LibraryManager.GetItemById(request.Id);
List<MediaStream> mediaStreams = null;
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
state.ItemType = item.GetType().Name;
state.ItemId = item.Id.ToString("N");
var archivable = item as IArchivable;
state.IsInputArchive = archivable != null && archivable.IsArchive;
if (item is ILiveTvRecording)
{
var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false);
state.VideoType = VideoType.VideoFile;
state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
var path = recording.RecordingInfo.Path;
var mediaUrl = recording.RecordingInfo.Url;
var source = string.IsNullOrEmpty(request.MediaSourceId)
? recording.GetMediaSources(false).First()
: MediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false);
mediaStreams = source.MediaStreams;
// Just to prevent this from being null and causing other methods to fail
state.MediaPath = string.Empty;
if (!string.IsNullOrEmpty(path))
{
state.MediaPath = path;
state.InputProtocol = MediaProtocol.File;
}
else if (!string.IsNullOrEmpty(mediaUrl))
{
state.MediaPath = mediaUrl;
state.InputProtocol = MediaProtocol.Http;
}
state.RunTimeTicks = recording.RunTimeTicks;
state.DeInterlace = true;
state.OutputAudioSync = "1000";
state.InputVideoSync = "-1";
state.InputAudioSync = "1";
state.InputContainer = recording.Container;
state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate;
}
else if (item is LiveTvChannel)
{
var channel = LiveTvManager.GetInternalChannel(request.Id);
state.VideoType = VideoType.VideoFile;
state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
mediaStreams = new List<MediaStream>();
state.DeInterlace = true;
// Just to prevent this from being null and causing other methods to fail
state.MediaPath = string.Empty;
}
else
{
var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false);
var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
mediaStreams = mediaSource.MediaStreams;
state.MediaPath = mediaSource.Path;
state.InputProtocol = mediaSource.Protocol;
state.InputContainer = mediaSource.Container;
state.InputFileSize = mediaSource.Size;
state.InputBitrate = mediaSource.Bitrate;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
state.RunTimeTicks = mediaSource.RunTimeTicks;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
var video = item as Video;
if (video != null)
{
state.IsInputVideo = true;
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
}
state.IsoType = mediaSource.IsoType;
state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
if (mediaSource.Timestamp.HasValue)
{
state.InputTimestamp = mediaSource.Timestamp.Value;
}
}
}
var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false);
var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
var videoRequest = request as VideoStreamRequest;
AttachMediaStreamInfo(state, mediaStreams, videoRequest, url);
AttachMediaSourceInfo(state, mediaSource, videoRequest, url);
var container = Path.GetExtension(state.RequestedUrl);
@ -1801,15 +1665,7 @@ namespace MediaBrowser.Api.Playback
if (videoRequest != null)
{
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
TryStreamCopy(state, videoRequest);
}
state.OutputFilePath = GetOutputFilePath(state);
@ -1817,11 +1673,47 @@ namespace MediaBrowser.Api.Playback
return state;
}
private void AttachMediaStreamInfo(StreamState state,
private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest)
{
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
}
private void AttachMediaSourceInfo(StreamState state,
MediaSourceInfo mediaSource,
VideoStreamRequest videoRequest,
string requestedUrl)
{
state.MediaPath = mediaSource.Path;
state.InputProtocol = mediaSource.Protocol;
state.InputContainer = mediaSource.Container;
state.InputFileSize = mediaSource.Size;
state.InputBitrate = mediaSource.Bitrate;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
state.RunTimeTicks = mediaSource.RunTimeTicks;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
}
state.IsoType = mediaSource.IsoType;
state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
if (mediaSource.Timestamp.HasValue)
{
state.InputTimestamp = mediaSource.Timestamp.Value;
}
state.InputProtocol = mediaSource.Protocol;
state.MediaPath = mediaSource.Path;
state.RunTimeTicks = mediaSource.RunTimeTicks;
@ -1830,21 +1722,16 @@ namespace MediaBrowser.Api.Playback
state.InputFileSize = mediaSource.Size;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
if (state.ReadInputAtNativeFramerate)
if (state.ReadInputAtNativeFramerate ||
mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
{
state.OutputAudioSync = "1000";
state.InputVideoSync = "-1";
state.InputAudioSync = "1";
}
AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest, requestedUrl);
}
var mediaStreams = mediaSource.MediaStreams;
private void AttachMediaStreamInfo(StreamState state,
List<MediaStream> mediaStreams,
VideoStreamRequest videoRequest,
string requestedUrl)
{
if (videoRequest != null)
{
if (string.IsNullOrEmpty(videoRequest.VideoCodec))
@ -1873,7 +1760,7 @@ namespace MediaBrowser.Api.Playback
state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
}
state.AllMediaStreams = mediaStreams;
state.MediaSource = mediaSource;
}
private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)

View File

@ -54,7 +54,7 @@ namespace MediaBrowser.Api.Playback.Dash
public class MpegDashService : BaseHlsService
{
public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{
NetworkManager = networkManager;
}
@ -447,7 +447,7 @@ namespace MediaBrowser.Api.Playback.Dash
return args;
}
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
// test url http://192.168.1.2:8096/videos/233e8905d559a8f230db9bffd2ac9d6d/master.mpd?mediasourceid=233e8905d559a8f230db9bffd2ac9d6d&videocodec=h264&audiocodec=aac&maxwidth=1280&videobitrate=500000&audiobitrate=128000&profile=baseline&level=3
// Good info on i-frames http://blog.streamroot.io/encode-multi-bitrate-videos-mpeg-dash-mse-based-media-players/
@ -461,7 +461,7 @@ namespace MediaBrowser.Api.Playback.Dash
var args = string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts {5} -f dash -init_seg_name \"{6}\" -media_seg_name \"{7}\" -use_template 0 -use_timeline 1 -min_seg_duration {8} -y \"{9}\"",
inputModifier,
GetInputArgument(transcodingJobId, state),
GetInputArgument(state),
threads,
GetMapArgs(state),
GetVideoArguments(state),

View File

@ -22,7 +22,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public abstract class BaseHlsService : BaseStreamingService
{
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{
}
@ -212,7 +212,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
@ -240,7 +240,7 @@ namespace MediaBrowser.Api.Playback.Hls
var args = string.Format("{0} {1} {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
itsOffset,
inputModifier,
GetInputArgument(transcodingJobId, state),
GetInputArgument(state),
threads,
GetMapArgs(state),
GetVideoArguments(state),

View File

@ -62,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
public class DynamicHlsService : BaseHlsService
{
public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{
NetworkManager = networkManager;
}
@ -414,7 +414,8 @@ namespace MediaBrowser.Api.Playback.Hls
var request = (GetMasterHlsVideoStream)state.Request;
var subtitleStreams = state.AllMediaStreams
var subtitleStreams = state.MediaSource
.MediaStreams
.Where(i => i.IsTextSubtitleStream)
.ToList();
@ -684,7 +685,7 @@ namespace MediaBrowser.Api.Playback.Hls
return args;
}
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
var threads = GetNumberOfThreads(state, false);
@ -699,7 +700,7 @@ namespace MediaBrowser.Api.Playback.Hls
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}\"",
inputModifier,
GetInputArgument(transcodingJobId, state),
GetInputArgument(state),
threads,
GetMapArgs(state),
GetVideoArguments(state),
@ -713,7 +714,7 @@ namespace MediaBrowser.Api.Playback.Hls
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}\"",
inputModifier,
GetInputArgument(transcodingJobId, state),
GetInputArgument(state),
threads,
GetMapArgs(state),
GetVideoArguments(state),

View File

@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public class VideoHlsService : BaseHlsService
{
public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{
}

View File

@ -4,6 +4,7 @@ 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;
@ -38,20 +39,23 @@ namespace MediaBrowser.Api.Playback
[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 = "GET")]
[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 = "GET")]
[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 = "GET")]
[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 = "GET")]
[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 = "GET")]
[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]
@ -82,7 +86,7 @@ namespace MediaBrowser.Api.Playback
public async Task<object> Post(GetPostedPlaybackInfo request)
{
var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSource).ConfigureAwait(false);
var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false);
var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
var profile = request.DeviceProfile;
@ -97,36 +101,36 @@ namespace MediaBrowser.Api.Playback
if (profile != null)
{
var mediaSourceId = request.MediaSource == null ? null : request.MediaSource.Id;
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, MediaSourceInfo mediaSource = null)
private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string mediaSourceId = null)
{
var result = new PlaybackInfoResponse();
if (mediaSource == null)
IEnumerable<MediaSourceInfo> mediaSources;
try
{
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();
mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false);
}
else
catch (PlaybackException ex)
{
result.MediaSources = new List<MediaSourceInfo> { mediaSource };
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)
@ -185,9 +189,9 @@ namespace MediaBrowser.Api.Playback
mediaSource.SupportsDirectStream = true;
// The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = item is Video ?
streamBuilder.BuildVideoItem(options) :
streamBuilder.BuildAudioItem(options);
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildAudioItem(options) :
streamBuilder.BuildVideoItem(options);
if (streamInfo == null || !streamInfo.IsDirectStream)
{
@ -201,9 +205,9 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectStream)
{
// The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = item is Video ?
streamBuilder.BuildVideoItem(options) :
streamBuilder.BuildAudioItem(options);
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildAudioItem(options) :
streamBuilder.BuildVideoItem(options);
if (streamInfo == null || !streamInfo.IsDirectStream)
{
@ -214,9 +218,9 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsTranscoding)
{
// The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = item is Video ?
streamBuilder.BuildVideoItem(options) :
streamBuilder.BuildAudioItem(options);
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildAudioItem(options) :
streamBuilder.BuildVideoItem(options);
if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode)
{
@ -227,6 +231,46 @@ namespace MediaBrowser.Api.Playback
}
}
}
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();
}
}
}

View File

@ -31,7 +31,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class AudioService : BaseProgressiveStreamingService
{
public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient)
public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient)
{
}
@ -55,7 +55,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true);
}
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
var audioTranscodeParams = new List<string>();
@ -84,7 +84,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
inputModifier,
GetInputArgument(transcodingJobId, state),
GetInputArgument(state),
threads,
vn,
string.Join(" ", audioTranscodeParams.ToArray()),

View File

@ -27,7 +27,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected readonly IImageProcessor ImageProcessor;
protected readonly IHttpClient HttpClient;
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{
ImageProcessor = imageProcessor;
HttpClient = httpClient;

View File

@ -62,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class VideoService : BaseProgressiveStreamingService
{
public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient)
public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient)
{
}
@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true);
}
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
// Get the output codec name
var videoCodec = state.OutputVideoCodec;
@ -106,7 +106,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"",
inputModifier,
GetInputArgument(transcodingJobId, state),
GetInputArgument(state),
keyFrame,
GetMapArgs(state),
GetVideoArguments(state, videoCodec),

View File

@ -1,6 +1,7 @@
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
@ -17,7 +18,7 @@ namespace MediaBrowser.Api.Playback
public class StreamState : IDisposable
{
private readonly ILogger _logger;
private readonly ILiveTvManager _liveTvManager;
private readonly IMediaSourceManager _mediaSourceManager;
public string RequestedUrl { get; set; }
@ -39,7 +40,7 @@ namespace MediaBrowser.Api.Playback
public string InputContainer { get; set; }
public List<MediaStream> AllMediaStreams { get; set; }
public MediaSourceInfo MediaSource { get; set; }
public MediaStream AudioStream { get; set; }
public MediaStream VideoStream { get; set; }
@ -64,8 +65,6 @@ namespace MediaBrowser.Api.Playback
public List<string> PlayableStreamFileNames { get; set; }
public string LiveTvStreamId { get; set; }
public int SegmentLength = 3;
public bool EnableGenericHlsSegmenter = false;
public int HlsListSize
@ -86,14 +85,13 @@ namespace MediaBrowser.Api.Playback
public List<string> SupportedAudioCodecs { get; set; }
public StreamState(ILiveTvManager liveTvManager, ILogger logger)
public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger)
{
_liveTvManager = liveTvManager;
_mediaSourceManager = mediaSourceManager;
_logger = logger;
SupportedAudioCodecs = new List<string>();
PlayableStreamFileNames = new List<string>();
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
AllMediaStreams = new List<MediaStream>();
}
public string InputAudioSync { get; set; }
@ -113,9 +111,6 @@ namespace MediaBrowser.Api.Playback
public long? EncodingDurationTicks { get; set; }
public string ItemType { get; set; }
public string ItemId { get; set; }
public string GetMimeType(string outputPath)
{
if (!string.IsNullOrEmpty(MimeType))
@ -187,15 +182,15 @@ namespace MediaBrowser.Api.Playback
private async void DisposeLiveStream()
{
if (!string.IsNullOrEmpty(LiveTvStreamId))
if (MediaSource.RequiresClosing)
{
try
{
await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false);
await _mediaSourceManager.CloseMediaSource(MediaSource.CloseKey, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error closing live tv stream", ex);
_logger.ErrorException("Error closing media source", ex);
}
}
}

View File

@ -192,7 +192,7 @@ namespace MediaBrowser.Api.Subtitles
{
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
var mediaSource = item.GetMediaSources(false)
var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null)
.First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id));
var subtitleStream = mediaSource.MediaStreams

View File

@ -75,17 +75,23 @@ namespace MediaBrowser.Controller.Channels
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var list = base.GetMediaSources(enablePathSubstitution).ToList();
var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None)
.Result.ToList();
var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None)
.Result.ToList();
if (sources.Count > 0)
{
return sources;
}
list.InsertRange(0, sources);
var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list;
}

View File

@ -90,17 +90,23 @@ namespace MediaBrowser.Controller.Channels
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var list = base.GetMediaSources(enablePathSubstitution).ToList();
var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None)
.Result.ToList();
var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None)
.Result.ToList();
if (sources.Count > 0)
{
return sources;
}
list.InsertRange(0, sources);
var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list;
}

View File

@ -112,11 +112,11 @@ namespace MediaBrowser.Controller.Channels
/// <summary>
/// Gets the channel item media sources.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="includeDynamicSources">if set to <c>true</c> [include dynamic sources].</param>
/// <param name="item">The item.</param>
/// <param name="includeCachedVersions">if set to <c>true</c> [include cached versions].</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken);
Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken);
/// <summary>
/// Gets the channel folder.

View File

@ -64,6 +64,14 @@ namespace MediaBrowser.Controller.Library
/// <returns>IEnumerable&lt;MediaSourceInfo&gt;.</returns>
IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user);
/// <summary>
/// Gets the static media sources.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
/// <returns>IEnumerable&lt;MediaSourceInfo&gt;.</returns>
IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution);
/// <summary>
/// Gets the static media source.
/// </summary>
@ -72,5 +80,21 @@ namespace MediaBrowser.Controller.Library
/// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
/// <returns>MediaSourceInfo.</returns>
MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution);
/// <summary>
/// Opens the media source.
/// </summary>
/// <param name="openKey">The open key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken);
/// <summary>
/// Closes the media source.
/// </summary>
/// <param name="closeKey">The close key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CloseMediaSource(string closeKey, CancellationToken cancellationToken);
}
}

View File

@ -15,5 +15,21 @@ namespace MediaBrowser.Controller.Library
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
/// <summary>
/// Opens the media source.
/// </summary>
/// <param name="openKey">The open key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken);
/// <summary>
/// Closes the media source.
/// </summary>
/// <param name="closeKey">The close key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CloseMediaSource(string closeKey, CancellationToken cancellationToken);
}
}

View File

@ -1,8 +1,10 @@

using System;
namespace MediaBrowser.Controller.LiveTv
{
public interface ILiveTvItem
{
Guid Id { get; }
string ServiceName { get; set; }
}
}

View File

@ -1,10 +1,12 @@
using System.Runtime.Serialization;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
@ -99,5 +101,20 @@ namespace MediaBrowser.Controller.LiveTv
{
return user.Policy.EnableLiveTvManagement;
}
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list;
}
}
}

View File

@ -127,7 +127,7 @@ namespace MediaBrowser.Controller.LiveTv
Name = Name,
Path = Path,
RunTimeTicks = RunTimeTicks,
Type = MediaSourceType.Default
Type = MediaSourceType.Placeholder
};
list.Add(info);

View File

@ -1,9 +1,11 @@
using System.Runtime.Serialization;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System.Linq;
using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
@ -97,5 +99,20 @@ namespace MediaBrowser.Controller.LiveTv
{
return user.Policy.EnableLiveTvManagement;
}
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list;
}
}
}

View File

@ -476,7 +476,7 @@ namespace MediaBrowser.Dlna.PlayTo
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
playlistItem.StreamInfo.StartPositionTicks = startPostionTicks;
playlistItem.StreamUrl = playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken);
playlistItem.StreamUrl = playlistItem.StreamInfo.ToDlnaUrl(_serverAddress, _accessToken);
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager)
.GetItemDidl(item, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);

View File

@ -239,6 +239,16 @@ namespace MediaBrowser.Model.Dlna
return playlistItem;
}
private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options)
{
if (item.Protocol == MediaProtocol.File)
{
return options.Profile.MaxStaticBitrate;
}
return options.GetMaxBitrate();
}
private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
{
DirectPlayProfile directPlayProfile = null;
@ -263,7 +273,7 @@ namespace MediaBrowser.Model.Dlna
// The profile describes what the device supports
// If device requirements are satisfied then allow both direct stream and direct play
if (IsAudioEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate))
if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
{
playMethods.Add(PlayMethod.DirectPlay);
}
@ -293,7 +303,7 @@ namespace MediaBrowser.Model.Dlna
MediaStream videoStream = item.VideoStream;
// TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate, subtitleStream, options);
bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options);
bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options);
if (isEligibleForDirectPlay || isEligibleForDirectStream)
@ -604,6 +614,11 @@ namespace MediaBrowser.Model.Dlna
// Look for an external profile that matches the stream type (text/graphical)
foreach (SubtitleProfile profile in subtitleProfiles)
{
if (!profile.SupportsLanguage(subtitleStream.Language))
{
continue;
}
if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
{
if (subtitleStream.SupportsExternalStream)
@ -621,6 +636,11 @@ namespace MediaBrowser.Model.Dlna
foreach (SubtitleProfile profile in subtitleProfiles)
{
if (!profile.SupportsLanguage(subtitleStream.Language))
{
continue;
}
if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
{
return profile;

View File

@ -1,4 +1,6 @@
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
{
@ -13,5 +15,28 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("didlMode")]
public string DidlMode { get; set; }
[XmlAttribute("language")]
public string Language { get; set; }
public List<string> GetLanguages()
{
List<string> list = new List<string>();
foreach (string i in (Language ?? string.Empty).Split(','))
{
if (!string.IsNullOrEmpty(i)) list.Add(i);
}
return list;
}
public bool SupportsLanguage(string language)
{
if (string.IsNullOrEmpty(language))
{
language = "und";
}
List<string> languages = GetLanguages();
return languages.Count == 0 || ListHelper.ContainsIgnoreCase(languages, language);
}
}
}

View File

@ -26,6 +26,11 @@ namespace MediaBrowser.Model.Dto
public bool SupportsDirectStream { get; set; }
public bool SupportsDirectPlay { get; set; }
public bool RequiresOpening { get; set; }
public string OpenKey { get; set; }
public bool RequiresClosing { get; set; }
public string CloseKey { get; set; }
public VideoType? VideoType { get; set; }
public IsoType? IsoType { get; set; }

View File

@ -4,6 +4,6 @@ namespace MediaBrowser.Model.Dto
{
Default = 0,
Grouping = 1,
Cache = 2
Placeholder = 2
}
}

View File

@ -141,6 +141,11 @@ namespace MediaBrowser.Model.Entities
{
if (Type != MediaStreamType.Subtitle) return false;
if (string.IsNullOrEmpty(Codec) && !IsExternal)
{
return false;
}
return IsTextFormat(Codec);
}
}

View File

@ -1,11 +1,9 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.MediaInfo
{
public class PlaybackInfoRequest
{
public DeviceProfile DeviceProfile { get; set; }
public MediaSourceInfo MediaSource { get; set; }
}
}

View File

@ -96,7 +96,7 @@ namespace MediaBrowser.Providers.MediaInfo
var album = item.Parent as MusicAlbum;
var filename = item.Album ?? string.Empty;
filename += item.Artists.FirstOrDefault() ?? string.Empty;
filename += string.Join(",", item.Artists.ToArray());
filename += album == null ? item.Id.ToString("N") + "_primary" + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary";
filename = filename.GetMD5() + ".jpg";

View File

@ -169,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Channels
foreach (var item in result.Items)
{
var channelItem = (IChannelItem)item;
var channelItem = (IChannelMediaItem)item;
var channelFeatures = _manager.GetChannelFeatures(channelItem.ChannelId);
@ -179,7 +179,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
try
{
await DownloadChannelItem(item, options, cancellationToken, path);
await DownloadChannelItem(channelItem, options, cancellationToken, path);
}
catch (OperationCanceledException)
{
@ -210,13 +210,13 @@ namespace MediaBrowser.Server.Implementations.Channels
return channelOptions.DownloadSizeLimit;
}
private async Task DownloadChannelItem(BaseItem item,
private async Task DownloadChannelItem(IChannelMediaItem item,
ChannelOptions channelOptions,
CancellationToken cancellationToken,
string path)
{
var itemId = item.Id.ToString("N");
var sources = await _manager.GetChannelItemMediaSources(itemId, false, cancellationToken)
var sources = await _manager.GetStaticMediaSources(item, true, cancellationToken)
.ConfigureAwait(false);
var cachedVersions = sources.Where(i => i.Protocol == MediaProtocol.File).ToList();
@ -237,11 +237,9 @@ namespace MediaBrowser.Server.Implementations.Channels
}
}
var channelItem = (IChannelMediaItem)item;
var destination = Path.Combine(path, item.ChannelId, itemId);
var destination = Path.Combine(path, channelItem.ChannelId, itemId);
await _manager.DownloadChannelItem(channelItem, destination, new Progress<double>(), cancellationToken)
await _manager.DownloadChannelItem(item, destination, new Progress<double>(), cancellationToken)
.ConfigureAwait(false);
await RefreshMediaSourceItem(destination, cancellationToken).ConfigureAwait(false);

View File

@ -0,0 +1,43 @@
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Channels
{
public class ChannelDynamicMediaSourceProvider : IMediaSourceProvider
{
private readonly ChannelManager _channelManager;
public ChannelDynamicMediaSourceProvider(IChannelManager channelManager)
{
_channelManager = (ChannelManager)channelManager;
}
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
{
var channelItem = item as IChannelMediaItem;
if (channelItem != null)
{
return _channelManager.GetDynamicMediaSources(channelItem, cancellationToken);
}
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
public Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}

View File

@ -241,10 +241,25 @@ namespace MediaBrowser.Server.Implementations.Channels
return item;
}
public async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken)
public async Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken)
{
var item = (IChannelMediaItem)_libraryManager.GetItemById(id);
IEnumerable<ChannelMediaInfo> results = item.ChannelMediaSources;
var sources = SortMediaInfoResults(results)
.Select(i => GetMediaSource(item, i))
.ToList();
if (includeCachedVersions)
{
var cachedVersions = GetCachedChannelItemMediaSources(item);
sources.InsertRange(0, cachedVersions);
}
return sources.Where(IsValidMediaSource);
}
public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IChannelMediaItem item, CancellationToken cancellationToken)
{
var channel = GetChannel(item.ChannelId);
var channelPlugin = GetChannelProvider(channel);
@ -252,24 +267,25 @@ namespace MediaBrowser.Server.Implementations.Channels
IEnumerable<ChannelMediaInfo> results;
if (requiresCallback != null && includeDynamicSources)
if (requiresCallback != null)
{
results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken)
.ConfigureAwait(false);
.ConfigureAwait(false);
}
else
{
results = item.ChannelMediaSources;
results = new List<ChannelMediaInfo>();
}
var sources = SortMediaInfoResults(results).Select(i => GetMediaSource(item, i))
var list = SortMediaInfoResults(results)
.Select(i => GetMediaSource(item, i))
.Where(IsValidMediaSource)
.ToList();
var cachedVersions = GetCachedChannelItemMediaSources(item);
list.InsertRange(0, cachedVersions);
sources.InsertRange(0, cachedVersions);
return sources.Where(IsValidMediaSource);
return list;
}
private readonly ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>> _channelItemMediaInfo =
@ -297,14 +313,7 @@ namespace MediaBrowser.Server.Implementations.Channels
return list;
}
public IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(string id)
{
var item = (IChannelMediaItem)_libraryManager.GetItemById(id);
return GetCachedChannelItemMediaSources(item);
}
public IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(IChannelMediaItem item)
private IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(IChannelMediaItem item)
{
var filenamePrefix = item.Id.ToString("N");
var parentPath = Path.Combine(ChannelDownloadPath, item.ChannelId);
@ -339,7 +348,6 @@ namespace MediaBrowser.Server.Implementations.Channels
if (source != null)
{
source.Type = MediaSourceType.Cache;
return new[] { source };
}
}
@ -1408,8 +1416,7 @@ namespace MediaBrowser.Server.Implementations.Channels
public async Task DownloadChannelItem(IChannelMediaItem item, string destination,
IProgress<double> progress, CancellationToken cancellationToken)
{
var itemId = item.Id.ToString("N");
var sources = await GetChannelItemMediaSources(itemId, true, cancellationToken)
var sources = await GetDynamicMediaSources(item, cancellationToken)
.ConfigureAwait(false);
var list = sources.Where(i => i.Protocol == MediaProtocol.Http).ToList();

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Channels;
using System.Collections.Concurrent;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
@ -13,25 +14,24 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Server.Implementations.LiveTv;
namespace MediaBrowser.Server.Implementations.Library
{
public class MediaSourceManager : IMediaSourceManager
public class MediaSourceManager : IMediaSourceManager, IDisposable
{
private readonly IItemRepository _itemRepo;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private readonly IChannelManager _channelManager;
private IMediaSourceProvider[] _providers;
private readonly ILogger _logger;
public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, IChannelManager channelManager, ILogger logger)
public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger)
{
_itemRepo = itemRepo;
_userManager = userManager;
_libraryManager = libraryManager;
_channelManager = channelManager;
_logger = logger;
}
@ -133,24 +133,15 @@ namespace MediaBrowser.Server.Implementations.Library
IEnumerable<MediaSourceInfo> mediaSources;
var hasMediaSources = (IHasMediaSources)item;
var channelItem = item as IChannelMediaItem;
if (channelItem != null)
if (string.IsNullOrWhiteSpace(userId))
{
mediaSources = await _channelManager.GetChannelItemMediaSources(id, true, cancellationToken)
.ConfigureAwait(false);
mediaSources = hasMediaSources.GetMediaSources(enablePathSubstitution);
}
else
{
if (string.IsNullOrWhiteSpace(userId))
{
mediaSources = hasMediaSources.GetMediaSources(enablePathSubstitution);
}
else
{
var user = _userManager.GetUserById(userId);
mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user);
}
var user = _userManager.GetUserById(userId);
mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user);
}
var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false);
@ -161,11 +152,16 @@ namespace MediaBrowser.Server.Implementations.Library
foreach (var source in dynamicMediaSources)
{
source.SupportsTranscoding = false;
if (source.Protocol == MediaProtocol.File)
{
source.SupportsDirectStream = File.Exists(source.Path);
// TODO: Path substitution
}
else if (source.Protocol == MediaProtocol.Http)
{
// TODO: Allow this when the source is plain http, e.g. not HLS or Mpeg Dash
source.SupportsDirectStream = false;
}
else
{
@ -175,7 +171,7 @@ namespace MediaBrowser.Server.Implementations.Library
list.Add(source);
}
return SortMediaSources(list);
return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder);
}
private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
@ -190,7 +186,15 @@ namespace MediaBrowser.Server.Implementations.Library
{
try
{
return await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false);
var sources = await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false);
var list = sources.ToList();
foreach (var mediaSource in list)
{
SetKeyProperties(provider, mediaSource);
}
return list;
}
catch (Exception ex)
{
@ -199,6 +203,21 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
private void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource)
{
var prefix = provider.GetType().FullName.GetMD5().ToString("N") + "|";
if (!string.IsNullOrWhiteSpace(mediaSource.OpenKey) && !mediaSource.OpenKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
mediaSource.OpenKey = prefix + mediaSource.OpenKey;
}
if (!string.IsNullOrWhiteSpace(mediaSource.CloseKey) && !mediaSource.CloseKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
mediaSource.CloseKey = prefix + mediaSource.CloseKey;
}
}
public Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken)
{
return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken);
@ -294,5 +313,90 @@ namespace MediaBrowser.Server.Implementations.Library
{
return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
}
private readonly ConcurrentDictionary<string, string> _openStreams =
new ConcurrentDictionary<string, string>();
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var tuple = GetProvider(openKey);
var provider = tuple.Item1;
var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
SetKeyProperties(provider, mediaSource);
_openStreams.AddOrUpdate(mediaSource.CloseKey, mediaSource.CloseKey, (key, i) => mediaSource.CloseKey);
return mediaSource;
}
finally
{
_liveStreamSemaphore.Release();
}
}
public async Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var tuple = GetProvider(closeKey);
await tuple.Item1.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
string removedKey;
_openStreams.TryRemove(closeKey, out removedKey);
}
finally
{
_liveStreamSemaphore.Release();
}
}
private Tuple<IMediaSourceProvider, string> GetProvider(string key)
{
var keys = key.Split(new[] { '|' }, 2);
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase));
return new Tuple<IMediaSourceProvider, string>(provider, keys[1]);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
private readonly object _disposeLock = new object();
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
lock (_disposeLock)
{
foreach (var key in _openStreams.Keys.ToList())
{
var task = CloseMediaSource(key, CancellationToken.None);
Task.WaitAll(task);
}
_openStreams.Clear();
}
}
}
}
}

View File

@ -1,10 +1,8 @@
using System.Globalization;
using MediaBrowser.Common;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
@ -15,7 +13,6 @@ using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
@ -342,6 +339,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var service = GetService(channel);
_logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
info = await service.GetChannelStream(channel.ExternalId, null, cancellationToken).ConfigureAwait(false);
info.RequiresClosing = true;
info.CloseKey = info.Id;
}
else
{
@ -351,6 +350,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.RecordingInfo.Id);
info = await service.GetRecordingStream(recording.RecordingInfo.Id, null, cancellationToken).ConfigureAwait(false);
info.RequiresClosing = true;
info.CloseKey = info.Id;
}
_logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info));

View File

@ -0,0 +1,77 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv
{
public class LiveTvMediaSourceProvider : IMediaSourceProvider
{
private readonly ILiveTvManager _liveTvManager;
public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager)
{
_liveTvManager = liveTvManager;
}
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
{
var channelItem = item as ILiveTvItem;
if (channelItem != null)
{
var hasMetadata = (IHasMetadata)channelItem;
if (string.IsNullOrWhiteSpace(hasMetadata.Path))
{
return GetMediaSourcesInternal(channelItem, cancellationToken);
}
}
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(ILiveTvItem item, CancellationToken cancellationToken)
{
var hasMediaSources = (IHasMediaSources)item;
var sources = hasMediaSources.GetMediaSources(false)
.ToList();
foreach (var source in sources)
{
source.Type = MediaSourceType.Default;
source.RequiresOpening = true;
var openKeys = new List<string>();
openKeys.Add(item.GetType().Name);
openKeys.Add(item.Id.ToString("N"));
source.OpenKey = string.Join("|", openKeys.ToArray());
}
return sources;
}
public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
var keys = openKey.Split(new[] { '|' }, 2);
if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase))
{
return await _liveTvManager.GetChannelStream(keys[1], cancellationToken).ConfigureAwait(false);
}
return await _liveTvManager.GetRecordingStream(keys[1], cancellationToken).ConfigureAwait(false);
}
public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
{
return _liveTvManager.CloseLiveStream(closeKey, cancellationToken);
}
}
}

View File

@ -111,6 +111,7 @@
<Compile Include="Branding\BrandingConfigurationFactory.cs" />
<Compile Include="Channels\ChannelConfigurations.cs" />
<Compile Include="Channels\ChannelDownloadScheduledTask.cs" />
<Compile Include="Channels\ChannelDynamicMediaSourceProvider.cs" />
<Compile Include="Channels\ChannelImageProvider.cs" />
<Compile Include="Channels\ChannelItemImageProvider.cs" />
<Compile Include="Channels\ChannelManager.cs" />
@ -225,6 +226,7 @@
<Compile Include="LiveTv\LiveTvConfigurationFactory.cs" />
<Compile Include="LiveTv\LiveTvDtoService.cs" />
<Compile Include="LiveTv\LiveTvManager.cs" />
<Compile Include="LiveTv\LiveTvMediaSourceProvider.cs" />
<Compile Include="LiveTv\ProgramImageProvider.cs" />
<Compile Include="LiveTv\RecordingImageProvider.cs" />
<Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />

View File

@ -161,6 +161,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
mediaSource.Path = sendFileResult.Path;
mediaSource.Protocol = sendFileResult.Protocol;
mediaSource.RequiredHttpHeaders = sendFileResult.RequiredHttpHeaders;
mediaSource.SupportsTranscoding = false;
}
}

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sync;
@ -61,7 +62,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
foreach (var mediaSource in localItem.Item.MediaSources)
{
await TryAddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget, cancellationToken).ConfigureAwait(false);
AddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget);
}
}
}
@ -71,41 +72,70 @@ namespace MediaBrowser.Server.Implementations.Sync
return list;
}
private async Task TryAddMediaSource(List<MediaSourceInfo> list,
private void AddMediaSource(List<MediaSourceInfo> list,
LocalItem item,
MediaSourceInfo mediaSource,
IServerSyncProvider provider,
SyncTarget target,
CancellationToken cancellationToken)
SyncTarget target)
{
SetStaticMediaSourceInfo(item, mediaSource);
var requiresDynamicAccess = provider as IHasDynamicAccess;
if (requiresDynamicAccess == null)
if (requiresDynamicAccess != null)
{
list.Add(mediaSource);
return;
mediaSource.RequiresOpening = true;
var keyList = new List<string>();
keyList.Add(provider.GetType().FullName.GetMD5().ToString("N"));
keyList.Add(target.Id.GetMD5().ToString("N"));
keyList.Add(item.Id);
mediaSource.OpenKey = string.Join("|", keyList.ToArray());
}
}
public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
var openKeys = openKey.Split(new[] { '|' }, 3);
var provider = _syncManager.ServerSyncProviders
.FirstOrDefault(i => string.Equals(openKeys[0], i.GetType().FullName.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
var target = provider.GetAllSyncTargets()
.FirstOrDefault(i => string.Equals(openKeys[1], i.Id.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
var dataProvider = _syncManager.GetDataProvider(provider, target);
var localItem = await dataProvider.Get(target, openKeys[2]).ConfigureAwait(false);
var requiresDynamicAccess = (IHasDynamicAccess)provider;
var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(localItem.LocalPath, target, cancellationToken).ConfigureAwait(false);
var mediaSource = localItem.Item.MediaSources.First();
SetStaticMediaSourceInfo(localItem, mediaSource);
foreach (var stream in mediaSource.MediaStreams)
{
var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false);
stream.Path = dynamicStreamInfo.Path;
}
try
{
var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(item.LocalPath, target, cancellationToken).ConfigureAwait(false);
mediaSource.Path = dynamicInfo.Path;
mediaSource.Protocol = dynamicInfo.Protocol;
mediaSource.RequiredHttpHeaders = dynamicInfo.RequiredHttpHeaders;
foreach (var stream in mediaSource.MediaStreams)
{
var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false);
return mediaSource;
}
stream.Path = dynamicStreamInfo.Path;
}
private void SetStaticMediaSourceInfo(LocalItem item, MediaSourceInfo mediaSource)
{
mediaSource.Id = item.Id;
mediaSource.SupportsTranscoding = false;
}
mediaSource.Path = dynamicInfo.Path;
mediaSource.Protocol = dynamicInfo.Protocol;
list.Add(mediaSource);
}
catch (Exception ex)
{
_logger.ErrorException("Error getting dynamic media source info", ex);
}
public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}

View File

@ -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, ChannelManager, LogManager.GetLogger("MediaSourceManager"));
MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"));
RegisterSingleInstance(MediaSourceManager);
SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager);

View File

@ -9,8 +9,8 @@
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.603" />
<dependency id="NLog" version="3.2.0.0" />

View File

@ -4,13 +4,13 @@
<id>MediaBrowser.Common</id>
<version>3.0.603</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common model objects and interfaces used by all Media Browser solutions.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<description>Contains common model objects and interfaces used by all Emby solutions.</description>
<copyright>Copyright © Emby 2013</copyright>
</metadata>
<files>
<file src="dlls\net35\MediaBrowser.Model.dll" target="lib\net35\MediaBrowser.Model.dll" />

View File

@ -4,13 +4,13 @@
<id>MediaBrowser.Model.Signed</id>
<version>3.0.603</version>
<title>MediaBrowser.Model - Signed Edition</title>
<authors>Media Browser Team</authors>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common model objects and interfaces used by all Media Browser solutions.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<description>Contains common model objects and interfaces used by all Emby solutions.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
</dependencies>
</metadata>

View File

@ -4,13 +4,13 @@
<id>MediaBrowser.Server.Core</id>
<version>3.0.603</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.603" />
</dependencies>