@nativescript/canvas-media 3.0.0-alpha.9 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ import { VideoFrameBase, VideoColorSpace, DOMRectReadOnly, VideoFrameInit, VideoFrameBufferInit, VideoFrameCopyToOptions, PlaneLayout, RectInit, VideoPixelFormat, VideoColorSpaceInit, _INTERNAL } from './common';
2
+ export { VideoColorSpace, DOMRectReadOnly, _INTERNAL };
3
+ export type { VideoPixelFormat, VideoFrameInit, VideoFrameBufferInit, VideoFrameCopyToOptions, PlaneLayout, RectInit, VideoColorSpaceInit };
4
+ export declare class VideoFrame extends VideoFrameBase {
5
+ constructor(source: ArrayBuffer | ArrayBufferView, init: VideoFrameBufferInit);
6
+ constructor(source: object, init?: VideoFrameInit);
7
+ static fromVideo(source: any, init?: VideoFrameInit): VideoFrame | null;
8
+ private static _dataFromBuffer;
9
+ private static _dataFromVideoSource;
10
+ private static _assertBufferInit;
11
+ protected _doClone(): VideoFrame;
12
+ }
@@ -0,0 +1,98 @@
1
+ import { VideoFrameBase, VideoColorSpace, DOMRectReadOnly, _INTERNAL, bgraToRgba } from './common';
2
+ export { VideoColorSpace, DOMRectReadOnly, _INTERNAL };
3
+ export class VideoFrame extends VideoFrameBase {
4
+ constructor(source, init) {
5
+ if (source === _INTERNAL) {
6
+ super(init);
7
+ return;
8
+ }
9
+ if (source instanceof ArrayBuffer || ArrayBuffer.isView(source)) {
10
+ super(VideoFrame._dataFromBuffer(source, init));
11
+ return;
12
+ }
13
+ super(VideoFrame._dataFromVideoSource(source, init));
14
+ }
15
+ static fromVideo(source, init) {
16
+ try {
17
+ const data = VideoFrame._dataFromVideoSource(source, init);
18
+ // Return null when no pixel data is available yet (no frame decoded).
19
+ // Callers can treat null as "not ready" and fall back to drawImage.
20
+ if (!data.pixelData)
21
+ return null;
22
+ return new VideoFrame(_INTERNAL, data);
23
+ }
24
+ catch (err) {
25
+ console.error('[VideoFrame] fromVideo:', err?.message ?? err);
26
+ return null;
27
+ }
28
+ }
29
+ static _dataFromBuffer(source, init) {
30
+ VideoFrame._assertBufferInit(init);
31
+ const buf = ArrayBuffer.isView(source) ? new Uint8Array(source.buffer, source.byteOffset, source.byteLength) : new Uint8Array(source);
32
+ const pixelData = init.format === 'BGRA' || init.format === 'BGRX' ? bgraToRgba(buf) : new Uint8Array(buf);
33
+ return {
34
+ pixelData,
35
+ codedWidth: init.codedWidth,
36
+ codedHeight: init.codedHeight,
37
+ displayWidth: init.displayWidth ?? init.codedWidth,
38
+ displayHeight: init.displayHeight ?? init.codedHeight,
39
+ timestamp: init.timestamp,
40
+ duration: init.duration ?? null,
41
+ format: 'RGBA',
42
+ colorSpace: init.colorSpace,
43
+ };
44
+ }
45
+ static _dataFromVideoSource(source, init) {
46
+ const video = source?._video ?? source;
47
+ if (typeof video?._currentFrame !== 'function') {
48
+ throw new TypeError('[VideoFrame] Source must be an HTMLVideoElement or a canvas-media Video instance');
49
+ }
50
+ const timestampUs = typeof init?.timestamp === 'number' ? init.timestamp : Math.round(video.currentTime * 1000000);
51
+ // A copy: the video hands the same buffer to every reader until its next frame.
52
+ const frame = video._currentFrame();
53
+ const pixelData = frame ? new Uint8Array(frame.pixels) : null;
54
+ const codedWidth = frame?.width ?? video.videoWidth ?? 0;
55
+ const codedHeight = frame?.height ?? video.videoHeight ?? 0;
56
+ return {
57
+ pixelData,
58
+ codedWidth,
59
+ codedHeight,
60
+ displayWidth: init?.displayWidth ?? codedWidth,
61
+ displayHeight: init?.displayHeight ?? codedHeight,
62
+ timestamp: timestampUs,
63
+ duration: init?.duration ?? null,
64
+ format: 'RGBA',
65
+ colorSpace: init?.colorSpace,
66
+ };
67
+ }
68
+ static _assertBufferInit(init) {
69
+ if (!init?.format) {
70
+ throw new TypeError('VideoFrame: init.format is required for the buffer constructor');
71
+ }
72
+ if (!init?.codedWidth || init.codedWidth <= 0) {
73
+ throw new TypeError('VideoFrame: init.codedWidth must be a positive integer');
74
+ }
75
+ if (!init?.codedHeight || init.codedHeight <= 0) {
76
+ throw new TypeError('VideoFrame: init.codedHeight must be a positive integer');
77
+ }
78
+ if (typeof init?.timestamp !== 'number' || !isFinite(init.timestamp)) {
79
+ throw new TypeError('VideoFrame: init.timestamp must be a finite number (microseconds)');
80
+ }
81
+ }
82
+ _doClone() {
83
+ const pixelCopy = this._pixelData ? new Uint8Array(this._pixelData) : null;
84
+ const data = {
85
+ pixelData: pixelCopy,
86
+ codedWidth: this.codedWidth,
87
+ codedHeight: this.codedHeight,
88
+ displayWidth: this.displayWidth,
89
+ displayHeight: this.displayHeight,
90
+ timestamp: this.timestamp,
91
+ duration: this.duration,
92
+ format: this.format,
93
+ colorSpace: this.colorSpace.toJSON(),
94
+ };
95
+ return new VideoFrame(_INTERNAL, data);
96
+ }
97
+ }
98
+ //# sourceMappingURL=index.windows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.windows.js","sourceRoot":"","sources":["../../../../packages/canvas-media/VideoFrame/index.windows.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { AudioBase } from './common';
2
+ import { Source } from '../source';
3
+ import { WindowsMediaPlayer } from '../player-windows';
4
+ export declare class Audio extends AudioBase {
5
+ _media: WindowsMediaPlayer;
6
+ _sourceView: Source[];
7
+ private _sources;
8
+ _isCustom: boolean;
9
+ private _controls;
10
+ private _grid;
11
+ private _element;
12
+ constructor();
13
+ static createCustomView(): Audio;
14
+ get _player(): any;
15
+ createNativeView(): any;
16
+ disposeNativeView(): void;
17
+ private _syncControls;
18
+ get readyState(): number;
19
+ canPlayType(type: string): "" | "maybe";
20
+ _addChildFromBuilder(name: string, value: any): void;
21
+ onLoaded(): void;
22
+ _onLoadError(): boolean;
23
+ get duration(): number;
24
+ get currentTime(): number;
25
+ set currentTime(value: number);
26
+ get muted(): boolean;
27
+ set muted(value: boolean);
28
+ get volume(): number;
29
+ set volume(value: number);
30
+ get paused(): boolean;
31
+ get src(): string;
32
+ set src(value: string);
33
+ load(): void;
34
+ play(): Promise<void>;
35
+ pause(): void;
36
+ get controls(): boolean;
37
+ set controls(value: boolean);
38
+ get autoplay(): boolean;
39
+ set autoplay(value: boolean);
40
+ get loop(): boolean;
41
+ set loop(value: boolean);
42
+ attachAudioContextTap(): any;
43
+ detachAudioContextTap(): void;
44
+ }
@@ -0,0 +1,158 @@
1
+ import { booleanConverter } from '@nativescript/core';
2
+ import { AudioBase } from './common';
3
+ import { Source } from '../source';
4
+ import { WindowsMediaPlayer, canPlayType } from '../player-windows';
5
+ export class Audio extends AudioBase {
6
+ constructor() {
7
+ super();
8
+ this._sourceView = [];
9
+ this._sources = [];
10
+ this._isCustom = false;
11
+ this._controls = false;
12
+ this._media = new WindowsMediaPlayer(this, false);
13
+ }
14
+ static createCustomView() {
15
+ const audio = new Audio();
16
+ audio._isCustom = true;
17
+ audio.width = 300;
18
+ audio.height = 40;
19
+ return audio;
20
+ }
21
+ get _player() {
22
+ return this._media.player;
23
+ }
24
+ createNativeView() {
25
+ this._grid = new Microsoft.UI.Xaml.Controls.Grid();
26
+ this._syncControls();
27
+ return this._grid;
28
+ }
29
+ disposeNativeView() {
30
+ try {
31
+ this._element?.SetMediaPlayer(null);
32
+ }
33
+ catch (e) { }
34
+ this._grid = this._element = null;
35
+ super.disposeNativeView();
36
+ }
37
+ _syncControls() {
38
+ const grid = this._grid;
39
+ if (!grid) {
40
+ return;
41
+ }
42
+ if (this._controls && !this._element) {
43
+ const element = new Microsoft.UI.Xaml.Controls.MediaPlayerElement();
44
+ element.SetMediaPlayer(this._player);
45
+ element.AreTransportControlsEnabled = true;
46
+ try {
47
+ const controls = element.TransportControls;
48
+ controls.IsCompact = true;
49
+ controls.IsFullWindowButtonVisible = false;
50
+ controls.IsZoomButtonVisible = false;
51
+ }
52
+ catch (e) { }
53
+ grid.Children.Append(element);
54
+ this._element = element;
55
+ }
56
+ else if (!this._controls && this._element) {
57
+ const element = this._element;
58
+ this._element = null;
59
+ try {
60
+ element.SetMediaPlayer(null);
61
+ grid.Children.Clear();
62
+ }
63
+ catch (e) { }
64
+ }
65
+ }
66
+ get readyState() {
67
+ return this._media.readyState;
68
+ }
69
+ canPlayType(type) {
70
+ return canPlayType(type);
71
+ }
72
+ _addChildFromBuilder(name, value) {
73
+ if (value instanceof Source) {
74
+ this._sourceView.push(value);
75
+ }
76
+ }
77
+ onLoaded() {
78
+ super.onLoaded();
79
+ if (!this._media.src) {
80
+ this._sources = this._sourceView.filter((item) => item.src && (!item.type || canPlayType(item.type) !== '')).map((item) => item.src);
81
+ this._onLoadError();
82
+ }
83
+ }
84
+ _onLoadError() {
85
+ const next = this._sources.shift();
86
+ if (next === undefined) {
87
+ return false;
88
+ }
89
+ this._media.src = next;
90
+ return true;
91
+ }
92
+ get duration() {
93
+ return this._media.duration;
94
+ }
95
+ get currentTime() {
96
+ return this._media.currentTime;
97
+ }
98
+ set currentTime(value) {
99
+ this._media.currentTime = value;
100
+ }
101
+ get muted() {
102
+ return this._media.muted;
103
+ }
104
+ set muted(value) {
105
+ this._media.muted = booleanConverter(value);
106
+ }
107
+ get volume() {
108
+ return this._media.volume;
109
+ }
110
+ set volume(value) {
111
+ this._media.volume = value;
112
+ }
113
+ get paused() {
114
+ return this._media.paused;
115
+ }
116
+ get src() {
117
+ return this._media.src;
118
+ }
119
+ set src(value) {
120
+ this._sources = [];
121
+ this._media.src = value;
122
+ }
123
+ load() {
124
+ this._media.load();
125
+ }
126
+ play() {
127
+ return this._media.play();
128
+ }
129
+ pause() {
130
+ this._media.pause();
131
+ }
132
+ get controls() {
133
+ return this._controls;
134
+ }
135
+ set controls(value) {
136
+ this._controls = booleanConverter(value);
137
+ this._syncControls();
138
+ }
139
+ get autoplay() {
140
+ return this._media.autoplay;
141
+ }
142
+ set autoplay(value) {
143
+ this._media.autoplay = booleanConverter(value);
144
+ }
145
+ get loop() {
146
+ return this._media.loop;
147
+ }
148
+ set loop(value) {
149
+ this._media.loop = booleanConverter(value);
150
+ }
151
+ attachAudioContextTap() {
152
+ return this._media.attachAudioTap();
153
+ }
154
+ detachAudioContextTap() {
155
+ this._media.detachAudioTap();
156
+ }
157
+ }
158
+ //# sourceMappingURL=index.windows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.windows.js","sourceRoot":"","sources":["../../../../packages/canvas-media/audio/index.windows.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@nativescript/canvas-media",
3
- "version": "3.0.0-alpha.9",
3
+ "version": "3.0.0-beta.2",
4
4
  "description": "Canvas media",
5
5
  "main": "index",
6
6
  "typings": "index.d.ts",
7
7
  "nativescript": {
8
8
  "platforms": {
9
9
  "ios": "6.0.0",
10
- "android": "6.0.0"
10
+ "android": "6.0.0",
11
+ "windows": "9.1.0"
11
12
  }
12
13
  },
13
14
  "repository": {
@@ -19,7 +20,8 @@
19
20
  "JavaScript",
20
21
  "TypeScript",
21
22
  "iOS",
22
- "Android"
23
+ "Android",
24
+ "Windows"
23
25
  ],
24
26
  "author": {
25
27
  "name": "NativeScript",
Binary file
@@ -751,9 +751,10 @@ public class VideoHelper implements Player.Listener, SurfaceTexture.OnFrameAvail
751
751
  try {
752
752
  // PRIVATE, not RGBA_8888: a hardware decoder refuses to convert and fails with
753
753
  // "producer output buffer format ... doesn't match the ImageReader's".
754
+ // A threaded 2D canvas holds a frame until the GPU has read it.
754
755
  _imageReader = ImageReader.newInstance(
755
756
  size[0], size[1],
756
- ImageFormat.PRIVATE, 2,
757
+ ImageFormat.PRIVATE, 3,
757
758
  android.hardware.HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
758
759
  _imageReader.setOnImageAvailableListener(reader -> {
759
760
  _hasFrame = true;
@@ -785,6 +786,11 @@ public class VideoHelper implements Player.Listener, SurfaceTexture.OnFrameAvail
785
786
  }
786
787
 
787
788
  public GPUFrame getCurrentGPUFrame() {
789
+ return getCurrentGPUFrame(true);
790
+ }
791
+
792
+ /** @param requireImportable RGB only, which Vulkan samples directly; GL takes YCbCr too. */
793
+ public GPUFrame getCurrentGPUFrame(boolean requireImportable) {
788
794
  if (!supportsGPUFrames()) return null;
789
795
 
790
796
  if (_imageReader == null && _surfaceOwner == SurfaceOwner.NONE) {
@@ -804,7 +810,7 @@ public class VideoHelper implements Player.Listener, SurfaceTexture.OnFrameAvail
804
810
  return null;
805
811
  }
806
812
 
807
- if (!isImportableFormat(buffer.getFormat())) {
813
+ if (requireImportable && !isImportableFormat(buffer.getFormat())) {
808
814
  // Give up for this player and hand the surface back to the upload path.
809
815
  android.util.Log.d("JS", "getCurrentGPUFrame: decoder format "
810
816
  + buffer.getFormat() + " cannot be imported; using the upload path");
@@ -1029,9 +1035,13 @@ public class VideoHelper implements Player.Listener, SurfaceTexture.OnFrameAvail
1029
1035
  // On some devices onVideoSizeChanged only fires after the first rendered frame,
1030
1036
  // so we must give ExoPlayer a surface first to break the deadlock.
1031
1037
  if (backendType == 2) ensureGL2DSurface(context);
1038
+ if (backendType == HARDWARE_BUFFER_2D && _imageReader == null && _surfaceOwner == SurfaceOwner.NONE) setupGpuSurface();
1032
1039
  float w = (float) _videoWidth;
1033
1040
  float h = (float) _videoHeight;
1034
1041
  if (w <= 0 || h <= 0) return false;
1042
+ if (backendType == HARDWARE_BUFFER_2D) {
1043
+ return drawFrame2DHardwareBuffer(context, 0, 0, w, h, dx, dy, w, h);
1044
+ }
1035
1045
  if (backendType == 2) {
1036
1046
  return drawFrame2DGL(context, 0, 0, w, h, dx, dy, w, h);
1037
1047
  }
@@ -1041,9 +1051,13 @@ public class VideoHelper implements Player.Listener, SurfaceTexture.OnFrameAvail
1041
1051
  public boolean drawVideoFrame2D(int backendType, long context, float dx, float dy, float dw, float dh) {
1042
1052
  if (context == 0) return false;
1043
1053
  if (backendType == 2) ensureGL2DSurface(context);
1054
+ if (backendType == HARDWARE_BUFFER_2D && _imageReader == null && _surfaceOwner == SurfaceOwner.NONE) setupGpuSurface();
1044
1055
  float w = (float) _videoWidth;
1045
1056
  float h = (float) _videoHeight;
1046
1057
  if (w <= 0 || h <= 0) return false;
1058
+ if (backendType == HARDWARE_BUFFER_2D) {
1059
+ return drawFrame2DHardwareBuffer(context, 0, 0, w, h, dx, dy, dw, dh);
1060
+ }
1047
1061
  if (backendType == 2) {
1048
1062
  return drawFrame2DGL(context, 0, 0, w, h, dx, dy, dw, dh);
1049
1063
  }
@@ -1053,7 +1067,11 @@ public class VideoHelper implements Player.Listener, SurfaceTexture.OnFrameAvail
1053
1067
  public boolean drawVideoFrame2D(int backendType, long context, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) {
1054
1068
  if (context == 0) return false;
1055
1069
  if (backendType == 2) ensureGL2DSurface(context);
1070
+ if (backendType == HARDWARE_BUFFER_2D && _imageReader == null && _surfaceOwner == SurfaceOwner.NONE) setupGpuSurface();
1056
1071
  if (_videoWidth <= 0 || _videoHeight <= 0) return false;
1072
+ if (backendType == HARDWARE_BUFFER_2D) {
1073
+ return drawFrame2DHardwareBuffer(context, sx, sy, sw, sh, dx, dy, dw, dh);
1074
+ }
1057
1075
  if (backendType == 2) {
1058
1076
  return drawFrame2DGL(context, sx, sy, sw, sh, dx, dy, dw, dh);
1059
1077
  }
@@ -1061,6 +1079,52 @@ public class VideoHelper implements Player.Listener, SurfaceTexture.OnFrameAvail
1061
1079
  }
1062
1080
 
1063
1081
 
1082
+ /** A threaded 2D canvas, which rasterizes off this thread. */
1083
+ public static final int HARDWARE_BUFFER_2D = 5;
1084
+
1085
+ private static Method _drawHardwareBuffer;
1086
+ private static boolean _drawHardwareBufferResolved = false;
1087
+
1088
+ private static Method drawHardwareBufferMethod() {
1089
+ if (_drawHardwareBufferResolved) return _drawHardwareBuffer;
1090
+ _drawHardwareBufferResolved = true;
1091
+ try {
1092
+ Class<?> utils = Class.forName("org.nativescript.canvas.Utils");
1093
+ _drawHardwareBuffer = utils.getMethod("drawVideoFrameHardwareBuffer",
1094
+ long.class, android.hardware.HardwareBuffer.class, Object.class, int.class, int.class,
1095
+ float.class, float.class, float.class, float.class,
1096
+ float.class, float.class, float.class, float.class);
1097
+ } catch (Throwable t) {
1098
+ if (IS_DEBUG) android.util.Log.d("JS", "drawVideoFrameHardwareBuffer unavailable: " + t);
1099
+ _drawHardwareBuffer = null;
1100
+ }
1101
+ return _drawHardwareBuffer;
1102
+ }
1103
+
1104
+ private boolean drawFrame2DHardwareBuffer(long context, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) {
1105
+ Method draw = drawHardwareBufferMethod();
1106
+ if (draw == null || !supportsGPUFrames()) {
1107
+ return drawFrame2DBitmap(context, sx, sy, sw, sh, dx, dy, dw, dh);
1108
+ }
1109
+ GPUFrame frame = getCurrentGPUFrame(false);
1110
+ if (frame == null) {
1111
+ // No new frame, or the GPU path gave up and handed the surface back.
1112
+ return _surfaceOwner == SurfaceOwner.GPU ? false : drawFrame2DBitmap(context, sx, sy, sw, sh, dx, dy, dw, dh);
1113
+ }
1114
+ boolean taken = false;
1115
+ try {
1116
+ Object result = draw.invoke(null, context, frame._buffer, frame, frame.getWidth(), frame.getHeight(),
1117
+ sx, sy, sw, sh, dx, dy, dw, dh);
1118
+ taken = result instanceof Boolean && (Boolean) result;
1119
+ } catch (Throwable t) {
1120
+ if (IS_DEBUG) android.util.Log.d("JS", "drawFrame2DHardwareBuffer: " + t);
1121
+ }
1122
+ if (!taken) {
1123
+ frame.close();
1124
+ }
1125
+ return taken;
1126
+ }
1127
+
1064
1128
  private void ensureGL2DSurface(long context) {
1065
1129
  if (_glInitFailed || _glSt != null) return;
1066
1130
  if (IS_DEBUG) android.util.Log.d("JS", "ensureGL2DSurface: context=" + context);
@@ -23,4 +23,8 @@
23
23
  @property (nonatomic, assign) BOOL inForeground;
24
24
  @property (nonatomic, assign) BOOL isLoop;
25
25
  @property (nonatomic, assign) BOOL loadedDataFired;
26
+ @property (nonatomic, assign) BOOL firstFrameReady;
27
+ @property (nonatomic, assign) BOOL waitingForFirstFrame;
28
+ /// Bumped per first-frame wait so a stale wait stops.
29
+ @property (nonatomic, assign) NSUInteger frameWaitGeneration;
26
30
  @end
@@ -30,6 +30,8 @@
30
30
  @property (nonatomic, readonly) BOOL isInForeground;
31
31
  @property (nonatomic, readonly) NSCPlayerState state;
32
32
  @property (nonatomic, readonly) NSCPlayerReadyState readyState;
33
+ /// A decoded frame exists for the current source.
34
+ @property (nonatomic, readonly) BOOL firstFrameReady;
33
35
  @property (nonatomic, readonly) CGSize videoSize;
34
36
  @property (nonatomic, strong, readonly) AVPlayerItemVideoOutput *assetOutput;
35
37
  @property (nonatomic, weak) id<NSCVideoHelperListener> listener;
@@ -81,6 +81,7 @@ static NSArray<AVAssetTrack *> *NSCTracksWithMediaType(AVAsset *asset, AVMediaTy
81
81
  _currentSrc = src;
82
82
  _readyState = NSCPlayerReadyStateHaveNothing;
83
83
  self.loadedDataFired = NO;
84
+ self.firstFrameReady = NO;
84
85
 
85
86
  if (!src) return;
86
87
 
@@ -191,6 +192,8 @@ static NSArray<AVAssetTrack *> *NSCTracksWithMediaType(AVAsset *asset, AVMediaTy
191
192
  self->_videoSize = [NSCTracksWithMediaType(self.asset, AVMediaTypeVideo) firstObject].naturalSize;
192
193
  [self.player.currentItem addOutput:self.assetOutput];
193
194
  self.readyState = NSCPlayerReadyStateHaveCurrentData;
195
+ // The output is only attached now, so check again.
196
+ [self _scheduleFirstFrameCheck];
194
197
  }
195
198
  }
196
199
  }
@@ -202,25 +205,60 @@ static NSArray<AVAssetTrack *> *NSCTracksWithMediaType(AVAsset *asset, AVMediaTy
202
205
  - (void)_checkForFirstFrameWithAttempts:(int)attemptsRemaining delayMs:(int)delayMs {
203
206
  if (self.loadedDataFired || attemptsRemaining <= 0) return;
204
207
 
205
- CMTime current = [self.player currentTime];
206
- CVPixelBufferRef buffer = NULL;
207
- if (self.assetOutput) {
208
- buffer = [self.assetOutput copyPixelBufferForItemTime:current itemTimeForDisplay:NULL];
208
+ if ([self _hasDecodedFrame]) {
209
+ [self _fireLoadedData];
210
+ return;
209
211
  }
210
212
 
211
- if (buffer != NULL) {
212
- CFRelease(buffer);
213
- self.loadedDataFired = YES;
214
- dispatch_async(dispatch_get_main_queue(), ^{
215
- if ([self.listener respondsToSelector:@selector(onLoadedData)]) {
216
- [self.listener onLoadedData];
217
- }
218
- });
213
+ __weak typeof(self) weakSelf = self;
214
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayMs * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
215
+ [weakSelf _checkForFirstFrameWithAttempts:attemptsRemaining - 1 delayMs:delayMs];
216
+ });
217
+ }
218
+
219
+ /// Non-consuming: `copyPixelBufferForItemTime:` would steal the first frame from whoever draws it.
220
+ - (BOOL)_hasDecodedFrame {
221
+ if (self.firstFrameReady) return YES;
222
+ if (!self.assetOutput) return NO;
223
+ if ([self.assetOutput hasNewPixelBufferForItemTime:self.player.currentTime]) {
224
+ self.firstFrameReady = YES;
225
+ }
226
+ return self.firstFrameReady;
227
+ }
228
+
229
+ - (void)_fireLoadedData {
230
+ if (self.loadedDataFired) return;
231
+ self.loadedDataFired = YES;
232
+ dispatch_async(dispatch_get_main_queue(), ^{
233
+ if ([self.listener respondsToSelector:@selector(onLoadedData)]) {
234
+ [self.listener onLoadedData];
235
+ }
236
+ });
237
+ }
238
+
239
+ /// Reports `playing` (resolving `play()`) once a frame is decoded, as on the web. Audio-only
240
+ /// reports at once; after ~10 s it reports anyway so `play()` cannot hang.
241
+ - (void)_notifyPlayingWhenFrameReady:(int)attemptsRemaining generation:(NSUInteger)generation {
242
+ if (generation != self.frameWaitGeneration) return;
243
+ if (self.state != NSCPlayerStatePlaying) {
244
+ self.waitingForFirstFrame = NO;
219
245
  return;
220
246
  }
247
+ self.waitingForFirstFrame = YES;
221
248
 
222
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayMs * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
223
- [self _checkForFirstFrameWithAttempts:attemptsRemaining - 1 delayMs:delayMs];
249
+ BOOL audioOnly = self.currentItem != nil && CGSizeEqualToSize(self.videoSize, CGSizeZero);
250
+ if ([self _hasDecodedFrame] || audioOnly || attemptsRemaining <= 0) {
251
+ self.waitingForFirstFrame = NO;
252
+ if (self.firstFrameReady) {
253
+ [self _fireLoadedData];
254
+ }
255
+ [self.listener onStateChange:self.state];
256
+ return;
257
+ }
258
+
259
+ __weak typeof(self) weakSelf = self;
260
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(16 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
261
+ [weakSelf _notifyPlayingWhenFrameReady:attemptsRemaining - 1 generation:generation];
224
262
  });
225
263
  }
226
264
 
@@ -295,11 +333,27 @@ static NSArray<AVAssetTrack *> *NSCTracksWithMediaType(AVAsset *asset, AVMediaTy
295
333
  }
296
334
 
297
335
  - (void)play {
298
- if (self.state == NSCPlayerStatePlaying) return;
336
+ if (self.state == NSCPlayerStatePlaying) {
337
+ // Autoplay started but has not reported playing yet.
338
+ if (!self.firstFrameReady && !self.waitingForFirstFrame) {
339
+ [self _startWaitingForFrame];
340
+ }
341
+ return;
342
+ }
299
343
  [self addTimeObserver];
300
344
  [self.player play];
301
345
  self.state = NSCPlayerStatePlaying;
302
- [self.listener onStateChange:self.state];
346
+ [self _startWaitingForFrame];
347
+ }
348
+
349
+ /// Deferred a turn, like the web's queued `play()` resolution, so an immediate `pause()` wins.
350
+ - (void)_startWaitingForFrame {
351
+ NSUInteger generation = ++self.frameWaitGeneration;
352
+ self.waitingForFirstFrame = YES;
353
+ __weak typeof(self) weakSelf = self;
354
+ dispatch_async(dispatch_get_main_queue(), ^{
355
+ [weakSelf _notifyPlayingWhenFrameReady:600 generation:generation];
356
+ });
303
357
  }
304
358
 
305
359
  - (void)addTimeObserver {
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project />