diff options
Diffstat (limited to 'MediaBrowser.Api/Playback')
9 files changed, 100 insertions, 85 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 5881e22a7..ab74bab1c 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -261,7 +261,7 @@ namespace MediaBrowser.Api.Playback var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt"); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - Stream logStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); + Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); @@ -759,7 +759,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource == null) { - var mediaSources = await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false); + var mediaSources = await MediaSourceManager.GetPlaybackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources[0] diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 5d0dc98dd..0cbfe4bdf 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -168,7 +168,7 @@ namespace MediaBrowser.Api.Playback.Hls private string GetLivePlaylistText(string path, int segmentLength) { - using (var stream = FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite)) + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (var reader = new StreamReader(stream)) { @@ -211,7 +211,7 @@ namespace MediaBrowser.Api.Playback.Hls { try { - // Need to use FileShareMode.ReadWrite because we're reading the file at the same time it's being written + // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written using (var fileStream = GetPlaylistFileStream(playlist)) { using (var reader = new StreamReader(fileStream)) @@ -252,11 +252,11 @@ namespace MediaBrowser.Api.Playback.Hls try { - return FileSystem.GetFileStream(tmpPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan); + return new FileStream(tmpPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.SequentialScan); } catch (IOException) { - return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan); + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.SequentialScan); } } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 0178f53af..e85ed2050 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -537,7 +537,7 @@ namespace MediaBrowser.Api.Playback.Hls return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions { Path = segmentPath, - FileShare = FileShareMode.ReadWrite, + FileShare = FileShare.ReadWrite, OnComplete = () => { Logger.LogDebug("finished serving {0}", segmentPath); @@ -954,12 +954,12 @@ namespace MediaBrowser.Api.Playback.Hls // Unable to force key frames to h264_qsv transcode if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { - Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv"); - } + Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv"); + } else { args += " " + keyFrameArg; - } + } //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index bb12ab1f0..87ccde2e0 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Hls var file = request.SegmentId + Path.GetExtension(Request.PathInfo); file = Path.Combine(ServerConfigurationManager.GetTranscodePath(), file); - return ResultFactory.GetStaticFileResult(Request, file, FileShareMode.ReadWrite); + return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite); } private Task<object> GetFileResult(string path, string playlistPath) @@ -150,7 +150,7 @@ namespace MediaBrowser.Api.Playback.Hls return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions { Path = path, - FileShare = FileShareMode.ReadWrite, + FileShare = FileShare.ReadWrite, OnComplete = () => { if (transcodingJob != null) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 24f6b6408..d60d9bfe7 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,6 +1,13 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1402 +#pragma warning disable SA1600 +#pragma warning disable SA1649 + using System; +using System.Buffers; using System.Collections.Generic; using System.Globalization; +using System.Text.Json; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -54,7 +61,7 @@ namespace MediaBrowser.Api.Playback public class GetBitrateTestBytes { [ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")] - public long Size { get; set; } + public int Size { get; set; } public GetBitrateTestBytes() { @@ -72,7 +79,6 @@ namespace MediaBrowser.Api.Playback private readonly INetworkManager _networkManager; private readonly IMediaEncoder _mediaEncoder; private readonly IUserManager _userManager; - private readonly IJsonSerializer _json; private readonly IAuthorizationContext _authContext; public MediaInfoService( @@ -85,7 +91,6 @@ namespace MediaBrowser.Api.Playback INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, - IJsonSerializer json, IAuthorizationContext authContext) : base(logger, serverConfigurationManager, httpResultFactory) { @@ -95,20 +100,35 @@ namespace MediaBrowser.Api.Playback _networkManager = networkManager; _mediaEncoder = mediaEncoder; _userManager = userManager; - _json = json; _authContext = authContext; } public object Get(GetBitrateTestBytes request) { - var bytes = new byte[request.Size]; + const int MaxSize = 10_000_000; + + var size = request.Size; + + if (size <= 0) + { + throw new ArgumentException($"The requested size ({size}) is equal to or smaller than 0.", nameof(request)); + } - for (var i = 0; i < bytes.Length; i++) + if (size > MaxSize) { - bytes[i] = 0; + throw new ArgumentException($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).", nameof(request)); } - return ResultFactory.GetResult(null, bytes, "application/octet-stream"); + byte[] buffer = ArrayPool<byte>.Shared.Rent(size); + try + { + new Random().NextBytes(buffer); + return ResultFactory.GetResult(null, buffer, "application/octet-stream"); + } + finally + { + ArrayPool<byte>.Shared.Return(buffer); + } } public async Task<object> Get(GetPlaybackInfo request) @@ -166,8 +186,7 @@ namespace MediaBrowser.Api.Playback public void Post(CloseMediaSource request) { - var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId); - Task.WaitAll(task); + _mediaSourceManager.CloseLiveStream(request.LiveStreamId).GetAwaiter().GetResult(); } public async Task<PlaybackInfoResponse> GetPlaybackInfo(GetPostedPlaybackInfo request) @@ -176,7 +195,7 @@ namespace MediaBrowser.Api.Playback var profile = request.DeviceProfile; - //Logger.LogInformation("GetPostedPlaybackInfo profile: {profile}", _json.SerializeToString(profile)); + Logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile); if (profile == null) { @@ -215,9 +234,7 @@ namespace MediaBrowser.Api.Playback StartTimeTicks = request.StartTimeTicks, SubtitleStreamIndex = request.SubtitleStreamIndex, UserId = request.UserId, - OpenToken = mediaSource.OpenToken, - //EnableMediaProbe = request.EnableMediaProbe - + OpenToken = mediaSource.OpenToken }).ConfigureAwait(false); info.MediaSources = new MediaSourceInfo[] { openStreamResult.MediaSource }; @@ -247,42 +264,26 @@ namespace MediaBrowser.Api.Playback return ToOptimizedResult(result); } - private T Clone<T>(T obj) - { - // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it - // Should we move this directly into MediaSourceManager? - - var json = _json.SerializeToString(obj); - return _json.DeserializeFromString<T>(json); - } - private async Task<PlaybackInfoResponse> GetPlaybackInfo(Guid id, Guid userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) { var user = _userManager.GetUserById(userId); var item = _libraryManager.GetItemById(id); var result = new PlaybackInfoResponse(); + MediaSourceInfo[] mediaSources; if (string.IsNullOrWhiteSpace(liveStreamId)) { - IEnumerable<MediaSourceInfo> mediaSources; - try - { - // TODO handle supportedLiveMediaTypes ? - mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - mediaSources = new List<MediaSourceInfo>(); - Logger.LogError(ex, "Could not find media sources for item id {id}", id); - // TODO PlaybackException ?? - //result.ErrorCode = ex.ErrorCode; - } - result.MediaSources = mediaSources.ToArray(); + // TODO handle supportedLiveMediaTypes? + var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, true, CancellationToken.None).ConfigureAwait(false); - if (!string.IsNullOrWhiteSpace(mediaSourceId)) + if (string.IsNullOrWhiteSpace(mediaSourceId)) { - result.MediaSources = result.MediaSources + mediaSources = mediaSourcesList.ToArray(); + } + else + { + mediaSources = mediaSourcesList .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) .ToArray(); } @@ -291,11 +292,13 @@ namespace MediaBrowser.Api.Playback { var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); - result.MediaSources = new MediaSourceInfo[] { mediaSource }; + mediaSources = new MediaSourceInfo[] { mediaSource }; } - if (result.MediaSources.Length == 0) + if (mediaSources.Length == 0) { + result.MediaSources = Array.Empty<MediaSourceInfo>(); + if (!result.ErrorCode.HasValue) { result.ErrorCode = PlaybackErrorCode.NoCompatibleStream; @@ -303,7 +306,9 @@ namespace MediaBrowser.Api.Playback } else { - result.MediaSources = Clone(result.MediaSources); + // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it + // Should we move this directly into MediaSourceManager? + result.MediaSources = JsonSerializer.Deserialize<MediaSourceInfo[]>(JsonSerializer.SerializeToUtf8Bytes(mediaSources)); result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } @@ -311,7 +316,8 @@ namespace MediaBrowser.Api.Playback return result; } - private void SetDeviceSpecificData(Guid itemId, + private void SetDeviceSpecificData( + Guid itemId, PlaybackInfoResponse result, DeviceProfile profile, AuthorizationInfo auth, @@ -339,7 +345,8 @@ namespace MediaBrowser.Api.Playback SortMediaSources(result, maxBitrate); } - private void SetDeviceSpecificData(BaseItem item, + private void SetDeviceSpecificData( + BaseItem item, MediaSourceInfo mediaSource, DeviceProfile profile, AuthorizationInfo auth, @@ -383,10 +390,12 @@ namespace MediaBrowser.Api.Playback { mediaSource.SupportsDirectPlay = false; } + if (!enableDirectStream) { mediaSource.SupportsDirectStream = false; } + if (!enableTranscoding) { mediaSource.SupportsTranscoding = false; @@ -440,9 +449,9 @@ namespace MediaBrowser.Api.Playback } // 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) : - streamBuilder.BuildVideoItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); if (streamInfo == null || !streamInfo.IsDirectStream) { @@ -488,10 +497,10 @@ namespace MediaBrowser.Api.Playback } } - // 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) : - streamBuilder.BuildVideoItem(options); + // 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) + : streamBuilder.BuildVideoItem(options); if (streamInfo == null || !streamInfo.IsDirectStream) { @@ -524,9 +533,9 @@ namespace MediaBrowser.Api.Playback } // 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) : - streamBuilder.BuildVideoItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) { @@ -567,6 +576,14 @@ namespace MediaBrowser.Api.Playback mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; } + if (!allowAudioStreamCopy) + { + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + } + + mediaSource.TranscodingContainer = streamInfo.Container; + mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + // Do this after the above so that StartPositionTicks is set SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); } @@ -659,14 +676,11 @@ namespace MediaBrowser.Api.Playback }).ThenBy(i => { - switch (i.Protocol) + return i.Protocol switch { - case MediaProtocol.File: - return 0; - default: - return 1; - } - + MediaProtocol.File => 0, + _ => 1, + }; }).ThenBy(i => { if (maxBitrate.HasValue) diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index ed30dbba6..ed68219c9 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -248,7 +248,7 @@ namespace MediaBrowser.Api.Playback.Progressive // ContentType = contentType, // IsHeadRequest = isHeadRequest, // Path = outputPath, - // FileShare = FileShareMode.ReadWrite, + // FileShare = FileShare.ReadWrite, // OnComplete = () => // { // if (transcodingJob != null) diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 660912065..a53b848f9 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -21,8 +21,6 @@ namespace MediaBrowser.Api.Playback.Progressive private readonly CancellationToken _cancellationToken; private readonly Dictionary<string, string> _outputHeaders; - const int StreamCopyToBufferSize = 81920; - private long _bytesWritten = 0; public long StartPosition { get; set; } public bool AllowEndOfFile = true; @@ -52,14 +50,14 @@ namespace MediaBrowser.Api.Playback.Progressive private Stream GetInputStream(bool allowAsyncFileRead) { - var fileOpenOptions = FileOpenOptions.SequentialScan; + var fileOptions = FileOptions.SequentialScan; if (allowAsyncFileRead) { - fileOpenOptions |= FileOpenOptions.Asynchronous; + fileOptions |= FileOptions.Asynchronous; } - return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); + return new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions); } public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) @@ -127,7 +125,7 @@ namespace MediaBrowser.Api.Playback.Progressive private async Task<int> CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; + var array = new byte[IODefaults.CopyToBufferSize]; int bytesRead; int totalBytesRead = 0; @@ -154,7 +152,7 @@ namespace MediaBrowser.Api.Playback.Progressive private async Task<int> CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; + var array = new byte[IODefaults.CopyToBufferSize]; int bytesRead; int totalBytesRead = 0; diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 7396b5c99..d5d2f58c0 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Playback _mediaSourceManager = mediaSourceManager; } - public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate) + public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate) { ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate); } diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index 9cba9df13..cbf981dfe 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -74,7 +74,6 @@ namespace MediaBrowser.Api.Playback [Authenticated] public class UniversalAudioService : BaseApiService { - private readonly ILoggerFactory _loggerFactory; private readonly EncodingHelper _encodingHelper; public UniversalAudioService( @@ -243,7 +242,6 @@ namespace MediaBrowser.Api.Playback NetworkManager, MediaEncoder, UserManager, - JsonSerializer, AuthorizationContext) { Request = Request @@ -300,6 +298,10 @@ namespace MediaBrowser.Api.Playback var transcodingProfile = deviceProfile.TranscodingProfiles[0]; + // hls segment container can only be mpegts or fmp4 per ffmpeg documentation + // TODO: remove this when we switch back to the segment muxer + var supportedHLSContainers = new string[] { "mpegts", "fmp4" }; + var newRequest = new GetMasterHlsAudioPlaylist { AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)), @@ -312,7 +314,8 @@ namespace MediaBrowser.Api.Playback PlaySessionId = playbackInfoResult.PlaySessionId, StartTimeTicks = request.StartTimeTicks, Static = isStatic, - SegmentContainer = request.TranscodingContainer, + // fallback to mpegts if device reports some weird value unsupported by hls + SegmentContainer = Array.Exists(supportedHLSContainers, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts", AudioSampleRate = request.MaxAudioSampleRate, MaxAudioBitDepth = request.MaxAudioBitDepth, BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames, |
