• egg搭建直播间


    npm 安装

    npm install node-media-server --save
    

    配置:config/config.default.js

    // 流媒体配置
    config.mediaServer = {
      rtmp: {
        port: 23480,
        chunk_size: 60000,
        gop_cache: true,
        ping: 30,
        ping_timeout: 60
      },
      http: {
        port: 23481,
        allow_origin: '*'
      },
      auth: {
        play: true,
        publish: true,
        secret: 'nodemedia2017privatekey',
      },
    };
    

    启动媒流体服务在egg根目录下创建app.js

    // 引入
    const NodeMediaServer = require('node-media-server');
    class AppBootHook {
        constructor(app) {
          this.app = app;
        }
      
        configWillLoad() {
          // 此时 config 文件已经被读取并合并,但是还并未生效
          // 这是应用层修改配置的最后时机
          // 注意:此函数只支持同步调用
      
          // 例如:参数中的密码是加密的,在此处进行解密
        }
      
        async didLoad() {
            // 所有的配置已经加载完毕
            // 可以用来加载应用自定义的文件,启动自定义的服务
    
            if(!this.app.nms){
                this.app.nms = new NodeMediaServer(this.app.config.mediaServer)
                this.app.nms.run();
    
                this.app.nms.on('preConnect', (id, args) => {
                    console.log('[NodeEvent on preConnect]', `id=${id} args=${JSON.stringify(args)}`);
                    // let session = nms.getSession(id);
                    // session.reject();
                });
    
                this.app.nms.on('postConnect', (id, args) => {
                    console.log('[NodeEvent on postConnect]', `id=${id} args=${JSON.stringify(args)}`);
                });
    
                this.app.nms.on('doneConnect', (id, args) => {
                    console.log('[NodeEvent on doneConnect]', `id=${id} args=${JSON.stringify(args)}`);
                });
    
                this.app.nms.on('prePublish', (id, StreamPath, args) => {
                    console.log('[NodeEvent on prePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
                    // let session = nms.getSession(id);
                    // session.reject();
                });
    
                this.app.nms.on('postPublish', (id, StreamPath, args) => {
                    console.log('[NodeEvent on postPublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
                });
    
                this.app.nms.on('donePublish', (id, StreamPath, args) => {
                    console.log('[NodeEvent on donePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
                });
    
                this.app.nms.on('prePlay', (id, StreamPath, args) => {
                    console.log('[NodeEvent on prePlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
                    // let session = nms.getSession(id);
                    // session.reject();
                });
    
                this.app.nms.on('postPlay', (id, StreamPath, args) => {
                    console.log('[NodeEvent on postPlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
                });
    
                this.app.nms.on('donePlay', (id, StreamPath, args) => {
                    console.log('[NodeEvent on donePlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
                });
            }
            
    
        }
      
        async willReady() {
          // 所有的插件都已启动完毕,但是应用整体还未 ready
          // 可以做一些数据初始化等操作,这些操作成功才会启动应用
      
          // 例如:从数据库加载数据到内存缓存
        //   this.app.cacheData = await this.app.model.query(QUERY_CACHE_SQL);
        }
      
        async didReady() {
          // 应用已经启动完毕
      
        //   const ctx = await this.app.createAnonymousContext();
        //   await ctx.service.Biz.request();
        }
      
        async serverDidReady() {
          // http / https server 已启动,开始接受外部请求
          // 此时可以从 app.server 拿到 server 的实例
      
        //   this.app.server.on('timeout', socket => {
        //     // handle socket timeout
        //   });
        }
      }
      
      module.exports = AppBootHook;
    

    客户端推流拉流协议 房间号和签名

    	//客户  拉流前缀
    	livePlayBaseUrl:"http://192.168.43.31:23481/live/gSS1lNdeRPQkrWAfgLQb.flv?sign=1608272255-d583dd4cbb1e23925bbe3e2301613c34",
    	// 主播  推流前缀
    	livePushBaseUrl:"rtmp://127.0.0.1:23480/live/gSS1lNdeRPQkrWAfgLQb?sign=1608272255-d583dd4cbb1e23925bbe3e2301613c34",
    

    推流调试工具
    下载OBS Studio
    https://obsproject.com/zh-cn/download
    安装必要的组件
    https://obsproject.com/visual-studio-2019-runtimes-64-bit
    找到菜单栏:文件——》设置——》推流
    在这里插入图片描述

    <view >
    	<video src="http://192.168.43.31:23481/live/keQ7pOdpu6Qfz4u1zeb5.flv?sign=1664599228-5f2457d3030f9563598f31097a39d247" autoplay></video>
    </view>
    

    安装加密md5

     npm i md5 --save
    

    控制器:app/controller/api/live.js

    'use strict';
    // 引入模块
    const md5 = require('md5');
    const Controller = require('egg').Controller;
    
    class LiveController extends Controller {
       // 创建直播间
       async save() {
        let { ctx, app } = this;
        let user_id = ctx.authUser.id;
        // 参数验证
        ctx.validate({
            title: {
                type: 'string',
                required: true,
                desc: '直播间标题'
            },
            cover: {
                type: 'string',
                required: true,
                desc: '直播间封面'
            }
        });
    
        let {
            title, cover
        } = ctx.request.body;
    
        // 生成唯一key  生成多少位的就输入多少数字
        let key = ctx.randomString(20);
    
        // 直接创建
        let res = await app.model.Live.create({
            title,
            cover,
            user_id,
            key,
        });
    
        // 生成签名
        let sign = this.sign(key)
    
        ctx.apiSuccess({
            data: res,
            sign
        });
    }
    
    // 生成签名
    sign(key) {
        let { ctx, app } = this;
        const secret = app.config.mediaServer.auth.secret
        const expire = parseInt((Date.now() + 100000000) / 1000);//100000000失效时间
        const hashValue = md5(`/live/${key}-${expire}-${secret}`);//加密
        return `${expire}-${hashValue}`
    }
    }
    
    module.exports = LiveController;
    

    扩展:app/extend/context.js

    randomString(length) {
        const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        var result = '';
        for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
        return result;
    },
    

    路由:app/router.js

    // 创建直播间
    router.post('/api/live/create', controller.api.live.save);
    

    表结构

    'use strict';
    
    module.exports = {
      up: (queryInterface, Sequelize) => {
        const { INTEGER, STRING, DATE, ENUM, TEXT } = Sequelize;
        return queryInterface.createTable('live', {
          id: {
            type: INTEGER(20),
            primaryKey: true,
            autoIncrement: true
          },
          title: {
            type: STRING(100),
            allowNull: false,
            defaultValue: '',
            comment: '直播间标题'
          },
          cover: {
            type: STRING,
            allowNull: true,
            defaultValue: '',
            comment: '直播间封面'
          },
          user_id: {
            type: INTEGER,
            allowNull: false,
            defaultValue: 0,
            comment: '用户id',
            references: {
              model: 'user',
              key: 'id'
            },
            onDelete: 'cascade',
            onUpdate: 'restrict', // 更新时操作
          },
          look_count: {
            type: INTEGER,
            allowNull: false,
            defaultValue: 0,
            comment: '总观看人数'
          },
          coin: {
            type: INTEGER,
            allowNull: false,
            defaultValue: 0,
            comment: '总金币'
          },
          key: {
            type: STRING,
            allowNull: false,
            defaultValue: '',
            comment: '唯一标识',
          },
          status: {
            type: INTEGER(1),
            allowNull: false,
            defaultValue: 0,
            comment: '直播间状态 0未开播 1直播中 2暂停直播 3直播结束'
          },
          created_time: DATE,
          updated_time: DATE,
        });
      },
    
      down: (queryInterface, Sequelize) => {
        return queryInterface.dropTable('live');
      }
    };
    '
    运行

    vue使用flv.js拉流

    安装flv.js

    yarn add flv.js
    

    引用

    
    <template>
     <div class="video">
          <video
           id="vPull"
           controls
           autoplay
           muted
           width="100%"
           height="100%">
          </video>
     </div>
    </template>
    
    <script>
    import flv from "flv.js";
    export default {
      name: "",
      data() {
        return {
          player: null,
        };
      },
      methods: {
        play(urls) {
          let video = document.getElementById("vPull"); //定义播放路径
          if (flv.isSupported()) {
            this.player = flv.createPlayer(
              {
                type: "flv",
                isLive: true,
                url: urls,
              },
              {
                enableWorker: false, //不启用分离线程
                enableStashBuffer: false, //关闭IO隐藏缓冲区
                isLive: true,
                lazyLoad: false,
              }
            );
          } else {
            console.log("不支持的格式");
            return;
          }
          this.player.attachMediaElement(video);
          this.player.load();
          this.player.play();
        },
        destruction() {
          //销毁对象
          if (this.player) {
            this.player.pause();
            this.player.destroy();
            this.player = null;
          }
        },
      },
      mounted() {
      
      	  this.play('http://127.0.0.1:23481/live/keQ7pOdpu6Qfz4u1zeb5.flv?sign=1664599228-5f2457d3030f9563598f31097a39d247');
      	
      },
     
    };
    </script>
    
    
  • 相关阅读:
    世界杯“引爆”东南亚电商狂潮,电商人如何选品和营销?
    【PAT甲级】1127 ZigZagging on a Tree
    电容笔适用所有平板吗?平价电容笔推荐
    node.js环境搭建
    基于Java+SpringBoot+Vue前后端分离精简博客系统设计和实现
    变电站远程维护解决方案,工程师从此不再“跑断腿“
    通达OA-通用版-V12,流程及表单自定义好用的类
    HarmonyOS 实战开发-使用canvas实现图表系列之折线图
    基于永磁同步发电机的风力发电系统连接到可控的三相整流器(Simulink)
    51单片机WIFI手机APP智能窗户窗帘控制系统手动自动定时
  • 原文地址:https://blog.csdn.net/weixin_45086164/article/details/127107978