魔坊APP项目-27-直播、客户端中调整窗口大小、能播放rtmp格式直播流的播放器模块

直播

客户端中直播的界面调整和当前窗口一致
live_list.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
  <link rel="stylesheet" href="../static/css/main.css">
  <script src="../static/js/vue.js"></script>
  <script src="../static/js/axios.js"></script>
  <script src="../static/js/main.js"></script>
  <script src="../static/js/uuid.js"></script>
  <script src="../static/js/settings.js"></script>
</head>
<body>
  <div class="app" id="app">
    <br><br><br><br>
    <br><br><br><br>
    <button @click="liver">创建直播间</button>
    <button @click="start_live">我要开播</button>
    <button id="viewer">我是观众</button>
  </div>
  <script>
    apiready = function(){
      init();
      new Vue({
        el: '#app',
        data(){
          return {
            music_play: true,
            stream_name: '',   // 直播流名称
            prev: {name: '',url: '',params: {}},
            current: {name: 'live', url: 'live_list.html', 'params': {}},
          }
        },
        methods: {
          liver(){
            var token = this.game.get('access_token') || this.game.fget('access_token');
            this.axios.post('', {
              'jsonrpc': '2.0',
              'id': this.uuid(),
              'method': 'Live.stream',
              'params': {
                'room_name': '爱的直播间'
              }
            },{
              headers:{
								Authorization: "jwt " + token,
              }
            }).then(response=>{
              var message = response.data.result;
              if(parseInt(message.errno) == 1005){
                this.game.goWin('user', 'login.html', this.current);
              }
              if(parseInt(message.errno) == 1000){
                this.stream_name = message.data.stream_name;
            }else{
              this.game.print(response.data);
            }
          }).catch(error=>{
            // 网络等异常
            this.game.print(error);
          });
        },
        start_live(){
          // 开始直播
          var acLive = api.require('acLive');
          // 打开摄像头采集视频信息
          acLive.open({
              camera:0, // 1为前置摄像头, 0为后置摄像头,默认1
              rect : {  // 采集画面的位置和尺寸
                  x : 0,
                  y : 0,
                  w : 450,
                  h : 1080,
              }
          },(ret, err)=>{
              this.game.print(ret);
              // 开启美颜
              acLive.beautyFace();
              // 开始推流
              acLive.start({
                  url: this.settings.live_stream_server+this.stream_name  // t1 就是流名称,可以理解为直播的房间号
              },(ret, err)=>{
                  this.game.print(ret); // 状态如果为2则表示连接成功,其他都表示不成功
              });
          });
        }

        }
      })


    }
  </script>
</body>
</html>

基于能播放rtmp格式直播流的播放器模块.

live_list.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
  <link rel="stylesheet" href="../static/css/main.css">
  <script src="../static/js/vue.js"></script>
  <script src="../static/js/axios.js"></script>
  <script src="../static/js/main.js"></script>
  <script src="../static/js/uuid.js"></script>
  <script src="../static/js/settings.js"></script>
</head>
<body>
  <div class="app" id="app">
    <br><br><br><br>
    <br><br><br><br>
    <button @click="liver">创建直播间</button>
    <button @click="start_live">我要开播</button>
    <button @click="in_live">进入直播间</button>
    <button @click="viewer">我是观众</button>
  </div>
  <script>
    apiready = function(){
      init();
      new Vue({
        el: '#app',
        data(){
          return {
            music_play: true,
            stream_name: '',   // 直播流名称
            prev: {name: '',url: '',params: {}},
            current: {name: 'live', url: 'live_list.html', 'params': {}},
          }
        },
        methods: {
          in_live(){
            // 进入直播间
          },
          viewer(){
            // 观看直播
            var obj = api.require('playModule');
            obj.play({
                rect:
                {   x: 0,
                    y: 0,
                    w: 450,
                    h: 1080,
                },
                fixedOn: api.frameName,
                title: 'test',
                scalingMode: 2,
                url: this.settings.live_stream_server+'room_',
                defaultBtn: false,
                enableFull : false,
                isTopView : false,
                isLive: true,
                placeholderText: true,
            }, (ret, err)=>{
              this.game.print(ret);
            });
          },
          liver(){
            var token = this.game.get('access_token') || this.game.fget('access_token');
            this.axios.post('', {
              'jsonrpc': '2.0',
              'id': this.uuid(),
              'method': 'Live.stream',
              'params': {
                'room_name': '爱的直播间'
              }
            },{
              headers:{
								Authorization: "jwt " + token,
              }
            }).then(response=>{
              var message = response.data.result;
              if(parseInt(message.errno) == 1005){
                this.game.goWin('user', 'login.html', this.current);
              }
              if(parseInt(message.errno) == 1000){
                this.stream_name = message.data.stream_name;
                this.game.print(this.stream_name)
            }else{
              this.game.print(response.data);
            }
          }).catch(error=>{
            // 网络等异常
            this.game.print(error);
          });
        },
        start_live(){
          // 开始直播
          var acLive = api.require('acLive');
          // 打开摄像头采集视频信息
          acLive.open({
              camera:0, // 1为前置摄像头, 0为后置摄像头,默认1
              rect : {  // 采集画面的位置和尺寸
                  x : 0,
                  y : 0,
                  w : 450,
                  h : 1080,
              }
          },(ret, err)=>{
              this.game.print(ret);
              // 开启美颜
              acLive.beautyFace();
              // 开始推流
              acLive.start({
                  url: this.settings.live_stream_server+this.stream_name  // t1 就是流名称,可以理解为直播的房间号
              },(ret, err)=>{
                  this.game.print(ret); // 状态如果为2则表示连接成功,其他都表示不成功
              });
          });
          api.addEventListener({
              name: 'keyback'
          }, (ret, err)=>{
            acLive.close();
            acLive.end();
          });

        }

        }
      })


    }
  </script>
</body>
</html>

服务端提供当前所有直播间的列表信息
live/marshmallow.py:,代码


from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, auto_field
from .models import LiveStream, db
from application.apps.users.models import User
from marshmallow import post_dump

class StreamInfoSchema(SQLAlchemyAutoSchema):
    id = auto_field()
    name = auto_field()
    room_name = auto_field()
    user = auto_field()
    
    class Meta:
        model = LiveStream
        include_fk = True
        include_relationships = True
        fields = ['id', 'name', 'room_name', 'user']
        sql_session = db.session
        
    @post_dump()
    def user_format(self, data, **kwargs):
        user = User.query.get(data['user'])
        if user is None:
            return data
        
        data['user'] = {
            'id': user.id,
            'nickname': user.nickname if user.nickname else "",
            'ip': user.ip_address if user.ip_address else "",
            'avatar': user.avatar if user.avatar else "",
        }
        return data
     

dev.py,

from . import InitConfig
class Config(InitConfig):
    """项目开发环境下的配置"""
    DEBUG = True
    # 数据库
    SQLALCHEMY_DATABASE_URI = "mysql://mofang_user:mofang@127.0.0.1:3306/mofang?charset=utf8mb4"
    SQLALCHEMY_ECHO = False

    # redis
    REDIS_URL = "redis://@127.0.0.1:6379/0"

    # session存储配置
    SESSION_REDIS_HOST = "127.0.0.1"
    SESSION_REDIS_PORT = 6379
    SESSION_REDIS_DB = 1

    # 日志配置
    LOG_LEVEL = "INFO"  # 日志输出到文件中的最低等级
    LOG_DIR = "/logs/mofang.log"  # 日志存储目录
    LOG_MAX_BYTES = 300 * 1024 * 1024   # 单个日志文件的存储上限[单位: b]
    LOG_BACKPU_COUNT = 20   # 日志文件的最大备份数量
    LOG_NAME = "mofang"  # 日志器名称

    # 注册蓝图
    INSTALLED_APPS = [
        "application.apps.home",
        "application.apps.users",
        "application.apps.marsh",
        "application.apps.orchard",
        "application.apps.live",
    ]

    # 短信相关配置
    SMS_ACCOUNT_ID = "8aaf0708754a3ef2017563ddb22d0773"  # 接口主账号
    SMS_ACCOUNT_TOKEN = "0b41612bc8a8429d84b5d37f29178743"  # 认证token令牌
    SMS_APP_ID = "8aaf0708754a3ef2017563ddb3110779"  # 应用ID
    SMS_TEMPLATE_ID = 1  # 短信模板ID
    SMS_EXPIRE_TIME = 60 * 5  # 短信有效时间,单位:秒/s
    SMS_INTERVAL_TIME = 60   # 短信发送冷却时间,单位:秒/s

    # jwt 相关配置
    # 加密算法,默认: HS256
    JWT_ALGORITHM = "HS256"
    # 秘钥,默认是flask配置中的SECRET_KEY
    JWT_SECRET_KEY = "y58Rsqzmts6VCBRHes1Sf2DHdGJaGqPMi6GYpBS4CKyCdi42KLSs9TQVTauZMLMw"
    # token令牌有效期,单位: 秒/s,默认: datetime.timedelta(minutes=15) 或者 15 * 60
    JWT_ACCESS_TOKEN_EXPIRES = 60 * 60 * 2
    # refresh刷新令牌有效期,单位: 秒/s,默认:datetime.timedelta(days=30) 或者 30*24*60*60
    JWT_REFRESH_TOKEN_EXPIRES = 30*24*60*60
    # 设置通过哪种方式传递jwt,默认是http请求头,也可以是query_string,json,cookies
    JWT_TOKEN_LOCATION = ["headers", "query_string"]
    # 当通过http请求头传递jwt时,请求头参数名称设置,默认值: Authorization
    JWT_HEADER_NAME = "Authorization"
    # 当通过查询字符串传递jwt时,查询字符串的参数名称设置,默认:jwt
    JWT_QUERY_STRING_NAME = 'token'
    # 当通过http请求头传递jwt时,令牌的前缀。
    # 默认值为 "Bearer",例如:Authorization: Bearer <JWT>
    JWT_HEADER_TYPE = "jwt"

    # 防水墙验证码
    CAPTCHA_GATEWAY = "https://ssl.captcha.qq.com/ticket/verify"
    CAPTCHA_APP_ID = "2041284967"
    CAPTCHA_APP_SECRET_KEY = "0FrDthTnnU8vG-jSwz7DOAA**"

    # mongoDB配置信息
    MONGO_URI = 'mongodb://127.0.0.1:27017/mofang'

    # 用户默认头像
    DEFAULT_AVATAR = '54270a03-3587-4638-9156-b1f479efc958.jpeg'

    # 服务端带外提供的url地址
    # SERVER_URL = "http://127.0.0.1:5000"

    # socketio
    CORS_ALLOWED_ORIGINS = '*'
    ASYNC_MODE = None
    HOST = '0.0.0.0'
    PORT = 5000

    # 支付宝配置信息
    ALIPAY_APP_ID = '2016110100783756'
    ALIPAY_SIGN_TYPE = 'RSA2'
    # ALIPAY_NOTIFY_URL = 'https://example.com/notify'
    ALIPAY_NOTIFY_URL = "http://127.0.0.1:5000/alipay/notify"
    ALIPAY_SANDBOX = True

    # ossrs 服务端
    SRS_HTTP_API = "http://127.0.0.1:1985/api/v1/"

live/views.py,代码:


from application import jsonrpc, db
from message import ErrorMessage as message
from status import APIStatus as status
from flask_jwt_extended import jwt_required, get_jwt_identity
from application.apps.users.models import User
from .models import LiveStream, LiveRoom
from datetime import datetime
import random

@jsonrpc.method('Live.stream')
@jwt_required
def live_stream(room_name):
    """创建直播流"""
    current_user_id = get_jwt_identity()
    user = User.query.get(current_user_id)
    if user is None:
        return {
            'errno': status.CODE_NO_USER,
            'errmsg': message.user_not_exists,
            'data': {

            }
        }

    # 申请创建直播流
    stream = LiveStream.query.filter(LiveStream.user == user.id).first()
    if stream is None:
        stream_name = 'room_%06d%s%06d' % (
        user.id, datetime.now().strftime('%Y%m%d%H%M%S'), random.randint(100, 999999))
        stream = LiveStream(
            name=stream_name,
            user=user.id,
            room_name=room_name
        )
        db.session.add(stream)
        db.session.commit()
    else:
        stream.room_name = room_name
    # 进入房间
    room = LiveRoom.query.filter(LiveRoom.user == user.id, LiveRoom.stream_id == stream.id).first()
    if room is None:
        room = LiveRoom(
            stream_id=stream.id,
            user=user.id
        )
        db.session.add(room)
        db.session.commit()

    return {
        'errno': status.CODE_OK,
        'errmsg': message.ok,
        'data': {
            'stream_name': stream_name,
            'room_name': room_name,
            'room_owner': user.id,
            'room_id': '%04d' % stream.id
        }
    }

from .marshmallow import StreamInfoSchema
from flask import current_app

@jsonrpc.method('Live.stream.list')
@jwt_required
def list_stream():
    current_user_id = get_jwt_identity()
    user = User.query.get(current_user_id)
    if user is None:
        return {
            'errno': status.CODE_NO_USER,
            'errmsg': message.user_not_exists,
            'data': {

            }
        }

    stream_list = LiveStream.query.filter(LiveStream.status == True, LiveStream.is_deleted == False).all()
    sis = StreamInfoSchema()
    data_list = sis.dump(stream_list, many=True)

    # 使用requests发送get请求
    import requests
    stream_response = requests.get(current_app.config['SRS_HTTP_API']+'streams/')
    client_response = requests.get(current_app.config['SRS_HTTP_API']+'clients/')
    import re, json
    stream_text = re.sub(r'[^\{\}\/\,0-9a-zA-Z\"\'\:\[\]\._]', "", stream_response.text)
    client_text = re.sub(r'[^\{\}\/\,0-9a-zA-Z\"\:\[\]\._]', "", client_response.text)
    stream_dict = json.loads(stream_text)
    client_dict = json.loads(client_text)
    for data in data_list:
        data['status'] = False
        for stream in stream_dict['streams']:
            if data['name'] == stream['name']:
                data['status'] = stream['publish']['active']
                break
        data['clients_number'] = 0
        for client in client_dict['clients']:
            if data['name'] == client['url'].split('/')[-1]:
                data['clients_number'] += 1
            if client['publish'] and '/live/' + data['name'] == client['url']:
                data['user']['ip'] = client['ip']
    return {
        'errno': status.CODE_OK,
        'errmsg': message.ok,
        'stream_list': data_list
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值