protected static async Task SendAsync(IOwinContext context, Stream resourceStream, ProfileMediaItem item, EndPointProfile profile, bool onlyHeaders, bool partialResource, Range byteRange) { if (onlyHeaders) { return; } bool clientDisconnected = false; Guid streamID = item.StartStreaming(); if (streamID == Guid.Empty) { Logger.Error("BaseSendData: Unable to start stream"); return; } try { if (context.Response.ContentLength == 0) { //Not allowed to have a content length of zero context.Response.ContentLength = null; } Logger.Debug("BaseSendData: Sending chunked: {0}", context.Response.ContentLength == null); string clientID = context.Request.RemoteIpAddress; int bufferSize = profile.Settings.Communication.DefaultBufferSize; if (bufferSize <= 0) { bufferSize = 1500; } byte[] buffer = new byte[bufferSize]; int bytesRead; long count = 0; bool isStream = false; long waitForSize = 0; if (byteRange.Length == 0 || (byteRange.Length > 0 && byteRange.Length >= profile.Settings.Communication.InitialBufferSize)) { waitForSize = profile.Settings.Communication.InitialBufferSize; } if (partialResource == false) { if (waitForSize < byteRange.From) { waitForSize = byteRange.From; } } if (await WaitForMinimumFileSizeAsync(resourceStream, waitForSize) == false) { Logger.Error("BaseSendData: Unable to send stream because of invalid length: {0} ({1} required)", resourceStream.Length, waitForSize); return; } long start = 0; if (partialResource == false) { start = byteRange.From; } if (resourceStream.CanSeek) { resourceStream.Seek(start, SeekOrigin.Begin); } long length = byteRange.Length; if (length <= 0 || item.IsLive || (item.IsSegmented == false && item.IsTranscoding == true)) { isStream = true; } int emptyCount = 0; while (item.IsStreamActive(streamID)) { if (isStream) { if (resourceStream.CanSeek) { length = resourceStream.Length - count; } else { length = bufferSize; //Keep stream alive } } bytesRead = await resourceStream.ReadAsync(buffer, 0, length > bufferSize?bufferSize : (int)length); count += bytesRead; if (bytesRead > 0) { emptyCount = 0; try { //Send fetched bytes await context.Response.WriteAsync(buffer, 0, bytesRead, SendDataCancellation.Token); } catch (Exception) { // Client disconnected Logger.Debug("BaseSendData: Connection lost after {0} bytes", count); clientDisconnected = true; break; } length -= bytesRead; if (isStream == false && length <= 0) { //All bytes in the requested range sent break; } } else { emptyCount++; if (emptyCount > 2) { Logger.Debug("BaseSendData: Buffer underrun delay"); await Task.Delay(100); } if (emptyCount > 10) { //Stream is not getting any bigger break; } } if (resourceStream.CanSeek) { if (item.IsTranscoding == false && resourceStream.Position == resourceStream.Length) { //No more data will be available break; } } } } finally { item.StopStreaming(streamID); if (clientDisconnected || item.IsSegmented == false) { if (clientDisconnected == false) { //Everything sent to client so presume watched if (item.IsLive == false) { Guid? userId = ResourceAccessUtils.GetUser(context); IMediaLibrary library = ServiceRegistration.Get <IMediaLibrary>(); if (library != null && userId.HasValue) { library.NotifyUserPlayback(userId.Value, item.MediaItemId, 100, true); } } } } Logger.Debug("BaseSendData: Sending complete"); } }