浏览代码

display additional transcoding info in dashboard

Luke Pulverenti 11 年之前
父节点
当前提交
f7cd7182d5

+ 57 - 7
MediaBrowser.Api/ApiEntryPoint.cs

@@ -1,5 +1,7 @@
-using MediaBrowser.Controller;
+using MediaBrowser.Api.Playback;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
@@ -9,6 +11,7 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Session;
 
 namespace MediaBrowser.Api
 {
@@ -33,15 +36,18 @@ namespace MediaBrowser.Api
         /// </summary>
         private readonly IServerApplicationPaths _appPaths;
 
+        private readonly ISessionManager _sessionManager;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ApiEntryPoint" /> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
         /// <param name="appPaths">The application paths.</param>
-        public ApiEntryPoint(ILogger logger, IServerApplicationPaths appPaths)
+        public ApiEntryPoint(ILogger logger, IServerApplicationPaths appPaths, ISessionManager sessionManager)
         {
             Logger = logger;
             _appPaths = appPaths;
+            _sessionManager = sessionManager;
 
             Instance = this;
         }
@@ -115,10 +121,16 @@ namespace MediaBrowser.Api
         /// <param name="type">The type.</param>
         /// <param name="process">The process.</param>
         /// <param name="startTimeTicks">The start time ticks.</param>
-        /// <param name="sourcePath">The source path.</param>
         /// <param name="deviceId">The device id.</param>
+        /// <param name="state">The state.</param>
         /// <param name="cancellationTokenSource">The cancellation token source.</param>
-        public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, long? startTimeTicks, string sourcePath, string deviceId, CancellationTokenSource cancellationTokenSource)
+        public void OnTranscodeBeginning(string path,
+            TranscodingJobType type,
+            Process process,
+            long? startTimeTicks,
+            string deviceId,
+            StreamState state,
+            CancellationTokenSource cancellationTokenSource)
         {
             lock (_activeTranscodingJobs)
             {
@@ -129,10 +141,43 @@ namespace MediaBrowser.Api
                     Process = process,
                     ActiveRequestCount = 1,
                     StartTimeTicks = startTimeTicks,
-                    SourcePath = sourcePath,
                     DeviceId = deviceId,
                     CancellationTokenSource = cancellationTokenSource
                 });
+
+                ReportTranscodingProgress(state, null, null);
+            }
+        }
+
+        public void ReportTranscodingProgress(StreamState state, float? framerate, double? percentComplete)
+        {
+            var deviceId = state.Request.DeviceId;
+
+            if (!string.IsNullOrWhiteSpace(deviceId))
+            {
+                var audioCodec = state.Request.AudioCodec;
+                var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
+
+                if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase) ||
+                    string.IsNullOrEmpty(audioCodec))
+                {
+                    audioCodec = state.OutputAudioCodec;
+                }
+                if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) ||
+                    string.IsNullOrEmpty(videoCodec))
+                {
+                    videoCodec = state.OutputVideoCodec;
+                }
+
+                _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
+                {
+                    Bitrate = state.TotalOutputBitrate,
+                    AudioCodec = audioCodec,
+                    VideoCodec = videoCodec,
+                    Container = state.OutputContainer,
+                    Framerate = framerate,
+                    CompletionPercentage = percentComplete
+                });
             }
         }
 
@@ -144,7 +189,8 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <param name="path">The path.</param>
         /// <param name="type">The type.</param>
-        public void OnTranscodeFailedToStart(string path, TranscodingJobType type)
+        /// <param name="state">The state.</param>
+        public void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state)
         {
             lock (_activeTranscodingJobs)
             {
@@ -152,6 +198,11 @@ namespace MediaBrowser.Api
 
                 _activeTranscodingJobs.Remove(job);
             }
+
+            if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
+            {
+                _sessionManager.ClearTranscodingInfo(state.Request.DeviceId);
+            }
         }
 
         /// <summary>
@@ -437,7 +488,6 @@ namespace MediaBrowser.Api
         public Timer KillTimer { get; set; }
 
         public long? StartTimeTicks { get; set; }
-        public string SourcePath { get; set; }
         public string DeviceId { get; set; }
 
         public CancellationTokenSource CancellationTokenSource { get; set; }

+ 108 - 23
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -126,7 +126,7 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="state">The state.</param>
         /// <returns>System.String.</returns>
-        protected string GetOutputFilePath(StreamState state)
+        private string GetOutputFilePath(StreamState state)
         {
             var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
 
@@ -726,12 +726,13 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="request">The request.</param>
         /// <param name="audioStream">The audio stream.</param>
+        /// <param name="outputAudioCodec">The output audio codec.</param>
         /// <returns>System.Nullable{System.Int32}.</returns>
-        private int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream)
+        private int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream, string outputAudioCodec)
         {
             if (audioStream != null)
             {
-                var codec = request.AudioCodec ?? string.Empty;
+                var codec = outputAudioCodec ?? string.Empty;
 
                 if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
                 {
@@ -769,7 +770,7 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.String.</returns>
-        protected string GetAudioCodec(StreamRequest request)
+        private string GetAudioCodec(StreamRequest request)
         {
             var codec = request.AudioCodec;
 
@@ -798,7 +799,7 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.String.</returns>
-        protected string GetVideoCodec(VideoStreamRequest request)
+        private string GetVideoCodec(VideoStreamRequest request)
         {
             var codec = request.VideoCodec;
 
@@ -866,7 +867,7 @@ namespace MediaBrowser.Api.Playback
 
             Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
 
-            if (state.IsInputVideo && state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
+            if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
             {
                 state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
             }
@@ -900,7 +901,13 @@ namespace MediaBrowser.Api.Playback
                 EnableRaisingEvents = true
             };
 
-            ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId, cancellationTokenSource);
+            ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
+                TranscodingJobType,
+                process,
+                state.Request.StartTimeTicks,
+                state.Request.DeviceId,
+                state,
+                cancellationTokenSource);
 
             var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
             Logger.Info(commandLineLogMessage);
@@ -924,7 +931,7 @@ namespace MediaBrowser.Api.Playback
             {
                 Logger.ErrorException("Error starting ffmpeg", ex);
 
-                ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType);
+                ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
 
                 throw;
             }
@@ -932,10 +939,8 @@ namespace MediaBrowser.Api.Playback
             // MUST read both stdout and stderr asynchronously or a deadlock may occurr
             process.BeginOutputReadLine();
 
-#pragma warning disable 4014
             // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
-            process.StandardError.BaseStream.CopyToAsync(state.LogFileStream);
-#pragma warning restore 4014
+            StartStreamingLog(state, process.StandardError.BaseStream, state.LogFileStream);
 
             // Wait for the file to exist before proceeeding
             while (!File.Exists(outputPath))
@@ -956,6 +961,82 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
+        private async void StartStreamingLog(StreamState state, Stream source, Stream target)
+        {
+            try
+            {
+                using (var reader = new StreamReader(source))
+                {
+                    while (!reader.EndOfStream)
+                    {
+                        var line = await reader.ReadLineAsync().ConfigureAwait(false);
+
+                        ParseLogLine(line, state);
+
+                        var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
+
+                        await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error reading ffmpeg log", ex);
+            }
+        }
+
+        private void ParseLogLine(string line, StreamState state)
+        {
+            float? framerate = null;
+            double? percent = null;
+
+            var parts = line.Split(' ');
+
+            var totalMs = state.RunTimeTicks.HasValue
+                ? TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds
+                : 0;
+
+            var startMs = state.Request.StartTimeTicks.HasValue
+                ? TimeSpan.FromTicks(state.Request.StartTimeTicks.Value).TotalMilliseconds
+                : 0;
+
+            for (var i = 0; i < parts.Length; i++)
+            {
+                var part = parts[i];
+
+                if (string.Equals(part, "fps=", StringComparison.OrdinalIgnoreCase) &&
+                    (i + 1 < parts.Length))
+                {
+                    var rate = parts[i + 1];
+                    float val;
+
+                    if (float.TryParse(rate, NumberStyles.Any, UsCulture, out val))
+                    {
+                        framerate = val;
+                    }
+                }
+                else if (state.RunTimeTicks.HasValue &&
+                    part.StartsWith("time=", StringComparison.OrdinalIgnoreCase))
+                {
+                    var time = part.Split(new[] { '=' }, 2).Last();
+                    TimeSpan val;
+
+                    if (TimeSpan.TryParse(time, UsCulture, out val))
+                    {
+                        var currentMs = startMs + val.TotalMilliseconds;
+
+                        var percentVal = currentMs / totalMs;
+                        percent = 100 * percentVal;
+                    }
+                }
+            }
+
+            if (framerate.HasValue || percent.HasValue)
+            {
+                ApiEntryPoint.Instance.ReportTranscodingProgress(state, framerate, percent);
+            }
+        }
+
         private int? GetVideoBitrateParamValue(VideoStreamRequest request, MediaStream videoStream)
         {
             var bitrate = request.VideoBitRate;
@@ -1500,23 +1581,27 @@ namespace MediaBrowser.Api.Playback
 
             state.OutputAudioBitrate = GetAudioBitrateParam(state.Request, state.AudioStream);
             state.OutputAudioSampleRate = request.AudioSampleRate;
-            state.OutputAudioChannels = GetNumAudioChannelsParam(state.Request, state.AudioStream);
+
+            state.OutputAudioCodec = GetAudioCodec(state.Request);
 
             if (videoRequest != null)
             {
+                state.OutputVideoCodec = GetVideoCodec(videoRequest);
                 state.OutputVideoBitrate = GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream);
 
                 if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
                 {
-                    videoRequest.VideoCodec = "copy";
+                    state.OutputVideoCodec = "copy";
                 }
 
                 if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs))
                 {
-                    request.AudioCodec = "copy";
+                    state.OutputAudioCodec = "copy";
                 }
             }
 
+            state.OutputFilePath = GetOutputFilePath(state);
+
             return state;
         }
 
@@ -1729,14 +1814,14 @@ namespace MediaBrowser.Api.Playback
                 return;
             }
 
-            var audioCodec = state.Request.AudioCodec;
+            var audioCodec = state.OutputAudioCodec;
 
             if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
             {
                 audioCodec = state.AudioStream.Codec;
             }
 
-            var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
+            var videoCodec = state.OutputVideoCodec;
 
             if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
             {
@@ -1807,7 +1892,12 @@ namespace MediaBrowser.Api.Playback
                 profile = DlnaManager.GetDefaultProfile();
             }
 
-            var audioCodec = state.Request.AudioCodec;
+            var audioCodec = state.OutputAudioCodec;
+
+            if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
+            {
+                audioCodec = state.AudioStream.Codec;
+            }
 
             if (state.VideoRequest == null)
             {
@@ -1825,12 +1915,7 @@ namespace MediaBrowser.Api.Playback
             }
             else
             {
-                if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
-                {
-                    audioCodec = state.AudioStream.Codec;
-                }
-
-                var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
+                var videoCodec = state.OutputVideoCodec;
 
                 if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
                 {

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

@@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             var state = GetState(request, cancellationTokenSource.Token).Result;
 
-            var playlist = GetOutputFilePath(state);
+            var playlist = state.OutputFilePath;
 
             if (File.Exists(playlist))
             {
@@ -307,7 +307,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             if (hlsVideoRequest != null)
             {
-                if (hlsVideoRequest.AppendBaselineStream && state.IsInputVideo)
+                if (hlsVideoRequest.AppendBaselineStream)
                 {
                     var lowBitratePath = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath) + "-low.m3u8");
 

+ 3 - 3
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
 
-            var playlistPath = Path.ChangeExtension(GetOutputFilePath(state), ".m3u8");
+            var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
 
             var path = GetSegmentPath(playlistPath, index);
 
@@ -231,7 +231,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
         protected override string GetAudioArguments(StreamState state)
         {
-            var codec = GetAudioCodec(state.Request);
+            var codec = state.OutputAudioCodec;
 
             if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
             {
@@ -266,7 +266,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
         protected override string GetVideoArguments(StreamState state, bool performSubtitleConversion)
         {
-            var codec = GetVideoCodec(state.VideoRequest);
+            var codec = state.OutputVideoCodec;
 
             // See if we can save come cpu cycles by avoiding encoding
             if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))

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

@@ -118,7 +118,7 @@ namespace MediaBrowser.Api.Playback.Hls
         /// <returns>System.String.</returns>
         protected override string GetAudioArguments(StreamState state)
         {
-            var codec = GetAudioCodec(state.Request);
+            var codec = state.OutputAudioCodec;
 
             if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
             {
@@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls
         protected override string GetVideoArguments(StreamState state, 
             bool performSubtitleConversion)
         {
-            var codec = GetVideoCodec(state.VideoRequest);
+            var codec = state.OutputVideoCodec;
 
             // See if we can save come cpu cycles by avoiding encoding
             if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))

+ 0 - 2
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -78,8 +78,6 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// <exception cref="System.InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
         protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
         {
-            var request = state.Request;
-
             var audioTranscodeParams = new List<string>();
 
             var bitrate = state.OutputAudioBitrate;

+ 27 - 29
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -45,53 +45,51 @@ namespace MediaBrowser.Api.Playback.Progressive
                 return ext;
             }
 
-            var videoRequest = state.Request as VideoStreamRequest;
+            var isVideoRequest = state.VideoRequest != null;
 
             // Try to infer based on the desired video codec
-            if (videoRequest != null && !string.IsNullOrEmpty(videoRequest.VideoCodec))
+            if (isVideoRequest)
             {
-                if (state.IsInputVideo)
-                {
-                    if (string.Equals(videoRequest.VideoCodec, "h264", StringComparison.OrdinalIgnoreCase))
+                var videoCodec = state.VideoRequest.VideoCodec;
+                
+                    if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
                     {
                         return ".ts";
                     }
-                    if (string.Equals(videoRequest.VideoCodec, "theora", StringComparison.OrdinalIgnoreCase))
+                    if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase))
                     {
                         return ".ogv";
                     }
-                    if (string.Equals(videoRequest.VideoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
+                    if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
                     {
                         return ".webm";
                     }
-                    if (string.Equals(videoRequest.VideoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
+                    if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
                     {
                         return ".asf";
                     }
-                }
             }
 
             // Try to infer based on the desired audio codec
-            if (!string.IsNullOrEmpty(state.Request.AudioCodec))
+            if (!isVideoRequest)
             {
-                if (!state.IsInputVideo)
+                var audioCodec = state.Request.AudioCodec;
+
+                if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
                 {
-                    if (string.Equals("aac", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
-                    {
-                        return ".aac";
-                    }
-                    if (string.Equals("mp3", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
-                    {
-                        return ".mp3";
-                    }
-                    if (string.Equals("vorbis", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
-                    {
-                        return ".ogg";
-                    }
-                    if (string.Equals("wma", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
-                    {
-                        return ".wma";
-                    }
+                    return ".aac";
+                }
+                if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase))
+                {
+                    return ".mp3";
+                }
+                if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase))
+                {
+                    return ".ogg";
+                }
+                if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase))
+                {
+                    return ".wma";
                 }
             }
 
@@ -134,7 +132,7 @@ namespace MediaBrowser.Api.Playback.Progressive
                 }
             }
 
-            var outputPath = GetOutputFilePath(state);
+            var outputPath = state.OutputFilePath;
             var outputPathExists = File.Exists(outputPath);
 
             var isStatic = request.Static ||
@@ -243,7 +241,7 @@ namespace MediaBrowser.Api.Playback.Progressive
         private async Task<object> GetStreamResult(StreamState state, IDictionary<string, string> responseHeaders, bool isHeadRequest)
         {
             // Use the command line args with a dummy playlist path
-            var outputPath = GetOutputFilePath(state);
+            var outputPath = state.OutputFilePath;
 
             responseHeaders["Accept-Ranges"] = "none";
 

+ 2 - 4
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -95,7 +95,7 @@ namespace MediaBrowser.Api.Playback.Progressive
         protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
         {
             // Get the output codec name
-            var videoCodec = GetVideoCodec(state.VideoRequest);
+            var videoCodec = state.OutputVideoCodec;
 
             var format = string.Empty;
             var keyFrame = string.Empty;
@@ -190,10 +190,8 @@ namespace MediaBrowser.Api.Playback.Progressive
                 return string.Empty;
             }
 
-            var request = state.Request;
-
             // Get the output codec name
-            var codec = GetAudioCodec(request);
+            var codec = state.OutputAudioCodec;
 
             if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
             {

+ 3 - 0
MediaBrowser.Api/Playback/StreamState.cs

@@ -163,6 +163,9 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
+        public string OutputFilePath { get; set; }
+        public string OutputVideoCodec { get; set; }
+        public string OutputAudioCodec { get; set; }
         public int? OutputAudioChannels;
         public int? OutputAudioSampleRate;
         public int? OutputAudioBitrate;

+ 13 - 0
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -226,5 +226,18 @@ namespace MediaBrowser.Controller.Session
         /// <param name="sessionId">The session identifier.</param>
         /// <param name="capabilities">The capabilities.</param>
         void ReportCapabilities(string sessionId, SessionCapabilities capabilities);
+
+        /// <summary>
+        /// Reports the transcoding information.
+        /// </summary>
+        /// <param name="deviceId">The device identifier.</param>
+        /// <param name="info">The information.</param>
+        void ReportTranscodingInfo(string deviceId, TranscodingInfo info);
+
+        /// <summary>
+        /// Clears the transcoding information.
+        /// </summary>
+        /// <param name="deviceId">The device identifier.</param>
+        void ClearTranscodingInfo(string deviceId);
     }
 }

+ 2 - 0
MediaBrowser.Controller/Session/SessionInfo.cs

@@ -122,6 +122,8 @@ namespace MediaBrowser.Controller.Session
         /// <value>The supported commands.</value>
         public List<string> SupportedCommands { get; set; }
 
+        public TranscodingInfo TranscodingInfo { get; set; }
+        
         /// <summary>
         /// Gets a value indicating whether this instance is active.
         /// </summary>

+ 11 - 0
MediaBrowser.Model/Session/PlayerStateInfo.cs

@@ -56,4 +56,15 @@
         /// <value>The play method.</value>
         public PlayMethod? PlayMethod { get; set; }
     }
+
+    public class TranscodingInfo
+    {
+        public string AudioCodec { get; set; }
+        public string VideoCodec { get; set; }
+        public string Container { get; set; }
+        public int? Bitrate { get; set; }
+
+        public float? Framerate { get; set; }
+        public double? CompletionPercentage { get; set; }
+    }
 }

+ 2 - 0
MediaBrowser.Model/Session/SessionInfoDto.cs

@@ -137,6 +137,8 @@ namespace MediaBrowser.Model.Session
 
         public PlayerStateInfo PlayState { get; set; }
 
+        public TranscodingInfo TranscodingInfo { get; set; }
+        
         public event PropertyChangedEventHandler PropertyChanged;
 
         public SessionInfoDto()

+ 1 - 1
MediaBrowser.Providers/Folders/ImagesByNameImageProvider.cs

@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Folders
 
         public bool Supports(IHasImages item)
         {
-            return item is ICollectionFolder;
+            return item is CollectionFolder;
         }
 
         public int Order

+ 27 - 1
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -347,6 +347,11 @@ namespace MediaBrowser.Server.Implementations.Session
         {
             session.NowPlayingItem = null;
             session.PlayState = new PlayerStateInfo();
+
+            if (!string.IsNullOrEmpty(session.DeviceId))
+            {
+                ClearTranscodingInfo(session.DeviceId);
+            }
         }
 
         private string GetSessionKey(string clientType, string appVersion, string deviceId)
@@ -459,6 +464,11 @@ namespace MediaBrowser.Server.Implementations.Session
 
             UpdateNowPlayingItem(session, info, libraryItem);
 
+            if (!string.IsNullOrEmpty(session.DeviceId))
+            {
+                ClearTranscodingInfo(session.DeviceId);
+            }
+
             session.QueueableMediaTypes = info.QueueableMediaTypes;
 
             var users = GetUsers(session);
@@ -1264,7 +1274,8 @@ namespace MediaBrowser.Server.Implementations.Session
                 UserName = session.UserName,
                 NowPlayingItem = session.NowPlayingItem,
                 SupportsRemoteControl = session.SupportsMediaControl,
-                PlayState = session.PlayState
+                PlayState = session.PlayState,
+                TranscodingInfo = session.TranscodingInfo
             };
 
             if (session.UserId.HasValue)
@@ -1490,5 +1501,20 @@ namespace MediaBrowser.Server.Implementations.Session
 
             session.NowViewingItem = item;
         }
+
+        public void ReportTranscodingInfo(string deviceId, TranscodingInfo info)
+        {
+            var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId));
+
+            if (session != null)
+            {
+                session.TranscodingInfo = info;
+            }
+        }
+
+        public void ClearTranscodingInfo(string deviceId)
+        {
+            ReportTranscodingInfo(deviceId, null);
+        }
     }
 }