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

news2024/11/23 9:30:02

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

Blazor VideoPlayer 视频播放器 组件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwxxYiKn-1671068849666)(null)]

示例

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. 组件化. {最终代码,请大家直接使用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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SCmE8PxD-1671068850805)(null)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LqcicZNw-1671068850127)(null)]

图片浏览器 Viewer

条码扫描 BarcodeScanner

手写签名 Handwritten

手写签名 SignaturePad

定位/持续定位 Geolocation

屏幕键盘 OnScreenKeyboard

百度地图 BaiduMap

谷歌地图 GoogleMap

蓝牙和打印 Bluetooth

PDF阅读器 PdfReader

文件系统访问 FileSystem

光学字符识别 OCR

电池信息/网络信息 WebAPI

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么现代企业的ERP系统必须具备CRM?

各行各业的企业都依赖ERP系统和客户关系管理 (CRM) 系统来简化业务运营。企业的销售和运营信息必须实时联系起来&#xff1b;否则&#xff0c;企业的客户活动在现代社会就会变得缓慢或没有效率。 以下业务对象通常被映射在用于销售的CRM系统和用于运营的ERP系统之间&#xff0…

【Processing】我给网友 “战场小包” 做了他的 “自画像”.

前言 突然疫情放开了&#xff0c;在掘金里认识的一个掘友&#xff08;战场小包&#xff09;&#xff0c;突然今天找我。 &#xff1a;寻思啥事呢&#xff0c;原来找我做个自画像。 &#xff1a;行&#xff01;没问题&#xff01; &#xff1a;结果等半天&#xff08;一晚上到今…

rocketmq源码-broker处理consumer拉取消息请求

前言 在前面consumer拉取消息的博客中&#xff0c;有说过&#xff0c;对于consumer&#xff0c;在拉取消息的时候&#xff0c;是需要指定code码的&#xff0c;在consumer去broker拉取消息的时候&#xff0c;指定的code码是&#xff1a;PULL_MESSAGE&#xff0c;所以这篇博客&a…

[附源码]Nodejs计算机毕业设计基于的婚恋系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

APISIX Ingress 如何支持自定义插件

摘要&#xff1a;本篇主要介绍了 Ingress 资源相关的语义&#xff0c;以及如何对 Ingress 资源进行能力的扩展。 作者&#xff1a;张晋涛&#xff0c;API7.ai 云原生技术专家&#xff0c;Apache APISIX PMC 成员&#xff0c;Apache APISIX Ingress Controller 项目维护者。 Ing…

基于C++ 实现简易图书管理系统【100010046】

图书管理系统 基于 C 实现简易图书管理系统 该项目是在学习完 C 语言后&#xff0c;独立完成设计开发的简易图书管理系统 设计的基本要求 基本完成对图书系统的设计&#xff0c;包含基本的功能&#xff0c;无界面设计。 要有明显的分类&#xff0c;对不同的进入者有不同的…

机器学习算法原理归纳总结:回归、聚类、支持向量、推荐、降维与神经网络

机器学习算法原理归纳总结&#xff1a;回归、聚类、支持向量、推荐、降维与神经网络 本文重点参考&#xff1a;唐宇迪博士的课程PPT [特别鸣谢] 完整版资料下载&#xff1a;机器学习算法原理详解代码实战 1.回归算法 2.逻辑回归 3.决策树 决策树实际上是根据样本的特征个数对样…

汇编语言第二章:寄存器

2. 寄存器 寄存器进行信息的存储&#xff0c;对于汇编程序员来说&#xff0c;CPU 中的主要部件是寄存器。8086CPU 有 14 个寄存器&#xff0c;这些寄存器分别是&#xff1a; AX BX CX DX SI DI SP BP IP CS SS DS ES PSW通用寄存器 8086所有的寄存器都是 16 位的&#xff0c…

Android Rust JNI系列教程(二) 创建第一个Rust JNI项目

前言 提到JNI,大家都会想到C,C.不过如今rust又给我们增加了一个选项,借助rust的jni库(https://github.com/jni-rs/jni-rs),我们可以很方便的使Android与rust交互.从本章起,我们将逐步地了解使用rust实现一些经典的jni方法. 创建Rust项目 创建工程 在命令行输入命令: cargo…

超算/先进计算的发展与应用是什么?

经过近十年的快速发展&#xff0c;我国在超算领域的实力已达到世界先进水平。1993年&#xff0c;我国第一台高性能计算机“曙光一号并行机”研制成功&#xff0c;打破了国外IT巨头对我国超算技术的垄断。 自此&#xff0c;我国不断加快超级计算机研制步伐。从全球超级计算机TO…

精华推荐 | 【MySQL技术专题】「主从同步架构」全面详细透析MySQL的三种主从复制(Replication)机制的原理和实战开发(原理+实战)

前提概要 随着应用业务数据不断的增大&#xff0c;应用的响应速度不断下降&#xff0c;在检测过程中我们不难发现大多数的请求都是查询操作。此时&#xff0c;我们可以将数据库扩展成主从复制模式&#xff0c;将读操作和写操作分离开来&#xff0c;多台数据库分摊请求&#xff…

NEUQACM双周赛(三)

目录7-1 打字&#xff08;C&#xff09;题目描述&#xff1a;输入格式:输出格式:输入样例1:输出样例1:输入样例2:输出样例2:解题思路&#xff1a;7-2 分香肠&#xff08;C&#xff0c;最大公约数&#xff09;题目描述&#xff1a;输入格式:输出格式:输入样例:输出样例:解题思路…

节能降耗 | AIRIOT智慧电力综合管理解决方案

电力技术的发展推动各行各业的生产力&#xff0c;与此同时&#xff0c;企业中高能耗设备的应用以及输配电过程中的电能损耗&#xff0c;也在一定程度上加剧了电能供应压力。以工业制造业为例&#xff0c;企业的管理水平、能耗结构、生产组织方式都关系到能源的有效利用率&#…

电子招投标系统nodejs+vue+elementui

前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 1、 node_modules文件夹(有npn install产生) 这文件夹就是在创建完项目后&#xff0c;cd到项目目录执行npm install后生成的文件夹&#xff0c;下载了项目需要的依赖项。 2、packag…

电商新模式——链动2+1模式为你带来社交电商新思路

随着流量入口价值的降低&#xff0c;电商 IP 时代的来临&#xff0c;移动社交电商获得了飞速的发展&#xff0c;在运营与营销的过程中&#xff0c;商家们往往为了降低营销成本&#xff0c;主动制造消费理由&#xff0c;通过各类促销、折扣来刺激消费&#xff0c;然而在回归商业…

Web3中文|NFT如何促进教育的发展?

自问世以来&#xff0c;NFT已经被应用于教育、艺术等多个领域。不过&#xff0c;相较于艺术行业&#xff0c;大多数人对NFT在教育界的作用知之甚少。 那么&#xff0c;就让我们来看看它们在课堂内外的影响都有哪些。 得益于区块链技术&#xff0c;NFT可以提高教育质量&#x…

【蓝桥杯选拔赛真题52】Scratch正话反说 少儿编程scratch图形化编程 蓝桥杯选拔赛真题讲解

目录 scratch正话反说 一、题目要求 编程实现 二、案例分析 1、角色分析

Android Rust JNI系列教程(三) Rust与Android互相调用

前言 Rust的JNI流程以及方法实际上和我们常见的C JNI是十分相似的.我们本章将使用Rust实现常见的JNI调用功能.关于更多的用法,可参考官方示例,github地址为https://github.com/jni-rs/jni-rs/blob/master/example/mylib/src/lib.rs. 基本交互功能实现 1. Java传String,返回b…

Java集合复习

文章目录集合概述、collection集合体系特点Collection集合的遍历增强for循环集合概述、collection集合体系特点 集合都是支持泛型的&#xff0c;但是集合只能存储对象&#xff0c;因此集合也叫做对象集合。 public static void main(String[] args) {Collection<String>l…

分布式 | 令人头疼的堆外内存泄露怎么排查?

作者&#xff1a;鲍凤其 爱可生 dble 团队开发成员&#xff0c;主要负责 dble 需求开发&#xff0c;故障排查和社区问题解答。少说废话&#xff0c;放码过来。 本文来源&#xff1a;原创投稿 *爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联…