我有下面的视频流工作代码,从我做过的各种例子和研究中创建.它在Chrome上运行得很好,但似乎无法在Safari和移动浏览器上运行.我特别想流媒体内容,而不是下载完整的文件之前播放.

我读到过移动浏览器不喜欢自动播放,但我遇到的问题似乎并不是这样.我想我可能遗漏了什么.我try 过以不同的方式更改代码.我目前的 idea 是,这可能是Safari和/或移动浏览器解释请求负载的方式,但即使在将内容类型设置为视频/MP4之后,我也没有运气.

在我的MacBook Air上try 播放()HTML视频元素时,遇到来自Safari(13.1.2)的错误.

NotSupportdError:不支持该操作.

状态:已拒绝

堆栈:Play@[本机代码]◄全局代码↔求值@[本机代码]

我还在同一台MacBook上试用了Chrome.工作正常.

Possible Theories

行动:

[HttpGet]
[CustomAuthorize(Roles = Roles._SYS_ADMIN)]
public async Task FetchVideo(int videoID)
{
    byte[] buffer = new Byte[4096];
    int length;
    long dataToRead;

    try
    {
        Video video = new Video();
        video.Get(videoID);

        using (MemoryStream memoryStream = new MemoryStream(video.Content))
        {
            // Total bytes to read:
            dataToRead = memoryStream.Length;

            Response.AddHeader("Accept-Ranges", "bytes");
            Response.AddHeader("Content-Disposition", "inline; filename=video.mp4");
            Response.ContentType = "video/mp4";

            int startbyte = 0;

            while (dataToRead > 0)
            {
                // Verify that the client is connected.
                if (Response.IsClientConnected)
                {
                    // Read the data in buffer.
                    length = await memoryStream.ReadAsync(buffer, 0, buffer.Length);

                    // Write the data to the current output stream.
                    await Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);

                    // Flush the data to the HTML output.
                    Response.Flush();

                    buffer = new Byte[buffer.Length];
                    dataToRead = dataToRead - buffer.Length;
                }
                else
                {
                    //prevent infinite loop if user disconnects
                    dataToRead = -1;
                }
            }
        }
    }
    catch (Exception ex)
    {
        Response.Write("Error : " + ex.Message);
    }
    finally
    {
        Response.Close();
    }
}

JQuery:

$('#play-video-modal').on('show.bs.modal', function (e) {
  var promptUrl = '/Admin/Bundle/FetchVideo?videoID=' + value.Video.ID;
  var videoPlayer = $('#play-video-modal #video-player');
  videoPlayer.attr({
     'src': promptUrl, 
     'type': 'video/mp4',
     'autoplay': 'autoplay',
     'loop': 'loop',
     'muted': 'muted',
     'controls': 'controls',
     'playsinline': 'playsinline' 
  });

  // Set video description
  videoPlayer.text(data.Description);
});

HTML标记:

<!-- start: play video modall -->
<div class="modal fade" id="play-video-modal" tabindex="-1" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
    <div class="modal-dialog modal-lg modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title text-uppercase" id="modal-title"></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <div class="row p-0 m-0">
                    <div class="col-md-12 p-0 m-0">
                        <div id="video-player-container" class="section d-flex justify-content-center embed-responsive embed-responsive-16by9">
                            <video id="video-player" class="embed-responsive-item" style="background-color: black;" autoplay playsinline muted>
                                <source src="" type="video/mp4">
                                Your browser does not support the video tag.
                            </video>
                        </div>
                    </div>
                </div>
            </div>
            <div id="modal-loader" class="text-center pt-4" style="display: none;">
                <div class="spinner-grow text-primary" role="status">
                    <span class="sr-only">Loading...</span>
                </div>
            </div>
            <div class="modal-footer">
                <button id="decline-modal" type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>
<!-- end: play video modal -->

我绑了几件东西.我唯一在所有浏览器上都能稳定工作的解决方案就是下载整个文件,然后使用AJAX请求设置源代码.我得到的最好的流媒体效果是我在这里的代码中提供的结果,但它只能在Chrome上运行.

经过进一步的测试,我已经能够在三星Galaxy S20手机上播放/播放视频了.因此,这似乎是一个与MacOS Safari和AppleOS浏览器特别相关的问题.

推荐答案

在启用开发人员工具后,我在Safari中偶然发现了请求网络响应.我看到有一个标头范围为0-1的初始请求.

我发现了一个相关答案‘HERE’,其中提到了@KajMagnus和@pedrociarlini提到的0-1字节(2字节)的初始字节范围请求. 进一步的研究让我得到了HERE分.

首先,Safari Web浏览器请求内容,如果它是一个 音频或视频文件,它打开它的媒体播放器.然后,媒体播放器 请求内容的前2个字节,以确保 Web服务器支持字节范围的请求.然后,如果它支持它们, IPhone的媒体播放器按字节范围请求其余内容 然后把它打出来.

然后我决定梳理我的代码,确保一切都完美地满足Safari非常精确的需求.我发现我的代码中读取‘Range’头的部分有点不对劲,需要进行一些调整.

在进行了所有更改之后,我只剩下下面的工作代码.它可以在所有浏览器(我测试过的浏览器)上运行,比如Chrome、SAFARAI以及这两种浏览器的移动版本.

行动:

public async Task FetchVideo(int videoID)
{
    try
    {
        Video video = new Video();
        video.Get(videoID);

        long fileSize = video.Content.Length;
        long totalByte = fileSize - 1;
        long startByte = 0;
        long endByte = totalByte;
        int bufferSize = 1024 * 1024; // 24KB buffer size

        if(!string.IsNullOrEmpty(Request.Headers["X-Playback-Session-Id"]))
            Response.AddHeader("X-Playback-Session-Id", Request.Headers["X-Playback-Session-Id"]);

        if (!string.IsNullOrEmpty(Request.Headers["Range"]))
        {
            //Range: <unit>=<range-start>
            string range = Request.Headers["Range"].Replace("bytes=", "");
            string[] rangeParts = range.Split('-');
            startByte = long.Parse(rangeParts[0]);
            if (rangeParts.Length > 1 && !string.IsNullOrEmpty(rangeParts[1]))
                endByte = long.Parse(rangeParts[1]);
        }

        // recalculate after range has been interpreted
        int bytesToRead = Math.Min((int)(endByte - startByte + 1), bufferSize);

        Response.AddHeader("Content-Range", $"bytes {startByte}-{endByte}/{fileSize}");
        Response.AddHeader("Accept-Ranges", "bytes");
        Response.AddHeader("Content-Type", "video/mp4");
        Response.AddHeader("Connection", "Keep-Alive");
        Response.AddHeader("Content-Name", video.Name);
        Response.AddHeader("Content-Version", "1.0");
        Response.AddHeader("Content-Vendor", "XMP");
        Response.AddHeader("Content-Size", bytesToRead.ToString());
        Response.AddHeader("Content-Length", bytesToRead.ToString());
     
        Response.StatusCode = 206;
        Response.ContentType = "video/mp4";

        using (MemoryStream memoryStream = new MemoryStream(video.Content))
        {
            memoryStream.Seek(startByte, SeekOrigin.Begin);

            byte[] buffer = new byte[bufferSize];
            long bytesRemaining = bytesToRead;

            while (bytesRemaining > 0)
            {
                int bytesRead = await memoryStream.ReadAsync(buffer, 0, bytesToRead);

                if (bytesRead == 0)
                    break;

                if (Response.IsClientConnected)
                {
                    await Response.OutputStream.WriteAsync(buffer, 0, bytesRead);
                    await Response.OutputStream.FlushAsync();
                    bytesRemaining -= bytesRead;
                }
                else
                {
                    break; // Client disconnected
                }
            }
        }
    }
    catch (Exception ex)
    {
        Response.Write("Error: " + ex.Message);
    }
}

Html:

<video id="video-player" class="embed-responsive-item" style="background-color: black;" controls muted autoplay loop playsinline>
    <source src="" type="video/mp4">
    Your browser does not support the video tag.
</video>

Js/jQuery:js/jQuery

var promptUrl = '/Store/Shop/FetchVideo?videoID=' + value.Video.ID;
var videoPlayer = $('#play-video-modal #video-player');
videoPlayer.attr({
    'src': promptUrl,
    'type': 'video/mp4',
    'autoplay': 'autoplay',
    'loop': 'loop',
    'muted': 'muted',
    'controls': 'controls',
    'playsinline': 'playsinline'
});

// Set video description
videoPlayer.text(data.Description);

Csharp相关问答推荐

C#类主构造函数中的调试参数

什么是通过反射创建类的泛型接口方法的正确方法?

为什么SignalR在每个Blazor服务器应用程序启动时最多启动8个服务器?

创建临时Collection 最有效的方法是什么?堆栈分配和集合表达式之间的区别?

需要在重新启动ApplicartionPool或IIS后启动/唤醒API的帮助

为什么在使用动态obj+类obj时会调用串联?

Docker Container中的HttpRequest后地址不可用

有没有类似于扩展元素的合并元组的语法?

在try 使用访问服务器上的文件夹时,如何解决CORS错误.NET核心API

.NET 8在appsettings.json中核心使用词典URI、URI&>

未在Windows上运行的Maui项目

在同一个捕获中可以有多种类型的异常吗?

C#如何获取字符串中引号之间的文本?

使用可空引用类型时C#接口实现错误

如何在使用Google.Drive.apis.V3下载文件/文件夹之前压缩?

Xamarin.Forms中具有类似AspectFill的图像zoom 的水平滚动视图

如何为控制器PUT操作绑定对象数组

如何在JSON:API中定义的&过滤查询参数系列&标准的GET请求中传递多个相关参数?

缩写的MonthNames有问题

从具有泛型类型约束的类继承-Blazor