|
@@ -24,7 +24,7 @@ import Speedy from 'speedy-vision';
|
24
|
24
|
import { SpeedyMedia } from 'speedy-vision/types/core/speedy-media';
|
25
|
25
|
import { SpeedyPromise } from 'speedy-vision/types/core/speedy-promise';
|
26
|
26
|
import { Utils, Nullable } from '../utils/utils';
|
27
|
|
-import { IllegalOperationError } from '../utils/errors';
|
|
27
|
+import { IllegalOperationError, NotSupportedError } from '../utils/errors';
|
28
|
28
|
import { Source } from './source';
|
29
|
29
|
|
30
|
30
|
|
|
@@ -94,7 +94,7 @@ export class VideoSource implements Source
|
94
|
94
|
*/
|
95
|
95
|
_init(): SpeedyPromise<void>
|
96
|
96
|
{
|
97
|
|
- this._handleBrowserPolicies(this._video);
|
|
97
|
+ this._handleBrowserQuirks(this._video);
|
98
|
98
|
|
99
|
99
|
return Speedy.load(this._video).then(media => {
|
100
|
100
|
Utils.log(`Source of data is a ${media.width}x${media.height} ${this._type}`);
|
|
@@ -117,11 +117,11 @@ export class VideoSource implements Source
|
117
|
117
|
}
|
118
|
118
|
|
119
|
119
|
/**
|
120
|
|
- * Handle browser-specific policies for <video> elements
|
|
120
|
+ * Handle browser-specific quirks for <video> elements
|
121
|
121
|
* @param video
|
122
|
122
|
* @internal
|
123
|
123
|
*/
|
124
|
|
- _handleBrowserPolicies(video: HTMLVideoElement): void
|
|
124
|
+ _handleBrowserQuirks(video: HTMLVideoElement): void
|
125
|
125
|
{
|
126
|
126
|
// WebKit <video> policies for iOS:
|
127
|
127
|
// https://webkit.org/blog/6784/new-video-policies-for-ios/
|
|
@@ -129,27 +129,11 @@ export class VideoSource implements Source
|
129
|
129
|
// required on iOS; nice to have in all browsers
|
130
|
130
|
video.setAttribute('playsinline', '');
|
131
|
131
|
|
132
|
|
- // autoplay videos should be muted
|
133
|
|
- if(video.autoplay) {
|
134
|
|
- video.muted = true;
|
135
|
|
- video.addEventListener('canplay', () => {
|
136
|
|
- const p = video.play();
|
137
|
|
- if(typeof p === 'object') {
|
138
|
|
- p.catch((error: DOMException) => {
|
139
|
|
- Utils.error(`Can't autoplay video!`, error, video);
|
140
|
|
-
|
141
|
|
- if(error.name == 'NotAllowedError') {
|
142
|
|
- Utils.warning('Tip: allow manual playback');
|
143
|
|
-
|
144
|
|
- if(Utils.isIOS())
|
145
|
|
- Utils.warning('Is low power mode on?');
|
146
|
|
- }
|
147
|
|
- });
|
148
|
|
- }
|
149
|
|
- });
|
150
|
|
- }
|
|
132
|
+ // handle autoplay
|
|
133
|
+ if(video.autoplay)
|
|
134
|
+ this._handleAutoPlay(video);
|
151
|
135
|
|
152
|
|
- // Handle WebKit policies
|
|
136
|
+ // Handle WebKit quirks
|
153
|
137
|
// note: navigator.vendor is deprecated. Alternatively, test GL_RENDERER == "Apple GPU"
|
154
|
138
|
if(Utils.isIOS() || /Apple/.test(navigator.vendor)) {
|
155
|
139
|
|
|
@@ -162,4 +146,55 @@ export class VideoSource implements Source
|
162
|
146
|
|
163
|
147
|
}
|
164
|
148
|
}
|
|
149
|
+
|
|
150
|
+ /**
|
|
151
|
+ * Handle browser-specific quirks for videos marked with autoplay
|
|
152
|
+ * @param video a <video> marked with autoplay
|
|
153
|
+ * @internal
|
|
154
|
+ */
|
|
155
|
+ _handleAutoPlay(video: HTMLVideoElement): void
|
|
156
|
+ {
|
|
157
|
+ Utils.assert(video.autoplay);
|
|
158
|
+
|
|
159
|
+ // videos marked with autoplay should be muted
|
|
160
|
+ video.muted = true;
|
|
161
|
+
|
|
162
|
+ // the browser may not honor the autoplay attribute if the video is not
|
|
163
|
+ // visible on-screen. So, let's try to play the video in any case.
|
|
164
|
+ video.addEventListener('canplay', () => {
|
|
165
|
+ const promise = video.play();
|
|
166
|
+
|
|
167
|
+ // handle older browsers
|
|
168
|
+ if(typeof promise !== 'object')
|
|
169
|
+ return;
|
|
170
|
+
|
|
171
|
+ // can't play the video
|
|
172
|
+ promise.catch((error: DOMException) => {
|
|
173
|
+ Utils.error(`Can't autoplay video!`, error, video);
|
|
174
|
+
|
|
175
|
+ // autoplay is blocked for some reason
|
|
176
|
+ if(error.name == 'NotAllowedError') {
|
|
177
|
+ Utils.warning('Tip: allow manual playback');
|
|
178
|
+
|
|
179
|
+ if(Utils.isIOS())
|
|
180
|
+ Utils.warning('Is low power mode on?');
|
|
181
|
+
|
|
182
|
+ // User interaction is required to play the video. We can
|
|
183
|
+ // solve this here (easy and convenient to do) or at the
|
|
184
|
+ // application layer (for a better user experience). If the
|
|
185
|
+ // latter is preferred, just disable autoplay and play the
|
|
186
|
+ // video programatically.
|
|
187
|
+ if(video.hidden || !video.controls || video.parentNode === null) {
|
|
188
|
+ // this is added for convenience
|
|
189
|
+ document.body.addEventListener('pointerdown', () => video.play());
|
|
190
|
+ alert('Tap on the screen to start');
|
|
191
|
+ }
|
|
192
|
+ }
|
|
193
|
+
|
|
194
|
+ // unsupported media source
|
|
195
|
+ else if(error.name == 'NotSupportedError')
|
|
196
|
+ throw new NotSupportedError('Unsupported video format', error);
|
|
197
|
+ });
|
|
198
|
+ });
|
|
199
|
+ }
|
165
|
200
|
}
|