docker安装OSSRS流媒体直播服务器

docker安装OSSRS流媒体直播服务器

在外界开发中, 如果要实现直播功能.常用的方式有:

1. 通过第三方接口来实现.
   可以申请阿里云,腾讯云,网易云,七牛云的直播接口,根据文档,下载集成SDK到项目中,在第三方用户平台上, 创建直播流[就是一个管道].有了直播流以后, 在客户端中集成一个推流[就是基于rtmp协议把视频摄像头采集到的信息push到直播服务器]的播放器或者第三放模块
   在另一个客户端中, 集成支持播放rtmp视频信息的播放器插件,基于这个插件向第三方直播服务器获取直播视频.
   
2. 自己部署搭建直播服务器.
   nginx+nginx-rtmp-module+ffmpeg
   ossrs

OSSRS官网:http://ossrs.net/srs.release/releases/

官方文档:https://github.com/ossrs/srs/wiki

SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。SRS提供了丰富的接入方案将RTMP流接入SRS,包括推送RTMP到SRS、推送RTSP/UDP/FLV到SRS、拉取流到SRS。 SRS还支持将接入的RTMP流进行各种变换,譬如将RTMP流转码、流截图、转发给其他服务器、转封装成HTTP-FLV流、转封装成HLS、 转封装成HDS、转封装成DASH、录制成FLV/MP4。SRS包含支大规模集群如CDN业务的关键特性,譬如RTMP多级集群、源站集群、VHOST虚拟服务器 、 无中断服务Reload、HTTP-FLV集群。此外,SRS还提供丰富的应用接口,包括HTTP回调、安全策略Security、HTTP API接口、 RTMP测速。SRS在源站和CDN集群中都得到了广泛的应用Applications。

安装

  1. 创建自定义网络
sudo docker network create --driver bridge --subnet 172.0.0.0/16 srs_network
# sudo docker network ls
  1. 创建配置文件
# 使用阿里云镜像安装启动srs
sudo docker run -p 1935:1935 -p 1985:1985 -p 8080:8080 --name srs registry.cn-hangzhou.aliyuncs.com/ossrs/srs:v4.0.34

# 把容器中的配置文件复制出来
sudo mkdir -p /home/docker/srs4
sudo docker cp -a srs:/usr/local/srs/conf /home/docker/srs4/conf
 
# 把容器中的日志文件复制出来
sudo docker cp -a srs:/usr/local/srs/objs /home/docker/srs4/objs

# 删除容器
sudo docker rm -f srs
  1. 挂载配置文件并启动
sudo docker run --restart=always -p 1935:1935 -p 1985:1985 -p 8080:8080 --name srs --network srs_network --ip 172.0.0.35 -v /home/docker/srs4/conf/:/usr/local/srs/conf/ -v /home/docker/srs4/objs/:/usr/local/srs/objs/ -d registry.cn-hangzhou.aliyuncs.com/ossrs/srs:v4.0.34
  1. 通过http://127.0.0.1:8080查看srs终端信息
  2. 通过http://127.0.0.1:1935/live/自定义直播流名称进行直播推流

基于APICloud的acLive直播推流模块实现RTMP直播推流

docker安装OSSRS流媒体直播服务器_第1张图片

生成APPLoader,并安装到手机[注意:推流必须依赖于摄像头,而前面我们使用的安卓模拟器是没有办法完成摄像头调用的。]

docker安装OSSRS流媒体直播服务器_第2张图片

客户端创建live_list.html


<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Documenttitle>
  <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 : 500,
                  h: 1080,
              },
              fixedOn: api.frameName,
              title: 'test',
              scalingMode:2,
              url: this.settings.live_stream_server+"room_00000520210112095154097509",
              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 : '424',
                  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'
          },()=>{
              acLive.close();
              acLive.end();
          });

        }
			}
		})
	}
	script>
body>
html>

直播流管理

cd application/apps/
python ../../manage.py blue -nlive 

live/models.py,代码:

from application.utils.models import BaseModel,db
class LiveStream(BaseModel):
    """直播流管理"""
    __tablename__ = "mf_live_stream"
    name = db.Column(db.String(255), unique=True, comment="流名称")
    room_name = db.Column(db.String(255), default="房间名称")
    user = db.Column(db.Integer, comment="房主")

class LiveRoom(BaseModel):
    """直播间"""
    __tablename__ = "mf_live_room"
    stream_id = db.Column(db.Integer, comment="直播流ID")
    user   = db.Column(db.Integer, comment="用户ID")

settings.dev.py,代码:

    INSTALLED_APPS = [
        "home",
        "users",
        "orchard",
        "live",
    ]

application/urls.py,代码:

from application.utils import include
urlpatterns = [
    include("","home.urls"),
    include("/users","users.urls"),
    include("/orchard","orchard.urls"),
    include("/orchard","live.urls"),
]

数据迁移

cd ../..
python manage.py db migrate -m "add live table"
python manage.py db upgrade

服务端提供创建直播流的API接口,代码:

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 # 验证jwt
def live_stream(room_name):
    """创建直播流"""
    current_user_id = get_jwt_identity() # get_jwt_identity 用于获取载荷中的数据
    user = User.query.get(current_user_id)

    # 申请创建直播流
    stream_name = "room_%06d%s%06d" % (user.id, datetime.now().strftime("%Y%m%d%H%M%S"), random.randint(100,999999))
    stream = LiveStream.query.filter(LiveStream.user==user.id).first()
    if stream is None:
        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,
        }
    }

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

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

服务端提供当前所有直播间的列表信息

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

live/views.py,代码:

from .marshmallow import StreamInfoSchema
from flask import current_app
@jsonrpc.method("Live.stream.list")
@jwt_required # 验证jwt
def list_stream():
    # 验证登陆用户信息
    current_user_id = get_jwt_identity() # 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请求,读取当前srs中所有的直播流和客户端列表
    import requests,re,json
    stream_response = requests.get(current_app.config["SRS_HTTP_API"]+"streams/")
    client_response = requests.get(current_app.config["SRS_HTTP_API"]+"clients/")
    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)
    
    # 在循环中匹配所有的客户端对应的总人数和当前推流用户的IP地址
    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
    }

你可能感兴趣的:(flask)