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)
{
transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger);
transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
state.TranscodingThrottler.Start();
}
}
@ -2012,7 +2012,6 @@ namespace MediaBrowser.Api.Playback
}
var audioCodec = state.ActualOutputAudioCodec;
var videoCodec = state.ActualOutputVideoCodec;
var mediaProfile = state.VideoRequest == null ?
@ -2033,7 +2032,9 @@ namespace MediaBrowser.Api.Playback
state.TargetTimestamp,
state.IsTargetAnamorphic,
state.IsTargetCabac,
state.TargetRefFrames);
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount);
if (mediaProfile != null)
{
@ -2118,7 +2119,9 @@ namespace MediaBrowser.Api.Playback
state.TranscodeSeekInfo,
state.IsTargetAnamorphic,
state.IsTargetCabac,
state.TargetRefFrames
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount
).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>
/// Predicts the audio sample rate that will be in the output stream
/// </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.IO;
using System.Threading;
@ -11,13 +13,18 @@ namespace MediaBrowser.Api.Playback
private readonly ILogger _logger;
private Timer _timer;
private bool _isPaused;
private readonly IConfigurationManager _config;
private readonly long _gapLengthInTicks = TimeSpan.FromMinutes(2).Ticks;
public TranscodingThrottler(TranscodingJob job, ILogger logger)
public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config)
{
_job = job;
_logger = logger;
_config = config;
}
private EncodingOptions GetOptions()
{
return _config.GetConfiguration<EncodingOptions>("encoding");
}
public void Start()
@ -33,7 +40,9 @@ namespace MediaBrowser.Api.Playback
return;
}
if (IsThrottleAllowed(_job))
var options = GetOptions();
if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds))
{
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 transcodingPositionTicks = job.TranscodingPositionTicks ?? 0;
var downloadPositionTicks = job.DownloadPositionTicks ?? 0;
var path = job.Path;
var gapLengthInTicks = TimeSpan.FromSeconds(thresholdSeconds).Ticks;
if (downloadPositionTicks > 0 && transcodingPositionTicks > 0)
{
// HLS - time-based consideration
var targetGap = _gapLengthInTicks;
var targetGap = gapLengthInTicks;
var gap = transcodingPositionTicks - downloadPositionTicks;
if (gap < targetGap)
@ -113,7 +123,7 @@ namespace MediaBrowser.Api.Playback
var bytesTranscoded = job.BytesTranscoded ?? new FileInfo(path).Length;
// Estimate the bytes the transcoder should be ahead
double gapFactor = _gapLengthInTicks;
double gapFactor = gapLengthInTicks;
gapFactor /= transcodingPositionTicks;
var targetGap = bytesTranscoded * gapFactor;

View File

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

View File

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

View File

@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
public class AudioEncoder : BaseEncoder
{
public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager)
public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager)
{
}

View File

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

View File

@ -1,7 +1,8 @@
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
@ -26,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
public EncodingJobOptions Options { get; set; }
public string InputContainer { get; set; }
public List<MediaStream> AllMediaStreams { get; set; }
public MediaSourceInfo MediaSource { get; set; }
public MediaStream AudioStream { get; set; }
public MediaStream VideoStream { get; set; }
public MediaStream SubtitleStream { get; set; }
@ -76,12 +77,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
private readonly ILogger _logger;
private readonly ILiveTvManager _liveTvManager;
private readonly IMediaSourceManager _mediaSourceManager;
public EncodingJob(ILogger logger, ILiveTvManager liveTvManager)
public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager)
{
_logger = logger;
_liveTvManager = liveTvManager;
_mediaSourceManager = mediaSourceManager;
Id = Guid.NewGuid().ToString("N");
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@ -89,7 +90,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
SupportedAudioCodecs = new List<string>();
PlayableStreamFileNames = new List<string>();
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
AllMediaStreams = new List<MediaStream>();
TaskCompletionSource = new TaskCompletionSource<bool>();
}
@ -136,15 +136,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
private async void DisposeLiveStream()
{
if (!string.IsNullOrEmpty(LiveTvStreamId))
if (MediaSource.RequiresClosing)
{
try
{
await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false);
await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error closing live tv stream", ex);
_logger.ErrorException("Error closing media source", ex);
}
}
}
@ -394,6 +394,42 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
public int? TargetVideoStreamCount
{
get
{
if (Options.Static)
{
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
}
return GetMediaStreamCount(MediaStreamType.Video, 1);
}
}
public int? TargetAudioStreamCount
{
get
{
if (Options.Static)
{
return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
}
return GetMediaStreamCount(MediaStreamType.Audio, 1);
}
}
private int? GetMediaStreamCount(MediaStreamType type, int limit)
{
var count = MediaSource.GetStreamCount(type);
if (count.HasValue)
{
count = Math.Min(count.Value, limit);
}
return count;
}
public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded)
{
var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;

View File

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

View File

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

View File

@ -15,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
public class VideoEncoder : BaseEncoder
{
public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager)
public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager)
{
}

View File

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

View File

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

View File

@ -117,7 +117,9 @@ namespace MediaBrowser.Model.Dlna
TranscodeSeekInfo transcodeSeekInfo,
bool? isAnamorphic,
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
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo);
@ -158,7 +160,9 @@ namespace MediaBrowser.Model.Dlna
timestamp,
isAnamorphic,
isCabac,
refFrames);
refFrames,
numVideoStreams,
numAudioStreams);
List<string> orgPnValues = new List<string>();

View File

@ -281,7 +281,9 @@ namespace MediaBrowser.Model.Dlna
TransportStreamTimestamp timestamp,
bool? isAnamorphic,
bool? isCabac,
int? refFrames)
int? refFrames,
int? numVideoStreams,
int? numAudioStreams)
{
container = StringHelper.TrimStart((container ?? string.Empty), '.');
@ -315,7 +317,7 @@ namespace MediaBrowser.Model.Dlna
var anyOff = false;
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;
break;

View File

@ -17,6 +17,9 @@
VideoTimestamp = 12,
IsAnamorphic = 13,
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? refFrames = videoStream == null ? null : videoStream.RefFrames;
int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
// Check container 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;
}
@ -525,7 +528,7 @@ namespace MediaBrowser.Model.Dlna
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;
}
@ -554,7 +557,8 @@ namespace MediaBrowser.Model.Dlna
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;
}
@ -752,6 +756,9 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.PacketLength:
case ProfileConditionValue.NumAudioStreams:
case ProfileConditionValue.NumVideoStreams:
case ProfileConditionValue.IsSecondaryAudio:
case ProfileConditionValue.VideoTimestamp:
{
// 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()
{
return GetSelectableStreams(MediaStreamType.Audio);

View File

@ -46,8 +46,8 @@ namespace MediaBrowser.Model.Dto
public int? Bitrate { 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 TranscodingSubProtocol { get; set; }
public string TranscodingContainer { get; set; }
@ -135,5 +135,35 @@ namespace MediaBrowser.Model.Dto
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",
"MessageKeyUpdated": "Thank you. Your supporter key has been updated.",
"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.",
"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:",
@ -93,7 +95,6 @@
"HeaderWelcomeToProjectWebClient": "Welcome to the Emby Web Client",
"ButtonTakeTheTour": "Take the tour",
"HeaderWelcomeBack": "Welcome back!",
"TitleSync": "Sync",
"TitlePlugins": "Plugins",
"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.",

View File

@ -1400,5 +1400,7 @@
"HeaderUpcomingPrograms": "Upcoming Programs",
"ButtonMoreItems": "More...",
"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
{
@ -25,6 +26,9 @@ namespace MediaBrowser.Server.Implementations.Sync
mkvAudio += ",dca";
}
var videoProfile = "high|main|baseline|constrained baseline";
var videoLevel = "41";
DirectPlayProfiles = new[]
{
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
{
Type = CodecType.Video,
Codec = "h264",
Conditions = new []
{
new ProfileCondition
@ -65,11 +93,18 @@ namespace MediaBrowser.Server.Implementations.Sync
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 = false
IsRequired = true
},
new ProfileCondition
{
@ -77,11 +112,115 @@ namespace MediaBrowser.Server.Implementations.Sync
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
},
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[]
{
new SubtitleProfile

View File

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

View File

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

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.608</version>
<version>3.0.609</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<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>
<copyright>Copyright © Emby 2013</copyright>
<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="SimpleInjector" version="2.7.0" />
</dependencies>

View File

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

View File

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

View File

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