@granite-js/video 1.0.0

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/GraniteVideo.podspec +72 -0
  3. package/android/README.md +232 -0
  4. package/android/build.gradle +117 -0
  5. package/android/gradle.properties +8 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/run/granite/video/GraniteVideoModule.kt +70 -0
  8. package/android/src/main/java/run/granite/video/GraniteVideoPackage.kt +43 -0
  9. package/android/src/main/java/run/granite/video/GraniteVideoView.kt +384 -0
  10. package/android/src/main/java/run/granite/video/GraniteVideoViewManager.kt +318 -0
  11. package/android/src/main/java/run/granite/video/event/GraniteVideoEvents.kt +273 -0
  12. package/android/src/main/java/run/granite/video/event/VideoEventDispatcher.kt +66 -0
  13. package/android/src/main/java/run/granite/video/event/VideoEventListenerAdapter.kt +157 -0
  14. package/android/src/main/java/run/granite/video/provider/GraniteVideoProvider.kt +346 -0
  15. package/android/src/media3/AndroidManifest.xml +9 -0
  16. package/android/src/media3/java/run/granite/video/provider/media3/ExoPlayerProvider.kt +386 -0
  17. package/android/src/media3/java/run/granite/video/provider/media3/Media3ContentProvider.kt +29 -0
  18. package/android/src/media3/java/run/granite/video/provider/media3/Media3Initializer.kt +25 -0
  19. package/android/src/media3/java/run/granite/video/provider/media3/factory/ExoPlayerFactory.kt +32 -0
  20. package/android/src/media3/java/run/granite/video/provider/media3/factory/MediaSourceFactory.kt +61 -0
  21. package/android/src/media3/java/run/granite/video/provider/media3/factory/TrackSelectorFactory.kt +26 -0
  22. package/android/src/media3/java/run/granite/video/provider/media3/factory/VideoSurfaceFactory.kt +62 -0
  23. package/android/src/media3/java/run/granite/video/provider/media3/listener/ExoPlayerEventListener.kt +104 -0
  24. package/android/src/media3/java/run/granite/video/provider/media3/scheduler/ProgressScheduler.kt +56 -0
  25. package/android/src/test/java/run/granite/video/GraniteVideoViewRobolectricTest.kt +598 -0
  26. package/android/src/test/java/run/granite/video/event/VideoEventListenerAdapterTest.kt +319 -0
  27. package/android/src/test/java/run/granite/video/helpers/FakeGraniteVideoProvider.kt +161 -0
  28. package/android/src/test/java/run/granite/video/helpers/TestProgressScheduler.kt +42 -0
  29. package/android/src/test/java/run/granite/video/provider/GraniteVideoRegistryTest.kt +232 -0
  30. package/android/src/test/java/run/granite/video/provider/ProviderContractTest.kt +174 -0
  31. package/android/src/test/java/run/granite/video/provider/media3/listener/ExoPlayerEventListenerTest.kt +243 -0
  32. package/android/src/test/resources/kotest.properties +2 -0
  33. package/dist/module/GraniteVideo.js +458 -0
  34. package/dist/module/GraniteVideo.js.map +1 -0
  35. package/dist/module/GraniteVideoNativeComponent.ts +265 -0
  36. package/dist/module/index.js +7 -0
  37. package/dist/module/index.js.map +1 -0
  38. package/dist/module/package.json +1 -0
  39. package/dist/module/types.js +4 -0
  40. package/dist/module/types.js.map +1 -0
  41. package/dist/typescript/GraniteVideo.d.ts +12 -0
  42. package/dist/typescript/GraniteVideoNativeComponent.d.ts +189 -0
  43. package/dist/typescript/index.d.ts +5 -0
  44. package/dist/typescript/types.d.ts +328 -0
  45. package/ios/GraniteVideoComponentsProvider.h +10 -0
  46. package/ios/GraniteVideoProvider.swift +280 -0
  47. package/ios/GraniteVideoView.h +15 -0
  48. package/ios/GraniteVideoView.mm +661 -0
  49. package/ios/Providers/AVPlayerProvider.swift +541 -0
  50. package/package.json +106 -0
  51. package/src/GraniteVideo.tsx +575 -0
  52. package/src/GraniteVideoNativeComponent.ts +265 -0
  53. package/src/index.ts +8 -0
  54. package/src/types.ts +464 -0
@@ -0,0 +1,661 @@
1
+ #import <UIKit/UIKit.h>
2
+ #import <AVKit/AVKit.h>
3
+
4
+ #import <React/RCTViewComponentView.h>
5
+ #import <React/RCTConversions.h>
6
+ #import <React/RCTFabricComponentsPlugins.h>
7
+ #import <React/RCTComponentViewFactory.h>
8
+
9
+ #import <react/renderer/components/GraniteVideoViewSpec/ComponentDescriptors.h>
10
+ #import <react/renderer/components/GraniteVideoViewSpec/EventEmitters.h>
11
+ #import <react/renderer/components/GraniteVideoViewSpec/Props.h>
12
+ #import <react/renderer/components/GraniteVideoViewSpec/RCTComponentViewHelpers.h>
13
+
14
+ // Import Swift module - the header is generated during build
15
+ #if __has_include(<GraniteVideo/GraniteVideo-Swift.h>)
16
+ #import <GraniteVideo/GraniteVideo-Swift.h>
17
+ #elif __has_include(<granite_video/granite_video-Swift.h>)
18
+ #import <granite_video/granite_video-Swift.h>
19
+ #else
20
+ #import "GraniteVideo-Swift.h"
21
+ #endif
22
+
23
+ using namespace facebook::react;
24
+
25
+ // Define GraniteVideoView inheriting from RCTViewComponentView here in .mm file
26
+ // The header declares it as UIView to avoid exposing C++ headers to Swift module
27
+ @interface GraniteVideoView : RCTViewComponentView <RCTGraniteVideoViewViewProtocol, GraniteVideoDelegate>
28
+ @end
29
+
30
+ @implementation GraniteVideoView {
31
+ UIView *_playerView;
32
+ id<GraniteVideoProvidable> _provider;
33
+ BOOL _paused;
34
+ float _volume;
35
+ float _rate;
36
+ BOOL _muted;
37
+ BOOL _repeat;
38
+ NSString *_resizeMode;
39
+ }
40
+
41
+ + (ComponentDescriptorProvider)componentDescriptorProvider
42
+ {
43
+ return concreteComponentDescriptorProvider<GraniteVideoViewComponentDescriptor>();
44
+ }
45
+
46
+ - (instancetype)initWithFrame:(CGRect)frame
47
+ {
48
+ if (self = [super initWithFrame:frame]) {
49
+ static const auto defaultProps = std::make_shared<const GraniteVideoViewProps>();
50
+ _props = defaultProps;
51
+
52
+ // Default values
53
+ _paused = NO;
54
+ _volume = 1.0f;
55
+ _rate = 1.0f;
56
+ _muted = NO;
57
+ _repeat = NO;
58
+ _resizeMode = @"contain";
59
+
60
+ // Create provider from registry
61
+ _provider = [[GraniteVideoRegistry shared] createProvider];
62
+
63
+ #if defined(GRANITE_VIDEO_DEFAULT_PROVIDER)
64
+ if (_provider == nil) {
65
+ // Use default AVPlayer provider
66
+ _provider = [[AVPlayerProvider alloc] init];
67
+ }
68
+ #endif
69
+
70
+ if (_provider == nil) {
71
+ NSLog(@"[GraniteVideo] No provider registered. Register a provider in AppDelegate or use default AVPlayer provider.");
72
+ return self;
73
+ }
74
+
75
+ [_provider setDelegate:self];
76
+
77
+ // Create player view
78
+ _playerView = [_provider createPlayerView];
79
+ _playerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
80
+
81
+ self.contentView = _playerView;
82
+ }
83
+
84
+ return self;
85
+ }
86
+
87
+ - (void)layoutSubviews
88
+ {
89
+ [super layoutSubviews];
90
+ _playerView.frame = self.bounds;
91
+ }
92
+
93
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
94
+ {
95
+ const auto &oldViewProps = *std::static_pointer_cast<GraniteVideoViewProps const>(_props);
96
+ const auto &newViewProps = *std::static_pointer_cast<GraniteVideoViewProps const>(props);
97
+
98
+ // Source
99
+ if (newViewProps.source.uri != oldViewProps.source.uri ||
100
+ newViewProps.source.type != oldViewProps.source.type) {
101
+ [self updateSource:newViewProps.source];
102
+ }
103
+
104
+ // Paused
105
+ if (newViewProps.paused != oldViewProps.paused) {
106
+ _paused = newViewProps.paused;
107
+ if (_paused) {
108
+ [_provider pause];
109
+ } else {
110
+ [_provider play];
111
+ }
112
+ }
113
+
114
+ // Muted
115
+ if (newViewProps.muted != oldViewProps.muted) {
116
+ _muted = newViewProps.muted;
117
+ if ([_provider respondsToSelector:@selector(setMuted:)]) {
118
+ [_provider setMuted:_muted];
119
+ }
120
+ }
121
+
122
+ // Volume
123
+ if (newViewProps.volume != oldViewProps.volume) {
124
+ _volume = newViewProps.volume;
125
+ if ([_provider respondsToSelector:@selector(setVolume:)]) {
126
+ [_provider setVolume:_volume];
127
+ }
128
+ }
129
+
130
+ // Rate
131
+ if (newViewProps.rate != oldViewProps.rate) {
132
+ _rate = newViewProps.rate;
133
+ if ([_provider respondsToSelector:@selector(setRate:)]) {
134
+ [_provider setRate:_rate];
135
+ }
136
+ }
137
+
138
+ // Repeat
139
+ if (newViewProps.repeat != oldViewProps.repeat) {
140
+ _repeat = newViewProps.repeat;
141
+ if ([_provider respondsToSelector:@selector(setRepeat:)]) {
142
+ [_provider setRepeat:_repeat];
143
+ }
144
+ }
145
+
146
+ // Resize Mode
147
+ if (newViewProps.resizeMode != oldViewProps.resizeMode) {
148
+ _resizeMode = [NSString stringWithUTF8String:newViewProps.resizeMode.c_str()];
149
+ [self updateResizeMode:_resizeMode];
150
+ }
151
+
152
+ // Controls
153
+ if (newViewProps.controls != oldViewProps.controls) {
154
+ if ([_provider respondsToSelector:@selector(setControlsEnabled:)]) {
155
+ [_provider setControlsEnabled:newViewProps.controls];
156
+ }
157
+ }
158
+
159
+ // Fullscreen
160
+ if (newViewProps.fullscreen != oldViewProps.fullscreen) {
161
+ if ([_provider respondsToSelector:@selector(setFullscreen:animated:)]) {
162
+ [_provider setFullscreen:newViewProps.fullscreen animated:YES];
163
+ }
164
+ }
165
+
166
+ // Picture in Picture
167
+ if (newViewProps.pictureInPicture != oldViewProps.pictureInPicture) {
168
+ if ([_provider respondsToSelector:@selector(setPictureInPictureEnabled:)]) {
169
+ [_provider setPictureInPictureEnabled:newViewProps.pictureInPicture];
170
+ }
171
+ }
172
+
173
+ // Play in Background
174
+ if (newViewProps.playInBackground != oldViewProps.playInBackground) {
175
+ if ([_provider respondsToSelector:@selector(setPlayInBackground:)]) {
176
+ [_provider setPlayInBackground:newViewProps.playInBackground];
177
+ }
178
+ }
179
+
180
+ // Play when Inactive
181
+ if (newViewProps.playWhenInactive != oldViewProps.playWhenInactive) {
182
+ if ([_provider respondsToSelector:@selector(setPlayWhenInactive:)]) {
183
+ [_provider setPlayWhenInactive:newViewProps.playWhenInactive];
184
+ }
185
+ }
186
+
187
+ // Max Bit Rate
188
+ if (newViewProps.maxBitRate != oldViewProps.maxBitRate) {
189
+ if ([_provider respondsToSelector:@selector(setMaxBitRate:)]) {
190
+ [_provider setMaxBitRate:newViewProps.maxBitRate];
191
+ }
192
+ }
193
+
194
+ // Preferred Forward Buffer Duration
195
+ if (newViewProps.preferredForwardBufferDuration != oldViewProps.preferredForwardBufferDuration) {
196
+ if ([_provider respondsToSelector:@selector(setPreferredForwardBufferDuration:)]) {
197
+ [_provider setPreferredForwardBufferDuration:newViewProps.preferredForwardBufferDuration];
198
+ }
199
+ }
200
+
201
+ // Automatically Waits to Minimize Stalling
202
+ if (newViewProps.automaticallyWaitsToMinimizeStalling != oldViewProps.automaticallyWaitsToMinimizeStalling) {
203
+ if ([_provider respondsToSelector:@selector(setAutomaticallyWaitsToMinimizeStalling:)]) {
204
+ [_provider setAutomaticallyWaitsToMinimizeStalling:newViewProps.automaticallyWaitsToMinimizeStalling];
205
+ }
206
+ }
207
+
208
+ // Allows External Playback
209
+ if (newViewProps.allowsExternalPlayback != oldViewProps.allowsExternalPlayback) {
210
+ if ([_provider respondsToSelector:@selector(setAllowsExternalPlayback:)]) {
211
+ [_provider setAllowsExternalPlayback:newViewProps.allowsExternalPlayback];
212
+ }
213
+ }
214
+
215
+ // Prevents Display Sleep
216
+ if (newViewProps.preventsDisplaySleepDuringVideoPlayback != oldViewProps.preventsDisplaySleepDuringVideoPlayback) {
217
+ if ([_provider respondsToSelector:@selector(setPreventsDisplaySleepDuringVideoPlayback:)]) {
218
+ [_provider setPreventsDisplaySleepDuringVideoPlayback:newViewProps.preventsDisplaySleepDuringVideoPlayback];
219
+ }
220
+ }
221
+
222
+ // DRM Config
223
+ if (newViewProps.drm.type != oldViewProps.drm.type ||
224
+ newViewProps.drm.licenseServer != oldViewProps.drm.licenseServer ||
225
+ newViewProps.drm.contentId != oldViewProps.drm.contentId ||
226
+ newViewProps.drm.certificateUrl != oldViewProps.drm.certificateUrl) {
227
+ [self updateDrmConfig:newViewProps.drm];
228
+ }
229
+
230
+ [super updateProps:props oldProps:oldProps];
231
+ }
232
+
233
+ - (void)updateSource:(const GraniteVideoViewSourceStruct &)source
234
+ {
235
+ if (source.uri.empty()) {
236
+ return;
237
+ }
238
+
239
+ GraniteVideoSource *videoSource = [[GraniteVideoSource alloc] init];
240
+ videoSource.uri = [NSString stringWithUTF8String:source.uri.c_str()];
241
+
242
+ if (!source.type.empty()) {
243
+ videoSource.type = [NSString stringWithUTF8String:source.type.c_str()];
244
+ }
245
+
246
+ videoSource.startPosition = source.startPosition;
247
+ videoSource.cropStart = source.cropStart;
248
+ videoSource.cropEnd = source.cropEnd;
249
+
250
+ [_provider loadSource:videoSource];
251
+
252
+ if (!_paused) {
253
+ [_provider play];
254
+ }
255
+ }
256
+
257
+ - (void)updateResizeMode:(NSString *)mode
258
+ {
259
+ if ([_provider respondsToSelector:@selector(setResizeMode:)]) {
260
+ GraniteVideoResizeMode resizeMode = GraniteVideoResizeModeContain;
261
+
262
+ if ([mode isEqualToString:@"cover"]) {
263
+ resizeMode = GraniteVideoResizeModeCover;
264
+ } else if ([mode isEqualToString:@"stretch"]) {
265
+ resizeMode = GraniteVideoResizeModeStretch;
266
+ } else if ([mode isEqualToString:@"none"]) {
267
+ resizeMode = GraniteVideoResizeModeNone;
268
+ }
269
+
270
+ [_provider setResizeMode:resizeMode];
271
+ }
272
+ }
273
+
274
+ - (void)updateDrmConfig:(const GraniteVideoViewDrmStruct &)drm
275
+ {
276
+ if (drm.type.empty()) {
277
+ return;
278
+ }
279
+
280
+ GraniteVideoDrmConfig *drmConfig = [[GraniteVideoDrmConfig alloc] init];
281
+
282
+ // Map DRM type
283
+ NSString *typeStr = [NSString stringWithUTF8String:drm.type.c_str()];
284
+ if ([typeStr isEqualToString:@"fairplay"]) {
285
+ drmConfig.type = GraniteVideoDrmTypeFairplay;
286
+ } else if ([typeStr isEqualToString:@"widevine"]) {
287
+ drmConfig.type = GraniteVideoDrmTypeWidevine;
288
+ } else if ([typeStr isEqualToString:@"playready"]) {
289
+ drmConfig.type = GraniteVideoDrmTypePlayready;
290
+ } else if ([typeStr isEqualToString:@"clearkey"]) {
291
+ drmConfig.type = GraniteVideoDrmTypeClearkey;
292
+ }
293
+
294
+ if (!drm.licenseServer.empty()) {
295
+ drmConfig.licenseServer = [NSString stringWithUTF8String:drm.licenseServer.c_str()];
296
+ }
297
+
298
+ // Map JS contentId -> iOS contentID
299
+ if (!drm.contentId.empty()) {
300
+ drmConfig.contentID = [NSString stringWithUTF8String:drm.contentId.c_str()];
301
+ }
302
+
303
+ // Map JS certificateUrl -> iOS certificateURL
304
+ if (!drm.certificateUrl.empty()) {
305
+ drmConfig.certificateURL = [NSString stringWithUTF8String:drm.certificateUrl.c_str()];
306
+ }
307
+
308
+ drmConfig.base64Certificate = drm.base64Certificate;
309
+
310
+ if ([_provider respondsToSelector:@selector(setDrmConfig:)]) {
311
+ [_provider setDrmConfig:drmConfig];
312
+ }
313
+ }
314
+
315
+ #pragma mark - Native Commands
316
+
317
+ - (void)seek:(double)time tolerance:(double)tolerance
318
+ {
319
+ [_provider seekTo:time toleranceBefore:tolerance toleranceAfter:tolerance];
320
+ }
321
+
322
+ - (void)adjustVolume:(float)volume
323
+ {
324
+ _volume = volume;
325
+ if ([_provider respondsToSelector:@selector(setVolume:)]) {
326
+ [_provider setVolume:volume];
327
+ }
328
+ }
329
+
330
+ - (void)setFullScreen:(BOOL)fullscreen
331
+ {
332
+ if ([_provider respondsToSelector:@selector(setFullscreen:animated:)]) {
333
+ [_provider setFullscreen:fullscreen animated:YES];
334
+ }
335
+ }
336
+
337
+ - (void)loadSource:(NSString *)uri
338
+ {
339
+ GraniteVideoSource *source = [[GraniteVideoSource alloc] initWithUri:uri];
340
+ [_provider loadSource:source];
341
+
342
+ if (!_paused) {
343
+ [_provider play];
344
+ }
345
+ }
346
+
347
+ - (void)pause
348
+ {
349
+ [_provider pause];
350
+ }
351
+
352
+ - (void)resume
353
+ {
354
+ [_provider play];
355
+ }
356
+
357
+ - (void)enterPictureInPicture
358
+ {
359
+ if ([_provider respondsToSelector:@selector(enterPictureInPicture)]) {
360
+ [_provider enterPictureInPicture];
361
+ }
362
+ }
363
+
364
+ - (void)exitPictureInPicture
365
+ {
366
+ if ([_provider respondsToSelector:@selector(exitPictureInPicture)]) {
367
+ [_provider exitPictureInPicture];
368
+ }
369
+ }
370
+
371
+ #pragma mark - GraniteVideoDelegate
372
+
373
+ - (void)videoDidLoadStartWithIsNetwork:(BOOL)isNetwork type:(nonnull NSString *)type uri:(nonnull NSString *)uri
374
+ {
375
+ if (_eventEmitter) {
376
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
377
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoLoadStart event = {
378
+ .isNetwork = isNetwork,
379
+ .type = std::string([type UTF8String] ?: ""),
380
+ .uri = std::string([uri UTF8String] ?: "")
381
+ };
382
+ emitter->onVideoLoadStart(event);
383
+ }
384
+ }
385
+
386
+ - (void)videoDidLoadWithData:(nonnull GraniteVideoLoadData *)data
387
+ {
388
+ if (_eventEmitter) {
389
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
390
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoLoad event = {
391
+ .currentTime = data.currentTime,
392
+ .duration = data.duration,
393
+ .naturalSize = {
394
+ .width = data.naturalWidth,
395
+ .height = data.naturalHeight,
396
+ .orientation = std::string([data.orientation UTF8String] ?: "landscape")
397
+ }
398
+ };
399
+ emitter->onVideoLoad(event);
400
+ }
401
+ }
402
+
403
+ - (void)videoDidFailWithError:(nonnull GraniteVideoErrorData *)error
404
+ {
405
+ if (_eventEmitter) {
406
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
407
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoError event = {
408
+ .error = {
409
+ .code = (int)error.code,
410
+ .domain = std::string([error.domain UTF8String] ?: ""),
411
+ .localizedDescription = std::string([error.localizedDescription_ UTF8String] ?: ""),
412
+ .localizedFailureReason = std::string([error.localizedFailureReason_ UTF8String] ?: ""),
413
+ .localizedRecoverySuggestion = std::string([error.localizedRecoverySuggestion_ UTF8String] ?: ""),
414
+ .errorString = std::string([error.localizedDescription_ UTF8String] ?: "")
415
+ }
416
+ };
417
+ emitter->onVideoError(event);
418
+ }
419
+ }
420
+
421
+ - (void)videoDidUpdateProgressWithData:(nonnull GraniteVideoProgressData *)data
422
+ {
423
+ if (_eventEmitter) {
424
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
425
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoProgress event = {
426
+ .currentTime = data.currentTime,
427
+ .playableDuration = data.playableDuration,
428
+ .seekableDuration = data.seekableDuration
429
+ };
430
+ emitter->onVideoProgress(event);
431
+ }
432
+ }
433
+
434
+ - (void)videoDidSeekWithCurrentTime:(double)currentTime seekTime:(double)seekTime
435
+ {
436
+ if (_eventEmitter) {
437
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
438
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoSeek event = {
439
+ .currentTime = currentTime,
440
+ .seekTime = seekTime
441
+ };
442
+ emitter->onVideoSeek(event);
443
+ }
444
+ }
445
+
446
+ - (void)videoDidEnd
447
+ {
448
+ if (_eventEmitter) {
449
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
450
+ emitter->onVideoEnd({});
451
+ }
452
+ }
453
+
454
+ - (void)videoBufferingStateChangedWithIsBuffering:(BOOL)isBuffering
455
+ {
456
+ if (_eventEmitter) {
457
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
458
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoBuffer event = {
459
+ .isBuffering = isBuffering
460
+ };
461
+ emitter->onVideoBuffer(event);
462
+ }
463
+ }
464
+
465
+ - (void)videoBandwidthDidUpdateWithBitrate:(double)bitrate width:(NSInteger)width height:(NSInteger)height
466
+ {
467
+ if (_eventEmitter) {
468
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
469
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoBandwidthUpdate event = {
470
+ .bitrate = bitrate,
471
+ .width = (int)width,
472
+ .height = (int)height
473
+ };
474
+ emitter->onVideoBandwidthUpdate(event);
475
+ }
476
+ }
477
+
478
+ - (void)videoPlaybackStateChangedWithIsPlaying:(BOOL)isPlaying isSeeking:(BOOL)isSeeking isLooping:(BOOL)isLooping
479
+ {
480
+ if (_eventEmitter) {
481
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
482
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoPlaybackStateChanged event = {
483
+ .isPlaying = isPlaying,
484
+ .isSeeking = isSeeking,
485
+ .isLooping = isLooping
486
+ };
487
+ emitter->onVideoPlaybackStateChanged(event);
488
+ }
489
+ }
490
+
491
+ - (void)videoPlaybackRateChangedWithRate:(float)rate
492
+ {
493
+ if (_eventEmitter) {
494
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
495
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoPlaybackRateChange event = {
496
+ .playbackRate = rate
497
+ };
498
+ emitter->onVideoPlaybackRateChange(event);
499
+ }
500
+ }
501
+
502
+ - (void)videoVolumeChangedWithVolume:(float)volume
503
+ {
504
+ if (_eventEmitter) {
505
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
506
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoVolumeChange event = {
507
+ .volume = volume
508
+ };
509
+ emitter->onVideoVolumeChange(event);
510
+ }
511
+ }
512
+
513
+ - (void)videoDidBecomeIdle
514
+ {
515
+ if (_eventEmitter) {
516
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
517
+ emitter->onVideoIdle({});
518
+ }
519
+ }
520
+
521
+ - (void)videoReadyForDisplay
522
+ {
523
+ if (_eventEmitter) {
524
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
525
+ emitter->onVideoReadyForDisplay({});
526
+ }
527
+ }
528
+
529
+ - (void)videoAudioBecomingNoisy
530
+ {
531
+ if (_eventEmitter) {
532
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
533
+ emitter->onVideoAudioBecomingNoisy({});
534
+ }
535
+ }
536
+
537
+ - (void)videoFullscreenPlayerWillPresent
538
+ {
539
+ if (_eventEmitter) {
540
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
541
+ emitter->onVideoFullscreenPlayerWillPresent({});
542
+ }
543
+ }
544
+
545
+ - (void)videoFullscreenPlayerDidPresent
546
+ {
547
+ if (_eventEmitter) {
548
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
549
+ emitter->onVideoFullscreenPlayerDidPresent({});
550
+ }
551
+ }
552
+
553
+ - (void)videoFullscreenPlayerWillDismiss
554
+ {
555
+ if (_eventEmitter) {
556
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
557
+ emitter->onVideoFullscreenPlayerWillDismiss({});
558
+ }
559
+ }
560
+
561
+ - (void)videoFullscreenPlayerDidDismiss
562
+ {
563
+ if (_eventEmitter) {
564
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
565
+ emitter->onVideoFullscreenPlayerDidDismiss({});
566
+ }
567
+ }
568
+
569
+ - (void)videoPictureInPictureStatusChangedWithIsActive:(BOOL)isActive
570
+ {
571
+ if (_eventEmitter) {
572
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
573
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoPictureInPictureStatusChanged event = {
574
+ .isActive = isActive
575
+ };
576
+ emitter->onVideoPictureInPictureStatusChanged(event);
577
+ }
578
+ }
579
+
580
+ - (void)videoRestoreUserInterfaceForPictureInPictureStop
581
+ {
582
+ if (_eventEmitter) {
583
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
584
+ emitter->onVideoRestoreUserInterfaceForPictureInPictureStop({});
585
+ }
586
+ }
587
+
588
+ - (void)videoControlsVisibilityChangedWithIsVisible:(BOOL)isVisible
589
+ {
590
+ if (_eventEmitter) {
591
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
592
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoControlsVisibilityChange event = {
593
+ .isVisible = isVisible
594
+ };
595
+ emitter->onVideoControlsVisibilityChange(event);
596
+ }
597
+ }
598
+
599
+ - (void)videoExternalPlaybackChangedWithIsActive:(BOOL)isActive
600
+ {
601
+ if (_eventEmitter) {
602
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
603
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoExternalPlaybackChange event = {
604
+ .isExternalPlaybackActive = isActive
605
+ };
606
+ emitter->onVideoExternalPlaybackChange(event);
607
+ }
608
+ }
609
+
610
+ - (void)videoAspectRatioChangedWithWidth:(double)width height:(double)height
611
+ {
612
+ if (_eventEmitter) {
613
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
614
+ facebook::react::GraniteVideoViewEventEmitter::OnVideoAspectRatio event = {
615
+ .width = width,
616
+ .height = height
617
+ };
618
+ emitter->onVideoAspectRatio(event);
619
+ }
620
+ }
621
+
622
+ - (void)videoTransferEndWithUri:(NSString *)uri bytesTransferred:(double)bytesTransferred
623
+ {
624
+ if (_eventEmitter) {
625
+ auto emitter = std::static_pointer_cast<GraniteVideoViewEventEmitter const>(_eventEmitter);
626
+ facebook::react::GraniteVideoViewEventEmitter::OnTransferEnd event = {
627
+ .uri = std::string([uri UTF8String] ?: ""),
628
+ .bytesTransferred = bytesTransferred
629
+ };
630
+ emitter->onTransferEnd(event);
631
+ }
632
+ }
633
+
634
+ #pragma mark - Command Handler
635
+
636
+ - (void)handleCommand:(NSString const *)commandName args:(NSArray const *)args
637
+ {
638
+ RCTGraniteVideoViewHandleCommand(self, commandName, args);
639
+ }
640
+
641
+ #pragma mark - Cleanup
642
+
643
+ - (void)prepareForRecycle
644
+ {
645
+ [super prepareForRecycle];
646
+ [_provider unload];
647
+ }
648
+
649
+ - (void)dealloc
650
+ {
651
+ [_provider unload];
652
+ _provider = nil;
653
+ }
654
+
655
+ @end
656
+
657
+ Class<RCTComponentViewProtocol> GraniteVideoViewCls(void)
658
+ {
659
+ return GraniteVideoView.class;
660
+ }
661
+