资讯专栏INFORMATION COLUMN

如何打造在线直播间(技术贴)

sugarmo / 1583人阅读

摘要:背景当下视频直播如此红火,打造一个在线直播间涉及到哪些技术呢视频直播由主播的直播端以及观众的观看端组成。保持心跳断开重连快速搭建在线直播间按前文所述,搭建直播间有非常多的细节需要考虑,包括采集推流分发播放体验优化聊天室性能调优等。

背景

当下视频直播如此红火,打造一个在线直播间涉及到哪些技术呢?

视频直播由主播的直播端以及观众的观看端组成。一个简单的观看端最起码应包含播放器以及聊天室。下面就围绕这两大模块来讲述相关技术。

视频直播

视频直播,可以分为视频采集、前处理、编码和封装、传输、解封装和解码、播放这几个环节。

直播端通过硬件设备采集音视频数据,经过前处理以及编码、封装后,还要传输到观看端。这一步一般交由CDN接力完成。推流端会把视频流推到源站,CDN从源站拉流,拉流成功后编码封装成不同的格式提供各观看端播放。简单示意图如下:

接下来讲一下观看端的基本要求:

多终端观看

观看稳定、不卡顿

延迟低、高并发

多终端观看

首先明确需要支持的终端,有移动端APP(iOS、Android)、移动端浏览器、小程序、PC浏览器、PC 客户端。对于PC浏览器,一般支持到IE8。并且,由于Chrome浏览器默认屏蔽Flash,所以在PC浏览器中采用的策略为:优先在兼容H5的浏览器使用H5播放,否则降级使用Flash播放。两者支持的直播播放视频格式差异如下:

Flash支持rtmp或 http-flv 直播播放。延时~3秒。

H5支持M3u8直播播放。延时~15秒。

观看稳定、不卡顿

造成视频直播卡顿的原因可能是多方面的,涉及到直播端、网络、观看端。

直播端

主要问题有硬件配置太低、推流参数配置问题、音视频时间戳不同步。可以通过以下措施解决:

升级硬件、软件设置,提高兼容性和容错率 ( 这部分是硬装,必须有好的装备才能有好的推流质量啊 )。

使用硬编硬解方案,充分利用GPU加速。

降低视频码率,选择流畅、标清画质或者使用动态码率推流。

网络

主要问题有网络抖动、拉流服务器与观看端链路过长,可以通过以下措施解决:

选用稳定的运营商网络并合理布局CDN节点。

使用充足的网络带宽。

观看端

在网络上观看视频,缓冲区就是在你看视频时提前储存部分视频数据,当数据到达一定的量后再播放画面,使得播放更流畅。若设置得过小,在网络不稳定时就无法流畅地连续播放;若设置过大则会累积时延。所以要设置一个适中的缓冲区。

// Flash
netStream.bufferTime = 1 // 单位:秒
// FLV.js
flvjs.createPlayer({
  type: "flv",
  isLive: true,
  url: "video.flv"
},{
  stashInitialSize: 120 // 默认:384KB
})
延时低、高并发

我们知道,视频其实是由一帧一帧的图像构成的,RTMP基于TCP不会丢包,所以当网络状态差时,服务器会将包缓存起来。等网络状况好了,就一起发给观看端,导致观看端累积太多视频帧数据,延时随时间增长而增加。对于这个问题,除了上文提及的设置适当的缓冲区长度外,还可以增加追帧和丢帧操作实现播放追赶。

Flash代码:

// Flash实现追帧:定时器轮询检测当前缓冲区长度大于30秒时重连,重新拉取直播流
netStream.bufferTimeMax = 0.1 // 设置bufferTimeMax主动追帧
​
if(netStream.bufferLength > 30) { 
  // 缓冲区长度大于30,重新连接  
  reconnectStream() 
}

H5代码:

// H5实现追帧,判断当前缓冲区结束时间与当前时间相差超过5秒,则追帧
if (video.buffered.length > 0 && video.buffered.end(0) - video.currentTime > 5) {
  // 直播流时间接近缓冲时间的话画面容易卡死,所以保险起见-1秒
  
  video.currentTime = video.buffered.end(0) - 1;

}

另外,如果用到FLV.js播放视频,可以开启它的Worker特性,多线程解析优化延迟 ,并减少buffer。

// FLV.js
flvjs.createPlayer({
  type: "flv",
  isLive: true,
  url: "video.flv"
},{
  enableWorker: true,
  enableStashBuffer: false,
  stashInitialSize: 120 // 默认:384KB
})
聊天室

即时聊天IM服务既要保证实时性,可靠性,又要抗住高并发。在实现过程中我们使用以下方法解决。

1、传输模式优先选择 WebSocket,若不支持则降级为轮询。

const io = require("socket.io")({ "transports":["websocket", "polling"]})

2、Node.js 服务器因消息并发大导致性能低下。

通过以下方案极大的优化了聊天室的稳定性和可靠性。

(1)使用命名空间的功能

命名空间的作用就是把消息限定在一定范围内传播。对于一些不需要全局接收的消息就加上命名空间,可以极大的节约资源的传输。

// 创建命名空间
const io = require("socket.io")()
const adminNamespace = io.of("/admin")
​
adminNamespace.to("level1").emit("an event", { some: "data" })

(2)聊天消息队列

观众进入聊天室房间会广播登录消息,比如房间内同时有2W人,每个人登录要对房间内所有人广播"我"登录了,相当于发送了2W条消息。若并发量大,对服务器性能要求极高。使用聊天队列消息分批显示可以防止同时处理大量消息,提高处理性能。

// 消息队列
let scoektMsgArr = [{
   EVENT: "SPEAK",
   uid: socketId,
   content: "这是第一条聊天消息" 
},...]
let minCount = 0
​
setInterval(()=>{
    const maxCount = minCount + 100
    const newScoektMsgArr = scoektMsgArr.slice(minCount, maxCount)
    newScoektMsgArr.forEach((item) => {
       socket.emit("message", JSON.stringify(item))
    })
}, 1000)

(3)服务器弹性伸缩

祭出最后的大招,能优化的已经极力优化了,那剩下的事情就是配置服务器弹(jia)性(fu)伸(wu)缩(qi)。

3、掉线重连机制。

掉线会触发disconcent 事件,监听它再创建socket连接即可。心跳检查即定时发送一次消息,保持连接状态。

// 保持心跳
setInterval(()=>{
    socket.emit("message", JSON.stringify({
      EVENT: "HEARTBEAT",
      uid: socketId
    }))
}, 30 * 60 * 1000)

// 断开重连
socket.on("disconnect", () => {
    this.connect()
})

connect() {
  socket.on("connect", () => {
     //TODO
  })
}
POLYV SDK 快速搭建在线直播间

按前文所述,搭建直播间有非常多的细节需要考虑,包括采集推流、CDN分发、播放体验优化、聊天室性能调优等。别担心,调用POLYV SDK可以快速搭建直播间。

(使用POLYV视频直播服务需要先免费注册账号)

STEP1 嵌入播放器:


STEP2 嵌入聊天室:

// 默认样式

var chatroom = new PolyvChatRoom({ roomId: 268682, userId: 153075602311, nick: "游客", pic: "http://livestatic.videocc.net/assets/wimages/missing_face.png", token: token, container: "#wrap", width: 300, height: 600, userType: "", roomMessage: function(data) { // TODO // data为聊天室socket消息,当有聊天室消息时会触发此方法 console.log(data); } });

如上图所示,嵌入直播播放SDK + 聊天室SDK即可创建直播间。聊天室SDK中有自带默认皮肤、发送表情、点赞、送花、在线列表、提问等功能。另外针对直播教育场景需用到的其他功能,如答题卡、签到等,可监听聊天室socket消息自定义实现:

1.答题卡

// 监听答题内容
var chatroom = new PolyvChatRoom({
    roomId: 268682,
    userId: 153075602313,
    nick: "学员_1",
    pic: "http://livestatic.videocc.net/assets/wimages/missing_face.png",
    token: token,
    container: "#wrap",
    width: 300,
    height: 600,
    userType: "",
    roomMessage: function(data) {
        // 监听聊天室消息
        switch(data.EVENT){
            case "GET_TEST_QUESTION_RESULT":
                // 获取答题卡内容,显示答题卡
              break;
            case "GET_TEST_QUESTION_RESULT": 
              // 获取答题卡结果, 显示答题结果
              break;  
        }
    }
});
​```
// 发送答题结果
chatroom.socket.emit("message", JSON.stringify({
    EVENT: "ANSWER_TEST_QUESTION",
    roomId: channelId,
    nick: nick,
    userId: userId,
    option: result,
    questionId: questionId
}));

2.签到

// 直播端发起签到
roomMessage: function(data) {
    // 监听聊天室消息
    switch(data.EVENT){
        case "SIGN_IN":
            // 发起签到
            break;
        case "STOP_SIGN_IN": 
            // 停止签到
            break;  
    }
}
​
// 观众端签到
chatroom.socket.emit("message", JSON.stringify({
    EVENT: "TO_SIGN_IN",
    roomId: roomId,
    checkinId: checkinId,
    user: {
        userId: 123456,
        nick: "polyv"
    }
}));

小程序

除了在浏览器端,我们在小程序端也提供了对应的SDK。

下载小程序SDK后,调用组件快速生成包含播放器、聊天室等功能的直播间。

// 嵌入polyv页面组件

  


// 初始化数据
import plv from "*/polyv-sdk/index";
// onLoad
onLoad() {
   const options = {
     channelId: "", // 频道ID
     openId: "", // 用户openId
     userName: "", // 用户名
     avatarUrl: "" // 用户头像
   };
   plv.init(options);
}
// onUnload
onUnload() {
  plv.destory();
}

总结:

如果以完全自研的方式实现教育直播的基础功能,至少需要10人的技术团队,5人的产品运营团队,才能在3个月内完成产品的上线。算下来至少需要几十万甚至上百万。POLYV为各企业提供了全面的解决方案以及相关文档,实现快速接入,轻松开启线上教育直播。

资料:

使用flv.js做直播 https://segmentfault.com/a/11...

视频直播秒开背后的技术与优化经验 https://juejin.im/post/58eaf4...

socket.io https://socket.io/docs/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104692.html

相关文章

发表评论

0条评论

最新活动
阅读需要支付1元查看
<