一直好奇直播這個東東是如何實現的,譬如音視頻流是如何採集的? 音視頻流是如何推送到訂閱方 ? 如何支撐上萬級、百萬級用戶同時觀看直播 ?javascript
下面就基於聲網 Agora SDK 開發一個簡單的直播網站來體驗一下吧。html
如上圖所示爲直播 Demo 實現的基本功能展現,包括:java
主播端:json
訪客端:api
下面看下本次實現的簡易架構圖:架構
在實現以前咱們須要建立 Agora 帳號並獲取 App ID(參見聲網的開發者中心)。app
本次實現對於直播間的建立只是簡單記錄下直播間的名稱ide
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 獲取直播間名稱
String room = req.getParameter("room");
// 新增直播間
RoomListHolder.addRoom(room);
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");
resp.getWriter().write("true");
}
複製代碼
// 建立 Agora Client
var client = AgoraRTC.createClient({mode: 'interop'});
// 初始化 client,並在初始化成功後執行加入直播間頻道
client.init('APP ID', function () {
// 加入直播間頻道
client.join(null, room, null, function (uid) {
console.log("用戶 " + uid + "建立了直播間 " + room);
// 執行本地視頻發佈
});
});
複製代碼
主播在完成直播間的建立以後,基於 Agora SDK 實現本地視頻的發佈,這樣當有訂閱該直播間頻道的用戶將會獲取視頻流。post
在發佈視頻前,須要獲取本地的音頻設備,也便是麥克風,攝像頭。網站
AgoraRTC.getDevices(function (devices) {
for (var i = 0; i !== devices.length; ++i) {
var device = devices[i];
if (device.kind === 'audioinput') {
// 獲取麥克風設備
media.audio = device.deviceId;
} else if (device.kind === 'videoinput') {
// 獲取攝像頭設備
media.video = device.deviceId;
}
}
});
複製代碼
// 建立本地音視頻流
var localStream = AgoraRTC.createStream({
streamID: uid, // Agora 分配的 uid
audio: true,
cameraId: camera,
microphoneId: microphone,
video: true,
screen: false
});
// 初始化音視頻流
localStream.init(function () {
// 指定元素 ID 播放音視頻流
localStream.play('video');
// 發佈音視頻流
client.publish(localStream, function (err) {
console.log("音視頻發佈失敗: " + err);
});
// 監聽音視頻發佈事件
client.on('stream-published', function (evt) {
console.log("音視頻發佈成功!");
});
}, function (err) {
console.log("", err);
});
複製代碼
下播對於 Agora SDK 來講便是離開頻道,同時從直播間列表中移除當前直播間。
client.leave(function () {
// 主播執行時 會觸發訪客端事件 peer-leave
// 訪客執行時 不會觸發
console.log("下播成功");
// 將 room 從直播間列表中移除
$.post("/removeRoom?room=" + room, {}, function(data, textStatus, jqXHR) {
// do nothing
}, 'json');
}, function (err) {
console.log("下播失敗");
});
複製代碼
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 獲取待移除的直播間
String room = req.getParameter("room");
// 移除直播間
RoomListHolder.removeRoom(room);
}
複製代碼
訪客經過瀏覽直播間列表,選擇本身感興趣的主播觀看。
// 獲取直播間列表數據
$.get("/getRooms", function (data, status) {
var roomHtml = '';
if (data == null) {
roomHtml = '當前沒有直播間';
} else {
$.each(data, function (i, room) {
roomHtml += '<div class="layui-col-md6">\n' +
' <div class="layui-card">\n' +
' <div class="layui-card-header">'+ room +'</div>\n' +
' <div class="layui-card-body">\n' +
' <button channel="'+ room +'" class="layui-btn layui-btn-radius layui-btn-warm view">\n' +
' <i class="layui-icon"></i> 觀看\n' +
' </button>\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' ';
});
}
// 渲染
$('#roomList').html(roomHtml);
});
複製代碼
訪客觀看直播,對於 Agora SDK 來講也就是加入直播間頻道,並訂閱頻道視頻流。
client.join(null, room, null, function (uid) {
console.log("用戶 " + uid + " 來到直播間 " + room);
}, function (err) {
console.log("加入直播間失敗")
});
client.on('stream-added', function (evt) {
// 主播發布視頻時會觸發 stream-added 事件
var stream = evt.stream;
// 訪客訂閱 stream 流
client.subscribe(stream, function (err) {
console.log("視頻流訂閱失敗", err);
});
});
client.on('stream-subscribed', function (evt) {
// 主播端視頻流訂閱成功後觸發 stram-subscribed 事件
var stream = evt.stream;
// 按指定元素 ID 播放視頻流
stream.play('video_remote');
});
client.on('stream-removed', function (evt) {
// 主播端執行 unpublish 時會觸發該事件
var stream = evt.stream;
// 中止視頻播放
stream.stop();
});
client.on('peer-leave', function (evt) {
// 主播端執行 leave 下播操做時會觸發該事件
var stream = evt.stream;
if (stream) {
// 中止視頻流播放
stream.stop();
}
});
複製代碼
本次只是對直播的簡單實現,彈幕的功能暫時忽略吧,o(╯□╰)o
在 Agora SDK 的使用過程當中,能夠感覺到其有如下優勢:
不過在官網提供的快速開始及 API 相關重要方法譬如client.publish中,最好能說明下調用了該方法會觸發哪些事件;而不是隻有在查閱了 event api 纔會發現其是哪些操做觸發的。