3

У меня есть приложение для Windows Store (C#/XAML), которое связывается с службой REST. В какой-то момент мне нужно воспроизвести видеопоток, предоставляемый этой службой.Как воспроизвести видеопоток, требующий аутентификации?

Если я просто назначил URI потока объекту MediaElement.Source, он не работает, потому что запрос должен быть аутентифицирован. Мне нужно настроить запрос, отправленный элементом управления MediaElement, чтобы добавить куки-файлы, учетные данные и другие пользовательские заголовки, но я не могу найти какой-либо метод или свойство для этого.

Как я могу это сделать? Возможно ли это?

+0

Проверьте [MediaElement.SetMediaStreamSource] (http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.mediaelement.setmediastreamsource.aspx). То, что вам, вероятно, придется сделать, - это аутентифицировать и создать поток отдельно с помощью HTTP Client (также проверьте библиотеку HTTP MSFT в Nuget), а затем установите источник «MediaElement» в этот поток. –

+0

@NateDiamond, спасибо, но у меня нет этого метода ... Согласно документации, он доступен в Windows 8.0, но для этого требуется параметр IMediaSource, который доступен только в версии 8.1. Я подозреваю, что этот метод существует в собственном элементе управления MediaElement, но не отображается в .NET API. –

+0

Ах, вы правы! Обычный [SetSource] (http://msdn.microsoft.com/en-us/library/windows/apps/br244338.aspx) принимает «RandomAccessStream». –

ответ

6

ОК, я получил его работу. В принципе, решение имеет 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 заголовок в первоначальном ответе.

+1

Если вам нужно эквивалентное решение для 'Windows.Web.Http. HttpClient', попробуйте 'HttpRandomAccessStream': https://github.com/kiewic/MediaElementWithHttpClient – kiewic

+0

@ kiewic, хорошо! Но было бы более гибким, чтобы иметь возможность передавать HttpRequestMessage, а не только клиент и URI –

+0

Я рассматривал этот параметр, но в 'Windows.Web.Http',' HttpRequestMessage' не может использоваться повторно, поэтому новый экземпляр каждый раз, когда позиция меняется. Более того, пользовательские заголовки, аутентификация, перенаправление, сжатие и т. Д. Могут быть настроены в HttpClient. – kiewic

Смежные вопросы