Blazor组件自做十三: VideoPlayer 视频播放器
Video.js 是一个具有大量功能的流行的视频和音频 JavaScript 库,今天我们试试集成到 Blazor .
Blazor VideoPlayer 视频播放器 组件 ![nuget]()
示例
https://blazor.app1.es/videoPlayers
1. 新建工程b13video
dotnet new blazorserver -o b13video
2. 将项目添加到解决方案中:
dotnet sln add b13video/b13video.csproj
3. Pages\_Host.cshtml 引用 video-js.css
<link href="//vjs.zencdn.net/7.10.2/video-js.min.css" rel="stylesheet">
4. 接下来,Pages\_Host.cshtml 引用 Video.js, 添加以下脚本文件到Pages\_Host.cshtml
<script src="https://vjs.zencdn.net/7.10.2/video.js"></script>
<script src="https://unpkg.com/video.js/dist/video.min.js"></script>
<script src="https://unpkg.com/@@videojs/http-streaming/dist/videojs-http-streaming.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-youtube/2.6.1/Youtube.min.js"></script>
5. 添加 app.js 文件到 wwwroot文件夹
文件内容
function loadPlayer(id, options) {
  videojs(id, options);
}
6. Pages\_Host.cshtml 引用 app.js
<script src="./app.js"></script>
完整文件看起来应该是这样
@page "/"
@using Microsoft.AspNetCore.Components.Web
@namespace b13video.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="b13video.styles.css" rel="stylesheet" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <link href="//vjs.zencdn.net/7.10.2/video-js.min.css" rel="stylesheet">
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    <component type="typeof(App)" render-mode="ServerPrerendered" />
    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.server.js"></script>
    <script src="https://unpkg.com/video.js/dist/video.min.js"></script>
    <script src="https://unpkg.com/@@videojs/http-streaming/dist/videojs-http-streaming.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-youtube/2.6.1/Youtube.min.js"></script>
    <script src="./app.js"></script>
</body>
</html>
7. Razor 页面, 我们直接在 Index.razor 里添加
<video id="my-player"
       class="video-js"
       controls
       preload="auto"
       poster="//vjs.zencdn.net/v/oceans.png"
       data-setup='{}'>
    <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4" />
    <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm" />
    <source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg" />
    <p class="vjs-no-js">
        To view this video please enable JavaScript, and consider upgrading to a
        web browser that
        <a href="https://videojs.com/html5-video-support/" target="_blank">
            supports HTML5 video
        </a>
    </p>
</video>
跑一下

8. 封装
取消几行html组件设定,改为c#提供参数, 最终代码如下
<video id="my-player"
       class="video-js"
       muted >
    <p class="vjs-no-js">
        To view this video please enable JavaScript, and consider upgrading to a
        web browser that
@ -17,3 +14,27 @@
        </a>
    </p>
</video>
@inject IJSRuntime jsRuntime
@code{
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await jsRuntime.InvokeVoidAsync("loadPlayer", "my-player", new
            {
                width = 600,
                height = 300,
                controls = true,
                autoplay = true,
                preload = "auto",
                poster = "//vjs.zencdn.net/v/oceans.png",
                sources = new[] {
                        new { type =  "application/x-mpegURL", src = "https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8"},
                        new { type =  "video/mp4", src = "//vjs.zencdn.net/v/oceans.mp4"}
                }
            });
        }
    }
}
调试一下,成功运行就进入下一步.
9. 组件化.
Pages\VideoPlayer.razor
@inject IJSRuntime jsRuntime
@namespace Blazor100.Components
<div @ref="element">
    <video id="video_@Id"
           class="video-js"
           muted
           webkit-playsinline
           playsinline
           x-webkit-airplay="allow"
           x5-video-player-type="h5"
           x5-video-player-fullscreen="true"
           x5-video-orientation="portraint">
        <p class="vjs-no-js">
            To view this video please enable JavaScript, and consider upgrading to a
            web browser that
            <a href="https://videojs.com/html5-video-support/" target="_blank">
                supports HTML5 video
            </a>
        </p>
    </video>
    @if (Debug)
    {
        <pre>@info</pre>
    }
</div>
Pages\VideoPlayer.razor.cs
using b13video.Pages;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
using System.Text.Json.Serialization;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Blazor100.Components; 
public partial class VideoPlayer : IAsyncDisposable
{
    [Inject] IJSRuntime? JS { get; set; }
    private IJSObjectReference? module;
    private DotNetObjectReference<VideoPlayer>? instance { get; set; }
    protected ElementReference element { get; set; }
    private bool init;
    private string? info;
    private string Id { get; set; } = Guid.NewGuid().ToString();
    /// <summary>
    /// 资源类型
    /// </summary>
    [Parameter]
    public string? SourcesType { get; set; }
    /// <summary>
    /// 资源地址
    /// </summary>
    [Parameter]
    public string? SourcesUrl { get; set; }
    [Parameter]
    public int Width { get; set; } = 300;
    [Parameter]
    public int Height { get; set; } = 200;
    [Parameter]
    public bool Controls { get; set; } = true;
    [Parameter]
    public bool Autoplay { get; set; } = true;
    [Parameter]
    public string Preload { get; set; } = "auto";
    /// <summary>
    /// 设置封面
    /// </summary>
    [Parameter]
    public string? Poster { get; set; }
    [Parameter]
    public VideoPlayerOption? Option { get; set; }
    [Parameter]
    public bool Debug { get; set; }
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        try
        {
            if (firstRender)
            {
                module = await JS!.InvokeAsync<IJSObjectReference>("import", "./app.js");
                instance = DotNetObjectReference.Create(this);
                Option = Option ?? new VideoPlayerOption()
                {
                    Width = Width,
                    Height = Height,
                    Controls = Controls,
                    Autoplay = Autoplay,
                    Preload = Preload,
                    Poster = Poster,
                    //EnableSourceset= true,
                    //TechOrder= "['fakeYoutube', 'html5']"
                };
                Option.Sources.Add(new VideoSources(SourcesType, SourcesUrl));
                try
                {
                    await module.InvokeVoidAsync("loadPlayer", instance, "video_" + Id, Option);
                }
                catch (Exception e)
                {
                    info = e.Message;
                    if (Debug) StateHasChanged();
                    Console.WriteLine(info);
                    if (OnError != null) await OnError.Invoke(info);
                }
            }
        }
        catch (Exception e)
        {
            if (OnError != null) await OnError.Invoke(e.Message);
        }
    }
    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.InvokeVoidAsync("destroy", Id);
            await module.DisposeAsync();
        }
    }
    /// <summary>
    /// 获得/设置 错误回调方法
    /// </summary>
    [Parameter]
    public Func<string, Task>? OnError { get; set; }
    /// <summary>
    /// JS回调方法
    /// </summary>
    /// <param name="init"></param>
    /// <returns></returns>
    [JSInvokable]
    public void GetInit(bool init) => this.init = init;
    /// <summary>
    /// JS回调方法
    /// </summary>
    /// <param name="error"></param>
    /// <returns></returns>
    [JSInvokable]
    public async Task GetError(string error)
    {
        info = error;
        if (Debug) StateHasChanged();
        if (OnError != null) await OnError.Invoke(error);
    }
}
Pages\VideoPlayerOption.cs
using System.Text.Json.Serialization;
namespace b13video.Pages
{
    public class VideoPlayerOption
    {
        [JsonPropertyName("width")]
        public int Width { get; set; } = 300;
        [JsonPropertyName("height")]
        public int Height { get; set; } = 200;
        [JsonPropertyName("controls")]
        public bool Controls { get; set; } = true;
        [JsonPropertyName("autoplay")]
        public bool Autoplay { get; set; } = true;
        [JsonPropertyName("preload")]
        public string Preload { get; set; } = "auto";
        /// <summary>
        /// 播放资源
        /// </summary>
        [JsonPropertyName("sources")]
        public List<VideoSources> Sources { get; set; } = new List<VideoSources>();
        /// <summary>
        /// 设置封面
        /// </summary>
        [JsonPropertyName("poster")]
        public string? Poster { get; set; } 
        //[JsonPropertyName("enableSourceset")]
        //public bool EnableSourceset { get; set; }
        //[JsonPropertyName("techOrder")]
        //public string? TechOrder { get; set; } = "['html5', 'flash']";
    }
    /// <summary>
    /// 播放资源
    /// </summary>
    public class VideoSources
    {
        public VideoSources() { }
        public VideoSources(string? type, string? src)
        {
            this.Type = type ?? throw new ArgumentNullException(nameof(type));
            this.Src = src ?? throw new ArgumentNullException(nameof(src));
        }
        /// <summary>
        /// 资源类型<para></para>video/mp4<para></para>application/x-mpegURL<para></para>video/youtube
        /// </summary>
        [JsonPropertyName("type")]
        public string Type { get; set; } = "application/x-mpegURL";
        /// <summary>
        /// 资源地址
        /// </summary>
        [JsonPropertyName("src")]
        public string Src { get; set; } = "application/x-mpegURL";
    } 
}
wwwroot\app.js
var player = null;
export function loadPlayer(instance, id, options) {
    console.log('player id', id);
    player = videojs(id, options);
    player.ready(function () {
       console.log('player.ready');
       var promise = player.play();
        if (promise !== undefined) {
            promise.then(function () {
                console.log('Autoplay started!');
            }).catch(function (error) {
                console.log('Autoplay was prevented.', error);
                instance.invokeMethodAsync('GetError', 'Autoplay was prevented.'+ error);
            });
        }
        instance.invokeMethodAsync('GetInit', true);
    });
    return false;
}
export function destroy(id) {
    if (undefined !== player && null !== player) {
        player = null;
        console.log('destroy');
    }
}
10. 页面调用
<div class="row">
    <div class="col-4">
        <VideoPlayer SourcesType="application/x-mpegURL" SourcesUrl="https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8" Debug="true" />
    </div>
    <div class="col-4">
        <VideoPlayer SourcesType="video/mp4" SourcesUrl="//vjs.zencdn.net/v/oceans.mp4" />
    </div>
    <div class="col-4">
        <VideoPlayer SourcesType="application/x-mpegURL" SourcesUrl="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" />
    </div>
</div>
项目源码
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
AlexChow
今日头条 | 博客园 | 知乎 | Gitee | GitHub

Blazor 组件
关联项目
FreeSql QQ群:4336577
BA & Blazor QQ群:795206915
Maui Blazor 中文社区 QQ群:645660665
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
转载声明
本文来自博客园,作者:周创琳 AlexChow,转载请注明原文链接:https://www.cnblogs.com/densen2014/p/16981092.html
AlexChow
今日头条 | 博客园 | 知乎 | Gitee | GitHub

                    
                
                
            
        
浙公网安备 33010602011771号