Hướng dẫn webrtc live streaming nodejs - webrtc phát trực tiếp nodejs

WebRTC là công nghệ giúp chúng ta xây dựng ứng dụng stream audio, video, chia sẻ file, desktop, ... thông qua kết nối peer to peer (kết nối trực tiếp giữa các client mà không cần truyền dữ liệu qua server trung gian). Loạt bài này sẽ giúp các bạn có thể tích hợp công nghệ WebRTC vào ứng dụng web của bạn.

Bạn chuẩn bị trước các tài khoản các trang sau:  http://heroku.com/ http://peerjs.com/ http://xirsys.com/ https://github.com/
http://heroku.com/
http://peerjs.com/
http://xirsys.com/
https://github.com/

Project khởi đầu:  https://github.com/vanpho93/rtc-start-kit
https://github.com/vanpho93/rtc-start-kit

Project kết thúc: https://github.com/vanpho93/khoapham-rtc Các bạn có thể theo dõi project cuối mỗi video trong mục commit
https://github.com/vanpho93/khoapham-rtc
Các bạn có thể theo dõi project cuối mỗi video trong mục commit


Xem thêm khoá học Lập trình Nodejs tại Khoa Phạm: http://khoapham.vn/khoa-hoc-lap-trinh-nodejs.html

Overview

Chúng ta sẽ phát triển ứng dụng demo WebRTC bằng NodeJS.

Nội dung chính

  • Lấy được video từ webcam
  • Stream video với RTCPeerConnection
  • Stream data với RTCDataChannel
  • Cài đặt một signaling service để trao đổi messages
  • Kết hợp peer connection và signaling
  • Chụp ảnh và chia sẻ nó qua một kênh dữ liệu (data channel)

Các service cần cài đặt trước

  • Chrome 47 trở lên
  • NodeJS, npm
  • Hiểu biết cơ bản về HTML, CSS và JavaScript
  • Text editor

Stream video từ webcam

Thêm một thẻ

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
6 và một thẻ
'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
7 vào file
'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
8 trong thư mục chính như sau:






  Realtime communication with WebRTC

  





  

Realtime communication with WebRTC

Thêm đoạn code sau vào file

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
9 trong thư mục
navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
0:

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

Giải thích

Khi

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
1 được gọi, trình duyệt sẽ yêu cầu quyền truy cập camera của người dùng. Nếu thành công, một
navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
2 sẽ được trả về và được sử dụng như một thẻ
navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
3 thông qua thuộc tính
navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
4:

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}

Tham số

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
5 cho phép chỉ định cái mà media sẽ lấy (có thể là video hoặc audio)

var constraints = {
  video: true
};

Có thể sử dụng

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
6 cho các options khác như
navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
7 chẳng hạn:

const hdConstraints = {
  video: {
    width: {
      min: 1280
    },
    height: {
      min: 720
    }
  }
}

MediaTrackConstraints specification liêt kê tất cả

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
8. Nếu resolution yêu cầu không được hỗ trợ bởi camera hiện tại
navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
9 sẽ từ chối với một lỗi
var constraints = {
  video: true
};
0 và người dùng sẽ không có quyền truy cập camera.

Nếu

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);

function handleSuccess(stream) {
  video.srcObject = stream;
}
9 thành công,
var constraints = {
  video: true
};
2 từ webcam sẽ được cài đặt làm
var constraints = {
  video: true
};
3 của thẻ
'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
6.

function handleSuccess(stream) {
  video.srcObject = stream;
}

Như vậy chúng ta đã biết được cách:

  • Lấy video từ webcam
  • Cài đặt media constraints.
  • Hiển thị video với thẻ
    'use strict';
    
    var constraints = {
      video: true
    };
    
    var video = document.querySelector('video');
    
    function handleSuccess(stream) {
      video.srcObject = stream;
    }
    
    function handleError(error) {
      console.error('getUserMedia error: ', error);
    }
    
    navigator.mediaDevices.getUserMedia(constraints).
      then(handleSuccess).catch(handleError);
    
    6. Một vài chú ý: Đừng quên thêm thuộc tính
    var constraints = {
      video: true
    };
    
    6 cho thẻ
    'use strict';
    
    var constraints = {
      video: true
    };
    
    var video = document.querySelector('video');
    
    function handleSuccess(stream) {
      video.srcObject = stream;
    }
    
    function handleError(error) {
      console.error('getUserMedia error: ', error);
    }
    
    navigator.mediaDevices.getUserMedia(constraints).
      then(handleSuccess).catch(handleError);
    
    6. Thông tin thêm về
    var constraints = {
      video: true
    };
    
    8 xem thêm ở đây Có thể thêm CSS cho thẻ video để hiện video ko bị tràn màn hình:
video {
  max-width: 100%;
  width: 320px;
}

Stream video với RTCPeerConnection

Stream data với RTCDataChannel

Cài đặt một signaling service để trao đổi messages

Kết hợp peer connection và signaling




Chụp ảnh và chia sẻ nó qua một kênh dữ liệu (data channel)

Các service cần cài đặt trước

Chrome 47 trở lên


Thêm thông tin về adapter.js

File

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
8 trông như sau:






  Realtime communication with WebRTC

  





  

Realtime communication with WebRTC

File

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
9 như sau:

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
0

Tạo một cuộc gọi

Mở file

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
8, click button
function handleSuccess(stream) {
  video.srcObject = stream;
}
2 để get video từ webcam và click
function handleSuccess(stream) {
  video.srcObject = stream;
}
3 to tạo một
function handleSuccess(stream) {
  video.srcObject = stream;
}
4. Chúng ta sẽ thấy 2 video giống nhau. WebRTC sử dụng RTCPeerConnection API to cài đặt một connection to stream video giữa các WebRTC clients được biết như
function handleSuccess(stream) {
  video.srcObject = stream;
}
5. Trong ví dụ này, 2 RTCPeerConnection object ở cùng 1 trang: pc1 và pc2

Cài đặt một cuộc gọi giữa 2 WebRTC peers bao gồm 3 bước sau:

  • Tạo một RTCPeerConnection cho mỗi client và thêm
    const hdConstraints = {
      video: {
        width: {
          min: 1280
        },
        height: {
          min: 720
        }
      }
    }
    
    4 từ getUserMedia()`.
  • Lấy và chia sẻ thông tin
    function handleSuccess(stream) {
      video.srcObject = stream;
    }
    
    7 được biết như là
    function handleSuccess(stream) {
      video.srcObject = stream;
    }
    
    8
  • Lấy và chia sẻ local và remote description, metadata về local media in SDP format

Tưởng tượng rằng Alice và Bob muốn sử dụng RTCPeerConnection để cài đặt một cuộc gọi video: Đầu tiên, Alice và Bob trao đổi các thông tin về network.

function handleSuccess(stream) {
  video.srcObject = stream;
}
9 chính là quá trình tìm kiếm
video {
  max-width: 100%;
  width: 320px;
}
0 và
video {
  max-width: 100%;
  width: 320px;
}
1 sử dụng ICE framework.

  1. Alice tạo một
    video {
      max-width: 100%;
      width: 320px;
    }
    
    2 với một
    video {
      max-width: 100%;
      width: 320px;
    }
    
    3:
'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
1
  1. Alice gọi getUserMedia() và thêm
    video {
      max-width: 100%;
      width: 320px;
    }
    
    4 thông qua:
'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
2
  1. Đối tượng
    video {
      max-width: 100%;
      width: 320px;
    }
    
    3 từ bước 1 được gọi khi
    video {
      max-width: 100%;
      width: 320px;
    }
    
    6 available.
  2. Alice gửi dữ liệu candidate đã được mã hóa cho Bob. Trên thực tế , quá trình này (được biết như là signaling) xảy ra thông quá một tin nhắn dịch vụ (messaging service). Ở đây 2 RTCPeerConnection object trên cùng một page nên nó có thể kết nối trực tiếp mà không cần một external messaging service.
  3. When Bob nhận được một
    video {
      max-width: 100%;
      width: 320px;
    }
    
    7 từ Alice, anh ấy sẽ gọi
    video {
      max-width: 100%;
      width: 320px;
    }
    
    8 để thêm candidate vào remote peer description:
'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
3

WebRTC peers cũng cần tìm và trao đổi thông tin local và remote audio và video media như resolution, dung lượng codec. Signaling trao đổi thông tin media configuration bởi trao đổi metadata được gọi là một

video {
  max-width: 100%;
  width: 320px;
}
9 và một



0 sử dụng



1 format, gọi tắt là SDP

Alice run phương thức




2 . Promise trả về một



3 (Alice's local session description):

'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
4
  1. Nếu thành công, Alice sẽ set một
    
    
    
    
    4 sử dụng
    
    
    
    
    5 và sau đó gửi
    
    
    
    
    6 này tới Bob thông qua
    
    
    
    
    7.
  2. Bob sẽ set
    
    
    
    
    8 mà Alice đã gửi như một
    
    
    
    
    9 sử dụng
    
    
    0.
  3. Bob run phương thức
    
    
    1, truyền tham số là
    
    
    
    
    9 mà nhận được từ Alice, Sau đó một
    
    
    3 thích hợp được sinh ra.
    
    
    4 truyền một
    
    
    
    
    3 : Bob sets nó như
    
    
    
    
    4 và gửi nó cho Alice.
  4. Khi Alice nhận session description của Bob, cô ấy set nó như một
    
    
    
    
    9 với phương thức
    
    
    0.
'use strict';

var constraints = {
  video: true
};

var video = document.querySelector('video');

function handleSuccess(stream) {
  video.srcObject = stream;
}

function handleError(error) {
  console.error('getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
  then(handleSuccess).catch(handleError);
5

Như vậy chúng ta đã hiểu rõ các bước kết nối và thiết lập các kết nối peer trong WebRTC. Phần sau mình sẽ giới thiệu cách stream qua


9.