mirror of https://github.com/jellyfin/jellyfin.git
3.0.5713.4
This commit is contained in:
parent
0e1b983ffc
commit
4ca526979d
|
@ -1634,11 +1634,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest)
|
private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest)
|
||||||
{
|
{
|
||||||
if (!EnableStreamCopy)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
|
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
|
||||||
{
|
{
|
||||||
state.OutputVideoCodec = "copy";
|
state.OutputVideoCodec = "copy";
|
||||||
|
@ -1650,14 +1645,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool EnableStreamCopy
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AttachMediaSourceInfo(StreamState state,
|
private void AttachMediaSourceInfo(StreamState state,
|
||||||
MediaSourceInfo mediaSource,
|
MediaSourceInfo mediaSource,
|
||||||
VideoStreamRequest videoRequest,
|
VideoStreamRequest videoRequest,
|
||||||
|
@ -1741,7 +1728,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
state.MediaSource = mediaSource;
|
state.MediaSource = mediaSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
|
protected virtual bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
|
||||||
{
|
{
|
||||||
if (videoStream.IsInterlaced)
|
if (videoStream.IsInterlaced)
|
||||||
{
|
{
|
||||||
|
@ -1889,7 +1876,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
|
return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
|
protected virtual bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
|
||||||
{
|
{
|
||||||
// Source and target codecs must match
|
// Source and target codecs must match
|
||||||
if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase))
|
if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase))
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
using MediaBrowser.Common.IO;
|
using System.Linq;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Extensions;
|
using MediaBrowser.Model.Extensions;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
|
@ -310,5 +312,33 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
|
||||||
|
{
|
||||||
|
if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousSegment = 0;
|
||||||
|
foreach (var frame in videoStream.KeyFrames)
|
||||||
|
{
|
||||||
|
var length = frame - previousSegment;
|
||||||
|
|
||||||
|
// Don't allow really long segments because this could result in long download times
|
||||||
|
if (length > 10000)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
previousSegment = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.CanStreamCopyVideo(request, videoStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
|
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
|
||||||
|
|
||||||
var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex);
|
var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex);
|
||||||
var segmentLength = state.SegmentLength;
|
|
||||||
|
|
||||||
var segmentExtension = GetSegmentFileExtension(state);
|
var segmentExtension = GetSegmentFileExtension(state);
|
||||||
|
|
||||||
|
@ -169,7 +168,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
if (File.Exists(segmentPath))
|
if (File.Exists(segmentPath))
|
||||||
{
|
{
|
||||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||||
return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
|
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
@ -178,7 +177,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
if (File.Exists(segmentPath))
|
if (File.Exists(segmentPath))
|
||||||
{
|
{
|
||||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||||
return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
|
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -214,7 +213,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
DeleteLastFile(playlistPath, segmentExtension, 0);
|
DeleteLastFile(playlistPath, segmentExtension, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.StartTimeTicks = GetSeekPositionTicks(state, playlistPath, requestedIndex);
|
request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex);
|
||||||
|
|
||||||
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
|
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -249,37 +248,76 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
|
|
||||||
Logger.Info("returning {0}", segmentPath);
|
Logger.Info("returning {0}", segmentPath);
|
||||||
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||||
return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
|
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 256k
|
// 256k
|
||||||
private const int BufferSize = 262144;
|
private const int BufferSize = 262144;
|
||||||
|
|
||||||
private long GetSeekPositionTicks(StreamState state, string playlist, int requestedIndex)
|
private long GetStartPositionTicks(StreamState state, int requestedIndex)
|
||||||
{
|
{
|
||||||
double startSeconds = 0;
|
double startSeconds = 0;
|
||||||
|
var lengths = GetSegmentLengths(state);
|
||||||
|
|
||||||
for (var i = 0; i < requestedIndex; i++)
|
for (var i = 0; i < requestedIndex; i++)
|
||||||
{
|
{
|
||||||
var segmentPath = GetSegmentPath(state, playlist, i);
|
startSeconds += lengths[requestedIndex];
|
||||||
|
|
||||||
//double length;
|
|
||||||
//if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length))
|
|
||||||
//{
|
|
||||||
// Logger.Debug("Found segment length of {0} for index {1}", length, i);
|
|
||||||
// startSeconds += length;
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// startSeconds += state.SegmentLength;
|
|
||||||
//}
|
|
||||||
startSeconds += state.SegmentLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var position = TimeSpan.FromSeconds(startSeconds).Ticks;
|
var position = TimeSpan.FromSeconds(startSeconds).Ticks;
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long GetEndPositionTicks(StreamState state, int requestedIndex)
|
||||||
|
{
|
||||||
|
double startSeconds = 0;
|
||||||
|
var lengths = GetSegmentLengths(state);
|
||||||
|
|
||||||
|
for (var i = 0; i <= requestedIndex; i++)
|
||||||
|
{
|
||||||
|
startSeconds += lengths[requestedIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = TimeSpan.FromSeconds(startSeconds).Ticks;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[] GetSegmentLengths(StreamState state)
|
||||||
|
{
|
||||||
|
var result = new List<double>();
|
||||||
|
var encoder = GetVideoEncoder(state);
|
||||||
|
|
||||||
|
if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var videoStream = state.VideoStream;
|
||||||
|
if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var frame in videoStream.KeyFrames)
|
||||||
|
{
|
||||||
|
var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds;
|
||||||
|
seconds -= result.Sum();
|
||||||
|
result.Add(seconds);
|
||||||
|
}
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticks = state.RunTimeTicks ?? 0;
|
||||||
|
|
||||||
|
var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
|
||||||
|
|
||||||
|
while (ticks > 0)
|
||||||
|
{
|
||||||
|
var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
|
||||||
|
|
||||||
|
result.Add(TimeSpan.FromTicks(length).TotalSeconds);
|
||||||
|
|
||||||
|
ticks -= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
|
public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
|
||||||
{
|
{
|
||||||
var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType);
|
var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType);
|
||||||
|
@ -384,17 +422,16 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state));
|
return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<object> GetSegmentResult(string playlistPath,
|
private async Task<object> GetSegmentResult(StreamState state, string playlistPath,
|
||||||
string segmentPath,
|
string segmentPath,
|
||||||
int segmentIndex,
|
int segmentIndex,
|
||||||
int segmentLength,
|
|
||||||
TranscodingJob transcodingJob,
|
TranscodingJob transcodingJob,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// If all transcoding has completed, just return immediately
|
// If all transcoding has completed, just return immediately
|
||||||
if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
|
if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
|
||||||
{
|
{
|
||||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
||||||
}
|
}
|
||||||
|
|
||||||
var segmentFilename = Path.GetFileName(segmentPath);
|
var segmentFilename = Path.GetFileName(segmentPath);
|
||||||
|
@ -414,7 +451,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
if (File.Exists(segmentPath))
|
if (File.Exists(segmentPath))
|
||||||
{
|
{
|
||||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
||||||
}
|
}
|
||||||
//break;
|
//break;
|
||||||
}
|
}
|
||||||
|
@ -465,13 +502,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
//}
|
//}
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
||||||
}
|
}
|
||||||
|
|
||||||
private object GetSegmentResult(string segmentPath, int index, int segmentLength, TranscodingJob transcodingJob)
|
private object GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJob transcodingJob)
|
||||||
{
|
{
|
||||||
var segmentEndingSeconds = (1 + index) * segmentLength;
|
var segmentEndingPositionTicks = GetEndPositionTicks(state, index);
|
||||||
var segmentEndingPositionTicks = TimeSpan.FromSeconds(segmentEndingSeconds).Ticks;
|
|
||||||
|
|
||||||
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
|
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
|
||||||
{
|
{
|
||||||
|
@ -698,26 +734,22 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
|
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var segmentLengths = GetSegmentLengths(state);
|
||||||
|
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
builder.AppendLine("#EXTM3U");
|
builder.AppendLine("#EXTM3U");
|
||||||
builder.AppendLine("#EXT-X-VERSION:3");
|
builder.AppendLine("#EXT-X-VERSION:3");
|
||||||
builder.AppendLine("#EXT-X-TARGETDURATION:" + (state.SegmentLength).ToString(UsCulture));
|
builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling((segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)).ToString(UsCulture));
|
||||||
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
|
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
|
||||||
|
|
||||||
var queryStringIndex = Request.RawUrl.IndexOf('?');
|
var queryStringIndex = Request.RawUrl.IndexOf('?');
|
||||||
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
|
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
|
||||||
|
|
||||||
var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
|
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
double segmentLength = state.SegmentLength;
|
foreach (var length in segmentLengths)
|
||||||
|
|
||||||
while (seconds > 0)
|
|
||||||
{
|
{
|
||||||
var length = seconds >= state.SegmentLength ? segmentLength : seconds;
|
|
||||||
|
|
||||||
builder.AppendLine("#EXTINF:" + length.ToString("0.000000", UsCulture) + ",");
|
builder.AppendLine("#EXTINF:" + length.ToString("0.000000", UsCulture) + ",");
|
||||||
|
|
||||||
builder.AppendLine(string.Format("hlsdynamic/{0}/{1}{2}{3}",
|
builder.AppendLine(string.Format("hlsdynamic/{0}/{1}{2}{3}",
|
||||||
|
@ -727,7 +759,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
GetSegmentFileExtension(isOutputVideo),
|
GetSegmentFileExtension(isOutputVideo),
|
||||||
queryString));
|
queryString));
|
||||||
|
|
||||||
seconds -= length;
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -850,11 +881,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
args += " -flags +loop-global_header -sc_threshold 0";
|
args += " -flags +loop-global_header -sc_threshold 0";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EnableSplitTranscoding(state))
|
|
||||||
{
|
|
||||||
//args += " -copyts";
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -870,21 +896,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
var toTimeParam = string.Empty;
|
var toTimeParam = string.Empty;
|
||||||
var timestampOffsetParam = string.Empty;
|
var timestampOffsetParam = string.Empty;
|
||||||
|
|
||||||
if (EnableSplitTranscoding(state))
|
|
||||||
{
|
|
||||||
var startTime = state.Request.StartTimeTicks ?? 0;
|
|
||||||
var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds;
|
|
||||||
|
|
||||||
var endTime = startTime + TimeSpan.FromSeconds(durationSeconds).Ticks;
|
|
||||||
endTime = Math.Min(endTime, state.RunTimeTicks.Value);
|
|
||||||
|
|
||||||
if (endTime < state.RunTimeTicks.Value)
|
|
||||||
{
|
|
||||||
//toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime);
|
|
||||||
toTimeParam = " -t " + MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(durationSeconds).Ticks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
|
if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
|
||||||
{
|
{
|
||||||
timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
|
timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
|
||||||
|
@ -927,36 +938,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
|
|
||||||
protected override bool EnableThrottling(StreamState state)
|
protected override bool EnableThrottling(StreamState state)
|
||||||
{
|
{
|
||||||
return !EnableSplitTranscoding(state);
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
private bool EnableSplitTranscoding(StreamState state)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
if (string.Equals(Request.QueryString["EnableSplitTranscoding"], "false", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.RunTimeTicks.HasValue && state.IsOutputVideo;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool EnableStreamCopy
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -226,7 +226,9 @@
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
|
|
@ -248,8 +248,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken)
|
//stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false);
|
||||||
// .ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
@ -283,7 +282,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
private async Task<List<int>> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken)
|
private async Task<List<int>> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
const string args = "-i {0} -select_streams v:{1} -show_frames -show_entries frame=pkt_dts,key_frame -print_format compact";
|
const string args = "-i {0} -select_streams v:{1} -show_packets -print_format compact -show_entries packet=flags -show_entries packet=pts_time";
|
||||||
|
|
||||||
var process = new Process
|
var process = new Process
|
||||||
{
|
{
|
||||||
|
@ -318,7 +317,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
process.BeginErrorReadLine();
|
process.BeginErrorReadLine();
|
||||||
|
|
||||||
await StartReadingOutput(process.StandardOutput.BaseStream, lines, 120000, cancellationToken).ConfigureAwait(false);
|
await StartReadingOutput(process.StandardOutput.BaseStream, lines, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
@ -336,7 +335,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartReadingOutput(Stream source, List<int> lines, int timeoutMs, CancellationToken cancellationToken)
|
private async Task StartReadingOutput(Stream source, List<int> lines, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -354,14 +353,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
.Where(i => i.Length == 2)
|
.Where(i => i.Length == 2)
|
||||||
.ToDictionary(i => i[0], i => i[1]);
|
.ToDictionary(i => i[0], i => i[1]);
|
||||||
|
|
||||||
string pktDts;
|
string flags;
|
||||||
int frameMs;
|
if (values.TryGetValue("flags", out flags) && string.Equals(flags, "k", StringComparison.OrdinalIgnoreCase))
|
||||||
if (values.TryGetValue("pkt_dts", out pktDts) && int.TryParse(pktDts, NumberStyles.Any, CultureInfo.InvariantCulture, out frameMs))
|
|
||||||
{
|
{
|
||||||
string keyFrame;
|
string pts_time;
|
||||||
if (values.TryGetValue("key_frame", out keyFrame) && string.Equals(keyFrame, "1", StringComparison.OrdinalIgnoreCase))
|
double frameSeconds;
|
||||||
|
if (values.TryGetValue("pts_time", out pts_time) && double.TryParse(pts_time, NumberStyles.Any, CultureInfo.InvariantCulture, out frameSeconds))
|
||||||
{
|
{
|
||||||
lines.Add(frameMs);
|
var ms = frameSeconds * 1000;
|
||||||
|
lines.Add(Convert.ToInt32(ms));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -376,7 +376,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
_logger.ErrorException("Error reading ffprobe output", ex);
|
_logger.ErrorException("Error reading ffprobe output", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The us culture
|
/// The us culture
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -802,7 +801,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
public ProcessWrapper(Process process, MediaEncoder mediaEncoder, ILogger logger)
|
public ProcessWrapper(Process process, MediaEncoder mediaEncoder, ILogger logger)
|
||||||
{
|
{
|
||||||
Process = process;
|
Process = process;
|
||||||
this._mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
Process.Exited += Process_Exited;
|
Process.Exited += Process_Exited;
|
||||||
}
|
}
|
||||||
|
@ -819,7 +818,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.ErrorException("Error determing process exit code", ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_mediaEncoder._runningProcesses)
|
lock (_mediaEncoder._runningProcesses)
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using MediaBrowser.Model.Dlna;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Extensions;
|
using MediaBrowser.Model.Extensions;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
@ -225,5 +227,8 @@ namespace MediaBrowser.Model.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>null</c> if [is cabac] contains no value, <c>true</c> if [is cabac]; otherwise, <c>false</c>.</value>
|
/// <value><c>null</c> if [is cabac] contains no value, <c>true</c> if [is cabac]; otherwise, <c>false</c>.</value>
|
||||||
public bool? IsCabac { get; set; }
|
public bool? IsCabac { get; set; }
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public List<int> KeyFrames { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return ItemUpdateType.MetadataImport;
|
return ItemUpdateType.MetadataImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string SchemaVersion = "5";
|
private const string SchemaVersion = "6";
|
||||||
|
|
||||||
private async Task<Model.MediaInfo.MediaInfo> GetMediaInfo(Video item,
|
private async Task<Model.MediaInfo.MediaInfo> GetMediaInfo(Video item,
|
||||||
IIsoMount isoMount,
|
IIsoMount isoMount,
|
||||||
|
@ -140,14 +140,14 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var idString = item.Id.ToString("N");
|
//var idString = item.Id.ToString("N");
|
||||||
var cachePath = Path.Combine(_appPaths.CachePath,
|
//var cachePath = Path.Combine(_appPaths.CachePath,
|
||||||
"ffprobe-video",
|
// "ffprobe-video",
|
||||||
idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
|
// idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _json.DeserializeFromFile<Model.MediaInfo.MediaInfo>(cachePath);
|
//return _json.DeserializeFromFile<Model.MediaInfo.MediaInfo>(cachePath);
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
|
@ -174,8 +174,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
}, cancellationToken).ConfigureAwait(false);
|
}, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
|
//Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
|
||||||
_json.SerializeToFile(result, cachePath);
|
//_json.SerializeToFile(result, cachePath);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,6 +187,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
|
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
private string[] _retriveItemColumns =
|
||||||
|
{
|
||||||
|
"type",
|
||||||
|
"data",
|
||||||
|
"IsOffline"
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prepares the statements.
|
/// Prepares the statements.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -455,7 +462,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
using (var cmd = _connection.CreateCommand())
|
using (var cmd = _connection.CreateCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = "select type,data from TypedBaseItems where guid = @guid";
|
cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid";
|
||||||
cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = id;
|
cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = id;
|
||||||
|
|
||||||
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
|
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
|
||||||
|
@ -482,11 +489,18 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseItem item;
|
||||||
|
|
||||||
using (var stream = reader.GetMemoryStream(1))
|
using (var stream = reader.GetMemoryStream(1))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
|
item = _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
|
||||||
|
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (SerializationException ex)
|
catch (SerializationException ex)
|
||||||
{
|
{
|
||||||
|
@ -494,6 +508,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!reader.IsDBNull(2))
|
||||||
|
{
|
||||||
|
item.IsOffline = reader.GetBoolean(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -685,7 +706,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
using (var cmd = _connection.CreateCommand())
|
using (var cmd = _connection.CreateCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = "select type,data from TypedBaseItems where guid in (select ItemId from ChildrenIds where ParentId = @ParentId)";
|
cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid in (select ItemId from ChildrenIds where ParentId = @ParentId)";
|
||||||
|
|
||||||
cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = parentId;
|
cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = parentId;
|
||||||
|
|
||||||
|
@ -715,7 +736,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
using (var cmd = _connection.CreateCommand())
|
using (var cmd = _connection.CreateCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = "select type,data from TypedBaseItems where type = @type";
|
cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where type = @type";
|
||||||
|
|
||||||
cmd.Parameters.Add(cmd, "@type", DbType.String).Value = type.FullName;
|
cmd.Parameters.Add(cmd, "@type", DbType.String).Value = type.FullName;
|
||||||
|
|
||||||
|
@ -745,7 +766,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
using (var cmd = _connection.CreateCommand())
|
using (var cmd = _connection.CreateCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = "select type,data from TypedBaseItems";
|
cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems";
|
||||||
|
|
||||||
var whereClauses = GetWhereClauses(query, cmd, false);
|
var whereClauses = GetWhereClauses(query, cmd, false);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using MediaBrowser.Controller.Persistence;
|
using System.Globalization;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
@ -38,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
// Add PixelFormat column
|
// Add PixelFormat column
|
||||||
|
|
||||||
createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, PRIMARY KEY (ItemId, StreamIndex))";
|
createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, KeyFrames TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
|
||||||
|
|
||||||
string[] queries = {
|
string[] queries = {
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
AddBitDepthCommand();
|
AddBitDepthCommand();
|
||||||
AddIsAnamorphicColumn();
|
AddIsAnamorphicColumn();
|
||||||
AddIsCabacColumn();
|
AddIsCabacColumn();
|
||||||
|
AddKeyFramesColumn();
|
||||||
AddRefFramesCommand();
|
AddRefFramesCommand();
|
||||||
|
|
||||||
PrepareStatements();
|
PrepareStatements();
|
||||||
|
@ -187,6 +189,37 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
_connection.RunQueries(new[] { builder.ToString() }, _logger);
|
_connection.RunQueries(new[] { builder.ToString() }, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddKeyFramesColumn()
|
||||||
|
{
|
||||||
|
using (var cmd = _connection.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = "PRAGMA table_info(mediastreams)";
|
||||||
|
|
||||||
|
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (!reader.IsDBNull(1))
|
||||||
|
{
|
||||||
|
var name = reader.GetString(1);
|
||||||
|
|
||||||
|
if (string.Equals(name, "KeyFrames", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.AppendLine("alter table mediastreams");
|
||||||
|
builder.AppendLine("add column KeyFrames TEXT NULL");
|
||||||
|
|
||||||
|
_connection.RunQueries(new[] { builder.ToString() }, _logger);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddIsAnamorphicColumn()
|
private void AddIsAnamorphicColumn()
|
||||||
{
|
{
|
||||||
using (var cmd = _connection.CreateCommand())
|
using (var cmd = _connection.CreateCommand())
|
||||||
|
@ -245,7 +278,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
"BitDepth",
|
"BitDepth",
|
||||||
"IsAnamorphic",
|
"IsAnamorphic",
|
||||||
"RefFrames",
|
"RefFrames",
|
||||||
"IsCabac"
|
"IsCabac",
|
||||||
|
"KeyFrames"
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -429,6 +463,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
item.IsCabac = reader.GetBoolean(25);
|
item.IsCabac = reader.GetBoolean(25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!reader.IsDBNull(26))
|
||||||
|
{
|
||||||
|
var frames = reader.GetString(26);
|
||||||
|
if (!string.IsNullOrWhiteSpace(frames))
|
||||||
|
{
|
||||||
|
item.KeyFrames = frames.Split(',').Select(i => int.Parse(i, CultureInfo.InvariantCulture)).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,6 +541,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
_saveStreamCommand.GetParameter(index++).Value = stream.RefFrames;
|
_saveStreamCommand.GetParameter(index++).Value = stream.RefFrames;
|
||||||
_saveStreamCommand.GetParameter(index++).Value = stream.IsCabac;
|
_saveStreamCommand.GetParameter(index++).Value = stream.IsCabac;
|
||||||
|
|
||||||
|
if (stream.KeyFrames == null || stream.KeyFrames.Count == 0)
|
||||||
|
{
|
||||||
|
_saveStreamCommand.GetParameter(index++).Value = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_saveStreamCommand.GetParameter(index++).Value = string.Join(",", stream.KeyFrames.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
_saveStreamCommand.Transaction = transaction;
|
_saveStreamCommand.Transaction = transaction;
|
||||||
_saveStreamCommand.ExecuteNonQuery();
|
_saveStreamCommand.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("3.0.*")]
|
//[assembly: AssemblyVersion("3.0.*")]
|
||||||
//[assembly: AssemblyVersion("3.0.5713.3")]
|
[assembly: AssemblyVersion("3.0.5713.4")]
|
||||||
|
|
Loading…
Reference in New Issue