Skip to main content

[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); // 自己和他人的都同時會觸發,可以取得 connectionIdsession.on('connectionDestroyed', handleConnectionDestroyed); // 只有他人的會觸發,可以取得 connectionIdsession.on('streamCreated', handleStreamCreated); // 只有他人的會觸發,可以取得 stream 和 connectionIdsession.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()