aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2017-03-29 02:25:37 -0400
committerGitHub <noreply@github.com>2017-03-29 02:25:37 -0400
commit88e3fcfdc78fcab201fad72466bf2aa50b453210 (patch)
tree1e40408a0167aa2dca47ac07b16afa26e94d18d1 /MediaBrowser.Controller
parent75f58d5665322a5045649aed89aecfef011b315c (diff)
parent9f9e81089f5606e958bc8c3f0e4d73475d02372a (diff)
Merge pull request #2554 from MediaBrowser/beta
Beta
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs7
-rw-r--r--MediaBrowser.Controller/Entities/GameGenre.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs7
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs7
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs11
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHost.cs2
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs237
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs11
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs10
-rw-r--r--MediaBrowser.Controller/MediaEncoding/JobLogger.cs149
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs13
17 files changed, 456 insertions, 32 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 20b2529c0..8d83f8a35 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -289,7 +289,12 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 74679b474..e26e0dfce 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -118,7 +118,12 @@ namespace MediaBrowser.Controller.Entities.Audio
return LibraryManager.GetItemList(query);
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index 22a8675c5..4187167b9 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -96,7 +96,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 1b746ae51..9769efdd0 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -108,7 +108,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
index 4c033dc00..2104bee09 100644
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ b/MediaBrowser.Controller/Entities/IHasImages.cs
@@ -206,6 +206,8 @@ namespace MediaBrowser.Controller.Entities
void SetImage(ItemImageInfo image, int index);
double? GetDefaultPrimaryImageAspectRatio();
+
+ int? ProductionYear { get; set; }
}
public static class HasImagesExtensions
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index ee1aea938..b68681d36 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -133,7 +133,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validFilename = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index b8ad691a9..2e5e6ce43 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -114,7 +114,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 78f907d61..890626419 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -614,7 +614,8 @@ namespace MediaBrowser.Controller.Entities
Timestamp = i.Timestamp,
Type = type,
PlayableStreamFileNames = i.PlayableStreamFileNames.ToList(),
- SupportsDirectStream = i.VideoType == VideoType.VideoFile
+ SupportsDirectStream = i.VideoType == VideoType.VideoFile,
+ IsRemote = i.IsShortcut
};
if (info.Protocol == MediaProtocol.File)
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index 75fb69435..b352950a0 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -122,7 +122,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index a908d2d3f..5242c5b1f 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -376,19 +376,14 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
Task OnRecordingFileDeleted(BaseItem recording);
- /// <summary>
- /// Gets the sat ini mappings.
- /// </summary>
- /// <returns>List&lt;NameValuePair&gt;.</returns>
- List<NameValuePair> GetSatIniMappings();
-
- Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken);
-
Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
List<IListingsProvider> ListingProviders { get; }
+ List<NameIdPair> GetTunerHostTypes();
+ Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
+
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
index 5615649c2..fc344298b 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -44,6 +44,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
+
+ Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken);
}
public interface IConfigurableTunerHost
{
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 4ebee531f..88153868f 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -190,6 +190,7 @@
<Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
<Compile Include="MediaEncoding\IMediaEncoder.cs" />
<Compile Include="MediaEncoding\ISubtitleEncoder.cs" />
+ <Compile Include="MediaEncoding\JobLogger.cs" />
<Compile Include="MediaEncoding\MediaInfoRequest.cs" />
<Compile Include="MediaEncoding\MediaStreamSelector.cs" />
<Compile Include="Net\AuthenticatedAttribute.cs" />
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 80b9cc154..b7b31509c 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -154,6 +154,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return "mpegts";
}
+
// For these need to find out the ffmpeg names
if (string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase))
{
@@ -163,6 +164,10 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return null;
}
+ if (string.Equals(container, "mts", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
if (string.Equals(container, "vob", StringComparison.OrdinalIgnoreCase))
{
return null;
@@ -179,12 +184,23 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return null;
}
+ if (string.Equals(container, "dvr-ms", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
// Seeing reported failures here, not sure yet if this is related to specfying input format
if (string.Equals(container, "m4v", StringComparison.OrdinalIgnoreCase))
{
return null;
}
+ // obviously don't do this for strm files
+ if (string.Equals(container, "strm", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
return container;
}
@@ -661,9 +677,8 @@ namespace MediaBrowser.Controller.MediaEncoding
var level = NormalizeTranscodingLevel(state.OutputVideoCodec, request.Level);
// h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
- // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
+ // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{
switch (level)
@@ -700,10 +715,15 @@ namespace MediaBrowser.Controller.MediaEncoding
break;
}
}
+ // nvenc doesn't decode with param -level set ?!
+ if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)){
+ param += "";
+ }
else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase))
{
param += " -level " + level;
}
+
}
if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
@@ -727,12 +747,18 @@ namespace MediaBrowser.Controller.MediaEncoding
if (videoStream.IsInterlaced)
{
- return false;
+ if (request.DeInterlace)
+ {
+ return false;
+ }
}
if (videoStream.IsAnamorphic ?? false)
{
- return false;
+ if (request.RequireNonAnamorphic)
+ {
+ return false;
+ }
}
// Can't stream copy if we're burning in subtitles
@@ -1561,6 +1587,15 @@ namespace MediaBrowser.Controller.MediaEncoding
MediaSourceInfo mediaSource,
string requestedUrl)
{
+ if (state == null)
+ {
+ throw new ArgumentNullException("state");
+ }
+ if (mediaSource == null)
+ {
+ throw new ArgumentNullException("mediaSource");
+ }
+
state.MediaPath = mediaSource.Path;
state.InputProtocol = mediaSource.Protocol;
state.InputContainer = mediaSource.Container;
@@ -1670,9 +1705,21 @@ namespace MediaBrowser.Controller.MediaEncoding
case "h264":
if (_mediaEncoder.SupportsDecoder("h264_qsv"))
{
+ // qsv decoder does not support 10-bit input
+ if ((state.VideoStream.BitDepth ?? 8) > 8)
+ {
+ return null;
+ }
return "-c:v h264_qsv ";
}
break;
+ //case "hevc":
+ //case "h265":
+ // if (_mediaEncoder.SupportsDecoder("hevc_qsv"))
+ // {
+ // return "-c:v hevc_qsv ";
+ // }
+ // break;
case "mpeg2video":
if (_mediaEncoder.SupportsDecoder("mpeg2_qsv"))
{
@@ -1715,5 +1762,187 @@ namespace MediaBrowser.Controller.MediaEncoding
return threads;
}
+
+ public string GetSubtitleEmbedArguments(EncodingJobInfo state)
+ {
+ if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)
+ {
+ return string.Empty;
+ }
+
+ var format = state.SupportedSubtitleCodecs.FirstOrDefault();
+ string codec;
+
+ if (string.IsNullOrWhiteSpace(format) || string.Equals(format, state.SubtitleStream.Codec, StringComparison.OrdinalIgnoreCase))
+ {
+ codec = "copy";
+ }
+ else
+ {
+ codec = format;
+ }
+
+ // Muxing in dvbsub via either copy or -codec dvbsub does not seem to work
+ // It doesn't throw any errors but vlc on android will not render them
+ // They will need to be converted to an alternative format
+ // TODO: This is incorrectly assuming that dvdsub will be supported by the player
+ // The api will need to be expanded to accomodate this.
+ if (string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase))
+ {
+ codec = "dvdsub";
+ }
+
+ var args = " -codec:s:0 " + codec;
+
+ args += " -disposition:s:0 default";
+
+ return args;
+ }
+
+ public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultH264Preset)
+ {
+ // Get the output codec name
+ var videoCodec = GetVideoEncoder(state, encodingOptions);
+
+ var format = string.Empty;
+ var keyFrame = string.Empty;
+
+ if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase) &&
+ state.BaseRequest.Context == EncodingContext.Streaming)
+ {
+ // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
+ format = " -f mp4 -movflags frag_keyframe+empty_moov";
+ }
+
+ var threads = GetNumberOfThreads(state, encodingOptions, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
+
+ var inputModifier = GetInputModifier(state, encodingOptions);
+
+ return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"",
+ inputModifier,
+ GetInputArgument(state, encodingOptions),
+ keyFrame,
+ GetMapArgs(state),
+ GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultH264Preset),
+ threads,
+ GetProgressiveVideoAudioArguments(state, encodingOptions),
+ GetSubtitleEmbedArguments(state),
+ format,
+ outputPath
+ ).Trim();
+ }
+
+ public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultH264Preset)
+ {
+ var args = "-codec:v:0 " + videoCodec;
+
+ if (state.EnableMpegtsM2TsMode)
+ {
+ args += " -mpegts_m2ts_mode 1";
+ }
+
+ if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ {
+ if (state.VideoStream != null && IsH264(state.VideoStream) && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
+ {
+ args += " -bsf:v h264_mp4toannexb";
+ }
+
+ if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
+ {
+ args += " -copyts -avoid_negative_ts disabled -start_at_zero";
+ }
+
+ if (!state.RunTimeTicks.HasValue)
+ {
+ args += " -flags -global_header -fflags +genpts";
+ }
+
+ return args;
+ }
+
+ var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
+ 5.ToString(_usCulture));
+
+ args += keyFrameArg;
+
+ var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.BaseRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
+
+ var hasCopyTs = false;
+ // Add resolution params, if specified
+ if (!hasGraphicalSubs)
+ {
+ var outputSizeParam = GetOutputSizeParam(state, videoCodec);
+ args += outputSizeParam;
+ hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1;
+ }
+
+ if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
+ {
+ if (!hasCopyTs)
+ {
+ args += " -copyts";
+ }
+ args += " -avoid_negative_ts disabled -start_at_zero";
+ }
+
+ var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset);
+
+ if (!string.IsNullOrEmpty(qualityParam))
+ {
+ args += " " + qualityParam.Trim();
+ }
+
+ // This is for internal graphical subs
+ if (hasGraphicalSubs)
+ {
+ args += GetGraphicalSubtitleParam(state, videoCodec);
+ }
+
+ if (!state.RunTimeTicks.HasValue)
+ {
+ args += " -flags -global_header";
+ }
+
+ return args;
+ }
+
+ public string GetProgressiveVideoAudioArguments(EncodingJobInfo state, EncodingOptions encodingOptions)
+ {
+ // If the video doesn't have an audio stream, return a default.
+ if (state.AudioStream == null && state.VideoStream != null)
+ {
+ return string.Empty;
+ }
+
+ // Get the output codec name
+ var codec = GetAudioEncoder(state);
+
+ var args = "-codec:a:0 " + codec;
+
+ if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ {
+ return args;
+ }
+
+ // Add the number of audio channels
+ var channels = state.OutputAudioChannels;
+
+ if (channels.HasValue)
+ {
+ args += " -ac " + channels.Value;
+ }
+
+ var bitrate = state.OutputAudioBitrate;
+
+ if (bitrate.HasValue)
+ {
+ args += " -ab " + bitrate.Value.ToString(_usCulture);
+ }
+
+ args += " " + GetAudioFilterParam(state, encodingOptions, false);
+
+ return args;
+ }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index a18b86432..f5878864b 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -12,7 +12,7 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Controller.MediaEncoding
{
// For now, a common base class until the API and MediaEncoding classes are unified
- public class EncodingJobInfo
+ public abstract class EncodingJobInfo
{
private readonly ILogger _logger;
@@ -29,6 +29,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public int? OutputVideoBitrate { get; set; }
public MediaStream SubtitleStream { get; set; }
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
+ public List<string> SupportedSubtitleCodecs { get; set; }
public int InternalSubtitleStreamOffset { get; set; }
public MediaSourceInfo MediaSource { get; set; }
@@ -52,6 +53,8 @@ namespace MediaBrowser.Controller.MediaEncoding
public string InputContainer { get; set; }
public IsoType? IsoType { get; set; }
+ public bool EnableMpegtsM2TsMode { get; set; }
+
public BaseEncodingJobOptions BaseRequest { get; set; }
public long? StartTimeTicks
@@ -64,6 +67,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get { return BaseRequest.CopyTimestamps; }
}
+ public int? OutputAudioBitrate;
public int? OutputAudioChannels;
public int? OutputAudioSampleRate;
public bool DeInterlace { get; set; }
@@ -74,8 +78,9 @@ namespace MediaBrowser.Controller.MediaEncoding
_logger = logger;
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
PlayableStreamFileNames = new List<string>();
+ SupportedAudioCodecs = new List<string>();
SupportedVideoCodecs = new List<string>();
- SupportedVideoCodecs = new List<string>();
+ SupportedSubtitleCodecs = new List<string>();
}
/// <summary>
@@ -110,5 +115,7 @@ namespace MediaBrowser.Controller.MediaEncoding
IsoMount = null;
}
}
+
+ public abstract void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index 4bb180d4b..73be78dc9 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -14,7 +14,6 @@ namespace MediaBrowser.Controller.MediaEncoding
public string AudioCodec { get; set; }
public DeviceProfile DeviceProfile { get; set; }
- public EncodingContext Context { get; set; }
public bool ReadInputAtNativeFramerate { get; set; }
@@ -46,7 +45,7 @@ namespace MediaBrowser.Controller.MediaEncoding
AudioBitRate = info.AudioBitrate;
AudioSampleRate = info.TargetAudioSampleRate;
DeviceProfile = deviceProfile;
- VideoCodec = info.VideoCodec;
+ VideoCodec = info.TargetVideoCodec;
VideoBitRate = info.VideoBitrate;
AudioStreamIndex = info.AudioStreamIndex;
MaxRefFrames = info.MaxRefFrames;
@@ -185,6 +184,8 @@ namespace MediaBrowser.Controller.MediaEncoding
[ApiMember(Name = "MaxVideoBitDepth", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? MaxVideoBitDepth { get; set; }
public bool RequireAvc { get; set; }
+ public bool DeInterlace { get; set; }
+ public bool RequireNonAnamorphic { get; set; }
public int? TranscodingMaxAudioChannels { get; set; }
public int? CpuCoreLimit { get; set; }
public string OutputContainer { get; set; }
@@ -196,6 +197,8 @@ namespace MediaBrowser.Controller.MediaEncoding
[ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string VideoCodec { get; set; }
+ public string SubtitleCodec { get; set; }
+
/// <summary>
/// Gets or sets the index of the audio stream.
/// </summary>
@@ -210,9 +213,12 @@ namespace MediaBrowser.Controller.MediaEncoding
[ApiMember(Name = "VideoStreamIndex", Description = "Optional. The index of the video stream to use. If omitted the first video stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? VideoStreamIndex { get; set; }
+ public EncodingContext Context { get; set; }
+
public BaseEncodingJobOptions()
{
EnableAutoStreamCopy = true;
+ Context = EncodingContext.Streaming;
}
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
new file mode 100644
index 000000000..03e4f7771
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
@@ -0,0 +1,149 @@
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ public class JobLogger
+ {
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+ private readonly ILogger _logger;
+
+ public JobLogger(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async void StartStreamingLog(EncodingJobInfo state, Stream source, Stream target)
+ {
+ try
+ {
+ using (var reader = new StreamReader(source))
+ {
+ while (!reader.EndOfStream)
+ {
+ var line = await reader.ReadLineAsync().ConfigureAwait(false);
+
+ ParseLogLine(line, state);
+
+ var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
+
+ await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ await target.FlushAsync().ConfigureAwait(false);
+ }
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error reading ffmpeg log", ex);
+ }
+ }
+
+ private void ParseLogLine(string line, EncodingJobInfo state)
+ {
+ float? framerate = null;
+ double? percent = null;
+ TimeSpan? transcodingPosition = null;
+ long? bytesTranscoded = null;
+ int? bitRate = null;
+
+ var parts = line.Split(' ');
+
+ var totalMs = state.RunTimeTicks.HasValue
+ ? TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds
+ : 0;
+
+ var startMs = state.BaseRequest.StartTimeTicks.HasValue
+ ? TimeSpan.FromTicks(state.BaseRequest.StartTimeTicks.Value).TotalMilliseconds
+ : 0;
+
+ for (var i = 0; i < parts.Length; i++)
+ {
+ var part = parts[i];
+
+ if (string.Equals(part, "fps=", StringComparison.OrdinalIgnoreCase) &&
+ (i + 1 < parts.Length))
+ {
+ var rate = parts[i + 1];
+ float val;
+
+ if (float.TryParse(rate, NumberStyles.Any, _usCulture, out val))
+ {
+ framerate = val;
+ }
+ }
+ else if (state.RunTimeTicks.HasValue &&
+ part.StartsWith("time=", StringComparison.OrdinalIgnoreCase))
+ {
+ var time = part.Split(new[] { '=' }, 2).Last();
+ TimeSpan val;
+
+ if (TimeSpan.TryParse(time, _usCulture, out val))
+ {
+ var currentMs = startMs + val.TotalMilliseconds;
+
+ var percentVal = currentMs / totalMs;
+ percent = 100 * percentVal;
+
+ transcodingPosition = val;
+ }
+ }
+ else if (part.StartsWith("size=", StringComparison.OrdinalIgnoreCase))
+ {
+ var size = part.Split(new[] { '=' }, 2).Last();
+
+ int? scale = null;
+ if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ scale = 1024;
+ size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
+ }
+
+ if (scale.HasValue)
+ {
+ long val;
+
+ if (long.TryParse(size, NumberStyles.Any, _usCulture, out val))
+ {
+ bytesTranscoded = val * scale.Value;
+ }
+ }
+ }
+ else if (part.StartsWith("bitrate=", StringComparison.OrdinalIgnoreCase))
+ {
+ var rate = part.Split(new[] { '=' }, 2).Last();
+
+ int? scale = null;
+ if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ scale = 1024;
+ rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
+ }
+
+ if (scale.HasValue)
+ {
+ float val;
+
+ if (float.TryParse(rate, NumberStyles.Any, _usCulture, out val))
+ {
+ bitRate = (int)Math.Ceiling(val * scale.Value);
+ }
+ }
+ }
+ }
+
+ if (framerate.HasValue || percent.HasValue)
+ {
+ state.ReportTranscodingProgress(transcodingPosition, framerate, percent, bytesTranscoded, bitRate);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
index 272fa8b3b..62c13fbb5 100644
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -23,21 +23,18 @@ namespace MediaBrowser.Controller.Net
public Action OnComplete { get; set; }
public Action OnError { get; set; }
+ public string Path { get; set; }
+
+ public FileShareMode FileShare { get; set; }
+
public StaticResultOptions()
{
ResponseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ FileShare = FileShareMode.Read;
}
}
public class StaticFileResultOptions : StaticResultOptions
{
- public string Path { get; set; }
-
- public FileShareMode FileShare { get; set; }
-
- public StaticFileResultOptions()
- {
- FileShare = FileShareMode.Read;
- }
}
}