Browse Source

Change the default resolution to 360p. Request native resolutions by default

customisations
alemart 3 months ago
parent
commit
437c7e7c89
1 changed files with 43 additions and 20 deletions
  1. 43
    20
      src/sources/camera-source.ts

+ 43
- 20
src/sources/camera-source.ts View File

@@ -21,11 +21,10 @@
21 21
  */
22 22
 
23 23
 import Speedy from 'speedy-vision';
24
-import { SpeedyMedia } from 'speedy-vision/types/core/speedy-media';
25 24
 import { SpeedyPromise } from 'speedy-vision/types/core/speedy-promise';
26 25
 import { Utils } from '../utils/utils';
27 26
 import { Resolution } from '../utils/resolution';
28
-import { NotSupportedError, AccessDeniedError, IllegalOperationError } from '../utils/errors';
27
+import { NotSupportedError, AccessDeniedError, IllegalOperationError, IllegalArgumentError } from '../utils/errors';
29 28
 import { VideoSource } from './video-source';
30 29
 
31 30
 
@@ -46,7 +45,14 @@ export interface CameraSourceOptions
46 45
 
47 46
 /** Default options for camera sources */
48 47
 const DEFAULT_CAMERA_OPTIONS: Readonly<Required<CameraSourceOptions>> = {
49
-    resolution: 'md',
48
+    /*
49
+
50
+    we use well-known standards in landscape mode to ensure broad compatibility
51
+    the spec encourages User Agents to make landscape the primary orientation
52
+    https://w3c.github.io/mediacapture-main/#dfn-primary-orientation
53
+
54
+    */
55
+    resolution: '360p',
50 56
     aspectRatio: 16/9,
51 57
     constraints: { facingMode: 'environment' },
52 58
 };
@@ -90,28 +96,41 @@ export class CameraSource extends VideoSource
90 96
      */
91 97
     _init(): SpeedyPromise<void>
92 98
     {
99
+        const options = this._options;
100
+
93 101
         Utils.log('Accessing the webcam...');
94 102
 
95 103
         // validate
96 104
         if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia)
97 105
             throw new NotSupportedError('Unsupported browser: no navigator.mediaDevices.getUserMedia()');
98 106
 
107
+        // for best compatibility, always request landscape resolutions,
108
+        // even when mobile devices are expected to be in portrait mode
109
+        if(options.aspectRatio < 1) {
110
+            if(options.aspectRatio > 0)
111
+                Utils.warning(`CameraSource: an aspectRatio of ${options.aspectRatio} was requested. Prefer standard landscape settings instead`);
112
+            else
113
+                throw new IllegalArgumentError(`Invalid aspect ratio: ${options.aspectRatio}`);
114
+        }
115
+
99 116
         // set up media constraints
100
-        const options = this._options;
101
-        const size = Utils.resolution(options.resolution, options.aspectRatio);
117
+        const idealSize = Utils.resolution(options.resolution, options.aspectRatio);
118
+        const userConstraints = options.constraints;
119
+        const ourConstraints/*: MediaTrackConstraints*/ = {
120
+            width: { ideal: idealSize.width },
121
+            height: { ideal: idealSize.height },
122
+            resizeMode: 'none' // request native resolution to encourage usage of standard resolutions
123
+                               // users can opt-in to 'crop-and-scale' if they so desire
124
+        };
102 125
         const constraints: MediaStreamConstraints = {
103 126
             audio: false,
104
-            video: {
105
-                width: size.width,
106
-                height: size.height,
107
-                aspectRatio: options.aspectRatio,
108
-                ...options.constraints,
109
-            }
127
+            video: Object.assign({}, ourConstraints, userConstraints)
110 128
         };
111 129
 
112 130
         // load camera stream
113
-        return new Speedy.Promise<HTMLVideoElement>((resolve, reject) => {
114
-            navigator.mediaDevices.getUserMedia(constraints).then(stream => {
131
+        return new Speedy.Promise<void>((resolve, reject) => {
132
+            navigator.mediaDevices.getUserMedia(constraints)
133
+            .then(stream => {
115 134
                 const video = this.video;
116 135
                 video.onloadedmetadata = () => {
117 136
                     const promise = video.play();
@@ -120,14 +139,14 @@ export class CameraSource extends VideoSource
120 139
                     // handle older browsers
121 140
                     if(promise === undefined) {
122 141
                         Utils.log(success);
123
-                        resolve(video);
142
+                        resolve();
124 143
                         return;
125 144
                     }
126 145
 
127 146
                     // handle promise
128 147
                     promise.then(() => {
129 148
                         Utils.log(success);
130
-                        resolve(video);
149
+                        resolve();
131 150
                     }).catch(error => {
132 151
                         reject(new IllegalOperationError(
133 152
                             'Webcam error!',
@@ -146,13 +165,15 @@ export class CameraSource extends VideoSource
146 165
                 video.autoplay = true;
147 166
 
148 167
                 video.srcObject = stream;
149
-            }).catch(error => {
168
+            })
169
+            .catch(error => {
150 170
                 reject(new AccessDeniedError(
151 171
                     'Please give access to the webcam and reload the page.',
152 172
                     error
153 173
                 ));
154 174
             });
155
-        }).then(_ => super._init()); // this will handle browser quirks
175
+        })
176
+        .then(() => super._init()); // this will handle browser quirks
156 177
     }
157 178
 
158 179
     /**
@@ -163,15 +184,17 @@ export class CameraSource extends VideoSource
163 184
     _release(): SpeedyPromise<void>
164 185
     {
165 186
         const video = this.video;
187
+
188
+        // stop the camera feed
166 189
         const stream = video.srcObject as MediaStream;
167 190
         const tracks = stream.getTracks();
168
-
169
-        // stop camera feed
170 191
         tracks.forEach(track => track.stop());
192
+
193
+        // release references
171 194
         video.onloadedmetadata = null;
172 195
         video.srcObject = null;
173 196
 
174 197
         // release the media
175 198
         return super._release();
176 199
     }
177
-}
200
+}

Loading…
Cancel
Save