aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/webrtc_api/taking_still_photos/index.html
blob: c8fc957a8b16d89bffff966b86a7a4c24e402c9a (plain)
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
---
title: Taking still photos with WebRTC
slug: Web/API/WebRTC_API/Taking_still_photos
tags:
  - API
  - Advanced
  - Example
  - Sample code
  - Video
  - WebRTC
  - webcam
translation_of: Web/API/WebRTC_API/Taking_still_photos
---
<p>{{WebRTCSidebar}}</p>

<p><span class="seoSummary">本文介绍如何使用 WebRTC在 支持 WebRTC 的计算机或手机上访问摄像机,并用其拍照。 </span><a href="https://mdn-samples.mozilla.org/s/webrtc-capturestill">尝试一下这个示例</a><span class="seoSummary">然后继续阅读,了解它的工作原理。</span></p>

<p><img alt="WebRTC-based image capture app — on the left we have a video stream taken from a webcam and a take photo button, on the right we have the still image output from taking the photo" src="https://mdn.mozillademos.org/files/10281/web-rtc-demo.png" style="display: block; height: 252px; margin: 0px auto; width: 677px;"></p>

<p>如果你喜欢,你也可以直接跳转到 <a href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill">GitHub 上的代码</a></p>

<h2 id="HTML_标记">HTML 标记</h2>

<p><a href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill/index.html">我们的 HTML 界面</a>有两个主要的操作区域:流和捕获面板以及演示面板。 它们俩都在它们自己的 {{HTMLElement("div")}} 中并排呈现,以便于造型和控制。</p>

<p>左边的面板包含两个组件:一个 {{HTMLElement("video")}} 元素,它将接收来自 WebRTC 的流,以及用户点击捕获视频帧的 {{HTMLElement("button")}}</p>

<pre class="brush: html">  &lt;div class="camera"&gt;
    &lt;video id="video"&gt;Video stream not available.&lt;/video&gt;
    &lt;button id="startbutton"&gt;Take photo&lt;/button&gt;
  &lt;/div&gt;</pre>

<p>这很简单,当我们进入 JavaScript 代码时,我们将看到他们是如何紧密联系在一起的。</p>

<p>接下来,我们有一个 {{HTMLElement("canvas")}} 元素,捕获的帧被存储到其中,可能以某种方式进行操作,然后转换为输出图像文件。 通过使用样式 {{cssxref("display")}}<code>:none</code> 将画布保持隐藏,以避免画面的混乱 —— 用户不需要看到这个中间过程。</p>

<p>我们还有一个 {{HTMLElement("img")}} 元素,我们将涌起绘制图像——这是让用户看到的最终显示。</p>

<pre class="brush: html">  &lt;canvas id="canvas"&gt;
  &lt;/canvas&gt;
  &lt;div class="output"&gt;
    &lt;img id="photo" alt="The screen capture will appear in this box."&gt;
  &lt;/div&gt;</pre>

<p>这是所有相关的HTML。 其余的只是一些页面布局和提供一个返回页面链接的些许文本。</p>

<h2 id="JavaScript_代码"><span style="font-size: 30px;">JavaScript 代码</span></h2>

<p>现在来看看 <a href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill/capture.js">JavaScript</a> 代码。 我们将把它分解成几个小的部分,使其更容易解释。</p>

<h3 id="初始化">初始化</h3>

<p>我们首先将整个脚本包装在匿名函数中,以避免使用全局变量,然后设置我们将要使用的各种变量。</p>

<pre class="brush: js">(function() {
  var width = 320;    // We will scale the photo width to this
  var height = 0;     // This will be computed based on the input stream

  var streaming = false;

  var video = null;
  var canvas = null;
  var photo = null;
  var startbutton = null;</pre>

<p>那些变量是:</p>

<dl>
 <dt><code><strong>width</strong></code></dt>
 <dd>无论输入视频的尺寸如何,我们将把所得到的图像缩放到 320 像素宽。</dd>
 <dt><code><strong>height</strong></code></dt>
 <dd>给定流的 <code>width</code> 和宽高比,计算出图像的输出高度。</dd>
 <dt><code><strong>streaming</strong></code></dt>
 <dd>指示当前是否有活动的视频流正在运行。</dd>
 <dt><code><strong>video</strong></code></dt>
 <dd>这将是页面加载完成后对 {{HTMLElement("video")}} 元素的引用。</dd>
 <dt><code><strong>canvas</strong></code></dt>
 <dd>这将是页面加载完成后对 {{HTMLElement("canvas")}} 元素的引用。</dd>
 <dt><code><strong>photo</strong></code></dt>
 <dd>这将在页面加载完成后引用 {{HTMLElement("img")}} 元素。</dd>
 <dt><code><strong>startbutton</strong></code></dt>
 <dd>这将引用用于触发捕获的 {{HTMLElement("button")}} 元素。 我们会在页面加载完成之后得到。</dd>
</dl>

<h3 id="startup_函数"><strong>startup( )函数</strong></h3>

<p>当页面加载完成时,<code>startup( )</code> 函数运行,由window.addEventListener( )提供。 此功能的作用是请求访问用户的网络摄像头,将输出&lt;img&gt;初始化为默认状态,并建立从相机接收每帧视频所需的事件侦听器,并在点击按钮捕获图像时作出反应 。</p>

<h4 id="获取元素引用">获取元素引用</h4>

<p>首先,我们参考我们需要访问的主要内容。</p>

<pre class="brush: js">  function startup() {
    video = document.getElementById('video');
    canvas = document.getElementById('canvas');
    photo = document.getElementById('photo');
    startbutton = document.getElementById('startbutton');</pre>

<h4 id="获取流媒体">获取流媒体</h4>

<p>接下来的任务是获取媒体流:</p>

<pre class="brush: js">    navigator.mediaDevices.getUserMedia({ video: true, audio: false })
    .then(function(stream) {
        video.srcObject = stream;
        video.play();
    })
    .catch(function(err) {
        console.log("An error occured! " + err);
    });
</pre>

<p>在这里,我们正在调用 {{domxref("MediaDevices.getUserMedia()")}}  并请求视频流(无音频)。 它返回一个 promise,我们给它附加成功和失败情况下的回调方法。</p>

<p>成功回调接收一个 stream 对象作为输入。它是新视频的 {{HTMLElement("video")}} 元素的源。</p>

<p>一旦流被链接到 <code>{{HTMLElement("video")}}</code> 元素,我们通过调用 <code><a href="https://wiki.developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#play">HTMLMediaElement.play()</a></code> 开始播放。</p>

<p>如果打开流失败,则调用失败回调函数。 在没有连接兼容的相机,或者用户拒绝访问时,则会发生这种情况。</p>

<h3 id="监听视频开始播放">监听视频开始播放</h3>

<p>在 <code>{{HTMLElement("video")}}</code> 上调用 <code><a href="https://wiki.developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#play">HTMLMediaElement.play()</a></code> 之后,在视频流开始流动之前,有一段(希望简短)的时间段过去了。 为了避免在此之前一直阻塞,我们为 <code>{{HTMLElement("video")}}</code> 加上一个 {{event("canplay")}} 事件的监听器,当视频播放实际开始时会触发该事件。 那时,视频对象中的所有属性都已基于流的格式进行配置。</p>

<pre class="brush: js">    video.addEventListener('canplay', function(ev){
      if (!streaming) {
        height = video.videoHeight / (video.videoWidth/width);

        video.setAttribute('width', width);
        video.setAttribute('height', height);
        canvas.setAttribute('width', width);
        canvas.setAttribute('height', height);
        streaming = true;
      }
    }, false);</pre>

<p>这个回调什么都不做,除非它是第一次被调用; 这是通过查看我们的流变量的值进行测试,这是第一次运行此方法时为false。</p>

<p>如果这是第一次运行,我们会根据视频的实际大小,video.videoWidth和要渲染视频宽度的宽度之间的大小差异来设置视频的高度。</p>

<p>最后,通过在每个元素的两个属性的每一个上调用Element.setAttribute()来设置视频和画布的宽度和高度,并根据需要设置宽度和高度。 最后,我们将流变量设置为true,以防止我们再次无意中运行此设置代码。</p>

<h4 id="处理按钮上的点击">处理按钮上的点击</h4>

<p>为了在每次用户单击启动按钮时捕获静态照片,我们需要向按钮添加一个事件侦听器,以便在发出点击事件时被调用:</p>

<h4 id="sect1"></h4>

<pre class="brush: js">    startbutton.addEventListener('click', function(ev){
      takepicture();
      ev.preventDefault();
    }, false);</pre>

<p>这个方法很简单:它只是调用我们的takepicture()函数,在从流中捕获一个帧的部分中定义,然后在接收的事件上调用Event.preventDefault(),以防止点击被多次处理。</p>

<p>包装startup()方法</p>

<p>startup()方法中只有两行代码:</p>

<pre class="brush: js">    clearphoto();
  }</pre>

<p>这就是我们在Clearflow()方法中,我们将在下面的清理照片框中进行描述。</p>

<h3 id="清除照片框">清除照片框</h3>

<p>清除照片框包括创建一个图像,然后将其转换为可以显示最近捕获的帧的&lt;img&gt;元素使用的格式。 该代码如下所示:</p>

<pre class="brush: js">  function clearphoto() {
    var context = canvas.getContext('2d');
    context.fillStyle = "#AAA";
    context.fillRect(0, 0, canvas.width, canvas.height);

    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  }</pre>

<p>我们首先得到对我们用于屏幕外渲染的隐藏的&lt;canvas&gt;元素的引用。 接下来,我们将fillStyle设置为#AAA(相当浅灰色),并通过调用fillRect()来填充整个画布。</p>

<p>最后在此功能中,我们将画布转换为PNG图像,并调用photo.setAttribute()以使我们捕获的静止框显示图像。</p>

<h3 id="从流中捕获帧">从流中捕获帧</h3>

<p>最后一个定义的功能是整个练习的重点:takepicture()函数,其捕获当前显示的视频帧的作业将其转换为PNG文件,并将其显示在捕获的帧框中。 代码如下所示:</p>

<pre class="brush: js">  function takepicture() {
    var context = canvas.getContext('2d');
    if (width &amp;&amp; height) {
      canvas.width = width;
      canvas.height = height;
      context.drawImage(video, 0, 0, width, height);

      var data = canvas.toDataURL('image/png');
      photo.setAttribute('src', data);
    } else {
      clearphoto();
    }
  }</pre>

<p>正如我们需要处理画布内容的情况一样,我们首先得到隐藏画布的2D绘图上下文。</p>

<p>然后,如果宽度和高度都是非零(意味着至少有潜在有效的图像数据),我们将画布的宽度和高度设置为与捕获帧的宽度和高度相匹配,然后调用drawImage()来绘制当前的 将视频帧放入上下文中,用帧图像填充整个画布。</p>

<div class="note">
<p><strong>注意:这可以利用HTMLVideoElement接口看起来像任何接受HTMLImageElement作为参数的API的HTMLImageElement,将视频的当前帧呈现为图像的内容。</strong></p>
</div>

<p>一旦画布包含捕获的图像,我们通过调用它的HTMLCanvasElement.toDataURL()将它转换为PNG 格式; 最后,我们调用photo.setAttribute()来使我们捕获的静态框显示图像。</p>

<p>如果没有可用的有效图像(即宽度和高度均为0),则通过调用clearphoto()清除捕获帧框的内容。</p>

<pre class="brush: js">// 加载完毕后开始运行
    window.addEventListener("load", startup, false);
})();</pre>

<h2 id="过滤器的乐趣">过滤器的乐趣</h2>

<p><br>
 由于我们通过从&lt;video&gt;元素中抓取帧来捕获用户网络摄像头的图像,因此我们可以非常轻松地将过滤器和有趣的效果应用于视频。 事实证明,使用过滤器属性应用于元素的任何CSS过滤器都会影响捕获的照片。 这些过滤器可以从简单(使图像黑白)到极限(高斯模糊和色调旋转)。</p>

<p>您可以使用例如Firefox开发人员工具的风格编辑器来播放此效果; 有关如何执行此操作的详细信息,请参阅编辑CSS过滤器。</p>

<h2 id="另请参阅">另请参阅</h2>

<ul>
 <li><a href="https://mdn-samples.mozilla.org/s/webrtc-capturestill">Try this sample</a></li>
 <li><a href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill">Sample code on Github</a></li>
 <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia" title="The Navigator.getUserMedia() method prompts the user for permission to use up to one video input device (such as a camera or shared screen) and up to one audio input device (such as a microphone). If permission is granted, a MediaStream whose video and/or audio tracks come from those devices is delivered to the specified success callback. If permission is denied, no compatible input devices exist, or any other error condition occurs, the error callback is executed with a MediaStreamError object describing what went wrong. If the user instead doesn't make a choice at all, neither callback is executed."><code>Navigator.getUserMedia()</code></a></li>
 <li>"<a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Using_frames_from_a_video">Using frames from a video</a>" in <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images">Using images</a></li>
 <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage" title="The CanvasRenderingContext2D.drawImage() method of the Canvas 2D API provides different ways to draw an image onto the canvas."><code>CanvasRenderingContext2D.drawImage()</code></a></li>
</ul>