Procházet zdrojové kódy

move media encoder to server project

Luke Pulverenti před 11 roky
rodič
revize
c8a106f485
25 změnil soubory, kde provedl 144 přidání a 73 odebrání
  1. 1 2
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 1 1
      MediaBrowser.Api/Playback/Hls/AudioHlsService.cs
  3. 1 1
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  4. 1 1
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  5. 1 1
      MediaBrowser.Api/Playback/Progressive/AudioService.cs
  6. 1 1
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  7. 1 1
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  8. 0 2
      MediaBrowser.Common/MediaBrowser.Common.csproj
  9. 1 1
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  10. 1 2
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  11. 2 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  12. 1 2
      MediaBrowser.Controller/MediaInfo/FFMpegManager.cs
  13. 6 9
      MediaBrowser.Controller/MediaInfo/IMediaEncoder.cs
  14. 4 4
      MediaBrowser.Controller/MediaInfo/InternalMediaInfoResult.cs
  15. 15 6
      MediaBrowser.Controller/MediaInfo/MediaEncoderHelpers.cs
  16. 22 1
      MediaBrowser.Model/Entities/MediaStream.cs
  17. 2 2
      MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
  18. 4 5
      MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs
  19. 2 3
      MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs
  20. 3 4
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs
  21. 1 2
      MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
  22. 1 1
      MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
  23. 57 5
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  24. 12 12
      MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs
  25. 3 4
      MediaBrowser.ServerApplication/ApplicationHost.cs

+ 1 - 2
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -1,6 +1,5 @@
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
@@ -733,7 +732,7 @@ namespace MediaBrowser.Api.Playback
                 return "-";
             }
 
-            var type = InputType.AudioFile;
+            var type = InputType.File;
 
             var inputPath = new[] { state.MediaPath };
 

+ 1 - 1
MediaBrowser.Api/Playback/Hls/AudioHlsService.cs

@@ -1,9 +1,9 @@
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.IO;

+ 1 - 1
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -1,11 +1,11 @@
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dto;

+ 1 - 1
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -1,9 +1,9 @@
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.IO;
 using ServiceStack;

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -1,10 +1,10 @@
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.IO;
 using ServiceStack;

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -1,12 +1,12 @@
 using System;
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.IO;

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -1,10 +1,10 @@
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.IO;
 using ServiceStack;

+ 0 - 2
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -65,8 +65,6 @@
     <Compile Include="IO\IFileSystem.cs" />
     <Compile Include="IO\ProgressStream.cs" />
     <Compile Include="IO\StreamDefaults.cs" />
-    <Compile Include="MediaInfo\MediaInfoResult.cs" />
-    <Compile Include="MediaInfo\IMediaEncoder.cs" />
     <Compile Include="Net\BasePeriodicWebSocketListener.cs" />
     <Compile Include="Configuration\IApplicationPaths.cs" />
     <Compile Include="Net\HttpRequestOptions.cs" />

+ 1 - 1
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -109,7 +109,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// <returns>System.String.</returns>
         public override string GetUserDataKey()
         {
-            var parent = Parent as MusicAlbum;
+            var parent = FindParent<MusicAlbum>();
 
             if (parent != null)
             {

+ 1 - 2
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -1,5 +1,4 @@
-using System.IO;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Querying;
 using System.Collections.Generic;

+ 2 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -125,6 +125,8 @@
     <Compile Include="LiveTv\SeriesTimerInfo.cs" />
     <Compile Include="LiveTv\TimerInfo.cs" />
     <Compile Include="Localization\ILocalizationManager.cs" />
+    <Compile Include="MediaInfo\IMediaEncoder.cs" />
+    <Compile Include="MediaInfo\InternalMediaInfoResult.cs" />
     <Compile Include="Net\IHasResultFactory.cs" />
     <Compile Include="Net\IHttpResultFactory.cs" />
     <Compile Include="Net\IHttpServer.cs" />

+ 1 - 2
MediaBrowser.Controller/MediaInfo/FFMpegManager.cs

@@ -1,6 +1,5 @@
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
@@ -178,7 +177,7 @@ namespace MediaBrowser.Controller.MediaInfo
 
                             Directory.CreateDirectory(parentPath);
 
-                            await _encoder.ExtractImage(inputPath, type, video.Video3DFormat, time, path, cancellationToken).ConfigureAwait(false);
+                            await _encoder.ExtractImage(inputPath, type, false, video.Video3DFormat, time, path, cancellationToken).ConfigureAwait(false);
                             chapter.ImagePath = path;
                             changesMade = true;
                         }

+ 6 - 9
MediaBrowser.Common/MediaInfo/IMediaEncoder.cs → MediaBrowser.Controller/MediaInfo/IMediaEncoder.cs

@@ -3,7 +3,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Entities;
 
-namespace MediaBrowser.Common.MediaInfo
+namespace MediaBrowser.Controller.MediaInfo
 {
     /// <summary>
     /// Interface IMediaEncoder
@@ -27,12 +27,13 @@ namespace MediaBrowser.Common.MediaInfo
         /// </summary>
         /// <param name="inputFiles">The input files.</param>
         /// <param name="type">The type.</param>
+        /// <param name="isAudio">if set to <c>true</c> [is audio].</param>
         /// <param name="threedFormat">The threed format.</param>
         /// <param name="offset">The offset.</param>
         /// <param name="outputPath">The output path.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task ExtractImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken);
+        Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken);
 
         /// <summary>
         /// Extracts the text subtitle.
@@ -62,7 +63,7 @@ namespace MediaBrowser.Common.MediaInfo
         /// <param name="type">The type.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task<MediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type, CancellationToken cancellationToken);
+        Task<InternalMediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the probe size argument.
@@ -86,13 +87,9 @@ namespace MediaBrowser.Common.MediaInfo
     public enum InputType
     {
         /// <summary>
-        /// The audio file
+        /// The file
         /// </summary>
-        AudioFile,
-        /// <summary>
-        /// The video file
-        /// </summary>
-        VideoFile,
+        File,
         /// <summary>
         /// The bluray
         /// </summary>

+ 4 - 4
MediaBrowser.Common/MediaInfo/MediaInfoResult.cs → MediaBrowser.Controller/MediaInfo/InternalMediaInfoResult.cs

@@ -1,12 +1,12 @@
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
 
-namespace MediaBrowser.Common.MediaInfo
+namespace MediaBrowser.Controller.MediaInfo
 {
     /// <summary>
     /// Class MediaInfoResult
     /// </summary>
-    public class MediaInfoResult
+    public class InternalMediaInfoResult
     {
         /// <summary>
         /// Gets or sets the streams.

+ 15 - 6
MediaBrowser.Controller/MediaInfo/MediaEncoderHelpers.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 
@@ -29,7 +28,7 @@ namespace MediaBrowser.Controller.MediaInfo
         {
             var inputPath = isoMount == null ? new[] { videoPath } : new[] { isoMount.MountedPath };
 
-            type = InputType.VideoFile;
+            type = InputType.File;
 
             switch (videoType)
             {
@@ -87,7 +86,7 @@ namespace MediaBrowser.Controller.MediaInfo
         /// <returns>InputType.</returns>
         public static InputType GetInputType(VideoType? videoType, IsoType? isoType)
         {
-            var type = InputType.AudioFile;
+            var type = InputType.File;
 
             if (videoType.HasValue)
             {
@@ -119,12 +118,22 @@ namespace MediaBrowser.Controller.MediaInfo
             return type;
         }
 
-        public static IEnumerable<MediaStream> GetMediaStreams(MediaInfoResult data)
+        public static Model.Entities.MediaInfo GetMediaInfo(InternalMediaInfoResult data)
         {
             var internalStreams = data.streams ?? new MediaStreamInfo[] { };
 
-            return internalStreams.Select(s => GetMediaStream(s, data.format))
-                .Where(i => i != null);
+            var info = new Model.Entities.MediaInfo();
+
+            info.MediaStreams = internalStreams.Select(s => GetMediaStream(s, data.format))
+                .Where(i => i != null)
+                .ToList();
+
+            if (data.format != null)
+            {
+                info.Format = data.format.format_name;
+            }
+
+            return info;
         }
 
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");

+ 22 - 1
MediaBrowser.Model/Entities/MediaStream.cs

@@ -1,4 +1,5 @@
-
+using System.Collections.Generic;
+
 namespace MediaBrowser.Model.Entities
 {
     /// <summary>
@@ -145,4 +146,24 @@ namespace MediaBrowser.Model.Entities
         /// </summary>
         Subtitle
     }
+
+    public class MediaInfo
+    {
+        /// <summary>
+        /// Gets or sets the media streams.
+        /// </summary>
+        /// <value>The media streams.</value>
+        public List<MediaStream> MediaStreams { get; set; }
+
+        /// <summary>
+        /// Gets or sets the format.
+        /// </summary>
+        /// <value>The format.</value>
+        public string Format { get; set; }
+
+        public MediaInfo()
+        {
+            MediaStreams = new List<MediaStream>();
+        }
+    }
 }

+ 2 - 2
MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs

@@ -1,9 +1,9 @@
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
@@ -163,7 +163,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
                         Directory.CreateDirectory(parentPath);
 
-                        await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.AudioFile, null, null, path, cancellationToken).ConfigureAwait(false);
+                        await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, path, cancellationToken).ConfigureAwait(false);
                     }
                     finally
                     {

+ 4 - 5
MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.MediaInfo;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Providers;
@@ -104,11 +103,11 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <exception cref="System.ArgumentNullException">inputPath
         /// or
         /// cache</exception>
-        protected async Task<MediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken)
+        protected async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
-            var type = InputType.AudioFile;
+            var type = InputType.File;
             var inputPath = isoMount == null ? new[] { item.Path } : new[] { isoMount.MountedPath };
 
             var video = item as Video;
@@ -146,7 +145,7 @@ namespace MediaBrowser.Providers.MediaInfo
         /// Normalizes the FF probe result.
         /// </summary>
         /// <param name="result">The result.</param>
-        protected void NormalizeFFProbeResult(MediaInfoResult result)
+        protected void NormalizeFFProbeResult(InternalMediaInfoResult result)
         {
             if (result.format != null && result.format.tags != null)
             {

+ 2 - 3
MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -58,9 +57,9 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="data">The data.</param>
         /// <returns>Task.</returns>
-        protected Task Fetch(Audio audio, CancellationToken cancellationToken, MediaInfoResult data)
+        protected Task Fetch(Audio audio, CancellationToken cancellationToken, InternalMediaInfoResult data)
         {
-            var mediaStreams = MediaEncoderHelpers.GetMediaStreams(data).ToList();
+            var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
 
             audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.Video);
 

+ 3 - 4
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs

@@ -1,5 +1,4 @@
 using DvdLib.Ifo;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Localization;
@@ -310,7 +309,7 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <param name="data">The data.</param>
         /// <param name="isoMount">The iso mount.</param>
         /// <returns>Task.</returns>
-        protected async Task Fetch(Video video, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken, MediaInfoResult data, IIsoMount isoMount)
+        protected async Task Fetch(Video video, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount)
         {
             if (data.format != null)
             {
@@ -323,7 +322,7 @@ namespace MediaBrowser.Providers.MediaInfo
                 }
             }
 
-            var mediaStreams = MediaEncoderHelpers.GetMediaStreams(data).ToList();
+            var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
 
             var chapters = data.Chapters ?? new List<ChapterInfo>();
 
@@ -370,7 +369,7 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <param name="video">The video.</param>
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="data">The data.</param>
-        private void FetchWtvInfo(Video video, bool force, MediaInfoResult data)
+        private void FetchWtvInfo(Video video, bool force, InternalMediaInfoResult data)
         {
             if (data.format == null || data.format.tags == null)
             {

+ 1 - 2
MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -255,7 +254,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
                 var inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, video.LocationType == LocationType.Remote, video.VideoType, video.IsoType, isoMount, video.PlayableStreamFileNames, out type);
 
-                await _mediaEncoder.ExtractImage(inputPath, type, video.Video3DFormat, imageOffset, path, cancellationToken).ConfigureAwait(false);
+                await _mediaEncoder.ExtractImage(inputPath, type, false, video.Video3DFormat, imageOffset, path, cancellationToken).ConfigureAwait(false);
 
                 video.PrimaryImagePath = path;
             }

+ 1 - 1
MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -368,7 +368,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return null;
         }
 
-        private const string InternalVersionNumber = "2";
+        private const string InternalVersionNumber = "3";
 
         public Guid GetInternalChannelId(string serviceName, string externalId)
         {

+ 57 - 5
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -6,12 +6,14 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -23,7 +25,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
     /// <summary>
     /// Class LiveTvManager
     /// </summary>
-    public class LiveTvManager : ILiveTvManager
+    public class LiveTvManager : ILiveTvManager, IDisposable
     {
         private readonly IServerApplicationPaths _appPaths;
         private readonly IFileSystem _fileSystem;
@@ -31,15 +33,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         private readonly IItemRepository _itemRepo;
         private readonly IUserManager _userManager;
         private readonly ILibraryManager _libraryManager;
+        private readonly IMediaEncoder _mediaEncoder;
 
         private readonly LiveTvDtoService _tvDtoService;
 
         private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
 
+        private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams =
+            new ConcurrentDictionary<string, LiveStreamInfo>();
+
         private List<Guid> _channelIdList = new List<Guid>();
         private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
 
-        public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager)
+        public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, IMediaEncoder mediaEncoder)
         {
             _appPaths = appPaths;
             _fileSystem = fileSystem;
@@ -47,6 +53,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             _itemRepo = itemRepo;
             _userManager = userManager;
             _libraryManager = libraryManager;
+            _mediaEncoder = mediaEncoder;
 
             _tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo);
         }
@@ -180,7 +187,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var recording = recordings.First(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
 
-            return await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
+            var result = await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
+
+            if (!string.IsNullOrEmpty(result.Id))
+            {
+                _openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
+            }
+
+            return result;
         }
 
         public async Task<LiveStreamInfo> GetChannelStream(string id, CancellationToken cancellationToken)
@@ -189,12 +203,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var channel = GetInternalChannel(id);
 
-            return await service.GetChannelStream(channel.ChannelInfo.Id, cancellationToken).ConfigureAwait(false);
+            var result = await service.GetChannelStream(channel.ChannelInfo.Id, cancellationToken).ConfigureAwait(false);
+
+            if (!string.IsNullOrEmpty(result.Id))
+            {
+                _openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
+            }
+
+            return result;
         }
 
         private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
         {
-            var path = Path.Combine(_appPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(serviceName), _fileSystem.GetValidFilename(channelInfo.Name));
+            var path = Path.Combine(_appPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(channelInfo.Name));
 
             var fileInfo = new DirectoryInfo(path);
 
@@ -1047,5 +1068,36 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 EndDate = endDate
             };
         }
+
+        /// <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 stream in _openStreams.Values.ToList())
+                    {
+                        var task = CloseLiveStream(stream.Id, CancellationToken.None);
+
+                        Task.WaitAll(task);
+                    }
+
+                    _openStreams.Clear();
+                }
+            }
+        }
     }
 }

+ 12 - 12
MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs

@@ -1,6 +1,6 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
+using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
@@ -104,10 +104,10 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
         /// <param name="type">The type.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        public Task<MediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type,
+        public Task<InternalMediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type,
                                                   CancellationToken cancellationToken)
         {
-            return GetMediaInfoInternal(GetInputArgument(inputFiles, type), type != InputType.AudioFile,
+            return GetMediaInfoInternal(GetInputArgument(inputFiles, type), type != InputType.File,
                                         GetProbeSizeArgument(type), cancellationToken);
         }
 
@@ -125,8 +125,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
             switch (type)
             {
                 case InputType.Dvd:
-                case InputType.VideoFile:
-                case InputType.AudioFile:
+                case InputType.File:
                     inputPath = GetConcatInputArgument(inputFiles);
                     break;
                 case InputType.Bluray:
@@ -173,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{MediaInfoResult}.</returns>
         /// <exception cref="System.ApplicationException"></exception>
-        private async Task<MediaInfoResult> GetMediaInfoInternal(string inputPath, bool extractChapters,
+        private async Task<InternalMediaInfoResult> GetMediaInfoInternal(string inputPath, bool extractChapters,
                                                                  string probeSizeArgument,
                                                                  CancellationToken cancellationToken)
         {
@@ -206,7 +205,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
 
             await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
 
-            MediaInfoResult result;
+            InternalMediaInfoResult result;
             string standardError = null;
 
             try
@@ -236,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
                     process.BeginErrorReadLine();
                 }
 
-                result = _jsonSerializer.DeserializeFromStream<MediaInfoResult>(process.StandardOutput.BaseStream);
+                result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
 
                 if (extractChapters)
                 {
@@ -307,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
         /// </summary>
         /// <param name="result">The result.</param>
         /// <param name="standardError">The standard error.</param>
-        private void AddChapters(MediaInfoResult result, string standardError)
+        private void AddChapters(InternalMediaInfoResult result, string standardError)
         {
             var lines = standardError.Split('\n').Select(l => l.TrimStart());
 
@@ -797,19 +796,20 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
         /// </summary>
         /// <param name="inputFiles">The input files.</param>
         /// <param name="type">The type.</param>
+        /// <param name="isAudio">if set to <c>true</c> [is audio].</param>
         /// <param name="threedFormat">The threed format.</param>
         /// <param name="offset">The offset.</param>
         /// <param name="outputPath">The output path.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentException">Must use inputPath list overload</exception>
-        public async Task ExtractImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken)
+        public async Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken)
         {
-            var resourcePool = type == InputType.AudioFile ? _audioImageResourcePool : _videoImageResourcePool;
+            var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool;
 
             var inputArgument = GetInputArgument(inputFiles, type);
 
-            if (type != InputType.AudioFile)
+            if (!isAudio)
             {
                 try
                 {

+ 3 - 4
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -6,7 +6,6 @@ using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Implementations;
 using MediaBrowser.Common.Implementations.ScheduledTasks;
 using MediaBrowser.Common.IO;
-using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller;
@@ -284,9 +283,6 @@ namespace MediaBrowser.ServerApplication
 
             DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
             RegisterSingleInstance(DtoService);
-
-            LiveTvManager = new LiveTvManager(ApplicationPaths, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager);
-            RegisterSingleInstance(LiveTvManager);
             progress.Report(15);
 
             var innerProgress = new ActionableProgress<double>();
@@ -295,6 +291,9 @@ namespace MediaBrowser.ServerApplication
             await RegisterMediaEncoder(innerProgress).ConfigureAwait(false);
             progress.Report(90);
 
+            LiveTvManager = new LiveTvManager(ApplicationPaths, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, MediaEncoder);
+            RegisterSingleInstance(LiveTvManager);
+
             var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
             var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false));
             var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false));