aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api/Playback/BaseStreamingService.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs')
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs113
1 files changed, 109 insertions, 4 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 75321f872..827aed4f2 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1026,7 +1026,7 @@ namespace MediaBrowser.Api.Playback
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
- var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
+ var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
@@ -1515,6 +1515,10 @@ namespace MediaBrowser.Api.Playback
}
else if (i == 22)
{
+ // api_key
+ }
+ else if (i == 23)
+ {
request.LiveStreamId = val;
}
}
@@ -1624,14 +1628,19 @@ namespace MediaBrowser.Api.Playback
var archivable = item as IArchivable;
state.IsInputArchive = archivable != null && archivable.IsArchive;
- MediaSourceInfo mediaSource = null;
+ MediaSourceInfo mediaSource;
if (string.IsNullOrWhiteSpace(request.LiveStreamId))
{
- var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false);
+ var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
- : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
+ : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
+
+ if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+ {
+ mediaSource = mediaSources.First();
+ }
}
else
{
@@ -1700,6 +1709,102 @@ namespace MediaBrowser.Api.Playback
{
state.OutputAudioCodec = "copy";
}
+
+ if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && TranscodingJobType == TranscodingJobType.Hls)
+ {
+ var segmentLength = GetSegmentLength(state);
+ if (segmentLength.HasValue)
+ {
+ state.SegmentLength = segmentLength.Value;
+ }
+ }
+ }
+
+ private int? GetSegmentLength(StreamState state)
+ {
+ var stream = state.VideoStream;
+
+ if (stream == null)
+ {
+ return null;
+ }
+
+ var frames = stream.KeyFrames;
+
+ if (frames == null || frames.Count < 2)
+ {
+ return null;
+ }
+
+ Logger.Debug("Found keyframes at {0}", string.Join(",", frames.ToArray()));
+
+ var intervals = new List<int>();
+ for (var i = 1; i < frames.Count; i++)
+ {
+ var start = frames[i - 1];
+ var end = frames[i];
+ intervals.Add(end - start);
+ }
+
+ Logger.Debug("Found keyframes intervals {0}", string.Join(",", intervals.ToArray()));
+
+ var results = new List<Tuple<int, int>>();
+
+ for (var i = 1; i <= 10; i++)
+ {
+ var idealMs = i*1000;
+
+ if (intervals.Max() < idealMs - 1000)
+ {
+ break;
+ }
+
+ var segments = PredictStreamCopySegments(intervals, idealMs);
+ var variance = segments.Select(s => Math.Abs(idealMs - s)).Sum();
+
+ results.Add(new Tuple<int, int>(i, variance));
+ }
+
+ if (results.Count == 0)
+ {
+ return null;
+ }
+
+ return results.OrderBy(i => i.Item2).ThenBy(i => i.Item1).Select(i => i.Item1).First();
+ }
+
+ private List<int> PredictStreamCopySegments(List<int> intervals, int idealMs)
+ {
+ var segments = new List<int>();
+ var currentLength = 0;
+
+ foreach (var interval in intervals)
+ {
+ if (currentLength == 0 || (currentLength + interval) <= idealMs)
+ {
+ currentLength += interval;
+ }
+
+ else
+ {
+ // The segment will either be above or below the ideal.
+ // Need to figure out which is preferable
+ var offset1 = Math.Abs(idealMs - currentLength);
+ var offset2 = Math.Abs(idealMs - (currentLength + interval));
+
+ if (offset1 <= offset2)
+ {
+ segments.Add(currentLength);
+ currentLength = interval;
+ }
+ else
+ {
+ currentLength += interval;
+ }
+ }
+ }
+ Logger.Debug("Predicted actual segment lengths for length {0}: {1}", idealMs, string.Join(",", segments.ToArray()));
+ return segments;
}
private void AttachMediaSourceInfo(StreamState state,