diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding')
9 files changed, 216 insertions, 238 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index a6a87a3fc..e9204ef5b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class AudioEncoder : BaseEncoder { - public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager) + public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) { } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 44e0d1517..78ac92f25 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -14,6 +13,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,10 +31,8 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly ILogger Logger; protected readonly IServerConfigurationManager ConfigurationManager; protected readonly IFileSystem FileSystem; - protected readonly ILiveTvManager LiveTvManager; protected readonly IIsoManager IsoManager; protected readonly ILibraryManager LibraryManager; - protected readonly IChannelManager ChannelManager; protected readonly ISessionManager SessionManager; protected readonly ISubtitleEncoder SubtitleEncoder; protected readonly IMediaSourceManager MediaSourceManager; @@ -45,20 +43,18 @@ namespace MediaBrowser.MediaEncoding.Encoder ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, - ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, - IChannelManager channelManager, - ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) + ISessionManager sessionManager, + ISubtitleEncoder subtitleEncoder, + IMediaSourceManager mediaSourceManager) { MediaEncoder = mediaEncoder; Logger = logger; ConfigurationManager = configurationManager; FileSystem = fileSystem; - LiveTvManager = liveTvManager; IsoManager = isoManager; LibraryManager = libraryManager; - ChannelManager = channelManager; SessionManager = sessionManager; SubtitleEncoder = subtitleEncoder; MediaSourceManager = mediaSourceManager; @@ -68,7 +64,7 @@ namespace MediaBrowser.MediaEncoding.Encoder IProgress<double> progress, CancellationToken cancellationToken) { - var encodingJob = await new EncodingJobFactory(Logger, LiveTvManager, LibraryManager, ChannelManager, MediaSourceManager) + var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager) .CreateJob(options, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false); encodingJob.OutputFilePath = GetOutputFilePath(encodingJob); @@ -477,53 +473,25 @@ namespace MediaBrowser.MediaEncoding.Encoder state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false); } - if (string.IsNullOrEmpty(state.MediaPath)) + if (state.MediaSource.RequiresOpening) { - var checkCodecs = false; - - if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name)) + var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest { - var streamInfo = await LiveTvManager.GetChannelStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; + OpenToken = state.MediaSource.OpenToken - await Task.Delay(1500, cancellationToken).ConfigureAwait(false); + }, false, cancellationToken).ConfigureAwait(false); - AttachMediaStreamInfo(state, streamInfo, state.Options); - checkCodecs = true; - } + AttachMediaStreamInfo(state, liveStreamResponse.MediaSource, state.Options); - else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) || - string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name)) + if (state.IsVideoRequest) { - var streamInfo = await LiveTvManager.GetRecordingStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; - - await Task.Delay(1500, cancellationToken).ConfigureAwait(false); - - AttachMediaStreamInfo(state, streamInfo, state.Options); - checkCodecs = true; + EncodingJobFactory.TryStreamCopy(state, state.Options); } + } - if (state.IsVideoRequest && checkCodecs) - { - if (state.VideoStream != null && EncodingJobFactory.CanStreamCopyVideo(state.Options, state.VideoStream)) - { - state.OutputVideoCodec = "copy"; - } - - if (state.AudioStream != null && EncodingJobFactory.CanStreamCopyAudio(state.Options, state.AudioStream, state.SupportedAudioCodecs)) - { - state.OutputAudioCodec = "copy"; - } - } + if (state.MediaSource.BufferMs.HasValue) + { + await Task.Delay(state.MediaSource.BufferMs.Value, cancellationToken).ConfigureAwait(false); } } @@ -531,22 +499,7 @@ namespace MediaBrowser.MediaEncoding.Encoder MediaSourceInfo mediaSource, EncodingJobOptions videoRequest) { - state.InputProtocol = mediaSource.Protocol; - state.MediaPath = mediaSource.Path; - state.RunTimeTicks = mediaSource.RunTimeTicks; - state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; - state.InputBitrate = mediaSource.Bitrate; - state.InputFileSize = mediaSource.Size; - state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - - if (state.ReadInputAtNativeFramerate) - { - state.OutputAudioSync = "1000"; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - } - - EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest); + EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource, videoRequest); } /// <summary> @@ -998,7 +951,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!string.IsNullOrEmpty(state.SubtitleStream.Language)) { - var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath); + var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.MediaSource.Protocol, CancellationToken.None).Result; if (!string.IsNullOrEmpty(charenc)) { diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index c8d121eea..767f3f829 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -1,7 +1,8 @@ -using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -26,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public EncodingJobOptions Options { get; set; } 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; } public MediaStream SubtitleStream { get; set; } @@ -76,12 +77,12 @@ namespace MediaBrowser.MediaEncoding.Encoder } private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; + private readonly IMediaSourceManager _mediaSourceManager; - public EncodingJob(ILogger logger, ILiveTvManager liveTvManager) + public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager) { _logger = logger; - _liveTvManager = liveTvManager; + _mediaSourceManager = mediaSourceManager; Id = Guid.NewGuid().ToString("N"); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); @@ -89,7 +90,6 @@ namespace MediaBrowser.MediaEncoding.Encoder SupportedAudioCodecs = new List<string>(); PlayableStreamFileNames = new List<string>(); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - AllMediaStreams = new List<MediaStream>(); TaskCompletionSource = new TaskCompletionSource<bool>(); } @@ -136,15 +136,15 @@ namespace MediaBrowser.MediaEncoding.Encoder private async void DisposeLiveStream() { - if (!string.IsNullOrEmpty(LiveTvStreamId)) + if (MediaSource.RequiresClosing) { try { - await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { - _logger.ErrorException("Error closing live tv stream", ex); + _logger.ErrorException("Error closing media source", ex); } } } @@ -394,6 +394,42 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + public int? TargetVideoStreamCount + { + get + { + if (Options.Static) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (Options.Static) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded) { var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index c5783e188..8d8201074 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -1,9 +1,10 @@ -using MediaBrowser.Controller.Channels; +using System.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; @@ -19,19 +20,15 @@ namespace MediaBrowser.MediaEncoding.Encoder public class EncodingJobFactory { private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; private readonly ILibraryManager _libraryManager; - private readonly IChannelManager _channelManager; private readonly IMediaSourceManager _mediaSourceManager; protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public EncodingJobFactory(ILogger logger, ILiveTvManager liveTvManager, ILibraryManager libraryManager, IChannelManager channelManager, IMediaSourceManager mediaSourceManager) + public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager) { _logger = logger; - _liveTvManager = liveTvManager; _libraryManager = libraryManager; - _channelManager = channelManager; _mediaSourceManager = mediaSourceManager; } @@ -42,9 +39,9 @@ namespace MediaBrowser.MediaEncoding.Encoder if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.OutputContainer); - } - - var state = new EncodingJob(_logger, _liveTvManager) + } + + var state = new EncodingJob(_logger, _mediaSourceManager) { Options = options, IsVideoRequest = isVideoRequest, @@ -58,106 +55,17 @@ namespace MediaBrowser.MediaEncoding.Encoder } var item = _libraryManager.GetItemById(request.ItemId); - - List<MediaStream> mediaStreams = null; - state.ItemType = item.GetType().Name; - if (item is ILiveTvRecording) - { - var recording = await _liveTvManager.GetInternalRecording(request.ItemId, 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.ItemId); - - 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.ItemId, false, cancellationToken).ConfigureAwait(false); + state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - 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; - } + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); - state.IsoType = mediaSource.IsoType; + var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) + ? mediaSources.First() + : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); - state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); - - if (mediaSource.Timestamp.HasValue) - { - state.InputTimestamp = mediaSource.Timestamp.Value; - } - } - - state.RunTimeTicks = mediaSource.RunTimeTicks; - } - - AttachMediaStreamInfo(state, mediaStreams, request); + AttachMediaStreamInfo(state, mediaSource, options); state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; @@ -185,26 +93,73 @@ namespace MediaBrowser.MediaEncoding.Encoder ApplyDeviceProfileSettings(state); - if (isVideoRequest) + TryStreamCopy(state, request); + + return state; + } + + internal static void TryStreamCopy(EncodingJob state, + EncodingJobOptions videoRequest) + { + if (state.IsVideoRequest) { - if (state.VideoStream != null && CanStreamCopyVideo(request, state.VideoStream)) + if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) { state.OutputVideoCodec = "copy"; } - if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs)) + if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs)) { state.OutputAudioCodec = "copy"; } } - - return state; } internal static void AttachMediaStreamInfo(EncodingJob state, - List<MediaStream> mediaStreams, + MediaSourceInfo mediaSource, EncodingJobOptions videoRequest) { + 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; + state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; + state.InputBitrate = mediaSource.Bitrate; + state.InputFileSize = mediaSource.Size; + state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; + + if (state.ReadInputAtNativeFramerate || + mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) + { + state.OutputAudioSync = "1000"; + state.InputVideoSync = "-1"; + state.InputAudioSync = "1"; + } + + var mediaStreams = mediaSource.MediaStreams; + if (videoRequest != null) { if (string.IsNullOrEmpty(videoRequest.VideoCodec)) @@ -233,7 +188,7 @@ namespace MediaBrowser.MediaEncoding.Encoder state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } - state.AllMediaStreams = mediaStreams; + state.MediaSource = mediaSource; } /// <summary> @@ -771,7 +726,9 @@ namespace MediaBrowser.MediaEncoding.Encoder state.TargetTimestamp, state.IsTargetAnamorphic, state.IsTargetCabac, - state.TargetRefFrames); + state.TargetRefFrames, + state.TargetVideoStreamCount, + state.TargetAudioStreamCount); if (mediaProfile != null) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7fd91bf6f..425889807 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -577,10 +577,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger, ConfigurationManager, FileSystem, - LiveTvManager, IsoManager, LibraryManager, - ChannelManager, SessionManager, SubtitleEncoder(), MediaSourceManager()) @@ -599,10 +597,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger, ConfigurationManager, FileSystem, - LiveTvManager, IsoManager, LibraryManager, - ChannelManager, SessionManager, SubtitleEncoder(), MediaSourceManager()) diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index a714b160e..26d4a7650 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class VideoEncoder : BaseEncoder { - public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager) + public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) { } @@ -73,10 +73,13 @@ namespace MediaBrowser.MediaEncoding.Encoder args; } - var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", - 5.ToString(UsCulture)); + if (state.Options.Context == EncodingContext.Streaming) + { + var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", + 5.ToString(UsCulture)); - args += keyFrameArg; + args += keyFrameArg; + } var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs index 4d426683f..e711c02b6 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -43,10 +43,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles subEvent.StartPositionTicks = GetTicks(sections[headers["Start"]]); subEvent.EndPositionTicks = GetTicks(sections[headers["End"]]); - //RemoteNativeFormatting(subEvent); - subEvent.Text = string.Join(",", sections.Skip(headers["Text"])); - subEvent.Text = subEvent.Text.Replace(@"\N", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase); + RemoteNativeFormatting(subEvent); + + subEvent.Text = subEvent.Text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase); + subEvent.Text = Regex.Replace(subEvent.Text, @"\{(\\[\w]+\(?([\w\d]+,?)+\)?)+\}", string.Empty, RegexOptions.IgnoreCase); trackInfo.TrackEvents.Add(subEvent); diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs index 358251625..bfc6c60fc 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -148,8 +148,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles public static string GetFormattedText(string text) { - text = text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase) - .Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase); + text = text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase); bool italic = false; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7d74c51ba..14d3e7284 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -29,8 +30,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IFileSystem _fileSystem; private readonly IMediaEncoder _mediaEncoder; private readonly IJsonSerializer _json; + private readonly IHttpClient _httpClient; + private readonly IMediaSourceManager _mediaSourceManager; - public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json) + public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager) { _libraryManager = libraryManager; _logger = logger; @@ -38,6 +41,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _json = json; + _httpClient = httpClient; + _mediaSourceManager = mediaSourceManager; } private string SubtitleCachePath @@ -127,9 +132,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles int subtitleStreamIndex, CancellationToken cancellationToken) { - var item = (Video)_libraryManager.GetItemById(new Guid(itemId)); + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, false, cancellationToken).ConfigureAwait(false); - var mediaSource = item.GetMediaSources(false) + var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId)); var subtitleStream = mediaSource.MediaStreams @@ -149,20 +154,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false); - var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item3).ConfigureAwait(false); + var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item2, fileInfo.Item4, cancellationToken).ConfigureAwait(false); - return new Tuple<Stream, string>(stream, fileInfo.Item2); + return new Tuple<Stream, string>(stream, fileInfo.Item3); } - private async Task<Stream> GetSubtitleStream(string path, bool requiresCharset) + private async Task<Stream> GetSubtitleStream(string path, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken) { if (requiresCharset) { - var charset = GetSubtitleFileCharacterSet(path); + var charset = await GetSubtitleFileCharacterSet(path, protocol, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(charset)) { - using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + using (var fs = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false)) { using (var reader = new StreamReader(fs, GetEncoding(charset))) { @@ -196,7 +201,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private async Task<Tuple<string, string, bool>> GetReadableFile(string mediaPath, + private async Task<Tuple<string, MediaProtocol, string, bool>> GetReadableFile(string mediaPath, string[] inputFiles, MediaProtocol protocol, MediaStream subtitleStream, @@ -228,12 +233,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles } // Extract - var outputPath = GetSubtitleCachePath(mediaPath, subtitleStream.Index, "." + outputFormat); + var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, "." + outputFormat); await ExtractTextSubtitle(inputFiles, protocol, subtitleStream.Index, outputCodec, outputPath, cancellationToken) .ConfigureAwait(false); - return new Tuple<string, string, bool>(outputPath, outputFormat, false); + return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, outputFormat, false); } var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) @@ -242,14 +247,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (GetReader(currentFormat, false) == null) { // Convert - var outputPath = GetSubtitleCachePath(mediaPath, subtitleStream.Index, ".srt"); + var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, ".srt"); - await ConvertTextSubtitleToSrt(subtitleStream.Path, outputPath, cancellationToken).ConfigureAwait(false); + await ConvertTextSubtitleToSrt(subtitleStream.Path, protocol, outputPath, cancellationToken).ConfigureAwait(false); - return new Tuple<string, string, bool>(outputPath, "srt", true); + return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, "srt", true); } - return new Tuple<string, string, bool>(subtitleStream.Path, currentFormat, true); + return new Tuple<string, MediaProtocol, string, bool>(subtitleStream.Path, protocol, currentFormat, true); } private async Task<SubtitleTrackInfo> GetTrackInfo(Stream stream, @@ -336,10 +341,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Converts the text subtitle to SRT. /// </summary> /// <param name="inputPath">The input path.</param> + /// <param name="inputProtocol">The input protocol.</param> /// <param name="outputPath">The output path.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - public async Task ConvertTextSubtitleToSrt(string inputPath, string outputPath, CancellationToken cancellationToken) + private async Task ConvertTextSubtitleToSrt(string inputPath, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -349,7 +355,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { if (!File.Exists(outputPath)) { - await ConvertTextSubtitleToSrtInternal(inputPath, outputPath).ConfigureAwait(false); + await ConvertTextSubtitleToSrtInternal(inputPath, inputProtocol, outputPath, cancellationToken).ConfigureAwait(false); } } finally @@ -362,13 +368,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Converts the text subtitle to SRT internal. /// </summary> /// <param name="inputPath">The input path.</param> + /// <param name="inputProtocol">The input protocol.</param> /// <param name="outputPath">The output path.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException">inputPath + /// <exception cref="System.ArgumentNullException"> + /// inputPath /// or - /// outputPath</exception> + /// outputPath + /// </exception> /// <exception cref="System.ApplicationException"></exception> - private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string outputPath) + private async Task ConvertTextSubtitleToSrtInternal(string inputPath, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { @@ -382,7 +392,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); - var encodingParam = GetSubtitleFileCharacterSet(inputPath); + var encodingParam = await GetSubtitleFileCharacterSet(inputPath, inputProtocol, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(encodingParam)) { @@ -688,32 +698,41 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension) + private string GetSubtitleCachePath(string mediaPath, MediaProtocol protocol, int subtitleStreamIndex, string outputSubtitleExtension) { - var ticksParam = string.Empty; + if (protocol == MediaProtocol.File) + { + var ticksParam = string.Empty; + + var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); - var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); + var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam).GetMD5() + outputSubtitleExtension; - var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam).GetMD5() + outputSubtitleExtension; + var prefix = filename.Substring(0, 1); + + return Path.Combine(SubtitleCachePath, prefix, filename); + } + else + { + var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5() + outputSubtitleExtension; - var prefix = filename.Substring(0, 1); + var prefix = filename.Substring(0, 1); - return Path.Combine(SubtitleCachePath, prefix, filename); + return Path.Combine(SubtitleCachePath, prefix, filename); + } } - /// <summary> - /// Gets the subtitle language encoding param. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>System.String.</returns> - public string GetSubtitleFileCharacterSet(string path) + public async Task<string> GetSubtitleFileCharacterSet(string path, MediaProtocol protocol, CancellationToken cancellationToken) { - if (GetFileEncoding(path).Equals(Encoding.UTF8)) + if (protocol == MediaProtocol.File) { - return string.Empty; + if (GetFileEncoding(path).Equals(Encoding.UTF8)) + { + return string.Empty; + } } - var charset = DetectCharset(path); + var charset = await DetectCharset(path, protocol, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(charset)) { @@ -769,11 +788,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private string DetectCharset(string path) + private async Task<string> DetectCharset(string path, MediaProtocol protocol, CancellationToken cancellationToken) { try { - using (var file = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var file = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false)) { var detector = new CharsetDetector(); detector.Feed(file); @@ -819,5 +838,19 @@ namespace MediaBrowser.MediaEncoding.Subtitles // It's ok - anything aside from utf is ok since that's what we're looking for return Encoding.Default; } + + private async Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken) + { + if (protocol == MediaProtocol.Http) + { + return await _httpClient.Get(path, cancellationToken).ConfigureAwait(false); + } + if (protocol == MediaProtocol.File) + { + return _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + } + + throw new ArgumentOutOfRangeException("protocol"); + } } } |
