@granite-js/video 3.0.0 → 3.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @granite-js/video
2
2
 
3
+ ## 3.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - f7bd69b: Forward iOS video playback options before source loading and restore them when Fabric reuses a view. The default iOS video provider now honors the progress update interval.
8
+
9
+ ## 3.0.1
10
+
3
11
  ## 3.0.0
4
12
 
5
13
  ## 2.5.5
@@ -67,6 +67,16 @@ Pod::Spec.new do |s|
67
67
 
68
68
  s.frameworks = ["AVFoundation", "AVKit", "CoreMedia"]
69
69
 
70
+ s.test_spec 'ProviderTests' do |test_spec|
71
+ test_spec.source_files = 'native-tests/ios/**/*.{m,mm,swift}'
72
+ test_spec.requires_app_host = true
73
+ test_spec.frameworks = ['XCTest']
74
+ test_spec.pod_target_xcconfig = {
75
+ 'HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_CONFIGURATION_BUILD_DIR)/ReactCodegen/ReactCodegen.framework/Headers"',
76
+ 'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => use_default_provider ? '$(inherited) GRANITE_VIDEO_DEFAULT_PROVIDER' : '$(inherited)'
77
+ }
78
+ end
79
+
70
80
  # React Native modules dependencies (Fabric/TurboModule)
71
81
  install_modules_dependencies(s)
72
82
  end
@@ -176,6 +176,10 @@ import AVFoundation
176
176
  @objc var duration: Double { get }
177
177
  @objc var isPlaying: Bool { get }
178
178
 
179
+ // Optional - Progress
180
+ /// Progress callback interval in milliseconds. Non-positive values use the provider default.
181
+ @objc optional func setProgressUpdateInterval(_ interval: Double)
182
+
179
183
  // Optional - Volume
180
184
  @objc optional func setVolume(_ volume: Float)
181
185
  @objc optional func setMuted(_ muted: Bool)
@@ -36,6 +36,8 @@ using namespace facebook::react;
36
36
  BOOL _muted;
37
37
  BOOL _repeat;
38
38
  NSString *_resizeMode;
39
+ BOOL _needsPlaybackPropsReapply;
40
+ BOOL _needsMixWithOthersReapply;
39
41
  }
40
42
 
41
43
  + (ComponentDescriptorProvider)componentDescriptorProvider
@@ -56,6 +58,8 @@ using namespace facebook::react;
56
58
  _muted = NO;
57
59
  _repeat = NO;
58
60
  _resizeMode = @"contain";
61
+ _needsPlaybackPropsReapply = YES;
62
+ _needsMixWithOthersReapply = NO;
59
63
 
60
64
  // Create provider from registry
61
65
  _provider = [[GraniteVideoRegistry shared] createProvider];
@@ -95,6 +99,23 @@ using namespace facebook::react;
95
99
  const auto &oldViewProps = *std::static_pointer_cast<GraniteVideoViewProps const>(_props);
96
100
  const auto &newViewProps = *std::static_pointer_cast<GraniteVideoViewProps const>(props);
97
101
 
102
+ // Apply options before loading: providers may configure audio, observers and playback in loadSource.
103
+ if (_needsMixWithOthersReapply || newViewProps.mixWithOthers != oldViewProps.mixWithOthers) {
104
+ [self updateMixWithOthers:[NSString stringWithUTF8String:newViewProps.mixWithOthers.c_str()]];
105
+ }
106
+ if (_needsPlaybackPropsReapply || newViewProps.progressUpdateInterval != oldViewProps.progressUpdateInterval) {
107
+ if ([_provider respondsToSelector:@selector(setProgressUpdateInterval:)]) {
108
+ [_provider setProgressUpdateInterval:newViewProps.progressUpdateInterval];
109
+ }
110
+ }
111
+ if (_needsPlaybackPropsReapply || newViewProps.automaticallyWaitsToMinimizeStalling != oldViewProps.automaticallyWaitsToMinimizeStalling) {
112
+ if ([_provider respondsToSelector:@selector(setAutomaticallyWaitsToMinimizeStalling:)]) {
113
+ [_provider setAutomaticallyWaitsToMinimizeStalling:newViewProps.automaticallyWaitsToMinimizeStalling];
114
+ }
115
+ }
116
+ _needsPlaybackPropsReapply = NO;
117
+ _needsMixWithOthersReapply = NO;
118
+
98
119
  // Source
99
120
  if (newViewProps.source.uri != oldViewProps.source.uri ||
100
121
  newViewProps.source.type != oldViewProps.source.type) {
@@ -198,13 +219,6 @@ using namespace facebook::react;
198
219
  }
199
220
  }
200
221
 
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
222
  // Allows External Playback
209
223
  if (newViewProps.allowsExternalPlayback != oldViewProps.allowsExternalPlayback) {
210
224
  if ([_provider respondsToSelector:@selector(setAllowsExternalPlayback:)]) {
@@ -281,6 +295,19 @@ using namespace facebook::react;
281
295
  }
282
296
  }
283
297
 
298
+ - (void)updateMixWithOthers:(NSString *)mode
299
+ {
300
+ if ([_provider respondsToSelector:@selector(setMixWithOthers:)]) {
301
+ GraniteVideoMixWithOthers value = GraniteVideoMixWithOthersInherit;
302
+ if ([mode isEqualToString:@"mix"]) {
303
+ value = GraniteVideoMixWithOthersMix;
304
+ } else if ([mode isEqualToString:@"duck"]) {
305
+ value = GraniteVideoMixWithOthersDuck;
306
+ }
307
+ [_provider setMixWithOthers:value];
308
+ }
309
+ }
310
+
284
311
  - (void)updateDrmConfig:(const GraniteVideoViewDrmStruct &)drm
285
312
  {
286
313
  if (drm.type.empty()) {
@@ -665,6 +692,9 @@ using namespace facebook::react;
665
692
  {
666
693
  [super prepareForRecycle];
667
694
  [_provider unload];
695
+ // The provider survives recycling, while Fabric resets the old props to defaults.
696
+ _needsPlaybackPropsReapply = YES;
697
+ _needsMixWithOthersReapply = YES;
668
698
  _props = std::make_shared<const GraniteVideoViewProps>();
669
699
  }
670
700
 
@@ -34,6 +34,7 @@ private class AVPlayerContainerView: UIView {
34
34
  private var playerView: AVPlayerContainerView?
35
35
  private var playerItem: AVPlayerItem?
36
36
  private var timeObserver: Any?
37
+ private var progressInterval: Double = 0.25
37
38
  private var pipController: AVPictureInPictureController?
38
39
 
39
40
  private var shouldRepeat: Bool = false
@@ -242,11 +243,7 @@ private class AVPlayerContainerView: UIView {
242
243
  player.seek(to: seekTime)
243
244
  }
244
245
 
245
- // Add time observer for progress
246
- let interval = CMTime(seconds: 0.25, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
247
- timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] _ in
248
- self?.handleProgressUpdate()
249
- }
246
+ updateProgressObserver()
250
247
 
251
248
  // Apply settings
252
249
  player.isMuted = isMuted
@@ -375,6 +372,11 @@ private class AVPlayerContainerView: UIView {
375
372
  playerItem?.preferredForwardBufferDuration = duration
376
373
  }
377
374
 
375
+ @objc public func setProgressUpdateInterval(_ interval: Double) {
376
+ progressInterval = interval.isFinite && interval > 0 ? interval / 1000.0 : 0.25
377
+ updateProgressObserver()
378
+ }
379
+
378
380
  @objc public func setAutomaticallyWaitsToMinimizeStalling(_ waits: Bool) {
379
381
  automaticallyWaits = waits
380
382
  player.automaticallyWaitsToMinimizeStalling = waits
@@ -400,6 +402,17 @@ private class AVPlayerContainerView: UIView {
400
402
 
401
403
  // MARK: - Private Methods
402
404
 
405
+ private func updateProgressObserver() {
406
+ guard playerItem != nil else { return }
407
+ if let observer = timeObserver {
408
+ player.removeTimeObserver(observer)
409
+ }
410
+ let interval = CMTime(seconds: progressInterval, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
411
+ timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] _ in
412
+ self?.handleProgressUpdate()
413
+ }
414
+ }
415
+
403
416
  private func unloadPlayerItem() {
404
417
  if let observer = timeObserver {
405
418
  player.removeTimeObserver(observer)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@granite-js/video",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Pluggable video component for React Native with provider selection",
5
5
  "type": "module",
6
6
  "main": "./dist/module/index.js",
@@ -19,6 +19,7 @@
19
19
  "dist",
20
20
  "android",
21
21
  "ios",
22
+ "native-tests/ios",
22
23
  "*.podspec",
23
24
  "react-native.config.js",
24
25
  "!example/**",