Merge pull request #3030 from yavor87/AndroidRangePlayback
Feature: Video range support
This commit is contained in:
commit
d4b92dab4a
15
API.md
15
API.md
@ -927,6 +927,21 @@ The following other types are supported on some platforms, but aren't fully docu
|
|||||||
`content://, ms-appx://, ms-appdata://, assets-library://`
|
`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={{ 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
|
||||||
|
|
||||||
#### subtitleStyle
|
#### subtitleStyle
|
||||||
|
|
||||||
Property | Description | Platforms
|
Property | Description | Platforms
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
### Version 6.0.0-alpha.6
|
### Version 6.0.0-alpha.6
|
||||||
|
- Feature: Video range support [#3030](https://github.com/react-native-video/react-native-video/pull/3030)
|
||||||
- iOS: remove undocumented `currentTime` property [#3064](https://github.com/react-native-video/react-native-video/pull/3064)
|
- 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 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
|
- iOS: make IMA build optionnal
|
||||||
|
2
Video.js
2
Video.js
@ -342,6 +342,8 @@ export default class Video extends Component {
|
|||||||
mainVer: source.mainVer || 0,
|
mainVer: source.mainVer || 0,
|
||||||
patchVer: source.patchVer || 0,
|
patchVer: source.patchVer || 0,
|
||||||
requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {},
|
requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {},
|
||||||
|
startTime: source.startTime || 0,
|
||||||
|
endTime: source.endTime
|
||||||
},
|
},
|
||||||
onVideoLoadStart: this._onLoadStart,
|
onVideoLoadStart: this._onLoadStart,
|
||||||
onVideoPlaybackStateChanged: this._onPlaybackStateChanged,
|
onVideoPlaybackStateChanged: this._onPlaybackStateChanged,
|
||||||
|
@ -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_HLS;
|
||||||
import static com.google.android.exoplayer2.C.CONTENT_TYPE_OTHER;
|
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.CONTENT_TYPE_SS;
|
||||||
|
import static com.google.android.exoplayer2.C.TIME_END_OF_SOURCE;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@ -93,6 +94,7 @@ import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
|||||||
import com.google.android.exoplayer2.ext.ima.ImaAdsLoader;
|
import com.google.android.exoplayer2.ext.ima.ImaAdsLoader;
|
||||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||||
|
import com.google.android.exoplayer2.source.ClippingMediaSource;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
@ -181,6 +183,8 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
// Props from React
|
// Props from React
|
||||||
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
|
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
|
||||||
private Uri srcUri;
|
private Uri srcUri;
|
||||||
|
private long startTimeMs = -1;
|
||||||
|
private long endTimeMs = -1;
|
||||||
private String extension;
|
private String extension;
|
||||||
private boolean repeat;
|
private boolean repeat;
|
||||||
private String audioTrackType;
|
private String audioTrackType;
|
||||||
@ -669,7 +673,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) {
|
private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) {
|
||||||
ArrayList<MediaSource> mediaSourceList = buildTextSources();
|
ArrayList<MediaSource> mediaSourceList = buildTextSources();
|
||||||
MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager);
|
MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager, startTimeMs, endTimeMs);
|
||||||
MediaSource mediaSourceWithAds = null;
|
MediaSource mediaSourceWithAds = null;
|
||||||
if (adTagUrl != null) {
|
if (adTagUrl != null) {
|
||||||
MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
|
MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
|
||||||
@ -764,7 +768,7 @@ 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) {
|
if (uri == null) {
|
||||||
throw new IllegalStateException("Invalid video uri");
|
throw new IllegalStateException("Invalid video uri");
|
||||||
}
|
}
|
||||||
@ -781,7 +785,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
MediaItem mediaItem = mediaItemBuilder.build();
|
MediaItem mediaItem = mediaItemBuilder.build();
|
||||||
|
MediaSource mediaSource = null;
|
||||||
DrmSessionManagerProvider drmProvider = null;
|
DrmSessionManagerProvider drmProvider = null;
|
||||||
if (drmSessionManager != null) {
|
if (drmSessionManager != null) {
|
||||||
drmProvider = new DrmSessionManagerProvider() {
|
drmProvider = new DrmSessionManagerProvider() {
|
||||||
@ -795,39 +799,54 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CONTENT_TYPE_SS:
|
case CONTENT_TYPE_SS:
|
||||||
return new SsMediaSource.Factory(
|
mediaSource = new SsMediaSource.Factory(
|
||||||
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
||||||
buildDataSourceFactory(false)
|
buildDataSourceFactory(false)
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
).setDrmSessionManagerProvider(drmProvider)
|
||||||
.setLoadErrorHandlingPolicy(
|
.setLoadErrorHandlingPolicy(
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
).createMediaSource(mediaItem);
|
).createMediaSource(mediaItem);
|
||||||
|
break;
|
||||||
case CONTENT_TYPE_DASH:
|
case CONTENT_TYPE_DASH:
|
||||||
return new DashMediaSource.Factory(
|
mediaSource = new DashMediaSource.Factory(
|
||||||
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
||||||
buildDataSourceFactory(false)
|
buildDataSourceFactory(false)
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
).setDrmSessionManagerProvider(drmProvider)
|
||||||
.setLoadErrorHandlingPolicy(
|
.setLoadErrorHandlingPolicy(
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
).createMediaSource(mediaItem);
|
).createMediaSource(mediaItem);
|
||||||
|
break;
|
||||||
case CONTENT_TYPE_HLS:
|
case CONTENT_TYPE_HLS:
|
||||||
return new HlsMediaSource.Factory(
|
mediaSource = new HlsMediaSource.Factory(
|
||||||
mediaDataSourceFactory
|
mediaDataSourceFactory
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
).setDrmSessionManagerProvider(drmProvider)
|
||||||
.setLoadErrorHandlingPolicy(
|
.setLoadErrorHandlingPolicy(
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
).createMediaSource(mediaItem);
|
).createMediaSource(mediaItem);
|
||||||
|
break;
|
||||||
case CONTENT_TYPE_OTHER:
|
case CONTENT_TYPE_OTHER:
|
||||||
return new ProgressiveMediaSource.Factory(
|
mediaSource = new ProgressiveMediaSource.Factory(
|
||||||
mediaDataSourceFactory
|
mediaDataSourceFactory
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
).setDrmSessionManagerProvider(drmProvider)
|
||||||
.setLoadErrorHandlingPolicy(
|
.setLoadErrorHandlingPolicy(
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
).createMediaSource(mediaItem);
|
).createMediaSource(mediaItem);
|
||||||
|
break;
|
||||||
default: {
|
default: {
|
||||||
throw new IllegalStateException("Unsupported type: " + type);
|
throw new IllegalStateException("Unsupported type: " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<MediaSource> buildTextSources() {
|
private ArrayList<MediaSource> buildTextSources() {
|
||||||
@ -1465,11 +1484,13 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
// ReactExoplayerViewManager public api
|
// ReactExoplayerViewManager public api
|
||||||
|
|
||||||
public void setSrc(final Uri uri, final String extension, Map<String, String> headers) {
|
public void setSrc(final Uri uri, final long startTimeMs, final long endTimeMs, final String extension, Map<String, String> headers) {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
boolean isSourceEqual = uri.equals(srcUri);
|
boolean isSourceEqual = uri.equals(srcUri) && startTimeMs == this.startTimeMs && endTimeMs == this.endTimeMs;
|
||||||
hasDrmFailed = false;
|
hasDrmFailed = false;
|
||||||
this.srcUri = uri;
|
this.srcUri = uri;
|
||||||
|
this.startTimeMs = startTimeMs;
|
||||||
|
this.endTimeMs = endTimeMs;
|
||||||
this.extension = extension;
|
this.extension = extension;
|
||||||
this.requestHeaders = headers;
|
this.requestHeaders = headers;
|
||||||
this.mediaDataSourceFactory =
|
this.mediaDataSourceFactory =
|
||||||
@ -1487,6 +1508,8 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
player.stop();
|
player.stop();
|
||||||
player.clearMediaItems();
|
player.clearMediaItems();
|
||||||
this.srcUri = null;
|
this.srcUri = null;
|
||||||
|
this.startTimeMs = -1;
|
||||||
|
this.endTimeMs = -1;
|
||||||
this.extension = null;
|
this.extension = null;
|
||||||
this.requestHeaders = null;
|
this.requestHeaders = null;
|
||||||
this.mediaDataSourceFactory = null;
|
this.mediaDataSourceFactory = null;
|
||||||
|
@ -28,9 +28,10 @@ import javax.annotation.Nullable;
|
|||||||
public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerView> {
|
public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerView> {
|
||||||
|
|
||||||
private static final String REACT_CLASS = "RCTVideo";
|
private static final String REACT_CLASS = "RCTVideo";
|
||||||
|
|
||||||
private static final String PROP_SRC = "src";
|
private static final String PROP_SRC = "src";
|
||||||
private static final String PROP_SRC_URI = "uri";
|
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_AD_TAG_URL = "adTagUrl";
|
||||||
private static final String PROP_SRC_TYPE = "type";
|
private static final String PROP_SRC_TYPE = "type";
|
||||||
private static final String PROP_DRM = "drm";
|
private static final String PROP_DRM = "drm";
|
||||||
@ -152,6 +153,8 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src) {
|
public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src) {
|
||||||
Context context = videoView.getContext().getApplicationContext();
|
Context context = videoView.getContext().getApplicationContext();
|
||||||
String uriString = src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null;
|
String uriString = src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null;
|
||||||
|
int startTimeMs = src.hasKey(PROP_SRC_START_TIME) ? src.getInt(PROP_SRC_START_TIME) : -1;
|
||||||
|
int endTimeMs = src.hasKey(PROP_SRC_END_TIME) ? src.getInt(PROP_SRC_END_TIME) : -1;
|
||||||
String extension = src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null;
|
String extension = src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null;
|
||||||
Map<String, String> headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null;
|
Map<String, String> headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null;
|
||||||
|
|
||||||
@ -164,7 +167,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
Uri srcUri = Uri.parse(uriString);
|
Uri srcUri = Uri.parse(uriString);
|
||||||
|
|
||||||
if (srcUri != null) {
|
if (srcUri != null) {
|
||||||
videoView.setSrc(srcUri, extension, headers);
|
videoView.setSrc(srcUri, startTimeMs, endTimeMs, extension, headers);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int identifier = context.getResources().getIdentifier(
|
int identifier = context.getResources().getIdentifier(
|
||||||
|
@ -6,6 +6,8 @@ struct VideoSource {
|
|||||||
let isAsset: Bool
|
let isAsset: Bool
|
||||||
let shouldCache: Bool
|
let shouldCache: Bool
|
||||||
let requestHeaders: Dictionary<String,Any>?
|
let requestHeaders: Dictionary<String,Any>?
|
||||||
|
let startTime: Int64?
|
||||||
|
let endTime: Int64?
|
||||||
|
|
||||||
let json: NSDictionary?
|
let json: NSDictionary?
|
||||||
|
|
||||||
@ -18,6 +20,8 @@ struct VideoSource {
|
|||||||
self.isAsset = false
|
self.isAsset = false
|
||||||
self.shouldCache = false
|
self.shouldCache = false
|
||||||
self.requestHeaders = nil
|
self.requestHeaders = nil
|
||||||
|
self.startTime = nil
|
||||||
|
self.endTime = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.json = json
|
self.json = json
|
||||||
@ -27,5 +31,7 @@ struct VideoSource {
|
|||||||
self.isAsset = json["isAsset"] as? Bool ?? false
|
self.isAsset = json["isAsset"] as? Bool ?? false
|
||||||
self.shouldCache = json["shouldCache"] as? Bool ?? false
|
self.shouldCache = json["shouldCache"] as? Bool ?? false
|
||||||
self.requestHeaders = json["requestHeaders"] as? Dictionary<String,Any>
|
self.requestHeaders = json["requestHeaders"] as? Dictionary<String,Any>
|
||||||
|
self.startTime = json["startTime"] as? Int64
|
||||||
|
self.endTime = json["endTime"] as? Int64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,17 @@ enum RCTVideoUtils {
|
|||||||
*
|
*
|
||||||
* \returns The playable duration of the current player item in seconds.
|
* \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,
|
guard let player = player,
|
||||||
let video:AVPlayerItem = player.currentItem,
|
let video:AVPlayerItem = player.currentItem,
|
||||||
video.status == AVPlayerItem.Status.readyToPlay else {
|
video.status == AVPlayerItem.Status.readyToPlay else {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (source?.startTime != nil && source?.endTime != nil) {
|
||||||
|
return NSNumber(value: (Float64(source?.endTime ?? 0) - Float64(source?.startTime ?? 0)) / 1000)
|
||||||
|
}
|
||||||
|
|
||||||
var effectiveTimeRange:CMTimeRange?
|
var effectiveTimeRange:CMTimeRange?
|
||||||
for (_, value) in video.loadedTimeRanges.enumerated() {
|
for (_, value) in video.loadedTimeRanges.enumerated() {
|
||||||
let timeRange:CMTimeRange = value.timeRangeValue
|
let timeRange:CMTimeRange = value.timeRangeValue
|
||||||
@ -31,6 +35,10 @@ enum RCTVideoUtils {
|
|||||||
if let effectiveTimeRange = effectiveTimeRange {
|
if let effectiveTimeRange = effectiveTimeRange {
|
||||||
let playableDuration:Float64 = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange))
|
let playableDuration:Float64 = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange))
|
||||||
if playableDuration > 0 {
|
if playableDuration > 0 {
|
||||||
|
if (source?.startTime != nil) {
|
||||||
|
return NSNumber(value: (playableDuration - Float64(source?.startTime ?? 0) / 1000))
|
||||||
|
}
|
||||||
|
|
||||||
return playableDuration as NSNumber
|
return playableDuration as NSNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
return
|
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 currentPlaybackTime = _player?.currentItem?.currentDate()
|
||||||
let duration = CMTimeGetSeconds(playerDuration)
|
let duration = CMTimeGetSeconds(playerDuration)
|
||||||
let currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero)
|
let currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero)
|
||||||
@ -231,7 +234,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
#endif
|
#endif
|
||||||
onVideoProgress?([
|
onVideoProgress?([
|
||||||
"currentTime": NSNumber(value: Float(currentTimeSecs)),
|
"currentTime": NSNumber(value: Float(currentTimeSecs)),
|
||||||
"playableDuration": RCTVideoUtils.calculatePlayableDuration(_player),
|
"playableDuration": RCTVideoUtils.calculatePlayableDuration(_player, withSource: _source),
|
||||||
"atValue": NSNumber(value: currentTime?.value ?? .zero),
|
"atValue": NSNumber(value: currentTime?.value ?? .zero),
|
||||||
"currentPlaybackTime": NSNumber(value: NSNumber(value: floor(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value),
|
"currentPlaybackTime": NSNumber(value: NSNumber(value: floor(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value),
|
||||||
"target": reactTag,
|
"target": reactTag,
|
||||||
@ -300,6 +303,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
self._playerItem = playerItem
|
self._playerItem = playerItem
|
||||||
self._playerObserver.playerItem = self._playerItem
|
self._playerObserver.playerItem = self._playerItem
|
||||||
self.setPreferredForwardBufferDuration(self._preferredForwardBufferDuration)
|
self.setPreferredForwardBufferDuration(self._preferredForwardBufferDuration)
|
||||||
|
self.setPlaybackRange(playerItem, withVideoStart: self._source?.startTime, withVideoEnd: self._source?.endTime)
|
||||||
self.setFilter(self._filterName)
|
self.setFilter(self._filterName)
|
||||||
if let maxBitRate = self._maxBitRate {
|
if let maxBitRate = self._maxBitRate {
|
||||||
self._playerItem?.preferredPeakBitRate = Double(maxBitRate)
|
self._playerItem?.preferredPeakBitRate = Double(maxBitRate)
|
||||||
@ -543,6 +547,18 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPlaybackRange(_ item:AVPlayerItem!, withVideoStart videoStart:Int64?, withVideoEnd videoEnd:Int64?) {
|
||||||
|
if (videoStart != nil) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func applyModifiers() {
|
func applyModifiers() {
|
||||||
if _muted {
|
if _muted {
|
||||||
|
Loading…
Reference in New Issue
Block a user