using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.LiveTv { public class ProgressiveFileCopier : IAsyncStreamWriter, IHasHeaders { private readonly IFileSystem _fileSystem; private readonly ILogger _logger; private readonly string _path; private readonly Dictionary _outputHeaders; const int StreamCopyToBufferSize = 81920; public long StartPosition { get; set; } public bool AllowEndOfFile = true; private readonly IDirectStreamProvider _directStreamProvider; private readonly IEnvironmentInfo _environment; private IStreamHelper _streamHelper; public ProgressiveFileCopier(IFileSystem fileSystem, IStreamHelper streamHelper, string path, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment) { _fileSystem = fileSystem; _path = path; _outputHeaders = outputHeaders; _logger = logger; _environment = environment; _streamHelper = streamHelper; } public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, IStreamHelper streamHelper, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment) { _directStreamProvider = directStreamProvider; _outputHeaders = outputHeaders; _logger = logger; _environment = environment; _streamHelper = streamHelper; } public IDictionary Headers => _outputHeaders; private Stream GetInputStream(bool allowAsyncFileRead) { var fileOpenOptions = FileOpenOptions.SequentialScan; if (allowAsyncFileRead) { fileOpenOptions |= FileOpenOptions.Asynchronous; } return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); } public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) { if (_directStreamProvider != null) { await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false); return; } var eofCount = 0; // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 var allowAsyncFileRead = true; using (var inputStream = GetInputStream(allowAsyncFileRead)) { if (StartPosition > 0) { inputStream.Position = StartPosition; } var emptyReadLimit = AllowEndOfFile ? 20 : 100; while (eofCount < emptyReadLimit) { int bytesRead; bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); //var position = fs.Position; //_logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); if (bytesRead == 0) { eofCount++; await Task.Delay(100, cancellationToken).ConfigureAwait(false); } else { eofCount = 0; } } } } } }