魔坊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_fieldfrom .models import LiveStream, dbfrom application.apps.users.models import Userfrom marshmallow import post_dumpclass 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 InitConfigclass 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, dbfrom message import ErrorMessage as messagefrom status import APIStatus as statusfrom flask_jwt_extended import jwt_required, get_jwt_identityfrom application.apps.users.models import Userfrom .models import LiveStream, LiveRoomfrom datetime import datetimeimport random@jsonrpc.method('Live.stream')@jwt_requireddef 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_d%sd' % ( 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': 'd' % stream.id } }from .marshmallow import StreamInfoSchemafrom flask import current_app@jsonrpc.method('Live.stream.list')@jwt_requireddef 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 }
赞 (0)