跳至主要内容

[WebService] Tokbox, OpenTok API 筆記

Session

串流相關

  • session.connect(token, completionHandler)
  • session.disconnect()
  • session.subscribe(stream, targetElement, properties, completionHandler) -> subscriber
  • session.unsubscribe(subscriber)
  • session.publish(publisher, completionHandler)
  • session.unpublish(publisher)

資訊取得與傳遞

事件相關

  • session.on(type, handler, context)
  • session.off(type, handler, context)

可用事件

  • archiveStarted, archiveStopped
  • connectionCreated, connectionDestroyed
  • sessionConnected, sessionDisconnected(見備註)
  • sessionReconnected, sessionReconnecting
  • streamCreated, streamDestroyed
  • streamPropertyChanged
  • signal, signal:type

⚠️ sessionConnectedsessionDisconnected 事件中的 SessionConnectEvent 已經 deprecated,因此只可用來確認事件觸發的時間點,但在 SessionConnectEvent 中得不到太多資訊。

session.on('connectionCreated', handleConnectionCreated); // 自己和他人的都同時會觸發,可以取得 connectionId
session.on('connectionDestroyed', handleConnectionDestroyed); // 只有他人的會觸發,可以取得 connectionId
session.on('streamCreated', handleStreamCreated); // 只有他人的會觸發,可以取得 stream 和 connectionId
session.on('streamDestroyed', handleStreamDestroyed); // 只有他人的會觸發,可以取得 stream 和 connectionId

// session.on('sessionDisconnected', handleSessionDisconnected); // 只有自己的會觸發,可以取得 connectionId

signal

signal

session.on('signal', (event) => {
event.from.id; // 發送 signal 的 id
event.data; // signal 中一併傳來的 data
});

Signal

Signal 物件

{
"type": "signal:foo",
"cancelable": false,
"_defaultPrevented": false,
"data": "bar",
"from": {
/* Connection Object */
},
"target": {
/* Session Object */
}
}

發送 Signal

// signal(signal, completionHandler)

session.signal({
type: "foo",
to: recipientConnection; // a Connection object
data: "hello"
},
function(error) {
if (error) {
console.log("signal error: " + error.message);
} else {
console.log("signal sent");
}
}
);

Session 物件

// Session 物件
{
"id": "1_MX40NjQ2MTY0Mn5-MTU3NzY3...",
"sessionId": "1_MX40NjQ2MTY0Mn5-MTU3NzY3...",
"currentState": "connected",
"connection": {}, // Connection Object
"connections": {},
"streams": {},
"archives": {},
"capabilities": {
"publish": 1,
"subscribe": 1,
"forceUnpublish": 0,
"forceDisconnect": 0,
"supportsWebRTC": 1
},
"token": "T1==cGFydG5lcl9p...",
"previousState": "connecting",
"apiKey": "46461642",
"staticConfig": {},
"sessionInfo": {}
}

Connect

ConnectionEvent 物件

ConnectionEvent 比較是從整個 session 的角度觀看,在 session 中:

  1. 當有新的 client(包含自己)進入 session,或者
  2. 自己第一次進入 session 時,原本就在 session 內的 client

都會觸發 connectionCreated 事件。當有 client 從 session 中離開時(不包含自己)會觸發 connectionDestroyed 事件。

ConnectionEvent 中會包含有 Connection Object, cancelablereason 等屬性:

// ConnectionEvent Object
{
"type": "connectionCreated",
"cancelable": false,
"connection": { /*...*/ }, // Connection Object
"reason": undefined,
"target": { /*...*/ }, // Session Object
}

Connection 物件

Connection 物件

每一個連進 session 的使用者都會有一個獨特的 connection ID。從這裡可以取得 Connection 物件:

  • session.connection:在 Session 物件中有一個 Connection 屬性,可以在 session.connect() 的 callback 中取得 connectionId
session.connect(TOKEN_PUBLISHER, (error) => {
if (error) {
handleError(error);
} else {
// 這裡的 session 可以取得 connection 物件
console.log(session.connection);
}
});
  • connectCreated 事件中,在 event 內包含有 connect 物件:
session.on('connectionCreated', (event) => {
// 這裡的 event 可以取得 connection 物件
console.log(event.connection);
});

Connection 物件中包含:

  • connectionId
  • creationTime:時間戳記(毫秒),可直接帶入 new Date() 使用
  • data:在後端產生 Token 時可以定義此資料
// Connection Object
{
"id": "6bd8a316-00bf-461f-a0cd-053cab7b20b4",
"connectionId": "6bd8a316-00bf-461f-a0cd-053cab7b20b4",
"creationTime": 1580443544160,
"data": "'{\"name\":\"Hamama\",\"deviceSystemVersion\":\"ios 13.3\"}'",
"capabilities": {
"supportsWebRTC": true
},
"permissions": {
"publish": 1,
"subscribe": 1,
"forceUnpublish": 0,
"forceDisconnect": 0,
"supportsWebRTC": 1
},
"quality": null
}

SessionConnectEvent(Deprecated)

SessionConnectEvent 比較是從使用者個人的角度,當使用者透過 session.connect() 成功連接後,會觸發 sessionConnected 事件;當使用者透過 session.disconnect() 斷開連接後,會觸發 sessionDisconnected 事件。在 v2.2 之後此事件已經 deprecated,請改用 session.connect() 中的 completeHandler

Stream

Stream 物件

當使用者呼叫 session.publish(),一個新的 Stream 會被建立。

  • 當一個 stream 加入到 session 時,Session 物件會觸發 streamCreated 事件
  • 當一個 stream 被 destroyed 時,Session 物件會觸發 streamDestroyed 事件
// stream object
{
"id": "6713a6e6-7df0-4acc-a7e2-b60e1a83d070",
"streamId": "6713a6e6-7df0-4acc-a7e2-b60e1a83d070",
"name": "",
"creationTime": 1580375164948,
"connection": {}, // Connection 物件
"publisher": {}, // Publisher 物件
"channel": [
{
"id": "video1",
"type": "video",
"active": true,
"orientation": 0,
"width": 640,
"height": 480,
"source": "camera",
"fitMode": "cover"
},
{
"id": "audio1",
"type": "audio",
"active": true,
"orientation": -1,
"width": -1,
"height": -1,
"source": "camera",
"fitMode": "cover"
}
],
"hasAudio": true,
"hasVideo": true,
"videoType": "camera",
"defaultFitMode": "cover",
"videoDimensions": {
"width": 640,
"height": 480,
"orientation": 0
},
"destroyed": false,
"_": {}
}

streamCreatedstreamDestroyed 這些事件的 event 物件中都可以取得 Stream Object

SteamEvent 的物件中會包含 stream, videoType, publisher 等等:

// StreamEvent
{
"type": "streamCreated",
"cancelable": false,
"stream": {
"id": "57e20522-698a-40f7-bb83-359d7fdde50a",
"streamId": "57e20522-698a-40f7-bb83-359d7fdde50a",
"name": "",
"creationTime": 1578304027311,
"connection": {
"id": "be7272f7-669e-4160-95b7-c216572e2348",
"connectionId": "be7272f7-669e-4160-95b7-c216572e2348",
"creationTime": 1578304022604,
"data": null,
"capabilities": {
"supportsWebRTC": true
},
"permissions": {
"publish": 1,
"subscribe": 1,
"forceUnpublish": 0,
"forceDisconnect": 0,
"supportsWebRTC": 1
},
"quality": null
},
"channel": [
{
"id": "video1",
"type": "video",
"active": true,
"orientation": 0,
"width": 640,
"height": 480,
"source": "camera",
"fitMode": "cover"
},
{
"id": "audio1",
"type": "audio",
"active": true,
"orientation": -1,
"width": -1,
"height": -1,
"source": "camera",
"fitMode": "cover"
}
],
"publisher": null,
"hasAudio": true,
"hasVideo": true,
"videoType": "camera",
"defaultFitMode": "cover",
"videoDimensions": {
"width": 640,
"height": 480,
"orientation": 0
},
"destroyed": false,
"_": {}
},
"reason": null,
"target": {
// ...
}
}

Stream Event

Stream Event 會在有使用者開始或停止將串流**發佈(publish)**到 Session 時觸發。

  • event.stream
  • event.reason
session
.on('streamCreated', function (event) {
// streamContainer is a DOM element
subscriber = session.subscribe(event.stream, targetElement);
})
.connect(token);

session
.on('streamDestroyed', function (event) {
console.log('Stream ' + event.stream.name + ' ended. ' + event.reason);
})
.connect(token);

OT

分享螢幕(Screen Sharing)

OT.initPublisher

initPublisher @ tokbox

// (static) initPublisher([targetElement], [properties], [completionHandler]) → {Publisher}

import OT from '@opentok/client';

const publisher = OT.initPublisher('publisher', {
name: '', // 預設是空字串
/**
* Layout 相關
**/
width: '264px', // Number 或 String,初始影片寬度
height: '198px', // Number 或 String,初始影片高度

// 當影片與 div 區塊的比例不同時要如何呈現,包含 'cover' 和 'contain'
fitMode: undefined,

// 定義 Publisher 物件如何被插到 HTML DOM 中
// 包含 replace, after, before, append,預設是 replace
insertMode: 'replace',

// 是否使用預設的 OpenTok UI,true 時可以使用額外的 fitMode, showControls 和 style
insertDefaultUI: true, // Boolean
showControls: true,
style: {
audioLevelDisplayMode: String, // "auto", "off", "on"
archiveStatusDisplayMode: String, // "auto", "off",見備註
backgroundImageURI: String,
buttonDisplayMode: String, // "auto", "off", "on"
nameDisplayMode: String, // "auto", "off", "on"
},

/**
* Audio 相關
**/
// 6,000 - 510,000. Default if 40,000.
audioBitrate: 40000,

// 這個 id 可以透過 OT.getDevices() 取得,或傳入 MediaStreamTrack 物件
audioSource: [deviceId || MediaStreamTrack Object],
audioFallbackEnabled: undefined, // Boolean
disableAudioProcessing: false,
enableStereo: false,

/**
* video 相關
**/
// String, MediaStreamTrack, Boolean, or null
videoSource: undefined, // 見備註

// 選擇要用哪個鏡頭,通常只適用於行動裝置,包含 'user', 'environment', 'left', 'right'
facingMode: 'user',

// 影片的 frame,合法的數值包含 30, 15, 7, and 1.
frameRate: undefined,

mirror: undefined, // Boolean

// 可接受的解析度包含 "1280x720", "640x480", and "320x240"
resolution: "640x480", // 見備註

// 這個設定只適用於 videoSource 屬性為 "application", "screen" 或 "window"
// 可設定的解析度介於 10 ~ 1920 之間
maxResolution: {
width: Number,
height: Number
},

/**
* publish 相關
**/
publishAudio: true, // Session.publish(publisher) 時就發佈串流
publishVideo: true, // Session.publish(publisher) 時就發佈串流

}, completionHandler);

function completionHandler(error) {
// 當使用者同意授權攝像頭和麥克風(成功)時,error 會是 null

if (error) {
// 當使用者不同意授權時,則判斷為失敗,error 會包含 code 和 message 兩個值
const { code, message } = error;
console.log({
code,
message,
})
} else {
console.log("Publisher initialized.");
}
};
  • resolution:由於各瀏覽器支援的解析度可能不同,因此實際的解析度需要透過 Publisher 物件/Subscriber 物件的 videoHeight()videoWidth() 取得。
  • style.archiveStatusDisplayMode:如果你把此值設為 off,那麼你可以根據 Session Object 發出的 archiveStartedarchiveStopped 事件來呈現 UI 上的提示。
  • videoSource
    • 可以透過 OT.getDevices() 取得後帶入,如果將此值設為 nullfalse 則不會要求攝像頭的權限,因此也不會有 video 被發佈,若只需要聲音的話則可以如此設定。
    • 如果要發佈共享螢幕畫面(screen-sharing stream),則將此值設為 "application""screen"、或 "window",Firefox 需要指應為 window,其他瀏覽器則都可以。
    • 可以呼叫 OT.checkScreenSharingCapability() 來確認開瀏覽器是否支援共享螢幕(screen-sharing),以及支援的 video sources(application, screenwindow)。
    • 在共享螢幕的串流下,預設值如下:audioFallbackEnabled == false, maxResolution == {width: 1920, height: 1920}, mirror == false, scaleMode == "fit"、subscriber 的 scaleMode 會是 "fit"

Publisher 物件

透過 OT.initPublisher() 可以取得 Publisher 物件,Publisher 物件中的 streamId 和 Stream 物件,需要在 session.publish(publishers) 後才能拿到

// Publisher Object
{
"isWebRTC": true,
"accessAllowed": true,
"id": "OT_e42a2474-6f36-4aeb-a864-db9ff15b95f3",
"element": {},
"session": {},
"streamId": "01969ca3-7a38-4cdf-b8d3-b2ac207daf4e",
"stream": {},
}

Subscriber 物件

透過 session.subscribe(<stream>) 可以取得 Subscriber 物件

// Subscriber 物件
{
"id": "OT_046054d4-a053-4b2d-8662-0b83f48b1392",
"widgetId": "33c88bb0-f0e1-4ebc-94ec-5c00d8ea6a61",
"session": {}, // Session Object
"stream": {}, // Stream Object
"streamId": "4e2f188a-8ecd-4916-9d94-c98a43336470",
"isWebRTC": true,
"element": DOMElement
}

OT.getDevices()

OT.getDevices()