aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/mediadevices/getusermedia/index.html
blob: 519733950abd83154608821ea90dcc39f9399024 (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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
---
title: MediaDevices.getUserMedia()
slug: Web/API/MediaDevices/getUserMedia
translation_of: Web/API/MediaDevices/getUserMedia
---
<div>{{APIRef("WebRTC")}}</div>

<div>
<p><code><strong>MediaDevices.getUserMedia()</strong></code> 会提示用户给予使用媒体输入的许可,媒体输入会产生一个{{domxref("MediaStream")}},里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等),也可能是其它轨道类型。</p>

<p>它返回一个 {{jsxref("Promise")}} 对象,成功后会<code>resolve</code>回调一个 {{domxref("MediaStream")}} 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,<code>promise</code><code>reject</code>回调一个  <code>PermissionDeniedError</code> 或者 <code>NotFoundError</code> 。</p>
</div>

<div class="note">
<p>返回的promise对象可能既不会resolve也不会reject,因为用户不是必须选择允许或拒绝。</p>
</div>

<p>通常你可以使用 {{domxref("navigator.mediaDevices")}} 来获取 {{domxref("MediaDevices")}} ,例如:</p>

<pre class="brush: js notranslate">navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
  /* 使用这个stream stream */
})
.catch(function(err) {
  /* 处理error */
});</pre>

<h2 id="语法">语法</h2>

<pre class="brush: js notranslate">var <em>promise</em> = navigator.mediaDevices.getUserMedia(<em>constraints</em>);</pre>

<h3 id="参数">参数</h3>

<dl>
 <dt><code>constraints</code></dt>
 <dd>
 <p>作为一个{{domxref("MediaStreamConstraints")}} 对象,指定了请求的媒体类型和相对应的参数。</p>

 <p>constraints 参数是一个包含了<code>video</code><code>audio</code>两个成员<font face="Open Sans, Arial, sans-serif"></font><code>MediaStreamConstraints</code> 对象,用于说明请求的媒体类型。必须至少一个类型或者两个同时可以被指定。如果浏览器无法找到指定的媒体类型或者无法满足相对应的参数要求,那么返回的Promise对象就会处于rejected[失败]状态,<code>NotFoundError</code>作为rejected[失败]回调的参数。 </p>

 <p>以下同时请求不带任何参数的音频和视频:</p>

 <pre class="brush: js notranslate">{ audio: true, video: true }</pre>

 <p>如果为某种媒体类型设置了 <code>true</code> ,得到的结果的流中就需要有此种类型的轨道。如果其中一个由于某种原因无法获得,<code>getUserMedia()</code> 将会产生一个错误。</p>

 <p>当由于隐私保护的原因,无法访问用户的摄像头和麦克风信息时,应用可以使用额外的constraints参数请求它所需要或者想要的摄像头和麦克风能力。下面演示了应用想要使用1280x720的摄像头分辨率:</p>

 <pre class="brush: js notranslate">{
  audio: true,
  video: { width: 1280, height: 720 }
}</pre>

 <p id="successCallback">浏览器会试着满足这个请求参数,但是如果无法准确满足此请求中参数要求或者用户选择覆盖了请求中的参数时,有可能返回其它的分辨率。</p>

 <p>强制要求获取特定的尺寸时,可以使用关键字<code>min</code>, <code>max</code>, 或者 <code>exact</code>(就是 min == max). 以下参数表示要求获取最低为1280x720的分辨率。</p>

 <pre class="brush: js notranslate">{
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}</pre>

 <p>如果摄像头不支持请求的或者更高的分辨率,返回的Promise会处于rejected状态,<code>NotFoundError作为</code>rejected回调的参数,而且用户将不会得到要求授权的提示。</p>

 <p>造成不同表现的原因是,相对于简单的请求值和<code>ideal</code>关键字而言,关键字<code>min</code>, <code>max</code>, 和 <code>exact</code>有着内在关联的强制性,请看一个更详细的例子:</p>

 <pre class="brush: js notranslate">{
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 776, ideal: 720, max: 1080 }
  }
}</pre>

 <p>当请求包含一个ideal(应用最理想的)值时,这个值有着更高的权重,意味着浏览器会先尝试找到最接近指定的理想值的设定或者摄像头(如果设备拥有不止一个摄像头)。</p>

 <p>简单的请求值也可以理解为是应用理想的值,因此我们的第一个指定分辨率的请求也可以写成如下:</p>

 <pre class="brush: js notranslate">{
  audio: true,
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 }
  }
}</pre>

 <p>并不是所有的constraints 都是数字。例如, 在移动设备上面,如下的例子表示优先使用前置摄像头(如果有的话):</p>

 <pre class="brush: js notranslate">{ audio: true, video: { facingMode: "user" } }</pre>

 <p>强制使用后置摄像头,请用:</p>

 <pre class="brush: js notranslate">{ audio: true, video: { facingMode: { exact: "environment" } } }</pre>
 </dd>
</dl>

<h3 id="返回值">返回值</h3>

<p>返回一个 {{jsxref("Promise")}} , 这个Promise成功后的回调函数带一个 {{domxref("MediaStream")}} 对象作为其参数。</p>

<h3 id="异常">异常</h3>

<p>返回一个失败状态的Promise,这个Promise失败后的回调函数带一个{{domxref("DOMException")}}对象作为其参数。 可能的异常有:</p>

<dl>
 <dt><code>AbortError</code>[中止错误]</dt>
 <dd>尽管用户和操作系统都授予了访问设备硬件的权利,而且未出现可能抛出<code>NotReadableError</code>异常的硬件问题,但仍然有一些问题的出现导致了设备无法被使用。</dd>
 <dt><code>NotAllowedError</code>[拒绝错误]</dt>
 <dd>用户拒绝了当前的浏览器实例的访问请求;或者用户拒绝了当前会话的访问;或者用户在全局范围内拒绝了所有媒体访问请求。</dd>
 <dd>
 <div class="note">较旧版本的规范使用了<code>SecurityError</code>,但在新版本当中<code>SecurityError</code>被赋予了新的意义。</div>
 </dd>
 <dt><code>NotFoundError</code>[找不到错误]</dt>
 <dd>找不到满足请求参数的媒体类型。</dd>
 <dt><code>NotReadableError</code>[无法读取错误]</dt>
 <dd>尽管用户已经授权使用相应的设备,操作系统上某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问。</dd>
 <dt><code>OverconstrainedError</code>[无法满足要求错误]</dt>
 <dd>指定的要求无法被设备满足,此异常是一个类型为<code>OverconstrainedError</code>的对象,拥有一个<code>constraint</code>属性,这个属性包含了当前无法被满足的<code>constraint</code>对象,还拥有一个<code>message</code>属性,包含了阅读友好的字符串用来说明情况。</dd>
 <dd>
 <div class="note">因为这个异常甚至可以在用户尚未授权使用当前设备的情况下抛出,所以应当可以当作一个探测设备能力属性的手段[fingerprinting surface]。</div>
 </dd>
 <dt><code>SecurityError</code>[安全错误]</dt>
 <dd><code>getUserMedia()</code> 被调用的 {{domxref("Document")}} 上面,使用设备媒体被禁止。这个机制是否开启或者关闭取决于单个用户的偏好设置。</dd>
 <dt><code>TypeError</code>[类型错误]</dt>
 <dd>constraints对象未设置[空],或者都被设置为<code>false</code></dd>
</dl>

<h2 id="示例">示例</h2>

<h3 id="宽度和高度">宽度和高度</h3>

<p>这个例子设置了摄像头分辨率,并把结果的 {{domxref("MediaStream")}} 分配给了一个video元素。 </p>

<pre class="brush: js notranslate">// 想要获取一个最接近 1280x720 的相机分辨率
var constraints = { audio: true, video: { width: 1280, height: 720 } };

navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) {
  var video = document.querySelector('video');
  video.srcObject = mediaStream;
  video.onloadedmetadata = function(e) {
    video.play();
  };
})
.catch(function(err) { console.log(err.name + ": " + err.message); }); // 总是在最后检查错误</pre>

<h3 id="在旧的浏览器中使用新的API">在旧的浏览器中使用新的API</h3>

<p>这是一个使用 <code>navigator.mediaDevices.getUserMedia()</code>的例子,带一个polyfill以适应旧的浏览器。 要注意的是这个polyfill并不能修正一些约束语法上的遗留差异,这表示约束在某些浏览器上可能不会很好地运行。推荐使用处理了约束的 <a href="https://github.com/webrtc/adapter">adapter.js</a> polyfill 来替代。</p>

<pre class="brush: js notranslate">// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {
  navigator.mediaDevices = {};
}

// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
  navigator.mediaDevices.getUserMedia = function(constraints) {

    // 首先,如果有getUserMedia的话,就获得它
    var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

    // 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
    if (!getUserMedia) {
      return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
    }

    // 否则,为老的navigator.getUserMedia方法包裹一个Promise
    return new Promise(function(resolve, reject) {
      getUserMedia.call(navigator, constraints, resolve, reject);
    });
  }
}

navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(function(stream) {
  var video = document.querySelector('video');
  // 旧的浏览器可能没有srcObject
  if ("srcObject" in video) {
    video.srcObject = stream;
  } else {
    // 防止在新的浏览器里使用它,应为它已经不再支持了
    video.src = window.URL.createObjectURL(stream);
  }
  video.onloadedmetadata = function(e) {
    video.play();
  };
})
.catch(function(err) {
  console.log(err.name + ": " + err.message);
});
</pre>

<h3 id="帧率">帧率</h3>

<p>在某些情况下,比如WebRTC上使用受限带宽传输时,低帧率可能更适宜。</p>

<pre class="brush: js notranslate">var constraints = { video: { frameRate: { ideal: 10, max: 15 } } };
</pre>

<h3 id="前置或者后置摄像头">前置或者后置摄像头</h3>

<p>在移动设备(电话)上</p>

<pre class="brush: js notranslate">var front = false;
document.getElementById('flip-button').onclick = function() { front = !front; };

var constraints = { video: { facingMode: (front? "user" : "environment") } };
</pre>

<h2 id="权限">权限</h2>

<p>在一个可安装的app(如<a href="/en-US/Apps/Build/Building_apps_for_Firefox_OS/Firefox_OS_app_beginners_tutorial">Firefox OS app</a>)中使用 <code>getUserMedia()</code> ,你需要在声明文件中指定以下的权限:</p>

<pre class="brush: js notranslate">"permissions": {
  "audio-capture": {
    "description": "Required to capture audio using getUserMedia()"
  },
  "video-capture": {
    "description": "Required to capture video using getUserMedia()"
  }
}</pre>

<p>参见 <a href="/en-US/Apps/Developing/App_permissions#audio-capture">permission: audio-capture</a><a href="/en-US/Apps/Developing/App_permissions#video-capture">permission: video-capture</a> 来获取更多信息。</p>

<h2 id="规范">规范</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">规范</th>
   <th scope="col">状态</th>
   <th scope="col">备注</th>
  </tr>
  <tr>
   <td>{{SpecName('Media Capture', '#dom-mediadevices-getusermedia', 'MediaDevices.getUserMedia()')}}</td>
   <td>{{Spec2('Media Capture')}}</td>
   <td>初始定义</td>
  </tr>
 </tbody>
</table>

<h2 id="浏览器兼容">浏览器兼容</h2>

<div>{{Compat("api.MediaDevices.getUserMedia")}}</div>

<h2 id="参考">参考</h2>

<ul>
 <li>旧的 <a href="/en-US/docs/Web/API/Navigator/getUserMedia">navigator.getUserMedia</a> 遗留API</li>
 <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices">navigator.enumerateDevices</a> - 获取用户可用的设备类型和数量</li>
 <li><a href="/en-US/docs/WebRTC">WebRTC</a> - WebRTC API的介绍页面</li>
 <li><a href="/en-US/docs/WebRTC/MediaStream_API">MediaStream API</a> - 媒体流对象的API</li>
 <li><a href="/en-US/docs/WebRTC/taking_webcam_photos">Taking webcam photos</a> - 一个关于使用 <code>getUserMedia()</code> 拍照而非录视频的教程</li>
</ul>