11

Blazor组件自做十三: VideoPlayer 视频播放器 - AlexChow

 2 years ago
source link: https://www.cnblogs.com/densen2014/p/16981092.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Video.js 是一个具有大量功能的流行的视频和音频 JavaScript 库,今天我们试试集成到 Blazor .

Blazor VideoPlayer 视频播放器 组件

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>
1980213-20221214040705591-405490728.png

取消几行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. 组件化. {最终代码,请大家直接使用CV大法}

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>

Github

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

AlexChow

今日头条 | 博客园 | 知乎 | Gitee | GitHub

image

Blazor 组件

条码扫描 ZXingBlazor

图片浏览器 Viewer

条码扫描 BarcodeScanner

手写签名 Handwritten

手写签名 SignaturePad

定位/持续定位 Geolocation

屏幕键盘 OnScreenKeyboard

百度地图 BaiduMap

谷歌地图 GoogleMap

蓝牙和打印 Bluetooth

PDF阅读器 PdfReader

文件系统访问 FileSystem

光学字符识别 OCR

电池信息/网络信息 WebAPI


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK