day122:MoFang:OSSRS流媒體直播伺服器&基於APICloud的acLive直播推流模組實現RTMP直播推流

目錄

1.docker安裝OSSRS流媒體直播伺服器

2.基於APICloud的acLive直播推流模組實現RTMP直播推流

3.直播流管理

1.docker安裝OSSRS流媒體直播伺服器

1.OSSRS簡介

在外界開發中, 如果要實現直播功能.常用的方式有:

1. 通過第三方介面來實現.

可以申請阿里雲,騰訊雲,網易雲,七牛雲的直播介面,根據文檔,下載集成SDK到項目中,在第三方用戶平台上, 創建直播流[就是一個管道].有了直播流以後, 在客戶端中集成一個推流[就是基於rtmp協議把影片攝影機採集到的資訊push到直播伺服器]的播放器或者第三放模組

在另一個客戶端中, 集成支援播放rtmp影片資訊的播放器插件,基於這個插件向第三方直播伺服器獲取直播影片.

2. 自己部署搭建直播伺服器.

nginx+nginx-rtmp-module+ffmpeg

ossrs

OSSRS官網://ossrs.net/srs.release/releases/

官方文檔://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。

2.OSSRS安裝

1.創建自定義網路

sudo docker network create --driver bridge --subnet 172.0.0.0/16 srs_network
# sudo docker network ls

2.創建配置文件

# 使用阿里雲鏡像安裝啟動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

3.掛載配置文件並啟動

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

通過//127.0.0.1:8080查看srs終端資訊

通過//127.0.0.1:1935/live/自定義直播流名稱進行直播推流

2.基於APICloud的acLive直播推流模組實現RTMP直播推流

生成APPLoader,並安裝到手機[注意:推流必須依賴於攝影機,而前面我們使用的Android模擬器是沒有辦法完成攝影機調用的。

客戶端創建live_list.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body style="background-color:#fff">
  <br><br><br><br>
  <br><br><br><br>
  <button id="liver">我是主播</button>
  <button id="viewer">我是觀眾</button>
  <script>
    apiready = function(){
      document.getElementById("viewer").onclick = function(){

      }
      document.getElementById("liver").onclick = function(){
        var acLive = api.require('acLive');
        // 打開攝影機採集影片資訊
        acLive.open({
            camera:0, // 1為前置攝影機, 0為後置攝影機,默認1
            rect : {  // 採集畫面的位置和尺寸
                x : 0,
                y : 0,
                w : 'auto',
                h : 'auto',
            }
        },(ret, err)=>{
            alert(JSON.stringify(ret));
            // 開啟美顏
            acLive.beautyFace();
            // 開始推流
            acLive.start({
                url:'rtmp://192.168.20.251:1935/live/t1' // t1 就是流名稱,可以理解為直播的房間號
            },function(ret, err){
                alert(JSON.stringify(ret)); // 狀態如果為2則表示連接成功,其他都表示不成功
            });
        });
      }
    }
  </script>
</body>
</html>

3.直播流管理

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 = [
        "application.apps.home",
        "application.apps.users",
        "application.apps.orchard",
        "application.apps.live",
    ]

application/urls.py,程式碼:

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

服務端提供創建直播流的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,
        }
    }

前端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 : 'auto',
                  h : 'auto',
              }
          },(ret, err)=>{
              alert(JSON.stringify(ret));
              // 開啟美顏
              acLive.beautyFace();
              // 開始推流
              alert(this.settings.live_stream_server+this.stream_name)
              acLive.start({
                  url: this.settings.live_stream_server+this.stream_name // t1 就是流名稱,可以理解為直播的房間號
              },function(ret, err){
                  alert(JSON.stringify(ret)); // 狀態如果為2則表示連接成功,其他都表示不成功
              });
          });
        }
            }
        })
    }
    </script>
</body>
</html>