-
Notifications
You must be signed in to change notification settings - Fork 493
Uri media source http headers #3169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
abdd329
667e6ab
ff201df
4df6f10
f438a19
7c676e3
0b9f9ad
ff62b10
c3e123b
d25acd0
1b90aa2
96cbd18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| using System.Runtime.InteropServices.WindowsRuntime; | ||
| using Windows.Foundation; | ||
| using Windows.Storage.Streams; | ||
| using HttpClient = Windows.Web.Http.HttpClient; | ||
| using HttpRequestMessage = Windows.Web.Http.HttpRequestMessage; | ||
| using HttpMethod = Windows.Web.Http.HttpMethod; | ||
| using HttpCompletionOption = Windows.Web.Http.HttpCompletionOption; | ||
|
|
||
| namespace CommunityToolkit.Maui.Core.Views; | ||
|
|
||
| /// <summary> | ||
| /// An <see cref="IRandomAccessStream"/> implementation backed by HTTP Range requests, | ||
| /// enabling progressive streaming of media content with custom HTTP headers without buffering the entire file in memory. | ||
| /// </summary> | ||
| sealed partial class HttpRandomAccessStream : IRandomAccessStream | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if this class should live inside
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure either.. Though it's an internal sealed class exclusively used by |
||
| { | ||
| readonly HttpClient httpClient; | ||
| readonly Uri requestUri; | ||
| readonly ulong size; | ||
|
|
||
| HttpRandomAccessStream(HttpClient httpClient, Uri requestUri, ulong size) | ||
| { | ||
| this.httpClient = httpClient; | ||
| this.requestUri = requestUri; | ||
| this.size = size; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public ulong Size | ||
| { | ||
| get => size; | ||
| set => throw new NotSupportedException("Cannot set size on a read-only HTTP stream."); | ||
Kaaybi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public ulong Position { get; private set; } | ||
|
|
||
| /// <inheritdoc/> | ||
| public bool CanRead => true; | ||
|
|
||
| /// <inheritdoc/> | ||
| public bool CanWrite => false; | ||
|
|
||
| /// <summary> | ||
| /// Creates an <see cref="HttpRandomAccessStream"/> by issuing a HEAD request to determine the content length. | ||
| /// </summary> | ||
| /// <param name="httpClient">The <see cref="HttpClient"/> configured with the desired HTTP headers.</param> | ||
| /// <param name="uri">The URI of the media resource.</param> | ||
| /// <param name="cancellationToken"><see cref="CancellationToken"/>.</param> | ||
| /// <returns>A new <see cref="HttpRandomAccessStream"/> instance.</returns> | ||
| internal static async Task<HttpRandomAccessStream> CreateAsync(HttpClient httpClient, Uri uri, CancellationToken cancellationToken = default) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(httpClient); | ||
| ArgumentNullException.ThrowIfNull(uri); | ||
|
|
||
| using var request = new HttpRequestMessage(HttpMethod.Head, uri); | ||
| using var response = await httpClient.SendRequestAsync(request).AsTask(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.ForceYielding); | ||
| response.EnsureSuccessStatusCode(); | ||
|
|
||
| var contentLength = response.Content.Headers.ContentLength ?? 0; | ||
| return new HttpRandomAccessStream(httpClient, uri, contentLength); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) | ||
| { | ||
| return AsyncInfo.Run<IBuffer, uint>(async (cancellationToken, _) => | ||
| { | ||
| if (count is 0) | ||
| { | ||
| return buffer; | ||
| } | ||
|
|
||
| using var request = new HttpRequestMessage(HttpMethod.Get, requestUri); | ||
| var rangeEnd = Position + count - 1; | ||
| request.Headers.TryAppendWithoutValidation("Range", $"bytes={Position}-{rangeEnd}"); | ||
|
|
||
TheCodeTraveler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| using var response = await httpClient.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead).AsTask(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.ForceYielding); | ||
| response.EnsureSuccessStatusCode(); | ||
|
|
||
| var inputStream = await response.Content.ReadAsInputStreamAsync().AsTask(cancellationToken); | ||
| var result = await inputStream.ReadAsync(buffer, count, options).AsTask(cancellationToken); | ||
|
|
||
| Position += result.Length; | ||
| return result; | ||
| }); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public void Seek(ulong position) => Position = position; | ||
|
|
||
| /// <inheritdoc/> | ||
| public IRandomAccessStream CloneStream() => throw new NotSupportedException(); | ||
|
|
||
| /// <inheritdoc/> | ||
| public IInputStream GetInputStreamAt(ulong position) | ||
| { | ||
| Position = position; | ||
| return this; | ||
TheCodeTraveler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public IOutputStream GetOutputStreamAt(ulong position) => throw new NotSupportedException(); | ||
|
|
||
| /// <inheritdoc/> | ||
| public IAsyncOperationWithProgress<uint, uint> WriteAsync(IBuffer buffer) => throw new NotSupportedException(); | ||
|
|
||
| /// <inheritdoc/> | ||
| public IAsyncOperation<bool> FlushAsync() => throw new NotSupportedException(); | ||
|
|
||
| /// <inheritdoc/> | ||
| public void Dispose() | ||
| { | ||
| // HttpClient is owned by the caller; do not dispose it here. | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.