c# - 如何播放需要认证的视频流?

标签 c# authentication windows-runtime video-streaming windows-store-apps

我有一个与 REST 服务通信的 Windows 应用商店应用 (C#/XAML)。在某些时候,我需要播放此服务提供的视频流。

如果我只是将流 URI 分配给 MediaElement.Source 属性,它不起作用,因为请求需要进行身份验证。我需要自定义 MediaElement 控件发送的请求,以便添加 cookie、凭据和一些其他自定义 header ,但我找不到任何方法或属性来执行此操作。

我该怎么做?有可能吗?

最佳答案

好的,我让它工作了。基本上,该解决方案有 2 个部分:

  • 手动发出 HTTP 请求(使用任何必需的凭据或 header )
  • 将响应流包装在自定义 IRandomAccessStream 中,该自定义 IRandomAccessStream 通过向服务器发出另一个请求来实现 Seek,使用 Range header 指定哪个请求我需要的流的一部分。

这是 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);
}

用户可以seek到视频中的任意位置,stream会再次发出请求获取指定位置的stream。

它仍然比我想象的要复杂,但它有效...

请注意,服务器必须支持请求中的 Range header ,并且必须在初始响应中发出 Content-Length header 。

关于c# - 如何播放需要认证的视频流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18594659/

相关文章:

c# - 是否可以仅使用键盘对 .NET DataGridView 列进行排序?

C# 如何以编程方式在控件之间切换

java - 如何在 Java 的 Native HTTP Client 上使用代理密码身份验证

authentication - 使用 Cognito 的 Multi-Tenancy : one big user pool or one pool per tenant?

event-handling - 恢复应用程序 Windows Phone

.net - WinRT/Win8 中的 HttpClient 缺少响应 header

c# - 多次调用 GetStringAsync 的更有效方法?

c# - 如何实例化在 C# DLL 中声明的类?

java - EJB3.1 TimerService @Timeout 当前主体

windows-8 - Metro 应用程序(Windows 8、WinRT)。在我的应用程序在 Windows 应用商店可用而无需更新应用程序后,我可以添加新的应用程序内购买产品吗