eTelecom docs
Tiếng Việt
Tiếng Việt
  • HƯỚNG DẪN SỬ DỤNG
    • Trang quản trị cs.etelecom.vn
    • Trang quản trị dành cho Partner
    • Google Chrome Extension
    • App eTelecom
  • TÍCH HỢP API
    • Shop
      • Tài khoản
      • Sử dụng
    • Partner
      • Tài khoản
      • Sử dụng
    • Tổng đài
      • VoIP SDK
        • Android
        • iOS
        • Web
        • Flutter
      • API tổng đài
        • Partner lấy danh sách tổng đài
        • Partner tạo hotline
        • Partner lấy danh sách hotline
        • Partner kích hoạt tổng đài
        • Partner gán hotline vào tổng đài
        • Partner gỡ hotline ra khỏi tổng đài
        • Partner xóa hotline ra khỏi hệ thống
        • Tạo tổng đài
        • Lấy thông tin tổng đài
        • Lấy cấu hình tổng đài
        • Lấy danh sách hotline
        • Tạo nhân viên
        • Lấy danh sách nhân viên
        • Tạo máy nhánh
        • Lấy thông tin máy nhánh
        • Lấy danh sách máy nhánh
        • Gán máy nhánh cho nhân viên
        • Thay đổi hotline cho máy nhánh
        • Gỡ máy nhánh khỏi nhân viên
        • Lấy lịch sử cuộc gọi
    • Zalo Cloud
      • Hướng dẫn kết nối Zalo Cloud
      • Zalo Cloud API
        • Tạo liên kết uỷ quyền OA
        • Kết nối OA
        • Lấy refresh token
        • Lấy danh sách OA
        • Lấy thông tin OA
        • Cập nhật Webhook cho OA
        • Lấy danh sách người dùng
        • Gửi tin tư vấn dạng văn bản
        • Gửi tin Tư vấn theo mẫu yêu cầu thông tin người dùng
        • Gửi tin tư vấn dạng sticker
        • Gửi tin tư vấn dạng hình ảnh
        • Gửi tin tư vấn dạng file
        • Gửi tin giao dịch
        • Upload file
        • Gửi yêu cầu cấp quyền gọi
        • Kiểm tra khách hàng đã cấp quyền gọi
        • Lấy danh sách yêu cầu cấp quyền gọi
        • Tạo mẫu tin
        • Lấy thông tin chi tiết mẫu tin
        • Lấy danh sách mẫu tin
        • Khởi tạo Journey Token
        • Lấy thông tin chi tiết Journey Token
        • Lấy danh sách Journey Token
        • Lấy danh sách tin nhắn
        • Lấy thông tin chi tiết tin nhắn
        • Gửi ZNS
        • Gửi ZNS với Journey Token
  • SMS BRANDNAME
    • Hướng dẫn sử dụng
    • SMS API
      • Gửi SMS
      • Lấy trạng thái tin
      • Lấy thông tin chi tiết mẫu tin
Powered by GitBook
On this page
  • I. Cài đặt
  • II. Đăng nhập máy nhánh (extension)
  • III. Đăng xuất máy nhánh (extension)
  • IV. Thực hiện cuộc gọi (outgoing call)
  • V. Nhận cuộc gọi (incoming call)
  • VI. Xử lý cuộc gọi, lấy remoteMediaStream
  • VII. Lắng nghe các State của cuộc gọi
  • VIII. Chủ động kết thúc cuộc gọi
  • IX. Giữ máy
  1. TÍCH HỢP API
  2. Tổng đài
  3. VoIP SDK

Web

PreviousiOSNextFlutter

Last updated 4 months ago

I. Cài đặt

  • Tải SIP.js tại đây: hoặc nếu project có sử dụng npm thì dùng npm install sip.js

  • Phiên bản mới nhất hiện tại: 0.20.0

  • Document SIP.js:

II. Đăng nhập máy nhánh (extension)

Khai báo các biến global và import các class sau:

import {
  Invitation,
  InvitationAcceptOptions,
  Inviter,
  InviterInviteOptions,
  Registerer,
  Session,
  SessionState,
  UserAgent,
  UserAgentOptions,
  Web
} from "sip.js";
import {IncomingResponse} from "sip.js/lib/core";

userAgent: UserAgent;

// registerer để quản lý các thao tác đăng nhập, đăng xuất máy nhánh.
registerer: Registerer;

// incomingInvitation để quản lý các sự kiện liên quan đến cuộc gọi đến (incoming call)
incomingInvitation: Invitation;

// outgoingInviter để quản lý các sự kiện liên quan đến cuộc gọi đi (outgoing call)
outgoingInviter: Inviter;

// khi một cuộc gọi bất kì diễn ra, 1 session được tạo ra, biến này dùng để theo dõi các trạng thái của cuộc gọi cho đến khi cuộc gọi kết thúc.
session: Session;

Khai báo UserAgent và Registerer để đăng nhập máy nhánh:

// Giải thích:
// username: extension_number (ví dụ: 1234, 5678, ...)
// password: extension_password (ví dụ: abcDefg123, xzyqerTT11, ...)
// domain: tenant_domain (ví dụ: voip.example.com, ...)
async login(username: string, password: string, domain: string) {

  const _uri = UserAgent.makeURI(`sip:${username}@${tenant_domain`);
  if (!_uri) { return; }

  const _userAgentOptions: UserAgentOptions = {
    uri: _uri,
    authorizationUsername: username,
    authorizationPassword: password,
    transportOptions: {
      server: "wss://sip.etelecom.vn:10443",
    },
    logLevel: "debug",
    displayName: username,
    delegate: {
      // chứa các sự kiện như có cuộc gọi đến, có cuộc gọi chuyển tiếp, ...
      // ví dụ ở mục V sẽ nói tới nhận cuộc gọi đến, phần đó sẽ được để trong này
    }
  };
  this.userAgent = new UserAgent(_userAgentOptions);

  this.registerer = new Registerer(this.userAgent, {});

  await this.userAgent?.start();

  await this.registerer?.register();

}

III. Đăng xuất máy nhánh (extension)

async logout() {
  await this.registerer?.unregister();
  await this.userAgent?.stop();
}

IV. Thực hiện cuộc gọi (outgoing call)

Để thực hiện cuộc gọi:

async call(phoneNumber: string) {
  const target = UserAgent.makeURI(`sip:${phone}@${tenant_domain}`);
  if (!target) { return; }

  const inviter = new Inviter(this.userAgent, target, {});

  this.outgoingInviter = inviter;
  this.session = inviter;

  **// lắng nghe các State của cuộc gọi: bắt đầu, kết thúc, ... Sẽ nói ở mục VII.** 
  this._sessionStateListener();

  const _inviterInviteOptions: InviterInviteOptions = {
    requestDelegate: {
      // Khi cuộc gọi bắt đầu thì sự kiện onProgress này xảy ra.
      onProgress: (response: IncomingResponse) => {
        if (response.message.headers['X-Session-Id']?.length) {
          // TODO: lấy X-Session-Id ra để phục vụ cho việc lấy Call Logs (nếu cần).
        }
      },
      // Khi cuộc gọi được người nhận chấp nhận thì sự kiện onAccept này xảy ra.
      onAccept: (response: IncomingResponse) => {
        this.session = inviter;

        **// Xử lý cuộc gọi, lấy remoteMediaStream (audio/video) của khách hàng
        // để có thể nghe và nói chuyện được với khách hàng.
        // Sẽ nói ở mục VI.**
        this._processCalls();
      }
    }
  };

  await this.outgoingInviter.invite(_inviterInviteOptions);
}

Cuộc gọi video (Video Call), tiếp tục là hàm call() bên trên:

// bạn có thể tuỳ chỉnh những thông số này theo nếu cần thiết.
const videoConstraints: MediaTrackConstraints = {
  advanced: [
    {
      width: 480,
      height: 640,
      echoCancellation: true,
      frameRate: 20,
    }
  ]
};

async call(phoneNumber: string) {
  
  [...]
  **// lắng nghe các State của cuộc gọi: bắt đầu, kết thúc, ... Sẽ nói ở mục VII.** 
  this._sessionStateListener();

  const constraints: MediaStreamConstraints = {
    video: videoConstraints,
    audio: true
  };

  **// chuẩn bị webcam để tiến hành video call.**
  navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
    // tìm Local Video Element (Local là phía user, phân biệt với Remote là phía người đàm thoại với User)
    const localVideo = document.getElementById("localVideo") as HTMLVideoElement;

    // sau đó gán MediaStream làm source của Video Element đó
    localVideo.srcObject = stream;

    // gọi hàm play() để phát video.
    localVideo.play().catch(err => console.error("ERROR in Streaming Local Video", err));
  });

  const _inviterInviteOptions: InviterInviteOptions = {
    requestDelegate: {
      [...]
      sessionDescriptionHandlerOptions: {constraints}
    }
  };
}

V. Nhận cuộc gọi (incoming call)

Thêm giá trị này vào field delegate của UserAgentOptions ở mục II, bước 2.

**// khi có cuộc gọi đến, sự kiện onInvite này sẽ xảy ra**
onInvite: (invitation: Invitation) => {

  this.incomingInvitation = invitation;
  this.session = invitation;
  **// lắng nghe các State của cuộc gọi: bắt đầu, kết thúc, ... Sẽ nói ở mục VII.** 
  this._sessionStateListener();

  if (this.incomingInvitation.request.headers['X-Session-Id']?.length) {
    // TODO: lấy X-Session-Id ra để phục vụ cho việc lấy Call Logs (nếu cần).
  }

}
  • Nếu là cuộc gọi video thì check biến incomingInvitation

this.incomingInvitation.body.includes("label:video_label")

Chấp nhận cuộc gọi

async answer() {
  await this.incomingInvitation.accept();
  **// Xử lý cuộc gọi, lấy remoteMediaStream (audio/video) của khách hàng
  // để có thể nghe và nói chuyện được với khách hàng.
  // Sẽ nói ở mục VI.**
  this._processCalls();
}
  • Nếu là cuộc gọi video thì cũng với hàm answer() bên trên, ta có thể hiện thực như sau:

async answer() {
  const constraints: MediaStreamConstraints = {
    video: videoConstraints,
    audio: true
  };

  **// chuẩn bị webcam để tiến hành video call.**
  navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
    // tìm Local Video Element (Local là phía user, phân biệt với Remote là phía người đàm thoại với User)
    const localVideo = document.getElementById("localVideo") as HTMLVideoElement;

    // sau đó gán MediaStream làm source của Video Element đó
    localVideo.srcObject = stream;

    // gọi hàm play() để phát video.
    localVideo.play().catch(err => console.error("ERROR in Streaming Local Video", err));
  });

  await this.incomingInvitation.accept({
    sessionDescriptionHandlerOptions: {constraints}
  });

  this._processCalls();
}

Từ chối cuộc gọi

async reject() {
  await this.incomingInvitation.reject();
}

VI. Xử lý cuộc gọi, lấy remoteMediaStream

_processCalls() {
  const sessionDescriptionHandler = this.session.sessionDescriptionHandler;
  if (sessionDescriptionHandler && sessionDescriptionHandler instanceof Web.SessionDescriptionHandler) {
    if (**cuộc gọi là video**) {
      // tìm Remote Video Element trên HTML DOM
      const remoteVideo = document.getElementById("remoteVideo") as HTMLVideoElement;
  
      // sau đó gán MediaStream làm source của Video Element đó
      remoteVideo.srcObject = sessionDescriptionHandler.remoteMediaStream;
  
      // gọi hàm play() để phát video.
      remoteVideo.play().catch(err => console.error("ERROR in Streaming Remote Video", err));
    } else {
      // tìm Audio Element trên HTML DOM
      const remoteAudio = document.getElementById("remote") as HTMLAudioElement;
  
      // sau đó gán MediaStream làm source của Audio Element đó
      remoteAudio.srcObject = sessionDescriptionHandler.remoteMediaStream;
  
      // gọi hàm play() để phát âm thanh.
      remoteAudio.play().catch(err => console.error("ERROR in Streaming Remote Audio", err));
    }
  }
}
  • Giao diện của bạn phải có các tag <audio> và <video> để hiển thị video và phát audio khi nghe gọi. Với local là phía user và remote là phía người đàm thoại với user.

  • Ví dụ:

<video id="remoteVideo" autoplay>
</video>
<video id="localVideo" autoplay>
</video>
<audio style="display: none" id="remoteAudio"></audio>
<!--localAudio có thể được dùng để chứa audio về ring back tone, nhạc chuông báo cuộc gọi đến...-->
<audio style="display: none" id="localAudio" loop></audio>

VII. Lắng nghe các State của cuộc gọi

_sessionStateListener() {
  this.session.stateChange.addListener((state: SessionState) => {
    switch (state) {
      case SessionState.Initial:
      // Cuộc gọi vừa được khởi tạo.

      case SessionState.Establishing:
      // Cuộc gọi vừa được chấp nhận.

      case SessionState.Established:
      // Cuộc gọi bắt đầu diễn ra.

      case SessionState.Terminating:
      // Cuộc gọi vừa được kết thúc.

      case SessionState.Terminated:
        // Cuộc gọi hoàn toàn kết thúc

      default:
    }
  });
}

VIII. Chủ động kết thúc cuộc gọi

async hangup() {
  if (**Cuộc gọi là cuộc gọi đi và mới được khởi tạo, chưa được chấp nhận**) {
    await this.outgoingInviter.cancel();
  } else {
    await this.session?.bye();
  }
}

IX. Giữ máy

async hold(isHold: boolean) {
  const _holdOptions: Web.SessionDescriptionHandlerOptions = {
    hold: isHold
  };
  
  this.session.sessionDescriptionHandlerOptionsReInvite = _holdOptions;
  
  this.session.invite().then();
}

https://github.com/onsip/SIP.js/releases
https://github.com/onsip/SIP.js/blob/master/docs/README.md