add setting to control transcodng throttle
This commit is contained in:
parent
0bd27381e0
commit
5f044cfd68
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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('?'));
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
VideoTimestamp = 12,
|
||||
IsAnamorphic = 13,
|
||||
RefFrames = 14,
|
||||
IsCabac = 15
|
||||
IsCabac = 15,
|
||||
NumAudioStreams = 16,
|
||||
NumVideoStreams = 17,
|
||||
IsSecondaryAudio
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -364,7 +364,7 @@ namespace MediaBrowser.WebDashboard.Api
|
|||
"backdrops.js",
|
||||
"sync.js",
|
||||
"syncjob.js",
|
||||
"syncservices.js",
|
||||
"appservices.js",
|
||||
"playlistmanager.js",
|
||||
|
||||
"mediaplayer.js",
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user