aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api/Playback
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api/Playback')
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs57
-rw-r--r--MediaBrowser.Api/Playback/Dash/MpegDashService.cs13
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs29
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs59
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs16
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs4
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs1
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs2
8 files changed, 111 insertions, 70 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 531d67eed..86f06213c 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -290,13 +290,6 @@ namespace MediaBrowser.Api.Playback
{
get
{
- var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder;
-
- if (!string.IsNullOrWhiteSpace(lib))
- {
- return lib;
- }
-
return "libx264";
}
}
@@ -810,13 +803,53 @@ namespace MediaBrowser.Api.Playback
}
/// <summary>
+ /// Gets the name of the output video codec
+ /// </summary>
+ /// <param name="state">The state.</param>
+ /// <returns>System.String.</returns>
+ protected string GetVideoDecoder(StreamState state)
+ {
+ if (string.Equals(ApiEntryPoint.Instance.GetEncodingOptions().HardwareVideoDecoder, "qsv", StringComparison.OrdinalIgnoreCase))
+ {
+ if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
+ {
+ switch (state.MediaSource.VideoStream.Codec.ToLower())
+ {
+ case "avc":
+ case "h264":
+ if (MediaEncoder.SupportsDecoder("h264_qsv"))
+ {
+ return "-c:v h264_qsv ";
+ }
+ break;
+ case "mpeg2video":
+ if (MediaEncoder.SupportsDecoder("mpeg2_qsv"))
+ {
+ return "-c:v mpeg2_qsv ";
+ }
+ break;
+ case "vc1":
+ if (MediaEncoder.SupportsDecoder("vc1_qsv"))
+ {
+ return "-c:v vc1_qsv ";
+ }
+ break;
+ }
+ }
+ }
+
+ // leave blank so ffmpeg will decide
+ return string.Empty;
+ }
+
+ /// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="state">The state.</param>
/// <returns>System.String.</returns>
protected string GetInputArgument(StreamState state)
{
- var arg = "-i " + GetInputPathArgument(state);
+ var arg = string.Format("{1}-i {0}", GetInputPathArgument(state), GetVideoDecoder(state));
if (state.SubtitleStream != null)
{
@@ -826,7 +859,7 @@ namespace MediaBrowser.Api.Playback
}
}
- return arg;
+ return arg.Trim();
}
private string GetInputPathArgument(StreamState state)
@@ -889,7 +922,7 @@ namespace MediaBrowser.Api.Playback
CancellationTokenSource cancellationTokenSource,
string workingDirectory = null)
{
- Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+ FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
@@ -942,7 +975,7 @@ namespace MediaBrowser.Api.Playback
Logger.Info(commandLineLogMessage);
var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt");
- Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
+ FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
// 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);
@@ -972,7 +1005,7 @@ namespace MediaBrowser.Api.Playback
StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream);
// Wait for the file to exist before proceeeding
- while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
+ while (!FileSystem.FileExists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
{
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
index c201ffd58..cbee821d2 100644
--- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
+++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
@@ -174,7 +174,7 @@ namespace MediaBrowser.Api.Playback.Dash
var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture));
state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath));
- Directory.CreateDirectory(workingDirectory);
+ FileSystem.CreateDirectory(workingDirectory);
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false);
await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false);
}
@@ -328,8 +328,7 @@ namespace MediaBrowser.Api.Playback.Dash
try
{
- return new DirectoryInfo(folder)
- .EnumerateFiles("*", SearchOption.AllDirectories)
+ return fileSystem.GetFiles(folder)
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
.Take(count)
@@ -348,20 +347,20 @@ namespace MediaBrowser.Api.Playback.Dash
if (requestedIndex == -1)
{
var path = Path.Combine(folder, "0", "stream" + representationId + "-" + "init" + segmentExtension);
- return File.Exists(path) ? path : null;
+ return FileSystem.FileExists(path) ? path : null;
}
try
{
- foreach (var subfolder in new DirectoryInfo(folder).EnumerateDirectories().ToList())
+ foreach (var subfolder in FileSystem.GetDirectoryPaths(folder).ToList())
{
- var subfolderName = Path.GetFileNameWithoutExtension(subfolder.FullName);
+ var subfolderName = Path.GetFileNameWithoutExtension(subfolder);
int startNumber;
if (int.TryParse(subfolderName, NumberStyles.Any, UsCulture, out startNumber))
{
var segmentIndex = requestedIndex - startNumber + 1;
var path = Path.Combine(folder, subfolderName, "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension);
- if (File.Exists(path))
+ if (FileSystem.FileExists(path))
{
return path;
}
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 5d377366a..036397b4f 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -90,12 +90,12 @@ namespace MediaBrowser.Api.Playback.Hls
TranscodingJob job = null;
var playlist = state.OutputFilePath;
- if (!File.Exists(playlist))
+ if (!FileSystem.FileExists(playlist))
{
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
- if (!File.Exists(playlist))
+ if (!FileSystem.FileExists(playlist))
{
// If the playlist doesn't already exist, startup ffmpeg
try
@@ -312,31 +312,6 @@ namespace MediaBrowser.Api.Playback.Hls
return 0;
}
- protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
- {
- if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
- {
- Logger.Debug("Cannot stream copy video due to missing keyframe info");
- 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)
- {
- Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
- return false;
- }
- previousSegment = frame;
- }
-
- return base.CanStreamCopyVideo(request, videoStream);
- }
-
protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
{
return false;
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index cbea1ca0c..9359c65f2 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -165,7 +165,7 @@ namespace MediaBrowser.Api.Playback.Hls
TranscodingJob job = null;
- if (File.Exists(segmentPath))
+ if (FileSystem.FileExists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
@@ -174,7 +174,7 @@ namespace MediaBrowser.Api.Playback.Hls
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
- if (File.Exists(segmentPath))
+ if (FileSystem.FileExists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
@@ -285,20 +285,23 @@ namespace MediaBrowser.Api.Playback.Hls
private double[] GetSegmentLengths(StreamState state)
{
var result = new List<double>();
- var encoder = GetVideoEncoder(state);
-
- if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase))
+ if (state.VideoRequest != null)
{
- var videoStream = state.VideoStream;
- if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0)
+ var encoder = GetVideoEncoder(state);
+
+ if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase))
{
- foreach (var frame in videoStream.KeyFrames)
+ var videoStream = state.VideoStream;
+ if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0)
{
- var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds;
- seconds -= result.Sum();
- result.Add(seconds);
+ foreach (var frame in videoStream.KeyFrames)
+ {
+ var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds;
+ seconds -= result.Sum();
+ result.Add(seconds);
+ }
+ return result.ToArray();
}
- return result.ToArray();
}
}
@@ -383,8 +386,7 @@ namespace MediaBrowser.Api.Playback.Hls
try
{
- return new DirectoryInfo(folder)
- .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+ return fileSystem.GetFiles(folder)
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase) && Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
.FirstOrDefault();
@@ -429,7 +431,7 @@ namespace MediaBrowser.Api.Playback.Hls
CancellationToken cancellationToken)
{
// If all transcoding has completed, just return immediately
- if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
+ if (transcodingJob != null && transcodingJob.HasExited && FileSystem.FileExists(segmentPath))
{
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
@@ -449,7 +451,7 @@ namespace MediaBrowser.Api.Playback.Hls
// If it appears in the playlist, it's done
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
{
- if (File.Exists(segmentPath))
+ if (FileSystem.FileExists(segmentPath))
{
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
@@ -960,5 +962,30 @@ namespace MediaBrowser.Api.Playback.Hls
{
return isOutputVideo ? ".ts" : ".ts";
}
+
+ protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
+ {
+ if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
+ {
+ Logger.Debug("Cannot stream copy video due to missing keyframe info");
+ 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)
+ {
+ Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
+ return false;
+ }
+ previousSegment = frame;
+ }
+
+ return base.CanStreamCopyVideo(request, videoStream);
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index 0b7b50134..2ab2d11f6 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Playback
Size = 102400;
}
}
-
+
[Authenticated]
public class MediaInfoService : BaseApiService
{
@@ -289,7 +289,7 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectStream)
{
options.MaxBitrate = GetMaxBitrate(maxBitrate);
-
+
// The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildAudioItem(options) :
@@ -309,7 +309,7 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsTranscoding)
{
options.MaxBitrate = GetMaxBitrate(maxBitrate);
-
+
// The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildAudioItem(options) :
@@ -336,9 +336,15 @@ namespace MediaBrowser.Api.Playback
var maxBitrate = clientMaxBitrate;
var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit;
- if (remoteClientMaxBitrate > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp))
+ if (remoteClientMaxBitrate > 0)
{
- maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate);
+ var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.RemoteIp);
+
+ Logger.Info("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.RemoteIp, isInLocalNetwork);
+ if (!isInLocalNetwork)
+ {
+ maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate);
+ }
}
return maxBitrate;
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index 910ac18e7..aa0cda133 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -139,7 +139,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
var outputPath = state.OutputFilePath;
- var outputPathExists = File.Exists(outputPath);
+ var outputPathExists = FileSystem.FileExists(outputPath);
var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);
@@ -325,7 +325,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{
TranscodingJob job;
- if (!File.Exists(outputPath))
+ if (!FileSystem.FileExists(outputPath))
{
job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 84ae26248..1dfb43387 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -17,6 +17,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// <summary>
/// Class GetVideoStream
/// </summary>
+ [Route("/Videos/{Id}/stream.mpegts", "GET")]
[Route("/Videos/{Id}/stream.ts", "GET")]
[Route("/Videos/{Id}/stream.webm", "GET")]
[Route("/Videos/{Id}/stream.asf", "GET")]
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 34dc5ea12..fdbe5835e 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -74,7 +74,7 @@ namespace MediaBrowser.Api.Playback
{
get
{
- return ReadInputAtNativeFramerate ? 1000 : 0;
+ return 0;
}
}