From 7da44c238f2d385d57b2d87dd31150bb93011fd4 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 6 Feb 2023 21:43:14 +0200 Subject: [PATCH 01/31] iOS playback range --- Video.js | 2 ++ ios/Video/DataStructures/VideoSource.swift | 6 ++++++ ios/Video/RCTVideo.swift | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/Video.js b/Video.js index ac6bc085..87aa103a 100644 --- a/Video.js +++ b/Video.js @@ -342,6 +342,8 @@ export default class Video extends Component { mainVer: source.mainVer || 0, patchVer: source.patchVer || 0, requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {}, + startTime: source.startTime, + endTime: source.endTime }, onVideoLoadStart: this._onLoadStart, onVideoPlaybackStateChanged: this._onPlaybackStateChanged, diff --git a/ios/Video/DataStructures/VideoSource.swift b/ios/Video/DataStructures/VideoSource.swift index 20ab7158..7cb7c38b 100644 --- a/ios/Video/DataStructures/VideoSource.swift +++ b/ios/Video/DataStructures/VideoSource.swift @@ -6,6 +6,8 @@ struct VideoSource { let isAsset: Bool let shouldCache: Bool let requestHeaders: Dictionary? + let startTime: Int64? + let endTime: Int64? let json: NSDictionary? @@ -18,6 +20,8 @@ struct VideoSource { self.isAsset = false self.shouldCache = false self.requestHeaders = nil + self.startTime = nil + self.endTime = nil return } self.json = json @@ -27,5 +31,7 @@ struct VideoSource { self.isAsset = json["isAsset"] as? Bool ?? false self.shouldCache = json["shouldCache"] as? Bool ?? false self.requestHeaders = json["requestHeaders"] as? Dictionary + self.startTime = json["startTime"] as? Int64 + self.endTime = json["endTime"] as? Int64 } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index baca4d1a..89974948 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -292,6 +292,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH self._playerItem = playerItem self._playerObserver.playerItem = self._playerItem self.setPreferredForwardBufferDuration(self._preferredForwardBufferDuration) + self.setPlaybackRange(playerItem, withVideoStart: self._source?.startTime, withVideoEnd: self._source?.endTime) self.setFilter(self._filterName) if let maxBitRate = self._maxBitRate { self._playerItem?.preferredPeakBitRate = Double(maxBitRate) @@ -533,6 +534,15 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH // Fallback on earlier versions } } + + func setPlaybackRange(_ item:AVPlayerItem!, withVideoStart videoStart:Int64?, withVideoEnd videoEnd:Int64?) { + if (videoStart != nil) { + item.reversePlaybackEndTime = CMTimeMake(value: videoStart!, timescale: 1000) + } + if (videoEnd != nil) { + item.forwardPlaybackEndTime = CMTimeMake(value: videoEnd!, timescale: 1000) + } + } func applyModifiers() { From 0f6057bea5708246d578e13cc992f19ddb53d8b0 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 6 Feb 2023 23:31:14 +0200 Subject: [PATCH 02/31] Seeking to start time --- ios/Video/RCTVideo.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 89974948..7427d51e 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -537,7 +537,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH func setPlaybackRange(_ item:AVPlayerItem!, withVideoStart videoStart:Int64?, withVideoEnd videoEnd:Int64?) { if (videoStart != nil) { - item.reversePlaybackEndTime = CMTimeMake(value: videoStart!, timescale: 1000) + let start = CMTimeMake(value: videoStart!, timescale: 1000) + item.reversePlaybackEndTime = start + _pendingSeekTime = Float(CMTimeGetSeconds(start)) + _pendingSeek = true } if (videoEnd != nil) { item.forwardPlaybackEndTime = CMTimeMake(value: videoEnd!, timescale: 1000) From fe5fc543b948a519b143f4fd82c0a371a2b331cb Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Tue, 7 Feb 2023 22:50:54 +0200 Subject: [PATCH 03/31] Corrected currentTime & playableDuration when using start & end time --- ios/Video/Features/RCTVideoUtils.swift | 10 +++++++++- ios/Video/RCTVideo.swift | 7 +++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 50df8e3f..5ff5928c 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -12,13 +12,17 @@ enum RCTVideoUtils { * * \returns The playable duration of the current player item in seconds. */ - static func calculatePlayableDuration(_ player:AVPlayer?) -> NSNumber { + static func calculatePlayableDuration(_ player:AVPlayer?, withSource source:VideoSource?) -> NSNumber { guard let player = player, let video:AVPlayerItem = player.currentItem, video.status == AVPlayerItem.Status.readyToPlay else { return 0 } + if (source?.startTime != nil && source?.endTime != nil) { + return NSNumber(value: (Float64(source?.endTime ?? 0) - Float64(source?.startTime ?? 0)) / 1000) + } + var effectiveTimeRange:CMTimeRange? for (_, value) in video.loadedTimeRanges.enumerated() { let timeRange:CMTimeRange = value.timeRangeValue @@ -31,6 +35,10 @@ enum RCTVideoUtils { if let effectiveTimeRange = effectiveTimeRange { let playableDuration:Float64 = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange)) if playableDuration > 0 { + if (source?.startTime != nil) { + return NSNumber(value: (playableDuration - Float64(source?.startTime ?? 0) / 1000)) + } + return playableDuration as NSNumber } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 7427d51e..03811242 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -207,7 +207,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH return } - let currentTime = _player?.currentTime() + var currentTime = _player?.currentTime() + if (currentTime != nil && _source?.startTime != nil) { + currentTime = CMTimeSubtract(currentTime!, CMTimeMake(value: _source?.startTime ?? 0, timescale: 1000)) + } let currentPlaybackTime = _player?.currentItem?.currentDate() let duration = CMTimeGetSeconds(playerDuration) let currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero) @@ -223,7 +226,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } onVideoProgress?([ "currentTime": NSNumber(value: Float(currentTimeSecs)), - "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player), + "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player, withSource: _source), "atValue": NSNumber(value: currentTime?.value ?? .zero), "currentPlaybackTime": NSNumber(value: NSNumber(value: floor(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value), "target": reactTag, From 6ca0ab3834c947ddfc0c958cee6c29f4fed603be Mon Sep 17 00:00:00 2001 From: Radin Gospodinov Date: Thu, 9 Feb 2023 09:38:05 +0200 Subject: [PATCH 04/31] Android range playback. --- .../exoplayer/ReactExoplayerView.java | 50 +++++++++++++++---- .../exoplayer/ReactExoplayerViewManager.java | 9 +++- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 0c0fbc17..0acc0d00 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -93,6 +93,7 @@ import com.google.android.exoplayer2.source.dash.manifest.Representation; import com.google.android.exoplayer2.ext.ima.ImaAdsLoader; import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.source.DefaultMediaSourceFactory; +import com.google.android.exoplayer2.source.ClippingMediaSource; import com.google.common.collect.ImmutableList; import java.net.CookieHandler; @@ -181,6 +182,10 @@ class ReactExoplayerView extends FrameLayout implements // Props from React private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; private Uri srcUri; + + private long startTimeMs = -1; + + private long endTimeMs = -1; private String extension; private boolean repeat; private String audioTrackType; @@ -669,7 +674,7 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager); + MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager, startTimeMs, endTimeMs); MediaSource mediaSourceWithAds = null; if (adTagUrl != null) { MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory) @@ -764,7 +769,13 @@ class ReactExoplayerView extends FrameLayout implements } } - private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { + private MediaSource buildMediaSource( + Uri uri, + String overrideExtension, + DrmSessionManager drmSessionManager, + long startTimeMs, + long endTimeMs) { + if (uri == null) { throw new IllegalStateException("Invalid video uri"); } @@ -781,7 +792,7 @@ class ReactExoplayerView extends FrameLayout implements } MediaItem mediaItem = mediaItemBuilder.build(); - + MediaSource mediaSource = null; DrmSessionManagerProvider drmProvider = null; if (drmSessionManager != null) { drmProvider = new DrmSessionManagerProvider() { @@ -795,39 +806,50 @@ class ReactExoplayerView extends FrameLayout implements } switch (type) { case CONTENT_TYPE_SS: - return new SsMediaSource.Factory( + mediaSource = SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; case CONTENT_TYPE_DASH: - return new DashMediaSource.Factory( + mediaSource = DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; case CONTENT_TYPE_HLS: - return new HlsMediaSource.Factory( + mediaSource = HlsMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; case CONTENT_TYPE_OTHER: - return new ProgressiveMediaSource.Factory( + mediaSource = ProgressiveMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; default: { throw new IllegalStateException("Unsupported type: " + type); } } + + if(startTimeMs >= 0 && endTimeMs >= 0) + { + return new ClippingMediaSource(mediaSource, startTimeMs * 1000, endTimeMs * 1000); + } + + return mediaSource; } private ArrayList buildTextSources() { @@ -1465,11 +1487,19 @@ class ReactExoplayerView extends FrameLayout implements // ReactExoplayerViewManager public api - public void setSrc(final Uri uri, final String extension, Map headers) { + public void setSrc( + final Uri uri, + final long startTimeMs, + final long endTimeMs, + final String extension, + Map headers) { + if (uri != null) { - boolean isSourceEqual = uri.equals(srcUri); + boolean isSourceEqual = uri.equals(srcUri) && startTimeMs == this.startTimeMs && endTimeMs == this.endTimeMs; hasDrmFailed = false; this.srcUri = uri; + this.startTimeMs = startTimeMs; + this.endTimeMs = endTimeMs; this.extension = extension; this.requestHeaders = headers; this.mediaDataSourceFactory = @@ -1487,6 +1517,8 @@ class ReactExoplayerView extends FrameLayout implements player.stop(); player.clearMediaItems(); this.srcUri = null; + this.startTimeMs = -1; + this.endTimeMs = -1; this.extension = null; this.requestHeaders = null; this.mediaDataSourceFactory = null; diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 8da025e7..6f2b1296 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -28,9 +28,12 @@ import javax.annotation.Nullable; public class ReactExoplayerViewManager extends ViewGroupManager { private static final String REACT_CLASS = "RCTVideo"; - private static final String PROP_SRC = "src"; private static final String PROP_SRC_URI = "uri"; + + private static final String PROP_SRC_START_TIME = "startTime"; + + private static final String PROP_SRC_END_TIME = "endTime"; private static final String PROP_AD_TAG_URL = "adTagUrl"; private static final String PROP_SRC_TYPE = "type"; private static final String PROP_DRM = "drm"; @@ -152,6 +155,8 @@ public class ReactExoplayerViewManager extends ViewGroupManager headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null; @@ -164,7 +169,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Thu, 9 Feb 2023 13:53:06 +0200 Subject: [PATCH 05/31] Fixed android build error --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 0acc0d00..00983c4e 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -806,7 +806,7 @@ class ReactExoplayerView extends FrameLayout implements } switch (type) { case CONTENT_TYPE_SS: - mediaSource = SsMediaSource.Factory( + mediaSource = new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) @@ -815,7 +815,7 @@ class ReactExoplayerView extends FrameLayout implements ).createMediaSource(mediaItem); break; case CONTENT_TYPE_DASH: - mediaSource = DashMediaSource.Factory( + mediaSource = new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) @@ -824,7 +824,7 @@ class ReactExoplayerView extends FrameLayout implements ).createMediaSource(mediaItem); break; case CONTENT_TYPE_HLS: - mediaSource = HlsMediaSource.Factory( + mediaSource = new HlsMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( @@ -832,7 +832,7 @@ class ReactExoplayerView extends FrameLayout implements ).createMediaSource(mediaItem); break; case CONTENT_TYPE_OTHER: - mediaSource = ProgressiveMediaSource.Factory( + mediaSource = new ProgressiveMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( From 63625b3ce35ded91dfeb9cc32b87f18c1c553567 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 13 Feb 2023 16:07:09 +0200 Subject: [PATCH 06/31] Adding documentation --- API.md | 11 +++++++++++ CHANGELOG.md | 3 +++ 2 files changed, 14 insertions(+) diff --git a/API.md b/API.md index a769aeba..1a012085 100644 --- a/API.md +++ b/API.md @@ -916,6 +916,17 @@ The following other types are supported on some platforms, but aren't fully docu `content://, ms-appx://, ms-appdata://, assets-library://` +##### Playing only a portion of the video (start & end time) + +Provide an optional `startTime` and/or `endTime` for the video. Value is in milliseconds. Useful when you want to play only a portion of a large video. + +Example +``` +source={{ startTime: 36012, endTime: 48500 }} +``` + +Platforms: iOS, Android + #### subtitleStyle Property | Description | Platforms diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b54f1c..f46f893e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## Changelog +### Version 6.0.0-alpha.6 +- Feature: Video range support [#3030](https://github.com/react-native-video/react-native-video/pull/3030) + ### Version 6.0.0-alpha.5 - iOS: ensure controls are not displayed when disabled by user [#3017](https://github.com/react-native-video/react-native-video/pull/3017) From e3fb49cebb51c23c1cc4f7b0a270c600b866f363 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 13 Feb 2023 17:30:24 +0200 Subject: [PATCH 07/31] Android formatting fixes --- .../exoplayer/ReactExoplayerView.java | 18 ++---------------- .../exoplayer/ReactExoplayerViewManager.java | 2 -- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 00983c4e..6e538fe8 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -182,9 +182,7 @@ class ReactExoplayerView extends FrameLayout implements // Props from React private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; private Uri srcUri; - private long startTimeMs = -1; - private long endTimeMs = -1; private String extension; private boolean repeat; @@ -769,13 +767,7 @@ class ReactExoplayerView extends FrameLayout implements } } - private MediaSource buildMediaSource( - Uri uri, - String overrideExtension, - DrmSessionManager drmSessionManager, - long startTimeMs, - long endTimeMs) { - + private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager, long startTimeMs, long endTimeMs) { if (uri == null) { throw new IllegalStateException("Invalid video uri"); } @@ -1487,13 +1479,7 @@ class ReactExoplayerView extends FrameLayout implements // ReactExoplayerViewManager public api - public void setSrc( - final Uri uri, - final long startTimeMs, - final long endTimeMs, - final String extension, - Map headers) { - + public void setSrc(final Uri uri, final long startTimeMs, final long endTimeMs, final String extension, Map headers) { if (uri != null) { boolean isSourceEqual = uri.equals(srcUri) && startTimeMs == this.startTimeMs && endTimeMs == this.endTimeMs; hasDrmFailed = false; diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 6f2b1296..e0468985 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -30,9 +30,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Mon, 13 Feb 2023 17:57:13 +0200 Subject: [PATCH 08/31] android support for partial video range --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 00983c4e..8e4e9850 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -4,6 +4,7 @@ import static com.google.android.exoplayer2.C.CONTENT_TYPE_DASH; import static com.google.android.exoplayer2.C.CONTENT_TYPE_HLS; import static com.google.android.exoplayer2.C.CONTENT_TYPE_OTHER; import static com.google.android.exoplayer2.C.CONTENT_TYPE_SS; +import static com.google.android.exoplayer2.C.TIME_END_OF_SOURCE; import android.annotation.SuppressLint; import android.app.Activity; @@ -844,9 +845,13 @@ class ReactExoplayerView extends FrameLayout implements } } - if(startTimeMs >= 0 && endTimeMs >= 0) + if (startTimeMs >= 0 && endTimeMs >= 0) { return new ClippingMediaSource(mediaSource, startTimeMs * 1000, endTimeMs * 1000); + } else if (startTimeMs >= 0) { + return new ClippingMediaSource(mediaSource, startTimeMs * 1000, TIME_END_OF_SOURCE); + } else if (endTimeMs >= 0) { + return new ClippingMediaSource(mediaSource, 0, endTimeMs * 1000); } return mediaSource; From dd4e50fca31fa92c2e09388fe9eef4270ece1b13 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 13 Feb 2023 18:14:20 +0200 Subject: [PATCH 09/31] updated documentation --- API.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/API.md b/API.md index 1a012085..ff968f06 100644 --- a/API.md +++ b/API.md @@ -922,7 +922,11 @@ Provide an optional `startTime` and/or `endTime` for the video. Value is in mill Example ``` -source={{ startTime: 36012, endTime: 48500 }} +source={{ uri: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8', startTime: 36012, endTime: 48500 }} + +source={{ uri: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8', startTime: 36012 }} + +source={{ uri: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8', endTime: 48500 }} ``` Platforms: iOS, Android From 2fc71935805e3e87a8921af6e9cfa18b8ac7de47 Mon Sep 17 00:00:00 2001 From: Roman Melnyk Date: Fri, 3 Mar 2023 16:47:05 +0200 Subject: [PATCH 10/31] Print error for configureAudio method try catch blocks. Add fallback for error: 'what' (AVAudioSessionErrorCodeUnspecified). --- ios/Video/Features/RCTPlayerOperations.swift | 29 +++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift index 8b76fafc..453a0a0c 100644 --- a/ios/Video/Features/RCTPlayerOperations.swift +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -192,36 +192,51 @@ enum RCTPlayerOperations { } static func configureAudio(ignoreSilentSwitch:String, mixWithOthers:String) { - let session:AVAudioSession! = AVAudioSession.sharedInstance() + let audioSession:AVAudioSession! = AVAudioSession.sharedInstance() var category:AVAudioSession.Category? = nil var options:AVAudioSession.CategoryOptions? = nil - + if (ignoreSilentSwitch == "ignore") { category = AVAudioSession.Category.playback } else if (ignoreSilentSwitch == "obey") { category = AVAudioSession.Category.ambient } - + if (mixWithOthers == "mix") { options = .mixWithOthers } else if (mixWithOthers == "duck") { options = .duckOthers } - + if let category = category, let options = options { do { - try session.setCategory(category, options: options) + try audioSession.setCategory(category, options: options) } catch { + debugPrint("[RCTPlayerOperations] Problem setting up AVAudioSession category and options. Error: \(error).") + // Handle specific set category abd option combination error + // setCategory:AVAudioSessionCategoryPlayback withOptions:mixWithOthers || duckOthers + // Failed to set category, error: 'what' Error Domain=NSOSStatusErrorDomain + // https://developer.apple.com/forums/thread/714598 + if #available(iOS 16.0, *) { + do { + debugPrint("[RCTPlayerOperations] Reseting AVAudioSession category to playAndRecord with defaultToSpeaker options.") + try audioSession.setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker) + } catch { + debugPrint("[RCTPlayerOperations] Reseting AVAudioSession category and options problem. Error: \(error).") + } + } } } else if let category = category, options == nil { do { - try session.setCategory(category) + try audioSession.setCategory(category) } catch { + debugPrint("[RCTPlayerOperations] Problem setting up AVAudioSession category. Error: \(error).") } } else if category == nil, let options = options { do { - try session.setCategory(session.category, options: options) + try audioSession.setCategory(audioSession.category, options: options) } catch { + debugPrint("[RCTPlayerOperations] Problem setting up AVAudioSession options. Error: \(error).") } } } From e3b685b8e36cdaf60d5e0e9417d04a091f3c07bd Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 13 Mar 2023 16:14:04 +0200 Subject: [PATCH 11/31] Update Video.js --- Video.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Video.js b/Video.js index 87aa103a..03fc95bf 100644 --- a/Video.js +++ b/Video.js @@ -342,7 +342,7 @@ export default class Video extends Component { mainVer: source.mainVer || 0, patchVer: source.patchVer || 0, requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {}, - startTime: source.startTime, + startTime: source.startTime || 0, endTime: source.endTime }, onVideoLoadStart: this._onLoadStart, From 2ef2b8eb98b96adb5f8cd645686a5ba7f8a628bc Mon Sep 17 00:00:00 2001 From: Sunbreak Date: Wed, 15 Mar 2023 00:09:31 +0800 Subject: [PATCH 12/31] fix: remove undocumented currentTime property --- CHANGELOG.md | 4 ++++ Video.js | 1 - ios/Video/RCTVideo.swift | 14 ++++---------- ios/Video/RCTVideoManager.m | 1 - 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b54f1c..b3ad99b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Changelog +### Version 6.0.0-alpha.6 + +- iOS: remove undocumented `currentTime` property [#3064](https://github.com/react-native-video/react-native-video/pull/3064) + ### Version 6.0.0-alpha.5 - iOS: ensure controls are not displayed when disabled by user [#3017](https://github.com/react-native-video/react-native-video/pull/3017) diff --git a/Video.js b/Video.js index ac6bc085..bbd7d621 100644 --- a/Video.js +++ b/Video.js @@ -520,7 +520,6 @@ Video.propTypes = { disableBuffering: PropTypes.bool, controls: PropTypes.bool, audioOnly: PropTypes.bool, - currentTime: PropTypes.number, fullscreenAutorotate: PropTypes.bool, fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']), progressUpdateInterval: PropTypes.number, diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index be1c0981..fee5eb60 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -448,15 +448,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _paused = paused } - @objc - func setCurrentTime(_ currentTime:Float) { - let info:NSDictionary = [ - "time": NSNumber(value: currentTime), - "tolerance": NSNumber(value: 100) - ] - setSeek(info) - } - @objc func setSeek(_ info:NSDictionary!) { let seekTime:NSNumber! = info["time"] as! NSNumber @@ -996,7 +987,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } if _pendingSeek { - setCurrentTime(_pendingSeekTime) + setSeek([ + "time": NSNumber(value: _pendingSeekTime), + "tolerance": NSNumber(value: 100) + ]) _pendingSeek = false } diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 2781656b..0d503d14 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -27,7 +27,6 @@ RCT_EXPORT_VIEW_PROPERTY(ignoreSilentSwitch, NSString); RCT_EXPORT_VIEW_PROPERTY(mixWithOthers, NSString); RCT_EXPORT_VIEW_PROPERTY(rate, float); RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary); -RCT_EXPORT_VIEW_PROPERTY(currentTime, float); RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL); RCT_EXPORT_VIEW_PROPERTY(fullscreenAutorotate, BOOL); RCT_EXPORT_VIEW_PROPERTY(fullscreenOrientation, NSString); From d9e4b1efecd3cc2f6092e0e541e886403f59e849 Mon Sep 17 00:00:00 2001 From: Sunbreak Date: Wed, 15 Mar 2023 09:58:16 +0800 Subject: [PATCH 13/31] fix: remove dummy scaleX/Y & translateX/Y property --- Video.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Video.js b/Video.js index ac6bc085..0f6bb32b 100644 --- a/Video.js +++ b/Video.js @@ -563,11 +563,6 @@ Video.propTypes = { onReceiveAdEvent: PropTypes.func, /* Required by react-native */ - scaleX: PropTypes.number, - scaleY: PropTypes.number, - translateX: PropTypes.number, - translateY: PropTypes.number, - rotation: PropTypes.number, ...ViewPropTypes, }; From 822f8c077476383fbadd2a47c3c4d27d9f8ee082 Mon Sep 17 00:00:00 2001 From: Sunbreak Date: Wed, 15 Mar 2023 09:54:48 +0800 Subject: [PATCH 14/31] fix: remove dummy needsToRestoreUserInterfaceForPictureInPictureStop --- Video.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Video.js b/Video.js index ac6bc085..2aad451a 100644 --- a/Video.js +++ b/Video.js @@ -557,7 +557,6 @@ Video.propTypes = { onAudioFocusChanged: PropTypes.func, onAudioBecomingNoisy: PropTypes.func, onPictureInPictureStatusChanged: PropTypes.func, - needsToRestoreUserInterfaceForPictureInPictureStop: PropTypes.func, onExternalPlaybackChange: PropTypes.func, adTagUrl: PropTypes.string, onReceiveAdEvent: PropTypes.func, From 9519c7bae76486e41b6ab2cf31afa3b8280448dd Mon Sep 17 00:00:00 2001 From: Francesco Benigno Date: Tue, 28 Mar 2023 13:14:48 +0200 Subject: [PATCH 15/31] set the ad volume to 0 when the player is muted on iOS --- ios/Video/Features/RCTIMAAdsManager.swift | 4 ++++ ios/Video/RCTVideo.swift | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/ios/Video/Features/RCTIMAAdsManager.swift b/ios/Video/Features/RCTIMAAdsManager.swift index 059ec639..21884857 100644 --- a/ios/Video/Features/RCTIMAAdsManager.swift +++ b/ios/Video/Features/RCTIMAAdsManager.swift @@ -76,6 +76,10 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate { // MARK: - IMAAdsManagerDelegate func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) { + // Mute ad if the main player is muted + if (_video.isMuted()) { + adsManager.volume = 0; + } // Play each ad once it has been loaded if event.type == IMAAdEventType.LOADED { adsManager.start() diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index be1c0981..cc0df2c6 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -496,6 +496,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH applyModifiers() } + @objc + func isMuted() -> Bool { + return _muted + } + @objc func setMuted(_ muted:Bool) { _muted = muted From 86cd6220bed6fc0c4db048f5e89d8a41b9094b50 Mon Sep 17 00:00:00 2001 From: Francesco Benigno Date: Tue, 28 Mar 2023 13:25:10 +0200 Subject: [PATCH 16/31] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b54f1c..045d3aca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Version 6.0.0-alpha.5 +- iOS: make sure that the audio in ads is muted when the player is muted. [#3068](https://github.com/react-native-video/react-native-video/pull/3077) - iOS: ensure controls are not displayed when disabled by user [#3017](https://github.com/react-native-video/react-native-video/pull/3017) - iOS: app crashes on call to presentFullScreenPlayer [#2808](https://github.com/react-native-video/react-native-video/pull/2971) - Android: Fix publicated progress handler causing duplicated progress event [#2972](https://github.com/react-native-video/react-native-video/pull/2972) From 2228838075c8901acb53691fd1c7aeb23fec858e Mon Sep 17 00:00:00 2001 From: olivier Date: Wed, 5 Apr 2023 22:41:27 +0200 Subject: [PATCH 17/31] chore: fix changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee575161..660d5573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,11 @@ ### Version 6.0.0-alpha.6 - iOS: remove undocumented `currentTime` property [#3064](https://github.com/react-native-video/react-native-video/pull/3064) +- iOS: make sure that the audio in ads is muted when the player is muted. [#3068](https://github.com/react-native-video/react-native-video/pull/3077) +- iOS: make IMA build optionnal ### Version 6.0.0-alpha.5 -- iOS: make sure that the audio in ads is muted when the player is muted. [#3068](https://github.com/react-native-video/react-native-video/pull/3077) - iOS: ensure controls are not displayed when disabled by user [#3017](https://github.com/react-native-video/react-native-video/pull/3017) - iOS: app crashes on call to presentFullScreenPlayer [#2808](https://github.com/react-native-video/react-native-video/pull/2971) - Android: Fix publicated progress handler causing duplicated progress event [#2972](https://github.com/react-native-video/react-native-video/pull/2972) From 87859bcc791d896826825e0bf1296bdd972e76ff Mon Sep 17 00:00:00 2001 From: Romick2005 <17779660+Romick2005@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:50:22 +0300 Subject: [PATCH 18/31] Update ios/Video/Features/RCTPlayerOperations.swift Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> --- ios/Video/Features/RCTPlayerOperations.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift index 453a0a0c..a6156445 100644 --- a/ios/Video/Features/RCTPlayerOperations.swift +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -213,7 +213,7 @@ enum RCTPlayerOperations { try audioSession.setCategory(category, options: options) } catch { debugPrint("[RCTPlayerOperations] Problem setting up AVAudioSession category and options. Error: \(error).") - // Handle specific set category abd option combination error + // Handle specific set category and option combination error // setCategory:AVAudioSessionCategoryPlayback withOptions:mixWithOthers || duckOthers // Failed to set category, error: 'what' Error Domain=NSOSStatusErrorDomain // https://developer.apple.com/forums/thread/714598 From de8c5329163a33fbd7da45e80cdc6f3b7f6ab096 Mon Sep 17 00:00:00 2001 From: olivier Date: Wed, 5 Apr 2023 23:16:38 +0200 Subject: [PATCH 19/31] chore: add dummy test rule to ensure np works --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index aa671cd9..9f25d438 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "license": "MIT", "author": "Community Contributors", "homepage": "https://github.com/react-native-video/react-native-video#readme", + "test": "", "repository": { "type": "git", "url": "git@github.com:react-native-video/react-native-video.git" From d9404951eb0dfa6eb5fdc6866a68311ffe4d9979 Mon Sep 17 00:00:00 2001 From: olivier Date: Wed, 5 Apr 2023 23:21:11 +0200 Subject: [PATCH 20/31] chore: fix previous commit --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9f25d438..4257f413 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "license": "MIT", "author": "Community Contributors", "homepage": "https://github.com/react-native-video/react-native-video#readme", - "test": "", "repository": { "type": "git", "url": "git@github.com:react-native-video/react-native-video.git" @@ -25,7 +24,8 @@ }, "scripts": { "lint": "yarn eslint .", - "xbasic": "yarn --cwd examples/basic" + "xbasic": "yarn --cwd examples/basic", + "test": "echo no test available" }, "files": [ "android", From e08ef2d5abfc0eb597d42f9d375fa61fe2ac4020 Mon Sep 17 00:00:00 2001 From: olivier Date: Wed, 5 Apr 2023 23:23:39 +0200 Subject: [PATCH 21/31] 6.0.0-alpha.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4257f413..4ffa8600 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-video", - "version": "6.0.0-alpha.5", + "version": "6.0.0-alpha.6", "description": "A