Merge pull request #2685 from MediaBrowser/beta

Beta
This commit is contained in:
Luke 2017-06-01 03:57:22 -04:00 committed by GitHub
commit 442d4e6699
15 changed files with 357 additions and 126 deletions

View File

@ -128,6 +128,11 @@ namespace Emby.Common.Implementations.Net
return _Socket.BeginReceiveFrom(buffer, offset, count, SocketFlags.None, ref receivedFromEndPoint, callback, buffer); return _Socket.BeginReceiveFrom(buffer, offset, count, SocketFlags.None, ref receivedFromEndPoint, callback, buffer);
} }
public int Receive(byte[] buffer, int offset, int count)
{
return _Socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
}
public SocketReceiveResult EndReceive(IAsyncResult result) public SocketReceiveResult EndReceive(IAsyncResult result)
{ {
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);

View File

@ -94,17 +94,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
private const int BufferSize = 81920; private const int BufferSize = 81920;
public static Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken) public static async Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken)
{
return CopyUntilCancelled(source, target, null, cancellationToken);
}
public static async Task CopyUntilCancelled(Stream source, Stream target, Action onStarted, CancellationToken cancellationToken)
{ {
byte[] buffer = new byte[BufferSize];
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
var bytesRead = await CopyToAsyncInternal(source, target, BufferSize, onStarted, cancellationToken).ConfigureAwait(false); var bytesRead = await CopyToAsyncInternal(source, target, buffer, cancellationToken).ConfigureAwait(false);
onStarted = null;
//var position = fs.Position; //var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
@ -116,23 +112,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, Action onStarted, CancellationToken cancellationToken) private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, byte[] buffer, CancellationToken cancellationToken)
{ {
byte[] buffer = new byte[bufferSize];
int bytesRead; int bytesRead;
int totalBytesRead = 0; int totalBytesRead = 0;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
{ {
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); destination.Write(buffer, 0, bytesRead);
totalBytesRead += bytesRead; totalBytesRead += bytesRead;
if (onStarted != null)
{
onStarted();
}
onStarted = null;
} }
return totalBytesRead; return totalBytesRead;

View File

@ -423,7 +423,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
IsInfiniteStream = true, IsInfiniteStream = true,
IgnoreDts = true, IgnoreDts = true,
//IgnoreIndex = true, //IgnoreIndex = true,
ReadAtNativeFramerate = true //ReadAtNativeFramerate = true
}; };
mediaSource.InferTotalBitrate(); mediaSource.InferTotalBitrate();

View File

@ -12,6 +12,8 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using System.Globalization;
using MediaBrowser.Controller.IO;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{ {
@ -24,7 +26,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>(); private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
private readonly MulticastStream _multicastStream;
private readonly string _tempFilePath; private readonly string _tempFilePath;
private bool _enableFileBuffer = false;
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment) public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
: base(mediaSource, environment, fileSystem) : base(mediaSource, environment, fileSystem)
@ -34,6 +39,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_appHost = appHost; _appHost = appHost;
OriginalStreamId = originalStreamId; OriginalStreamId = originalStreamId;
_multicastStream = new MulticastStream(_logger);
_tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts"); _tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
} }
@ -101,13 +107,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{ {
_logger.Info("Beginning multicastStream.CopyUntilCancelled"); _logger.Info("Beginning multicastStream.CopyUntilCancelled");
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath)); if (_enableFileBuffer)
using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous))
{ {
ResolveAfterDelay(3000, openTaskCompletionSource); FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath));
using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
//await response.Content.CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false); {
await AsyncStreamCopier.CopyStream(response.Content, fileStream, 81920, 4, cancellationToken).ConfigureAwait(false); StreamHelper.CopyTo(response.Content, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken);
}
}
else
{
await _multicastStream.CopyUntilCancelled(response.Content, () => Resolve(openTaskCompletionSource), cancellationToken).ConfigureAwait(false);
} }
} }
} }
@ -132,58 +142,70 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
} }
_liveStreamTaskCompletionSource.TrySetResult(true); _liveStreamTaskCompletionSource.TrySetResult(true);
await DeleteTempFile(_tempFilePath).ConfigureAwait(false); //await DeleteTempFile(_tempFilePath).ConfigureAwait(false);
}); });
} }
private void ResolveAfterDelay(int delayMs, TaskCompletionSource<bool> openTaskCompletionSource) private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
{ {
Task.Run(async () => Task.Run(() =>
{ {
await Task.Delay(delayMs).ConfigureAwait(false);
openTaskCompletionSource.TrySetResult(true); openTaskCompletionSource.TrySetResult(true);
}); });
} }
public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{ {
return CopyFileTo(_tempFilePath, false, stream, cancellationToken); if (_enableFileBuffer)
{
return CopyFileTo(_tempFilePath, stream, cancellationToken);
}
return _multicastStream.CopyToAsync(stream, cancellationToken);
//return CopyFileTo(_tempFilePath, stream, cancellationToken);
} }
protected async Task CopyFileTo(string path, bool allowEndOfFile, Stream outputStream, CancellationToken cancellationToken) protected async Task CopyFileTo(string path, Stream outputStream, CancellationToken cancellationToken)
{ {
var eofCount = 0; long startPosition = -20000;
long startPosition = -25000;
if (startPosition < 0) if (startPosition < 0)
{ {
var length = FileSystem.GetFileInfo(path).Length; var length = FileSystem.GetFileInfo(path).Length;
startPosition = Math.Max(length - startPosition, 0); startPosition = Math.Max(length - startPosition, 0);
} }
using (var inputStream = GetInputStream(path, startPosition, true)) _logger.Info("Live stream starting position is {0} bytes", startPosition.ToString(CultureInfo.InvariantCulture));
var allowAsync = Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
using (var inputStream = GetInputStream(path, startPosition, allowAsync))
{ {
if (startPosition > 0) if (startPosition > 0)
{ {
inputStream.Position = startPosition; inputStream.Position = startPosition;
} }
while (eofCount < 20 || !allowEndOfFile) while (!cancellationToken.IsCancellationRequested)
{ {
var bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 4, cancellationToken).ConfigureAwait(false); long bytesRead;
if (allowAsync)
{
bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 2, cancellationToken).ConfigureAwait(false);
}
else
{
StreamHelper.CopyTo(inputStream, outputStream, 81920, cancellationToken);
bytesRead = 1;
}
//var position = fs.Position; //var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
if (bytesRead == 0) if (bytesRead == 0)
{ {
eofCount++;
await Task.Delay(100, cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false);
} }
else
{
eofCount = 0;
}
} }
} }
} }

View File

@ -16,6 +16,8 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using System.Globalization;
using MediaBrowser.Controller.IO;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{ {
@ -32,6 +34,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
private readonly string _tempFilePath; private readonly string _tempFilePath;
private bool _enableFileBuffer = false;
private readonly MulticastStream _multicastStream;
public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment)
: base(mediaSource, environment, fileSystem) : base(mediaSource, environment, fileSystem)
@ -44,6 +48,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_channelCommands = channelCommands; _channelCommands = channelCommands;
_numTuners = numTuners; _numTuners = numTuners;
_tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts"); _tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
_multicastStream = new MulticastStream(_logger);
} }
protected override async Task OpenInternal(CancellationToken openCancellationToken) protected override async Task OpenInternal(CancellationToken openCancellationToken)
@ -121,10 +126,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (!cancellationToken.IsCancellationRequested) if (!cancellationToken.IsCancellationRequested)
{ {
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath)); if (_enableFileBuffer)
using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous))
{ {
await CopyTo(udpClient, fileStream, openTaskCompletionSource, cancellationToken).ConfigureAwait(false); FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath));
using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
{
CopyTo(udpClient, fileStream, openTaskCompletionSource, cancellationToken);
}
}
else
{
await _multicastStream.CopyUntilCancelled(new UdpClientStream(udpClient), () => Resolve(openTaskCompletionSource), cancellationToken).ConfigureAwait(false);
} }
} }
} }
@ -166,80 +178,111 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}); });
} }
public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) public async Task CopyToAsync(Stream outputStream, CancellationToken cancellationToken)
{ {
return CopyFileTo(_tempFilePath, false, stream, cancellationToken); if (!_enableFileBuffer)
{
await _multicastStream.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
return;
} }
protected async Task CopyFileTo(string path, bool allowEndOfFile, Stream outputStream, CancellationToken cancellationToken) var path = _tempFilePath;
{
var eofCount = 0;
long startPosition = -25000; long startPosition = -20000;
if (startPosition < 0) if (startPosition < 0)
{ {
var length = FileSystem.GetFileInfo(path).Length; var length = FileSystem.GetFileInfo(path).Length;
startPosition = Math.Max(length - startPosition, 0); startPosition = Math.Max(length - startPosition, 0);
} }
using (var inputStream = GetInputStream(path, startPosition, true)) _logger.Info("Live stream starting position is {0} bytes", startPosition.ToString(CultureInfo.InvariantCulture));
var allowAsync = Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
using (var inputStream = GetInputStream(path, startPosition, allowAsync))
{ {
if (startPosition > 0) if (startPosition > 0)
{ {
inputStream.Position = startPosition; inputStream.Position = startPosition;
} }
while (eofCount < 20 || !allowEndOfFile) while (!cancellationToken.IsCancellationRequested)
{ {
var bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 4, cancellationToken).ConfigureAwait(false); long bytesRead;
if (allowAsync)
{
bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 2, cancellationToken).ConfigureAwait(false);
}
else
{
StreamHelper.CopyTo(inputStream, outputStream, 81920, cancellationToken);
bytesRead = 1;
}
//var position = fs.Position; //var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
if (bytesRead == 0) if (bytesRead == 0)
{ {
eofCount++;
await Task.Delay(100, cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false);
} }
else
{
eofCount = 0;
}
} }
} }
} }
private static int RtpHeaderBytes = 12; private static int RtpHeaderBytes = 12;
private Task CopyTo(ISocket udpClient, Stream outputStream, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken) private void CopyTo(ISocket udpClient, Stream target, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{ {
return CopyStream(_socketFactory.CreateNetworkStream(udpClient, false), outputStream, 81920, 4, openTaskCompletionSource, cancellationToken); var source = _socketFactory.CreateNetworkStream(udpClient, false);
var bufferSize = 81920;
byte[] buffer = new byte[bufferSize];
int read;
var resolved = false;
while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
{
cancellationToken.ThrowIfCancellationRequested();
read -= RtpHeaderBytes;
if (read > 0)
{
target.Write(buffer, RtpHeaderBytes, read);
} }
private Task CopyStream(Stream source, Stream target, int bufferSize, int bufferCount, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken) if (!resolved)
{
var copier = new AsyncStreamCopier(source, target, 0, cancellationToken, false, bufferSize, bufferCount);
copier.IndividualReadOffset = RtpHeaderBytes;
var taskCompletion = new TaskCompletionSource<long>();
copier.TaskCompletionSource = taskCompletion;
var result = copier.BeginCopy(StreamCopyCallback, copier);
if (openTaskCompletionSource != null)
{ {
resolved = true;
Resolve(openTaskCompletionSource); Resolve(openTaskCompletionSource);
openTaskCompletionSource = null; }
} }
if (result.CompletedSynchronously) //var copier = new AsyncStreamCopier(source, target, 0, cancellationToken, false, bufferSize, bufferCount);
{ //copier.IndividualReadOffset = RtpHeaderBytes;
StreamCopyCallback(result);
}
cancellationToken.Register(() => taskCompletion.TrySetCanceled()); //var taskCompletion = new TaskCompletionSource<long>();
return taskCompletion.Task; //copier.TaskCompletionSource = taskCompletion;
//var result = copier.BeginCopy(StreamCopyCallback, copier);
//if (openTaskCompletionSource != null)
//{
// Resolve(openTaskCompletionSource);
// openTaskCompletionSource = null;
//}
//if (result.CompletedSynchronously)
//{
// StreamCopyCallback(result);
//}
//cancellationToken.Register(() => taskCompletion.TrySetCanceled());
//return taskCompletion.Task;
} }
private void StreamCopyCallback(IAsyncResult result) private void StreamCopyCallback(IAsyncResult result)
@ -258,5 +301,155 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
} }
} }
public class UdpClientStream : Stream
{
private static int RtpHeaderBytes = 12;
private static int PacketSize = 1316;
private readonly ISocket _udpClient;
bool disposed;
public UdpClientStream(ISocket udpClient) : base()
{
_udpClient = udpClient;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset + count < 0)
throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count");
if (offset + count > buffer.Length)
throw new ArgumentException("offset + count must not be greater than the length of buffer", "offset+count");
if (disposed)
throw new ObjectDisposedException(typeof(UdpClientStream).ToString());
// This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
// The RTP header will be stripped so see how many reads we need to make to fill the buffer.
int numReads = count / PacketSize;
int totalBytesRead = 0;
byte[] receiveBuffer = new byte[81920];
for (int i = 0; i < numReads; ++i)
{
var data = await _udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
var bytesRead = data.ReceivedBytes - RtpHeaderBytes;
// remove rtp header
Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead);
offset += bytesRead;
totalBytesRead += bytesRead;
}
return totalBytesRead;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset + count < 0)
throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count");
if (offset + count > buffer.Length)
throw new ArgumentException("offset + count must not be greater than the length of buffer", "offset+count");
if (disposed)
throw new ObjectDisposedException(typeof(UdpClientStream).ToString());
// This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
// The RTP header will be stripped so see how many reads we need to make to fill the buffer.
int numReads = count / PacketSize;
int totalBytesRead = 0;
byte[] receiveBuffer = new byte[81920];
for (int i = 0; i < numReads; ++i)
{
var receivedBytes = _udpClient.Receive(receiveBuffer, 0, receiveBuffer.Length);
var bytesRead = receivedBytes - RtpHeaderBytes;
// remove rtp header
Buffer.BlockCopy(receiveBuffer, RtpHeaderBytes, buffer, offset, bytesRead);
offset += bytesRead;
totalBytesRead += bytesRead;
}
return totalBytesRead;
}
protected override void Dispose(bool disposing)
{
disposed = true;
}
public override bool CanRead
{
get
{
throw new NotImplementedException();
}
}
public override bool CanSeek
{
get
{
throw new NotImplementedException();
}
}
public override bool CanWrite
{
get
{
throw new NotImplementedException();
}
}
public override long Length
{
get
{
throw new NotImplementedException();
}
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void Flush()
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
} }
} }

View File

@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
public class MulticastStream public class MulticastStream
{ {
private readonly ConcurrentDictionary<Guid,QueueStream> _outputStreams = new ConcurrentDictionary<Guid, QueueStream>(); private readonly ConcurrentDictionary<Guid, QueueStream> _outputStreams = new ConcurrentDictionary<Guid, QueueStream>();
private const int BufferSize = 81920; private const int BufferSize = 81920;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -31,9 +31,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
throw new ArgumentNullException("source"); throw new ArgumentNullException("source");
} }
while (!cancellationToken.IsCancellationRequested) while (true)
{ {
var bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested();
var bytesRead = source.Read(buffer, 0, buffer.Length);
if (bytesRead > 0) if (bytesRead > 0)
{ {
@ -41,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
//if (allStreams.Count == 1) //if (allStreams.Count == 1)
//{ //{
// await allStreams[0].Value.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false); // allStreams[0].Value.Write(buffer, 0, bytesRead);
//} //}
//else //else
{ {

View File

@ -14,7 +14,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
private readonly Stream _outputStream; private readonly Stream _outputStream;
private readonly ConcurrentQueue<Tuple<byte[], int, int>> _queue = new ConcurrentQueue<Tuple<byte[], int, int>>(); private readonly ConcurrentQueue<Tuple<byte[], int, int>> _queue = new ConcurrentQueue<Tuple<byte[], int, int>>();
private CancellationToken _cancellationToken;
public TaskCompletionSource<bool> TaskCompletion { get; private set; } public TaskCompletionSource<bool> TaskCompletion { get; private set; }
public Action<QueueStream> OnFinished { get; set; } public Action<QueueStream> OnFinished { get; set; }
@ -35,8 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public void Start(CancellationToken cancellationToken) public void Start(CancellationToken cancellationToken)
{ {
_cancellationToken = cancellationToken; Task.Run(() => StartInternal(cancellationToken));
Task.Run(() => StartInternal());
} }
private Tuple<byte[], int, int> Dequeue() private Tuple<byte[], int, int> Dequeue()
@ -59,14 +57,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
} }
} }
public async Task WriteAsync(byte[] bytes, int offset, int count) public void Write(byte[] bytes, int offset, int count)
{ {
//return _outputStream.WriteAsync(bytes, offset, count, cancellationToken); //return _outputStream.WriteAsync(bytes, offset, count, cancellationToken);
var cancellationToken = _cancellationToken;
try try
{ {
await _outputStream.WriteAsync(bytes, offset, count, cancellationToken).ConfigureAwait(false); _outputStream.Write(bytes, offset, count);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -82,18 +79,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
} }
} }
private async Task StartInternal() private async Task StartInternal(CancellationToken cancellationToken)
{ {
var cancellationToken = _cancellationToken;
try try
{ {
while (true) while (true)
{ {
cancellationToken.ThrowIfCancellationRequested();
var result = Dequeue(); var result = Dequeue();
if (result != null) if (result != null)
{ {
await _outputStream.WriteAsync(result.Item1, result.Item2, result.Item3, cancellationToken).ConfigureAwait(false); _outputStream.Write(result.Item1, result.Item2, result.Item3);
} }
else else
{ {

View File

@ -202,7 +202,7 @@ namespace MediaBrowser.Api.Playback.Hls
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
var line = await reader.ReadLineAsync().ConfigureAwait(false); var line = reader.ReadLine();
if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1) if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
{ {
@ -234,11 +234,11 @@ namespace MediaBrowser.Api.Playback.Hls
try try
{ {
return FileSystem.GetFileStream(tmpPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.Asynchronous | FileOpenOptions.SequentialScan); return FileSystem.GetFileStream(tmpPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan);
} }
catch (IOException) catch (IOException)
{ {
return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.Asynchronous | FileOpenOptions.SequentialScan); return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan);
} }
} }

View File

@ -456,11 +456,7 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
try try
{ {
using (var fileStream = GetPlaylistFileStream(playlistPath)) var text = FileSystem.ReadAllText(playlistPath, Encoding.UTF8);
{
using (var reader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize))
{
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
// If it appears in the playlist, it's done // If it appears in the playlist, it's done
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1) if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
@ -476,8 +472,6 @@ namespace MediaBrowser.Api.Playback.Hls
//break; //break;
} }
} }
}
}
catch (IOException) catch (IOException)
{ {
// May get an error if the file is locked // May get an error if the file is locked

View File

@ -63,9 +63,7 @@ namespace MediaBrowser.Api.Playback.Progressive
private Stream GetInputStream(bool allowAsyncFileRead) private Stream GetInputStream(bool allowAsyncFileRead)
{ {
var fileOpenOptions = StartPosition > 0 var fileOpenOptions = FileOpenOptions.SequentialScan;
? FileOpenOptions.RandomAccess
: FileOpenOptions.SequentialScan;
if (allowAsyncFileRead) if (allowAsyncFileRead)
{ {

View File

@ -0,0 +1,32 @@
using System.IO;
using System.Threading;
using System;
namespace MediaBrowser.Controller.IO
{
public static class StreamHelper
{
public static void CopyTo(Stream source, Stream destination, int bufferSize, CancellationToken cancellationToken)
{
CopyTo(source, destination, bufferSize, null, cancellationToken);
}
public static void CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
{
byte[] buffer = new byte[bufferSize];
int read;
while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
{
cancellationToken.ThrowIfCancellationRequested();
destination.Write(buffer, 0, read);
if (onStarted != null)
{
onStarted();
onStarted = null;
}
}
}
}
}

View File

@ -53,9 +53,7 @@ namespace MediaBrowser.Controller.LiveTv
protected Stream GetInputStream(string path, long startPosition, bool allowAsyncFileRead) protected Stream GetInputStream(string path, long startPosition, bool allowAsyncFileRead)
{ {
var fileOpenOptions = startPosition > 0 var fileOpenOptions = FileOpenOptions.SequentialScan;
? FileOpenOptions.RandomAccess
: FileOpenOptions.SequentialScan;
if (allowAsyncFileRead) if (allowAsyncFileRead)
{ {

View File

@ -134,6 +134,7 @@
<Compile Include="Entities\UserViewBuilder.cs" /> <Compile Include="Entities\UserViewBuilder.cs" />
<Compile Include="Extensions\StringExtensions.cs" /> <Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="FileOrganization\IFileOrganizationService.cs" /> <Compile Include="FileOrganization\IFileOrganizationService.cs" />
<Compile Include="IO\StreamHelper.cs" />
<Compile Include="Library\DeleteOptions.cs" /> <Compile Include="Library\DeleteOptions.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" /> <Compile Include="Library\ILibraryPostScanTask.cs" />
<Compile Include="Library\IMediaSourceManager.cs" /> <Compile Include="Library\IMediaSourceManager.cs" />

View File

@ -16,6 +16,8 @@ namespace MediaBrowser.Model.Net
Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
int Receive(byte[] buffer, int offset, int count);
IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback); IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback);
SocketReceiveResult EndReceive(IAsyncResult result); SocketReceiveResult EndReceive(IAsyncResult result);

View File

@ -357,9 +357,7 @@ namespace SocketHttpListener.Net
// allowAsync = true; // allowAsync = true;
//} //}
var fileOpenOptions = offset > 0 var fileOpenOptions = FileOpenOptions.SequentialScan;
? FileOpenOptions.RandomAccess
: FileOpenOptions.SequentialScan;
if (allowAsync) if (allowAsync)
{ {