add setting to control transcodng throttle

This commit is contained in:
Luke Pulverenti 2015-03-30 12:16:34 -04:00
parent 0bd27381e0
commit 5f044cfd68
28 changed files with 475 additions and 241 deletions

View File

@ -1080,7 +1080,7 @@ namespace MediaBrowser.Api.Playback
{ {
if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo) if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo)
{ {
transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger); transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
state.TranscodingThrottler.Start(); state.TranscodingThrottler.Start();
} }
} }
@ -2012,7 +2012,6 @@ namespace MediaBrowser.Api.Playback
} }
var audioCodec = state.ActualOutputAudioCodec; var audioCodec = state.ActualOutputAudioCodec;
var videoCodec = state.ActualOutputVideoCodec; var videoCodec = state.ActualOutputVideoCodec;
var mediaProfile = state.VideoRequest == null ? var mediaProfile = state.VideoRequest == null ?
@ -2033,7 +2032,9 @@ namespace MediaBrowser.Api.Playback
state.TargetTimestamp, state.TargetTimestamp,
state.IsTargetAnamorphic, state.IsTargetAnamorphic,
state.IsTargetCabac, state.IsTargetCabac,
state.TargetRefFrames); state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount);
if (mediaProfile != null) if (mediaProfile != null)
{ {
@ -2118,7 +2119,9 @@ namespace MediaBrowser.Api.Playback
state.TranscodeSeekInfo, state.TranscodeSeekInfo,
state.IsTargetAnamorphic, state.IsTargetAnamorphic,
state.IsTargetCabac, state.IsTargetCabac,
state.TargetRefFrames state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount
).FirstOrDefault() ?? string.Empty; ).FirstOrDefault() ?? string.Empty;
} }

View File

@ -346,6 +346,42 @@ namespace MediaBrowser.Api.Playback
} }
} }
public int? TargetVideoStreamCount
{
get
{
if (Request.Static)
{
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
}
return GetMediaStreamCount(MediaStreamType.Video, 1);
}
}
public int? TargetAudioStreamCount
{
get
{
if (Request.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;
}
/// <summary> /// <summary>
/// Predicts the audio sample rate that will be in the output stream /// Predicts the audio sample rate that will be in the output stream
/// </summary> /// </summary>

View File

@ -1,4 +1,6 @@
using MediaBrowser.Model.Logging; using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
using System; using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
@ -11,13 +13,18 @@ namespace MediaBrowser.Api.Playback
private readonly ILogger _logger; private readonly ILogger _logger;
private Timer _timer; private Timer _timer;
private bool _isPaused; private bool _isPaused;
private readonly IConfigurationManager _config;
private readonly long _gapLengthInTicks = TimeSpan.FromMinutes(2).Ticks; public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config)
public TranscodingThrottler(TranscodingJob job, ILogger logger)
{ {
_job = job; _job = job;
_logger = logger; _logger = logger;
_config = config;
}
private EncodingOptions GetOptions()
{
return _config.GetConfiguration<EncodingOptions>("encoding");
} }
public void Start() public void Start()
@ -33,7 +40,9 @@ namespace MediaBrowser.Api.Playback
return; return;
} }
if (IsThrottleAllowed(_job)) var options = GetOptions();
if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds))
{ {
PauseTranscoding(); PauseTranscoding();
} }
@ -79,19 +88,20 @@ namespace MediaBrowser.Api.Playback
} }
} }
private bool IsThrottleAllowed(TranscodingJob job) private bool IsThrottleAllowed(TranscodingJob job, int thresholdSeconds)
{ {
var bytesDownloaded = job.BytesDownloaded ?? 0; var bytesDownloaded = job.BytesDownloaded ?? 0;
var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0; var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0;
var downloadPositionTicks = job.DownloadPositionTicks ?? 0; var downloadPositionTicks = job.DownloadPositionTicks ?? 0;
var path = job.Path; var path = job.Path;
var gapLengthInTicks = TimeSpan.FromSeconds(thresholdSeconds).Ticks;
if (downloadPositionTicks > 0 && transcodingPositionTicks > 0) if (downloadPositionTicks > 0 && transcodingPositionTicks > 0)
{ {
// HLS - time-based consideration // HLS - time-based consideration
var targetGap = _gapLengthInTicks; var targetGap = gapLengthInTicks;
var gap = transcodingPositionTicks - downloadPositionTicks; var gap = transcodingPositionTicks - downloadPositionTicks;
if (gap < targetGap) if (gap < targetGap)
@ -113,7 +123,7 @@ namespace MediaBrowser.Api.Playback
var bytesTranscoded = job.BytesTranscoded ?? new FileInfo(path).Length; var bytesTranscoded = job.BytesTranscoded ?? new FileInfo(path).Length;
// Estimate the bytes the transcoder should be ahead // Estimate the bytes the transcoder should be ahead
double gapFactor = _gapLengthInTicks; double gapFactor = gapLengthInTicks;
gapFactor /= transcodingPositionTicks; gapFactor /= transcodingPositionTicks;
var targetGap = bytesTranscoded * gapFactor; var targetGap = bytesTranscoded * gapFactor;

View File

@ -158,7 +158,9 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.TranscodeSeekInfo, streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
streamInfo.IsTargetCabac, streamInfo.IsTargetCabac,
streamInfo.TargetRefFrames); streamInfo.TargetRefFrames,
streamInfo.TargetVideoStreamCount,
streamInfo.TargetAudioStreamCount);
foreach (var contentFeature in contentFeatureList) foreach (var contentFeature in contentFeatureList)
{ {
@ -280,7 +282,9 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.TargetTimestamp, streamInfo.TargetTimestamp,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
streamInfo.IsTargetCabac, streamInfo.IsTargetCabac,
streamInfo.TargetRefFrames); streamInfo.TargetRefFrames,
streamInfo.TargetVideoStreamCount,
streamInfo.TargetAudioStreamCount);
var filename = url.Substring(0, url.IndexOf('?')); var filename = url.Substring(0, url.IndexOf('?'));

View File

@ -526,7 +526,9 @@ namespace MediaBrowser.Dlna.PlayTo
streamInfo.TranscodeSeekInfo, streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
streamInfo.IsTargetCabac, streamInfo.IsTargetCabac,
streamInfo.TargetRefFrames); streamInfo.TargetRefFrames,
streamInfo.TargetVideoStreamCount,
streamInfo.TargetAudioStreamCount);
return list.FirstOrDefault(); return list.FirstOrDefault();
} }

View File

@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ {
public class AudioEncoder : BaseEncoder 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)
{ {
} }

View File

@ -1,6 +1,5 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
@ -14,6 +13,7 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -31,10 +31,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected readonly ILogger Logger; protected readonly ILogger Logger;
protected readonly IServerConfigurationManager ConfigurationManager; protected readonly IServerConfigurationManager ConfigurationManager;
protected readonly IFileSystem FileSystem; protected readonly IFileSystem FileSystem;
protected readonly ILiveTvManager LiveTvManager;
protected readonly IIsoManager IsoManager; protected readonly IIsoManager IsoManager;
protected readonly ILibraryManager LibraryManager; protected readonly ILibraryManager LibraryManager;
protected readonly IChannelManager ChannelManager;
protected readonly ISessionManager SessionManager; protected readonly ISessionManager SessionManager;
protected readonly ISubtitleEncoder SubtitleEncoder; protected readonly ISubtitleEncoder SubtitleEncoder;
protected readonly IMediaSourceManager MediaSourceManager; protected readonly IMediaSourceManager MediaSourceManager;
@ -45,20 +43,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
ILogger logger, ILogger logger,
IServerConfigurationManager configurationManager, IServerConfigurationManager configurationManager,
IFileSystem fileSystem, IFileSystem fileSystem,
ILiveTvManager liveTvManager,
IIsoManager isoManager, IIsoManager isoManager,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IChannelManager channelManager, ISessionManager sessionManager,
ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) ISubtitleEncoder subtitleEncoder,
IMediaSourceManager mediaSourceManager)
{ {
MediaEncoder = mediaEncoder; MediaEncoder = mediaEncoder;
Logger = logger; Logger = logger;
ConfigurationManager = configurationManager; ConfigurationManager = configurationManager;
FileSystem = fileSystem; FileSystem = fileSystem;
LiveTvManager = liveTvManager;
IsoManager = isoManager; IsoManager = isoManager;
LibraryManager = libraryManager; LibraryManager = libraryManager;
ChannelManager = channelManager;
SessionManager = sessionManager; SessionManager = sessionManager;
SubtitleEncoder = subtitleEncoder; SubtitleEncoder = subtitleEncoder;
MediaSourceManager = mediaSourceManager; MediaSourceManager = mediaSourceManager;
@ -68,7 +64,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
IProgress<double> progress, IProgress<double> progress,
CancellationToken cancellationToken) 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); .CreateJob(options, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false);
encodingJob.OutputFilePath = GetOutputFilePath(encodingJob); encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);
@ -477,53 +473,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false); state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false);
} }
if (string.IsNullOrEmpty(state.MediaPath)) if (state.MediaSource.RequiresOpening)
{ {
var checkCodecs = false; var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name))
{ {
var streamInfo = await LiveTvManager.GetChannelStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); OpenToken = state.MediaSource.OpenToken
state.LiveTvStreamId = streamInfo.Id; }, false, cancellationToken).ConfigureAwait(false);
state.MediaPath = streamInfo.Path; AttachMediaStreamInfo(state, liveStreamResponse.MediaSource, state.Options);
state.InputProtocol = streamInfo.Protocol;
await Task.Delay(1500, cancellationToken).ConfigureAwait(false); if (state.IsVideoRequest)
AttachMediaStreamInfo(state, streamInfo, state.Options);
checkCodecs = true;
}
else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) ||
string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name))
{ {
var streamInfo = await LiveTvManager.GetRecordingStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); EncodingJobFactory.TryStreamCopy(state, state.Options);
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;
} }
}
if (state.IsVideoRequest && checkCodecs) if (state.MediaSource.BufferMs.HasValue)
{ {
if (state.VideoStream != null && EncodingJobFactory.CanStreamCopyVideo(state.Options, state.VideoStream)) await Task.Delay(state.MediaSource.BufferMs.Value, cancellationToken).ConfigureAwait(false);
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && EncodingJobFactory.CanStreamCopyAudio(state.Options, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
}
} }
} }
@ -531,22 +499,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
MediaSourceInfo mediaSource, MediaSourceInfo mediaSource,
EncodingJobOptions videoRequest) EncodingJobOptions videoRequest)
{ {
state.InputProtocol = mediaSource.Protocol; EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource, videoRequest);
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);
} }
/// <summary> /// <summary>

View File

@ -1,7 +1,8 @@
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -26,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
public EncodingJobOptions Options { get; set; } public EncodingJobOptions Options { get; set; }
public string InputContainer { 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 AudioStream { get; set; }
public MediaStream VideoStream { get; set; } public MediaStream VideoStream { get; set; }
public MediaStream SubtitleStream { get; set; } public MediaStream SubtitleStream { get; set; }
@ -76,12 +77,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
private readonly ILogger _logger; 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; _logger = logger;
_liveTvManager = liveTvManager; _mediaSourceManager = mediaSourceManager;
Id = Guid.NewGuid().ToString("N"); Id = Guid.NewGuid().ToString("N");
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@ -89,7 +90,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
SupportedAudioCodecs = new List<string>(); SupportedAudioCodecs = new List<string>();
PlayableStreamFileNames = new List<string>(); PlayableStreamFileNames = new List<string>();
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
AllMediaStreams = new List<MediaStream>();
TaskCompletionSource = new TaskCompletionSource<bool>(); TaskCompletionSource = new TaskCompletionSource<bool>();
} }
@ -136,15 +136,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
private async void DisposeLiveStream() private async void DisposeLiveStream()
{ {
if (!string.IsNullOrEmpty(LiveTvStreamId)) if (MediaSource.RequiresClosing)
{ {
try try
{ {
await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) 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) public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded)
{ {
var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null; var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;

View File

@ -1,9 +1,10 @@
using MediaBrowser.Controller.Channels; using System.IO;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
@ -19,19 +20,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
public class EncodingJobFactory public class EncodingJobFactory
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ILiveTvManager _liveTvManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IChannelManager _channelManager;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); 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; _logger = logger;
_liveTvManager = liveTvManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_channelManager = channelManager;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
} }
@ -42,9 +39,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (string.IsNullOrEmpty(request.AudioCodec)) if (string.IsNullOrEmpty(request.AudioCodec))
{ {
request.AudioCodec = InferAudioCodec(request.OutputContainer); request.AudioCodec = InferAudioCodec(request.OutputContainer);
} }
var state = new EncodingJob(_logger, _liveTvManager) var state = new EncodingJob(_logger, _mediaSourceManager)
{ {
Options = options, Options = options,
IsVideoRequest = isVideoRequest, IsVideoRequest = isVideoRequest,
@ -58,106 +55,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
var item = _libraryManager.GetItemById(request.ItemId); var item = _libraryManager.GetItemById(request.ItemId);
List<MediaStream> mediaStreams = null;
state.ItemType = item.GetType().Name; state.ItemType = item.GetType().Name;
if (item is ILiveTvRecording) state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
{
var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false);
state.VideoType = VideoType.VideoFile; var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false);
state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
var path = recording.RecordingInfo.Path; var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
var mediaUrl = recording.RecordingInfo.Url; ? mediaSources.First()
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
var source = string.IsNullOrEmpty(request.MediaSourceId)
? recording.GetMediaSources(false).First()
: _mediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false);
mediaStreams = source.MediaStreams; AttachMediaStreamInfo(state, mediaSource, options);
// 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);
var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
mediaStreams = mediaSource.MediaStreams;
state.MediaPath = mediaSource.Path;
state.InputProtocol = mediaSource.Protocol;
state.InputContainer = mediaSource.Container;
state.InputFileSize = mediaSource.Size;
state.InputBitrate = mediaSource.Bitrate;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
state.RunTimeTicks = mediaSource.RunTimeTicks;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
var video = item as Video;
if (video != null)
{
state.IsInputVideo = true;
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
}
state.IsoType = mediaSource.IsoType;
state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
if (mediaSource.Timestamp.HasValue)
{
state.InputTimestamp = mediaSource.Timestamp.Value;
}
}
state.RunTimeTicks = mediaSource.RunTimeTicks;
}
AttachMediaStreamInfo(state, mediaStreams, request);
state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream); state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream);
state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioSampleRate = request.AudioSampleRate;
@ -185,26 +93,73 @@ namespace MediaBrowser.MediaEncoding.Encoder
ApplyDeviceProfileSettings(state); ApplyDeviceProfileSettings(state);
if (isVideoRequest) TryStreamCopy(state, request);
{
if (state.VideoStream != null && CanStreamCopyVideo(request, state.VideoStream))
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
}
return state; return state;
} }
internal static void AttachMediaStreamInfo(EncodingJob state, internal static void TryStreamCopy(EncodingJob state,
List<MediaStream> mediaStreams,
EncodingJobOptions videoRequest) EncodingJobOptions videoRequest)
{ {
if (state.IsVideoRequest)
{
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
}
}
internal static void AttachMediaStreamInfo(EncodingJob state,
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 (videoRequest != null)
{ {
if (string.IsNullOrEmpty(videoRequest.VideoCodec)) if (string.IsNullOrEmpty(videoRequest.VideoCodec))
@ -233,7 +188,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
} }
state.AllMediaStreams = mediaStreams; state.MediaSource = mediaSource;
} }
/// <summary> /// <summary>
@ -771,7 +726,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
state.TargetTimestamp, state.TargetTimestamp,
state.IsTargetAnamorphic, state.IsTargetAnamorphic,
state.IsTargetCabac, state.IsTargetCabac,
state.TargetRefFrames); state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount);
if (mediaProfile != null) if (mediaProfile != null)
{ {

View File

@ -577,10 +577,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger, _logger,
ConfigurationManager, ConfigurationManager,
FileSystem, FileSystem,
LiveTvManager,
IsoManager, IsoManager,
LibraryManager, LibraryManager,
ChannelManager,
SessionManager, SessionManager,
SubtitleEncoder(), SubtitleEncoder(),
MediaSourceManager()) MediaSourceManager())
@ -599,10 +597,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger, _logger,
ConfigurationManager, ConfigurationManager,
FileSystem, FileSystem,
LiveTvManager,
IsoManager, IsoManager,
LibraryManager, LibraryManager,
ChannelManager,
SessionManager, SessionManager,
SubtitleEncoder(), SubtitleEncoder(),
MediaSourceManager()) MediaSourceManager())

View File

@ -15,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ {
public class VideoEncoder : BaseEncoder 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)
{ {
} }

View File

@ -8,12 +8,16 @@ namespace MediaBrowser.Model.Configuration
public double DownMixAudioBoost { get; set; } public double DownMixAudioBoost { get; set; }
public string H264Encoder { get; set; } public string H264Encoder { get; set; }
public bool EnableDebugLogging { get; set; } public bool EnableDebugLogging { get; set; }
public bool EnableThrottling { get; set; }
public int ThrottleThresholdSeconds { get; set; }
public EncodingOptions() public EncodingOptions()
{ {
H264Encoder = "libx264"; H264Encoder = "libx264";
DownMixAudioBoost = 2; DownMixAudioBoost = 2;
EncodingQuality = EncodingQuality.Auto; EncodingQuality = EncodingQuality.Auto;
EnableThrottling = true;
ThrottleThresholdSeconds = 120;
} }
} }
} }

View File

@ -20,7 +20,9 @@ namespace MediaBrowser.Model.Dlna
TransportStreamTimestamp? timestamp, TransportStreamTimestamp? timestamp,
bool? isAnamorphic, bool? isAnamorphic,
bool? isCabac, bool? isCabac,
int? refFrames) int? refFrames,
int? numVideoStreams,
int? numAudioStreams)
{ {
switch (condition.Property) switch (condition.Property)
{ {
@ -56,6 +58,10 @@ namespace MediaBrowser.Model.Dlna
return IsConditionSatisfied(condition, width); return IsConditionSatisfied(condition, width);
case ProfileConditionValue.RefFrames: case ProfileConditionValue.RefFrames:
return IsConditionSatisfied(condition, refFrames); return IsConditionSatisfied(condition, refFrames);
case ProfileConditionValue.NumAudioStreams:
return IsConditionSatisfied(condition, numAudioStreams);
case ProfileConditionValue.NumVideoStreams:
return IsConditionSatisfied(condition, numVideoStreams);
case ProfileConditionValue.VideoTimestamp: case ProfileConditionValue.VideoTimestamp:
return IsConditionSatisfied(condition, timestamp); return IsConditionSatisfied(condition, timestamp);
default: default:
@ -92,7 +98,8 @@ namespace MediaBrowser.Model.Dlna
public bool IsVideoAudioConditionSatisfied(ProfileCondition condition, public bool IsVideoAudioConditionSatisfied(ProfileCondition condition,
int? audioChannels, int? audioChannels,
int? audioBitrate, int? audioBitrate,
string audioProfile) string audioProfile,
bool? isSecondaryTrack)
{ {
switch (condition.Property) switch (condition.Property)
{ {
@ -102,6 +109,8 @@ namespace MediaBrowser.Model.Dlna
return IsConditionSatisfied(condition, audioBitrate); return IsConditionSatisfied(condition, audioBitrate);
case ProfileConditionValue.AudioChannels: case ProfileConditionValue.AudioChannels:
return IsConditionSatisfied(condition, audioChannels); return IsConditionSatisfied(condition, audioChannels);
case ProfileConditionValue.IsSecondaryAudio:
return IsConditionSatisfied(condition, isSecondaryTrack);
default: default:
throw new ArgumentException("Unexpected condition on audio file: " + condition.Property); throw new ArgumentException("Unexpected condition on audio file: " + condition.Property);
} }

View File

@ -117,7 +117,9 @@ namespace MediaBrowser.Model.Dlna
TranscodeSeekInfo transcodeSeekInfo, TranscodeSeekInfo transcodeSeekInfo,
bool? isAnamorphic, bool? isAnamorphic,
bool? isCabac, bool? isCabac,
int? refFrames) int? refFrames,
int? numVideoStreams,
int? numAudioStreams)
{ {
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo); string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo);
@ -158,7 +160,9 @@ namespace MediaBrowser.Model.Dlna
timestamp, timestamp,
isAnamorphic, isAnamorphic,
isCabac, isCabac,
refFrames); refFrames,
numVideoStreams,
numAudioStreams);
List<string> orgPnValues = new List<string>(); List<string> orgPnValues = new List<string>();

View File

@ -281,7 +281,9 @@ namespace MediaBrowser.Model.Dlna
TransportStreamTimestamp timestamp, TransportStreamTimestamp timestamp,
bool? isAnamorphic, bool? isAnamorphic,
bool? isCabac, bool? isCabac,
int? refFrames) int? refFrames,
int? numVideoStreams,
int? numAudioStreams)
{ {
container = StringHelper.TrimStart((container ?? string.Empty), '.'); container = StringHelper.TrimStart((container ?? string.Empty), '.');
@ -315,7 +317,7 @@ namespace MediaBrowser.Model.Dlna
var anyOff = false; var anyOff = false;
foreach (ProfileCondition c in i.Conditions) foreach (ProfileCondition c in i.Conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
{ {
anyOff = true; anyOff = true;
break; break;

View File

@ -17,6 +17,9 @@
VideoTimestamp = 12, VideoTimestamp = 12,
IsAnamorphic = 13, IsAnamorphic = 13,
RefFrames = 14, RefFrames = 14,
IsCabac = 15 IsCabac = 15,
NumAudioStreams = 16,
NumVideoStreams = 17,
IsSecondaryAudio
} }
} }

View File

@ -495,10 +495,13 @@ namespace MediaBrowser.Model.Dlna
int? packetLength = videoStream == null ? null : videoStream.PacketLength; int? packetLength = videoStream == null ? null : videoStream.PacketLength;
int? refFrames = videoStream == null ? null : videoStream.RefFrames; int? refFrames = videoStream == null ? null : videoStream.RefFrames;
int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
// Check container conditions // Check container conditions
foreach (ProfileCondition i in conditions) foreach (ProfileCondition i in conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
{ {
return null; return null;
} }
@ -525,7 +528,7 @@ namespace MediaBrowser.Model.Dlna
foreach (ProfileCondition i in conditions) foreach (ProfileCondition i in conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
{ {
return null; return null;
} }
@ -554,7 +557,8 @@ namespace MediaBrowser.Model.Dlna
foreach (ProfileCondition i in conditions) foreach (ProfileCondition i in conditions)
{ {
if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile)) bool? isSecondaryAudio = audioStream == null ? null : mediaSource.IsSecondaryAudio(audioStream);
if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile, isSecondaryAudio))
{ {
return null; return null;
} }
@ -752,6 +756,9 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.AudioProfile: case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.PacketLength: case ProfileConditionValue.PacketLength:
case ProfileConditionValue.NumAudioStreams:
case ProfileConditionValue.NumVideoStreams:
case ProfileConditionValue.IsSecondaryAudio:
case ProfileConditionValue.VideoTimestamp: case ProfileConditionValue.VideoTimestamp:
{ {
// Not supported yet // Not supported yet

View File

@ -672,6 +672,42 @@ namespace MediaBrowser.Model.Dlna
} }
} }
public int? TargetVideoStreamCount
{
get
{
if (IsDirectStream)
{
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
}
return GetMediaStreamCount(MediaStreamType.Video, 1);
}
}
public int? TargetAudioStreamCount
{
get
{
if (IsDirectStream)
{
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 List<MediaStream> GetSelectableAudioStreams() public List<MediaStream> GetSelectableAudioStreams()
{ {
return GetSelectableStreams(MediaStreamType.Audio); return GetSelectableStreams(MediaStreamType.Audio);

View File

@ -46,8 +46,8 @@ namespace MediaBrowser.Model.Dto
public int? Bitrate { get; set; } public int? Bitrate { get; set; }
public TransportStreamTimestamp? Timestamp { get; set; } public TransportStreamTimestamp? Timestamp { get; set; }
public Dictionary<string, string> RequiredHttpHeaders { get; set; } public Dictionary<string, string> RequiredHttpHeaders { get; set; }
public string TranscodingUrl { get; set; } public string TranscodingUrl { get; set; }
public string TranscodingSubProtocol { get; set; } public string TranscodingSubProtocol { get; set; }
public string TranscodingContainer { get; set; } public string TranscodingContainer { get; set; }
@ -135,5 +135,35 @@ namespace MediaBrowser.Model.Dto
return null; return null;
} }
public int? GetStreamCount(MediaStreamType type)
{
int numMatches = 0;
int numStreams = 0;
foreach (MediaStream i in MediaStreams)
{
numStreams++;
if (i.Type == type)
{
numMatches++;
}
}
return numStreams == 0 ? (int?)null : numMatches;
}
public bool? IsSecondaryAudio(MediaStream stream)
{
foreach (MediaStream currentStream in MediaStreams)
{
if (currentStream.Type == MediaStreamType.Audio)
{
return currentStream.Index != stream.Index;
}
}
return null;
}
} }
} }

View File

@ -35,6 +35,8 @@
"HeaderConfirmation": "Confirmation", "HeaderConfirmation": "Confirmation",
"MessageKeyUpdated": "Thank you. Your supporter key has been updated.", "MessageKeyUpdated": "Thank you. Your supporter key has been updated.",
"MessageKeyRemoved": "Thank you. Your supporter key has been removed.", "MessageKeyRemoved": "Thank you. Your supporter key has been removed.",
"TitleLiveTV": "Live TV",
"TitleSync": "Sync",
"ErrorLaunchingChromecast": "There was an error launching chromecast. Please ensure your device is connected to your wireless network.", "ErrorLaunchingChromecast": "There was an error launching chromecast. Please ensure your device is connected to your wireless network.",
"MessageErrorLoadingSupporterInfo": "There was an error loading supporter information. Please try again later.", "MessageErrorLoadingSupporterInfo": "There was an error loading supporter information. Please try again later.",
"MessageLinkYourSupporterKey": "Link your supporter key with up to {0} Emby Connect members to enjoy free access to the following apps:", "MessageLinkYourSupporterKey": "Link your supporter key with up to {0} Emby Connect members to enjoy free access to the following apps:",
@ -93,7 +95,6 @@
"HeaderWelcomeToProjectWebClient": "Welcome to the Emby Web Client", "HeaderWelcomeToProjectWebClient": "Welcome to the Emby Web Client",
"ButtonTakeTheTour": "Take the tour", "ButtonTakeTheTour": "Take the tour",
"HeaderWelcomeBack": "Welcome back!", "HeaderWelcomeBack": "Welcome back!",
"TitleSync": "Sync",
"TitlePlugins": "Plugins", "TitlePlugins": "Plugins",
"ButtonTakeTheTourToSeeWhatsNew": "Take the tour to see what's new", "ButtonTakeTheTourToSeeWhatsNew": "Take the tour to see what's new",
"MessageNoSyncJobsFound": "No sync jobs found. Create sync jobs using the Sync buttons found throughout the web interface.", "MessageNoSyncJobsFound": "No sync jobs found. Create sync jobs using the Sync buttons found throughout the web interface.",

View File

@ -1400,5 +1400,7 @@
"HeaderUpcomingPrograms": "Upcoming Programs", "HeaderUpcomingPrograms": "Upcoming Programs",
"ButtonMoreItems": "More...", "ButtonMoreItems": "More...",
"LabelShowLibraryTileNames": "Show library tile names", "LabelShowLibraryTileNames": "Show library tile names",
"LabelShowLibraryTileNamesHelp": "Determines if labels will be displayed underneath library tiles on the home page" "LabelShowLibraryTileNamesHelp": "Determines if labels will be displayed underneath library tiles on the home page",
"OptionEnableTranscodingThrottle": "Enable throttling",
"OptionEnableTranscodingThrottleHelp": "Throttling will automatically adjust transcoding speed in order to minimize server cpu utilization during playback."
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Model.Dlna; using System.Collections.Generic;
using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Server.Implementations.Sync namespace MediaBrowser.Server.Implementations.Sync
{ {
@ -25,6 +26,9 @@ namespace MediaBrowser.Server.Implementations.Sync
mkvAudio += ",dca"; mkvAudio += ",dca";
} }
var videoProfile = "high|main|baseline|constrained baseline";
var videoLevel = "41";
DirectPlayProfiles = new[] DirectPlayProfiles = new[]
{ {
new DirectPlayProfile new DirectPlayProfile
@ -48,13 +52,37 @@ namespace MediaBrowser.Server.Implementations.Sync
} }
}; };
ContainerProfiles = new ContainerProfile[] { }; ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.NotEquals,
Property = ProfileConditionValue.NumAudioStreams,
Value = "0",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.EqualsAny,
Property = ProfileConditionValue.NumVideoStreams,
Value = "1",
IsRequired = false
}
}
}
};
CodecProfiles = new[] var codecProfiles = new List<CodecProfile>
{ {
new CodecProfile new CodecProfile
{ {
Type = CodecType.Video, Type = CodecType.Video,
Codec = "h264",
Conditions = new [] Conditions = new []
{ {
new ProfileCondition new ProfileCondition
@ -65,11 +93,18 @@ namespace MediaBrowser.Server.Implementations.Sync
IsRequired = false IsRequired = false
}, },
new ProfileCondition new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920",
IsRequired = true
},
new ProfileCondition
{ {
Condition = ProfileConditionType.LessThanEqual, Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height, Property = ProfileConditionValue.Height,
Value = "1080", Value = "1080",
IsRequired = false IsRequired = true
}, },
new ProfileCondition new ProfileCondition
{ {
@ -77,11 +112,115 @@ namespace MediaBrowser.Server.Implementations.Sync
Property = ProfileConditionValue.RefFrames, Property = ProfileConditionValue.RefFrames,
Value = "4", Value = "4",
IsRequired = false IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.Equals,
Property = ProfileConditionValue.IsAnamorphic,
Value = "false",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = videoLevel,
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.EqualsAny,
Property = ProfileConditionValue.VideoProfile,
Value = videoProfile,
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.Video,
Codec = "mpeg4",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitDepth,
Value = "8",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920",
IsRequired = true
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080",
IsRequired = true
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.RefFrames,
Value = "4",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.Equals,
Property = ProfileConditionValue.IsAnamorphic,
Value = "false",
IsRequired = false
} }
} }
} }
}; };
var maxAudioChannels = supportsAc3 || supportsDca ? "5" : "2";
codecProfiles.Add(new CodecProfile
{
Type = CodecType.Audio,
Codec = "mpeg4",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = maxAudioChannels,
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.Equals,
Property = ProfileConditionValue.IsSecondaryAudio,
Value = "false",
IsRequired = false
}
}
});
CodecProfiles = codecProfiles.ToArray();
SubtitleProfiles = new[] SubtitleProfiles = new[]
{ {
new SubtitleProfile new SubtitleProfile

View File

@ -364,7 +364,7 @@ namespace MediaBrowser.WebDashboard.Api
"backdrops.js", "backdrops.js",
"sync.js", "sync.js",
"syncjob.js", "syncjob.js",
"syncservices.js", "appservices.js",
"playlistmanager.js", "playlistmanager.js",
"mediaplayer.js", "mediaplayer.js",

View File

@ -151,7 +151,7 @@
<Content Include="dashboard-ui\scripts\syncjob.js"> <Content Include="dashboard-ui\scripts\syncjob.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\syncservices.js"> <Content Include="dashboard-ui\scripts\appservices.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\syncsettings.js"> <Content Include="dashboard-ui\scripts\syncsettings.js">
@ -175,7 +175,7 @@
<Content Include="dashboard-ui\syncjob.html"> <Content Include="dashboard-ui\syncjob.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\syncservices.html"> <Content Include="dashboard-ui\appservices.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\syncsettings.html"> <Content Include="dashboard-ui\syncsettings.html">

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Common.Internal</id> <id>MediaBrowser.Common.Internal</id>
<version>3.0.608</version> <version>3.0.609</version>
<title>MediaBrowser.Common.Internal</title> <title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors> <authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description> <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Emby 2013</copyright> <copyright>Copyright © Emby 2013</copyright>
<dependencies> <dependencies>
<dependency id="MediaBrowser.Common" version="3.0.608" /> <dependency id="MediaBrowser.Common" version="3.0.609" />
<dependency id="NLog" version="3.2.0.0" /> <dependency id="NLog" version="3.2.0.0" />
<dependency id="SimpleInjector" version="2.7.0" /> <dependency id="SimpleInjector" version="2.7.0" />
</dependencies> </dependencies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Common</id> <id>MediaBrowser.Common</id>
<version>3.0.608</version> <version>3.0.609</version>
<title>MediaBrowser.Common</title> <title>MediaBrowser.Common</title>
<authors>Emby Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Model.Signed</id> <id>MediaBrowser.Model.Signed</id>
<version>3.0.608</version> <version>3.0.609</version>
<title>MediaBrowser.Model - Signed Edition</title> <title>MediaBrowser.Model - Signed Edition</title>
<authors>Emby Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Server.Core</id> <id>MediaBrowser.Server.Core</id>
<version>3.0.608</version> <version>3.0.609</version>
<title>Media Browser.Server.Core</title> <title>Media Browser.Server.Core</title>
<authors>Emby Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description> <description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright> <copyright>Copyright © Emby 2013</copyright>
<dependencies> <dependencies>
<dependency id="MediaBrowser.Common" version="3.0.608" /> <dependency id="MediaBrowser.Common" version="3.0.609" />
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>