你将学到

通过本节,你将学到:

  • 使用canvas标签拍摄一张照片并获取它的数据
  • 与远端用户交换图片数据

本节的所有内容位于 step-06 文件夹

How it works

前面我们学习了使用RTCDataChannel交换文本信息。

本章节将共享整个文件:本示例,图片通过getUserMedia()采集。

本章节的核心步骤如下:

  1. 构建一个数据通道。注意这一步你不需要添加任何媒体流到peer connection。
  2. 使用getUserMedia()采集用户webcam的视频流:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var video = document.getElementById('video');

function grabWebCamVideo() {
  console.log('Getting user media (video) ...');
  navigator.mediaDevices.getUserMedia({
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    alert('getUserMedia() error: ' + e.name);
  });
}
  1. 当用户点击Snap按钮,从视频流获取一张截图,并展示到canvas标签:
1
2
3
4
5
6
7
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');

function snapPhoto() {
  photoContext.drawImage(video, 0, 0, photo.width, photo.height);
  show(photo, sendBtn);
}
  1. 当用户点击Send按钮,将图片转换为字节,并通过数据通道发送出去:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function sendPhoto() {
  // Split data channel message in chunks of this byte length.
  var CHUNK_LEN = 64000;
  var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
    len = img.data.byteLength,
    n = len / CHUNK_LEN | 0;

  console.log('Sending a total of ' + len + ' byte(s)');
  dataChannel.send(len);

  // split the photo and send in chunks of about 64KB
  for (var i = 0; i < n; i++) {
    var start = i * CHUNK_LEN,
      end = (i + 1) * CHUNK_LEN;
    console.log(start + ' - ' + (end - 1));
    dataChannel.send(img.data.subarray(start, end));
  }

  // send the reminder, if any
  if (len % CHUNK_LEN) {
    console.log('last ' + len % CHUNK_LEN + ' byte(s)');
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
  }
}
  1. 接收方将数据通道消息字节恢复为图片,并展示给用户:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function receiveDataChromeFactory() {
  var buf, count;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
      count = 0;
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }

    var data = new Uint8ClampedArray(event.data);
    buf.set(data, count);

    count += data.byteLength;
    console.log('count: ' + count);

    if (count === buf.byteLength) {
      // we're done: all data chunks have been received
      console.log('Done. Rendering photo.');
      renderPhoto(buf);
    }
  };
}

function renderPhoto(data) {
  var canvas = document.createElement('canvas');
  canvas.width = photoContextW;
  canvas.height = photoContextH;
  canvas.classList.add('incomingPhoto');
  // trail is the element holding the incoming images
  trail.insertBefore(canvas, trail.firstChild);

  var context = canvas.getContext('2d');
  var img = context.createImageData(photoContextW, photoContextH);
  img.data.set(data);
  context.putImageData(img, 0, 0);
}

运行

程序将随机生成一个房间ID,将ID添加到URL。在新的tab或window打开URL。

点击Snap & Send按钮,然后观察另一个tab页接收区域。程序将在tab页之间传输图片。你会看到如下图所示: snap-image-via-datachannel.jpeg

加分项(Bonus points)

  1. 能否修改代码使得程序可以分享任何类型的文件?

了解更多

  • The MediaStream Image Capture API: an API for taking photographs and controlling cameras — coming soon to a browser near you!
  • The MediaRecorder API, for recording audio and video: demo, documentation.