-
Notifications
You must be signed in to change notification settings - Fork 0
/
getUserMedia.js
143 lines (131 loc) · 5.58 KB
/
getUserMedia.js
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
'use strict';
import {Platform, NativeModules} from 'react-native';
import * as RTCUtil from './RTCUtil';
import MediaStream from './MediaStream';
import MediaStreamError from './MediaStreamError';
import MediaStreamTrack from './MediaStreamTrack';
import withLegacyCallbacks from './withLegacyCallbacks';
const {WebRTCModule} = NativeModules;
// native side consume string eventually
const DEFAULT_VIDEO_CONSTRAINTS = {
mandatory: {
minWidth: '1280',
minHeight: '720',
minFrameRate: '30',
},
facingMode: "environment",
optional: [],
};
function getDefaultMediaConstraints(mediaType) {
return (mediaType === 'audio'
? true // no audio default constraint currently
: RTCUtil.mergeMediaConstraints(DEFAULT_VIDEO_CONSTRAINTS));
}
// this will make sure we have the correct constraint structure
// TODO: support width/height range and the latest param names according to spec
// media constraints param name should follow spec. then we need a function to convert these `js names`
// into the real `const name that native defined` on both iOS and Android.
// see mediaTrackConstraints: https://www.w3.org/TR/mediacapture-streams/#dom-mediatrackconstraints
function parseMediaConstraints(customConstraints, mediaType) {
return (mediaType === 'audio'
? RTCUtil.mergeMediaConstraints(customConstraints, {}) // no audio default constraint currently
: RTCUtil.mergeMediaConstraints(customConstraints, DEFAULT_VIDEO_CONSTRAINTS));
}
// this will make sure we have the correct value type
function normalizeMediaConstraints(constraints, mediaType) {
if (mediaType === 'audio') {
; // to be added
} else {
// NOTE: android only support minXXX currently
for (const param of ['minWidth', 'minHeight', 'minFrameRate', 'maxWidth', 'maxHeight', 'maxFrameRate', ]) {
if (constraints.mandatory.hasOwnProperty(param)) {
// convert to correct type here so that native can consume directly without worries.
constraints.mandatory[param] = (Platform.OS === 'ios'
? constraints.mandatory[param].toString() // ios consumes string
: parseInt(constraints.mandatory[param])); // android eats integer
}
}
}
return constraints;
}
export default withLegacyCallbacks(constraints => {
// According to
// https://www.w3.org/TR/mediacapture-streams/#dom-mediadevices-getusermedia,
// the constraints argument is a dictionary of type MediaStreamConstraints.
if (typeof constraints === 'object') {
// According to step 2 of the getUserMedia() algorithm, requestedMediaTypes
// is the set of media types in constraints with either a dictionary value
// or a value of "true".
let requestedMediaTypes = 0;
for (const mediaType of [ 'audio', 'video' ]) {
// According to the spec, the types of the audio and video members of
// MediaStreamConstraints are either boolean or MediaTrackConstraints
// (i.e. dictionary).
const mediaTypeConstraints = constraints[mediaType];
const typeofMediaTypeConstraints = typeof mediaTypeConstraints;
if (typeofMediaTypeConstraints !== 'undefined') {
if (typeofMediaTypeConstraints === 'boolean') {
if (mediaTypeConstraints) {
++requestedMediaTypes;
constraints[mediaType] = getDefaultMediaConstraints(mediaType);
}
} else if (typeofMediaTypeConstraints == 'object') {
// Note: object constraints for audio is not implemented in native side
++requestedMediaTypes;
constraints[mediaType] = parseMediaConstraints(constraints[mediaType], mediaType);
} else {
throw new TypeError('constraints.' + mediaType + ' is neither a boolean nor a dictionary');
}
// final check constraints and convert value to native accepted type
if (typeof constraints[mediaType] === 'object') {
constraints[mediaType] = normalizeMediaConstraints(constraints[mediaType], mediaType);
}
}
}
// According to step 3 of the getUserMedia() algorithm, if
// requestedMediaTypes is the empty set, the method invocation fails with
// a TypeError.
if (requestedMediaTypes === 0) {
throw new TypeError('constraints requests no media types');
}
} else {
throw new TypeError('constraints is not a dictionary');
}
return WebRTCModule.getUserMedia(constraints)
.then(([id, tracks]) => {
const stream = new MediaStream(id);
for (const track of tracks) {
stream.addTrack(new MediaStreamTrack(track));
}
return stream;
})
.catch(({ message, code }) => {
let error;
switch (code) {
case 'DOMException':
// According to
// https://www.w3.org/TR/mediacapture-streams/#idl-def-MediaStreamError,
// MediaStreamError is either a DOMException object or an
// OverconstrainedError object. We are very likely to not have a
// definition of DOMException on React Native (unless the client has
// provided such a definition). If necessary, we will fall back to our
// definition of MediaStreamError.
if (typeof DOMException === 'function') {
error = new DOMException(/* message */ undefined, /* name */ message);
}
break;
case 'OverconstrainedError':
if (typeof OverconstrainedError === 'function') {
error = new OverconstrainedError(/* constraint */ undefined, message);
}
break;
case 'TypeError':
error = new TypeError(message);
break;
}
if (!error) {
error = new MediaStreamError({ message, name: code });
}
throw error;
});
});