private async Task SendResponse(HttpResponse response, MediaSegment mediaSegment) { response.StatusCode = 200; var length = mediaSegment.Length; if (mediaSegment.Complete) { response.ContentLength = mediaSegment.Length; _logger.LogWarning($"Sending full response for {mediaSegment.Path} length:{mediaSegment.Length} "); } else { _logger.LogWarning($"Chunked transfer encoding for {mediaSegment.Path}"); } response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue { Public = true, NoCache = true, NoStore = true }; foreach (var index in mediaSegment.GetBufferIndex()) { var cacheKey = mediaSegment.GetChunkKey(index); var buffer = _cache.Get <MediaBuffer>(cacheKey); if (buffer == null) { throw new InvalidOperationException($"Missing cache entry for {cacheKey}"); } await response.Body.WriteAsync(buffer.Memory); await response.Body.FlushAsync(); } }
private async Task SendResponse(HttpRequest request, HttpResponse response, MediaSegment mediaSegment) { response.Headers.Add("Accept-Ranges", "bytes"); if (mediaSegment.Complete) { var length = mediaSegment.Length; response.ContentLength = mediaSegment.Length; _logger.LogInformation($"Sending complete segment for {mediaSegment.Path} length:{mediaSegment.Length} "); } else { _logger.LogWarning($"Chunked transfer encoding for {mediaSegment.Path}"); } response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue { Public = true, NoCache = true, NoStore = true }; // check for byte range. var values = request.Headers["Range"]; if (values.Count > 0) { var rangeHeader = RangeHeaderValue.Parse(values[0]); response.StatusCode = 206; var range = rangeHeader.Ranges.Single(); // only one range is supported. var offset = range.From ?? 0; var length = range.To.HasValue ? range.To.Value - offset : long.MaxValue; length = mediaSegment.Complete ? Math.Min(length, mediaSegment.Length) : length; response.Headers.ContentLength = length; var lengthString = mediaSegment.Complete ? mediaSegment.Length.ToString() : "*"; response.Headers.Add("Content-Range", $"bytes {range.From}-{range.To}/{lengthString}"); foreach (var buffer in GetRangeBuffers(mediaSegment, (int)offset, (int)length)) { await SendBufferAsync(response, buffer, !mediaSegment.Complete); } } else { response.StatusCode = 200; foreach (var index in mediaSegment.GetBufferIndex()) { var buffer = GetMediaBuffer(mediaSegment, index); await SendBufferAsync(response, buffer.Memory, flush : !mediaSegment.Complete); } } }
private IEnumerable <ReadOnlyMemory <byte> > GetRangeBuffers(MediaSegment segment, int offset, int length) { var curOffset = 0; foreach (var index in segment.GetBufferIndex()) { var buffer = GetMediaBuffer(segment, index); if (offset > curOffset + buffer.Length) { curOffset += buffer.Length; continue; } var curLength = Math.Min(length, buffer.Length); yield return(buffer.Memory.Slice(offset - curOffset, curLength)); length -= curLength; if (length == 0) { break; } } }