• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

视频谈话记录和直播系统的实现二blazor+srs+nginx开发和运行环境搭建

武飞扬头像
旭腾恒力
帮助1

本文涉及到的主要技术框架、开源软件有:.netcore blazor srs ffmpeg nginx javascript efcore

引言

  上篇对设计目标和技术原型进行了初步测试,证明思路是可行的,本篇开始着手实现,并记录其中的要点。

为什么选中Blazor

  选择的理由有一百种,不选择的理由就有一万种。- 熟悉:C#和asp net core是个人相对熟悉的技术,开发起来较为顺手 。刚开始接触blazor的话,会有一些vue的感觉。
  在blazor项目中,大部分时间不用写js,如果你做的是全栈开发,就不用像以前一样在c#和js中来回穿梭了,这种行云流水的开发体验,是其他框架和工具没有的。当然,blazor和js交互也是完全没有问题的。
  SignalR用在这种对实时性要求较高的项目中再合适不过了,而blazo对SignalR天然支持。

关于生产环境

  为了方便,开发过程中使用了Sqlite,在生产环境中,建议MySql或者SqlServer。服务器操作系统建议Ubuntu、debian、centos,以及国产操作系统对应的服务器版本,或者也可以分开部署。
  虽然整个项目运行在windows上没有任何问题,但还是有一些细小的差别。比如:srs的webrtc推流默认采用udp方式,在实际测试中,发现有很大概率出现丢包,会导致直播卡顿,录像丢失片段,这是不能容忍的,这个问题在srs5.0.60中已经得到了解决,可以选择采用tcp协议传输。但由于官方的windows版本目前还停留在5.0.19,并不包含这个新的特性。
  此外,关于性能和安全方面的问题,已经讨论的足够多了,这里不再赘述。

要点记录

在Blazor Server项目中引入Ant Design Of Blazor

  blazor自身有一些基础组件,可以基于它们实现自己的组件库,也可以引用一些开源项目,可选择的范围还是比较广泛的,比如MASA BlazorAnt Design of BlazorBootstrap Blazor。在这个项目中,笔者选择了Ant Design of Blazor。

  1. 在项目上右击。选择“管理NuGet程序包”,搜索“AntDesign”安装即可。
  2. 在Program.cs中注入服务:
builder.Services.AddAntDesign();
  1. 在_Layout.cshtml中,引入
<link href="_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet" />
<script src="_content/AntDesign/js/ant-design-blazor.js"></script>
  1. 没错,就这么简单,组件使用文档请参阅官网。

IHostedService的注入

  在asp.net core中,这个神奇的接口,能让你在程序正式启动前运行自己的业务逻辑,比如检测系统启动的环境、构建基础数据、检测授权信息等。在本项目中,主要是实现启动srs和nginx。

  1. 新建一个cs类,继承IHostedService接口。
public class MyInitAppService : IHostedService
{
	public MyInitAppService(IDbContextFactory<MyDataContext> dbContextFactory)
	{
		//根据需要注入EF Core数据工厂
	}
}
  1. 在StartAsync或者构造函数中编写启动代码。
public Task StartAsync(CancellationToken cancellationToken)
{
	try
    {
        Console.WriteLine("清理my-srs和my-nginx进程");
        Process[] processes = System.Diagnostics.Process.GetProcesses();
        foreach (Process item in processes)
        {
            if (item.ProcessName.Contains("my-srs") || item.ProcessName.Contains("my-nginx"))
            {
                item.Kill();
            }
        }
        Thread.Sleep(2000);
    }
    catch
    {

    }

    Console.WriteLine("启动必需的第三方软件,目标平台:"   Environment.OSVersion.Platform);
    Console.WriteLine("启动:srs");
    try
    {
        Task.Run(() => {
            ProcessStartInfo info = new ProcessStartInfo();
            info.FileName = @"srs-win\my-srs.exe";
            info.Arguments = @"-c conf\rtc.conf";
            info.WorkingDirectory = AppContext.BaseDirectory   "srs-win";
            info.UseShellExecute = false;
            info.CreateNoWindow = true;
            Process p = new Process();
            p.StartInfo = info;
            p.Start();
        });

    }
    catch (Exception ex)
    {
        Console.WriteLine("srs启动失败,部分功能无法使用,可能是:"   ex.Message);
    }
    Console.WriteLine("启动:nginx");
    try
    {
        Task.Run(() =>
        {
            ProcessStartInfo info = new ProcessStartInfo();
            info.FileName = @"nginx-win\my-nginx.exe";
            info.WorkingDirectory = AppContext.BaseDirectory   "nginx-win";
            info.UseShellExecute = false;
            info.CreateNoWindow = false;
            Process p = new Process();
            p.StartInfo = info;
            p.Start();
        });
    }
    catch (Exception ex)
    {
        Console.WriteLine("nginx启动失败,部分功能无法使用,可能是:"   ex.Message);
    }
    return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
	//停止时终止srs和nginx
	try
	{
	    Console.WriteLine("清理my-srs和my-nginx进程");
	    Process[] processes = System.Diagnostics.Process.GetProcesses();
	    foreach (Process item in processes)
	    {
	        if (item.ProcessName.Contains("my-srs") || item.ProcessName.Contains("my-nginx"))
	        {
	            item.Kill();
	        }
	    }
	    Thread.Sleep(2000);
	}
	catch { }
    return Task.CompletedTask;
}
学新通

不要完全依赖于StopAsync,因为只有通过Ctrl C正常退出时才会执行这个方法。
3. 测试一下,当然是顺利启动了,从任务管理器中可以看到这两个进程。
学新通
4.在实际项目中,可以从数据库读取srs和nginx的配置,动态生成conf文件,可以避免和系统中已经存在的组件发生端口冲突。

WebRTC推流的前提:HTTPS通信

  使用nginx的最基本的意图,就是把srs和blazor server的端口统一进行代理,从而实现https通信。因为webrtc仅支持localhost和https。
  nginx的配置要点如下:

http {
    map $http_upgrade $connection_upgrade {
	        default Upgrade;
	        ''      close;
	    }

    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;

    #keepalive_timeout  65;
    #gzip  on;
    server {
        listen       5200 ssl default http2;
        charset utf-8;
        ssl_certificate      my.com.crt;
        ssl_certificate_key  my.com.key;
        ssl_protocols   TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers     ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
        ssl_prefer_server_ciphers  on;

        location / {
        	#代理blazor server端口
            proxy_pass http://127.0.0.1:5201;
            #下面几行是为signalr配置
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
        }  

        location ~ ^/(api|rtc)/ {
           #代理srs http api端口
           proxy_pass http://127.0.0.1:5285$request_uri;
        }

        location /live/ {
           #代理srs http端口
           proxy_pass http://127.0.0.1:5288/live/;
        }
    }
}
学新通

  通过上面的配置,就实现了一个证书,通吃所有通信。同时,也为后期负载均衡、故障转移等留了余地。
  这个证书是自签名的,所以浏览器会出现风险提示,用户只要允许访问即可,不影响正常使用。当然,如果客户网络中有自己的dns系统,就可以申请一个域名证书,避免出现这样的提示。
学新通
学新通

  其实,srs自身也是支持https的,但由于使用了自签名证书,在推流时默认是拒绝访问的,需要再有一次交互过程。

在Blazor组件中模块化加载JavaScript

  blazor组件中嵌入js脚本的方式有几种,最简单的是在_layout.cshtml直接引入,但是这种方式会导致每个blazor组件都会加载这些脚本,延长客户端加载时间,并且极易产生变量污染。比较理想的做法是模块化加载,也就是每个blazor只加载和自己相关的js文件。

[Inject] IJSRuntime _js { get; set; } //注入依赖

//在首次渲染时加载js文件
protected override async Task OnAfterRenderAsync(bool firstRender)
{
	if(firstRender)
	{
		jsModule = await _js.InvokeAsync<IJSObjectReference>("import", "./js/TalkingPlayer.js");		//TalkingPlayer.js放在wwwroot/js下
	}
}

  调用也很简单:

//res接收返回结果
var res = await jsModule.InvokeAsync<string>("start", "这是参数"); // start是js中的方法名称

  js中的start方法需要export

export async function start(arg) {
	console.log("接受到的参数:"   arg);
	return "调用成功";
}

  下一篇,将处理一些意外情况。

原型测试代码下载地址

  已经上传到资源下载。传送门在此。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhggabff
系列文章
更多 icon
同类精品
更多 icon
继续加载