ОК, я получил его работу. В принципе, решение имеет 2-х частей:
- сделать запрос HTTP вручную (с любыми требуемыми учетными данными или заголовками)
- обернуть поток ответа в пользовательских
IRandomAccessStream
, который реализует Seek
, сделав еще один запрос на сервер, используя заголовок Range
, чтобы указать, какая часть потока мне нужна.
Вот RandomAccessStream
реализация:
delegate Task<Stream> AsyncRangeDownloader(ulong start, ulong? end);
class StreamingRandomAccessStream : IRandomAccessStream
{
private readonly AsyncRangeDownloader _downloader;
private readonly ulong _size;
public StreamingRandomAccessStream(Stream startStream, AsyncRangeDownloader downloader, ulong size)
{
if (startStream != null)
_stream = startStream.AsInputStream();
_downloader = downloader;
_size = size;
}
private IInputStream _stream;
private ulong _requestedPosition;
public void Dispose()
{
if (_stream != null)
_stream.Dispose();
}
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
{
return AsyncInfo.Run<IBuffer, uint>(async (cancellationToken, progress) =>
{
progress.Report(0);
if (_stream == null)
{
var netStream = await _downloader(_requestedPosition, null);
_stream = netStream.AsInputStream();
}
var result = await _stream.ReadAsync(buffer, count, options).AsTask(cancellationToken, progress);
return result;
});
}
public void Seek(ulong position)
{
if (_stream != null)
_stream.Dispose();
_requestedPosition = position;
_stream = null;
}
public bool CanRead { get { return true; } }
public bool CanWrite { get { return false; } }
public ulong Size { get { return _size; } set { throw new NotSupportedException(); } }
public IAsyncOperationWithProgress<uint, uint> WriteAsync(IBuffer buffer) { throw new NotSupportedException(); }
public IAsyncOperation<bool> FlushAsync() { throw new NotSupportedException(); }
public IInputStream GetInputStreamAt(ulong position) { throw new NotSupportedException(); }
public IOutputStream GetOutputStreamAt(ulong position) { throw new NotSupportedException(); }
public IRandomAccessStream CloneStream() { throw new NotSupportedException(); }
public ulong Position { get { throw new NotSupportedException(); } }
}
Он может быть использован, как это:
private HttpClient _client;
private void InitClient()
{
_client = new HttpClient();
// Configure the client as needed with CookieContainer, Credentials, etc
// ...
}
private async Task StartVideoStreamingAsync(Uri uri)
{
var request = new HttpRequestMessage(HttpMethod.Get, uri);
// Add required headers
// ...
var response = await _client.SendAsync(request);
ulong length = (ulong)response.Content.Headers.ContentLength;
string mimeType = response.Content.Headers.ContentType.MediaType;
Stream responseStream = await response.Content.ReadAsStreamAsync();
// Delegate that will fetch a stream for the specified range
AsyncRangeDownloader downloader = async (start, end) =>
{
var request2 = new HttpRequestMessage();
request2.Headers.Range = new RangeHeaderValue((long?)start, (long?)end);
// Add other required headers
// ...
var response2 = await _client.SendAsync(request2);
return await response2.Content.ReadAsStreamAsync();
};
var videoStream = new StreamingRandomAccessStream(responseStream, downloader, length);
_mediaElement.SetSource(videoStream, mimeType);
}
Пользователь может обратиться к произвольной позиции в видео, и поток будет выдавать другой запрос на получение потока в указанной позиции.
Это еще более сложным, чем я думаю, что это должно быть, но это работает ...
Обратите внимание, что сервер должен поддерживать Range
заголовок в запросах, и должен выдать Content-Length
заголовок в первоначальном ответе.
Проверьте [MediaElement.SetMediaStreamSource] (http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.mediaelement.setmediastreamsource.aspx). То, что вам, вероятно, придется сделать, - это аутентифицировать и создать поток отдельно с помощью HTTP Client (также проверьте библиотеку HTTP MSFT в Nuget), а затем установите источник «MediaElement» в этот поток. –
@NateDiamond, спасибо, но у меня нет этого метода ... Согласно документации, он доступен в Windows 8.0, но для этого требуется параметр IMediaSource, который доступен только в версии 8.1. Я подозреваю, что этот метод существует в собственном элементе управления MediaElement, но не отображается в .NET API. –
Ах, вы правы! Обычный [SetSource] (http://msdn.microsoft.com/en-us/library/windows/apps/br244338.aspx) принимает «RandomAccessStream». –