diff --git a/README.md b/README.md index 6e8da8a..045b708 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This package implements native binding for Amazon IVS Player for iOS and Android Mobile Only. | Android Mobile | iOS | Android TV | tvOS | | :-----: | :-: | :---: | :-: | -| SDK >= 21 | >= 13 | **X** | **X** | +| SDK >= 24 | >= 15.1 | **X** | **X** | ## Installation diff --git a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt index 2483c46..ed8b6b2 100644 --- a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt +++ b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt @@ -80,11 +80,13 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte LOAD_START("onLoadStart"), REBUFFERING("onRebuffering"), SEEK("onSeek"), + SEEK_COMPLETE("onSeekComplete"), DATA("onData"), LIVE_LATENCY_CHANGED("onLiveLatencyChange"), VIDEO_STATISTICS("onVideoStatistics"), PROGRESS("onProgress"), - TIME_POINT("onTimePoint"); + TIME_POINT("onTimePoint"), + VIDEO_SIZE_CHANGE("onVideoSizeChange"); override fun toString(): String { return mName @@ -125,6 +127,7 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte override fun onSeekCompleted(position: Long) { onSeek(position) + onSeekComplete(true) } override fun onQualityChanged(quality: Quality) { @@ -132,6 +135,8 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte } override fun onVideoSizeChanged(p0: Int, p1: Int) { + onVideoSizeChange(p0, p1) + post(mLayoutRunnable) } @@ -302,9 +307,13 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte } fun setQuality(quality: ReadableMap?) { - if (quality != null) { - findQuality(quality)?.let { - player?.quality = it + if (quality == null) return + val target = if (quality.hasKey("target")) quality.getMap("target") else null + val isAdaptive = if (quality.hasKey("adaptive")) quality.getBoolean("adaptive") else true + + if (target != null) { + findQuality(target)?.let { selectedQuality-> + player?.setQuality(selectedQuality, isAdaptive) } } } @@ -610,6 +619,40 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte ) } + fun onVideoSizeChange(p0: Int, p1: Int) { + val reactContext = context as ReactContext + val size = Arguments.createMap() + size.putInt("height", p0) + size.putInt("width", p1) + + val data = Arguments.createMap() + data.putMap("size", size) + + eventDispatcher.dispatchEvent( + IVSEvent( + getSurfaceId(reactContext), + id, + Events.VIDEO_SIZE_CHANGE, + data + ) + ) + } + + fun onSeekComplete(success: Boolean) { + val reactContext = context as ReactContext + val data = Arguments.createMap() + data.putBoolean("success", success) + + eventDispatcher.dispatchEvent( + IVSEvent( + getSurfaceId(reactContext), + id, + Events.SEEK_COMPLETE, + data + ) + ) + } + fun onPlayerRebuffering() { val reactContext = context as ReactContext @@ -784,6 +827,19 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte }, 0, updatedProgressInterval) } + fun setMaxVideoSize(maxSize: ReadableMap?) { + val width = maxSize?.getInt("width") + val height = maxSize?.getInt("height") + + if (width != null && height != null) { + player?.setAutoMaxVideoSize(width, height) + } + } + + fun setNetworkRecoveryMode(mode: String?) { + player?.setNetworkRecoveryMode(findNetworkRecoveryMode((mode))) + } + override fun onHostResume() { isInBackground = false stopBackgroundService() @@ -867,4 +923,12 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte } } + private fun findNetworkRecoveryMode(mode: String?): Player.NetworkRecoveryMode { + return when (mode) { + "none" -> Player.NetworkRecoveryMode.NONE + "resume" -> Player.NetworkRecoveryMode.RESUME + else -> Player.NetworkRecoveryMode.NONE + } + } + } diff --git a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt index cab3914..cbea7f1 100644 --- a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt +++ b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt @@ -189,6 +189,20 @@ class AmazonIvsViewManager : SimpleViewManager(), view?.setProgressInterval(value) } + override fun setMaxVideoSize( + view: AmazonIvsView?, + value: ReadableMap? + ) { + view?.setMaxVideoSize(value) + } + + override fun setNetworkRecoveryMode( + view: AmazonIvsView?, + value: String? + ) { + view?.setNetworkRecoveryMode(value) + } + override fun setPlayInBackground( view: AmazonIvsView?, value: Boolean diff --git a/docs/types.md b/docs/types.md index 4aa058d..fa10ca7 100644 --- a/docs/types.md +++ b/docs/types.md @@ -75,6 +75,12 @@ export type TextMetadataCue = { export type ResizeMode = 'aspectFill' | 'aspectFit' | 'aspectZoom'; ``` +## NetworkRecoveryMode + +```ts +export type NetworkRecoveryMode = 'none' | 'resume'; +``` + ## Source ```ts diff --git a/docs/usage-guide.md b/docs/usage-guide.md index 12726e0..bccbc51 100644 --- a/docs/usage-guide.md +++ b/docs/usage-guide.md @@ -46,6 +46,13 @@ You can also set the video volume or its quality using component props. The whol In order to set video quality, you need to get the list of available qualities that come from the `onData` callback. +Optional `adaptive` flag: + +- `adaptive: false`: Hard-locks the player to the specific resolution. It disables auto-quality (ABR). Use this when a user explicitly selects a resolution (e.g., "1080p"). + +- `adaptive: true`: Switches to the target resolution immediately but keeps auto-quality (ABR) enabled. The player may switch away from this resolution later if network conditions change. + + ```tsx import IVSPlayer, { Quality } from 'amazon-ivs-react-native'; @@ -56,7 +63,31 @@ export default function App() { setQualities(data.qualities)} - quality={qualities[0]} + quality={{ + target: qualities[0], + adaptive: true // Optional: defaults to true. Set to false to force manual mode. + }} + /> + ); +} +``` + +## LIMITING MAXIMUM VIDEO SIZE + +You can limit the maximum video resolution chosen by the auto-quality algorithm using the `maxVideoSize` prop. This is useful for saving bandwidth or restricting quality on specific devices. + +```tsx +import IVSPlayer, { Quality } from 'amazon-ivs-react-native'; + +export default function App() { + const [qualities, setQualities] = useState(); + + return ( + ); } @@ -129,6 +160,10 @@ You can find the full list of events in the [api-reference](./ivs-player-referen onTimePoint={(timePoint) => { console.log('time point', timePoint) }} + onVideoSizeChange={(videoSize) => { + console.log('video width', videoSize.size.width) + console.log('video height', videoSize.size.height) + }} /> ``` @@ -153,7 +188,13 @@ export default function App() { }; const handleSeekToPress = () => { + // Basic usage mediaPlayerRef?.current?.seekTo(15); + + // OR usage with completion callback + mediaPlayerRef?.current?.seekTo(15, (success) => { + console.log('Seek finished, success:', success); + }); }; const handleTogglePipToPress = () => { diff --git a/example/src/screens/PlaygroundExample.tsx b/example/src/screens/PlaygroundExample.tsx index 3281c91..2d89840 100644 --- a/example/src/screens/PlaygroundExample.tsx +++ b/example/src/screens/PlaygroundExample.tsx @@ -174,7 +174,9 @@ export default function PlaygroundExample() { progressInterval={progressInterval} volume={volume} autoQualityMode={autoQualityMode} - quality={manualQuality} + quality={{ + target: manualQuality, + }} autoMaxQuality={autoMaxQuality} breakpoints={breakpoints} onSeek={(newPosition) => console.log('new position', newPosition)} diff --git a/ios/AmazonIvs.mm b/ios/AmazonIvs.mm index f8ab241..dcccd6e 100644 --- a/ios/AmazonIvs.mm +++ b/ios/AmazonIvs.mm @@ -29,8 +29,7 @@ + (ComponentDescriptorProvider)componentDescriptorProvider { } // disable view recycling otherwise AV resourses fail to load on recycled view -+ (BOOL)shouldBeRecycled -{ ++ (BOOL)shouldBeRecycled { return NO; } @@ -112,6 +111,14 @@ - (instancetype)initWithFrame:(CGRect)frame { _ivsView.onTimePoint = ^(NSDictionary *onTimePointPayload) { [weakSelf onTimePoint:onTimePointPayload]; }; + + _ivsView.onVideoSizeChange = ^(NSDictionary *onVideoSizeChangePayload) { + [weakSelf onVideoSizeChange:onVideoSizeChangePayload]; + }; + + _ivsView.onSeekComplete = ^(NSDictionary *onSeekCompletePayload) { + [weakSelf onSeekComplete:onSeekCompletePayload]; + }; } self.contentView = _ivsView; @@ -167,8 +174,12 @@ - (void)updateProps:(Props::Shared const &)props _ivsView.volume = newViewProps.volume; } - if (oldViewProps.quality.name != newViewProps.quality.name) { - _ivsView.quality = DictionaryFromQuality(newViewProps.quality); + if (oldViewProps.quality.target.name != newViewProps.quality.target.name || + oldViewProps.quality.adaptive != newViewProps.quality.adaptive) { + NSDictionary *target = DictionaryFromQuality(newViewProps.quality.target); + + _ivsView.quality = + @{@"adaptive" : @(newViewProps.quality.adaptive), @"target" : target}; } if (oldViewProps.autoMaxQuality.name != newViewProps.autoMaxQuality.name) { @@ -202,11 +213,25 @@ - (void)updateProps:(Props::Shared const &)props if (oldViewProps.progressInterval != newViewProps.progressInterval) { _ivsView.progressInterval = @(newViewProps.progressInterval); } - + if (oldViewProps.playInBackground != newViewProps.playInBackground) { _ivsView.playInBackground = newViewProps.playInBackground; } + if (oldViewProps.networkRecoveryMode != newViewProps.networkRecoveryMode) { + _ivsView.networkRecoveryMode = @(newViewProps.networkRecoveryMode.c_str()); + } + + if (oldViewProps.maxVideoSize.size.width != + newViewProps.maxVideoSize.size.width || + oldViewProps.maxVideoSize.size.height != + newViewProps.maxVideoSize.size.height) { + _ivsView.maxVideoSize = @{ + @"width" : @(newViewProps.maxVideoSize.size.width), + @"height" : @(newViewProps.maxVideoSize.size.height) + }; + } + [super updateProps:props oldProps:oldProps]; } @@ -457,7 +482,6 @@ - (void)onTimePoint:(NSDictionary *)onTimePointPayload { const auto eventEmitter = [self getEventEmitter]; double position = [onTimePointPayload[@"position"] doubleValue]; - ; AmazonIvsEventEmitter::OnTimePoint eventData; eventData.position = position; @@ -536,45 +560,54 @@ - (void)onLiveLatencyChange:(NSDictionary *)onLiveLatencyChangePayload { - (void)onQualityChange:(NSDictionary *)onQualityChangePayload { const auto eventEmitter = [self getEventEmitter]; - id qualityObj = onQualityChangePayload[@"quality"]; + AmazonIvsEventEmitter::OnQualityChange eventData; - if (qualityObj == nil || qualityObj == [NSNull null]) { - return; - } + id qualityObj = onQualityChangePayload[@"quality"]; - AmazonIvsEventEmitter::OnQualityChange eventData; - NSDictionary *qualityData = (NSDictionary *)qualityObj; + if ([qualityObj isKindOfClass:[NSDictionary class]]) { + NSDictionary *qualityData = (NSDictionary *)qualityObj; - AmazonIvsEventEmitter::OnQualityChangeQuality qualityStruct; + std::string name = [qualityData[@"name"] UTF8String] ?: ""; + std::string codecs = [qualityData[@"codecs"] UTF8String] ?: ""; + int bitrate = [qualityData[@"bitrate"] intValue]; + double framerate = [qualityData[@"framerate"] doubleValue]; + int width = [qualityData[@"width"] intValue]; + int height = [qualityData[@"height"] intValue]; - id nameObj = qualityData[@"name"]; - qualityStruct.name = (nameObj != [NSNull null]) ? [nameObj UTF8String] : ""; + eventData.quality = {name, codecs, bitrate, framerate, width, height}; + if (eventEmitter != nullptr) { + eventEmitter->onQualityChange(eventData); + } + } +} - id codecsObj = qualityData[@"codecs"]; - qualityStruct.codecs = - (codecsObj != [NSNull null]) ? [codecsObj UTF8String] : ""; +- (void)onSeekComplete:(NSDictionary *)onSeekCompletePayload { + const auto eventEmitter = [self getEventEmitter]; + AmazonIvsEventEmitter::OnSeekComplete eventData; - id bitrateObj = qualityData[@"bitrate"]; - qualityStruct.bitrate = - (bitrateObj != [NSNull null]) ? [bitrateObj intValue] : 0; + BOOL success = [onSeekCompletePayload[@"success"] boolValue]; + eventData.success = success; - id framerateObj = qualityData[@"framerate"]; - qualityStruct.framerate = - (framerateObj != [NSNull null]) ? [framerateObj doubleValue] : 0.0; + if (eventEmitter != nullptr) { + eventEmitter->onSeekComplete(eventData); + } +} - id widthObj = qualityData[@"width"]; - qualityStruct.width = (widthObj != [NSNull null]) ? [widthObj intValue] : 0; +- (void)onVideoSizeChange:(NSDictionary *)onVideoSizeChangePayload { + const auto eventEmitter = [self getEventEmitter]; + AmazonIvsEventEmitter::OnVideoSizeChange eventData; - id heightObj = qualityData[@"height"]; - qualityStruct.height = - (heightObj != [NSNull null]) ? [heightObj intValue] : 0; + NSDictionary *videoSize = onVideoSizeChangePayload[@"size"]; - eventData.quality = qualityStruct; + int width = [videoSize[@"width"] intValue]; + int height = [videoSize[@"height"] intValue]; + eventData.size = {width, height}; if (eventEmitter != nullptr) { - eventEmitter->onQualityChange(eventData); + eventEmitter->onVideoSizeChange(eventData); } } + - (void)onLoad:(NSDictionary *)durationDict { const auto eventEmitter = [self getEventEmitter]; diff --git a/ios/AmazonIvsView.swift b/ios/AmazonIvsView.swift index d4eae04..51f9447 100644 --- a/ios/AmazonIvsView.swift +++ b/ios/AmazonIvsView.swift @@ -5,6 +5,7 @@ import UIKit @objcMembers public class AmazonIvsView: UIView, IVSPlayer.Delegate { public var onSeek: ((NSDictionary) -> Void)? + public var onSeekComplete: ((NSDictionary) -> Void)? public var onData: ((NSDictionary) -> Void)? public var onVideoStatistics: ((NSDictionary) -> Void)? public var onPlayerStateChange: ((NSDictionary) -> Void)? @@ -20,6 +21,7 @@ import UIKit public var onProgress: ((NSDictionary) -> Void)? public var onTimePoint: ((NSDictionary?) -> Void)? public var onError: ((NSDictionary) -> Void)? + public var onVideoSizeChange: ((NSDictionary) -> Void)? private let player = IVSPlayer() private let playerView = IVSPlayerView() @@ -168,8 +170,13 @@ import UIKit public var quality: NSDictionary? { didSet { - let newQuality = findQuality(quality: quality) - player.quality = newQuality + let isAdaptive = quality?["adaptive"] as? Bool ?? true + let target = quality?["target"] as? NSDictionary + + guard let selectedQuality = findQuality(quality: target) else { + return + } + player.setQuality(selectedQuality, adaptive: isAdaptive) } } @@ -294,6 +301,14 @@ import UIKit } } + public var networkRecoveryMode: String? { + didSet { + player.setNetworkRecoveryMode( + findNetworkRecoveryMode(mode: networkRecoveryMode) + ) + } + } + private func findResizeMode(mode: String?) -> AVLayerVideoGravity { switch mode { case "aspectFill": @@ -307,6 +322,15 @@ import UIKit } } + public var maxVideoSize: NSDictionary? { + didSet { + guard let size = findSize(size: maxVideoSize) else { + return + } + player.setMaxVideoSize(size) + } + } + public var breakpoints: NSArray { didSet { self.removeTimePointObserver() @@ -332,7 +356,9 @@ import UIKit position, preferredTimescale: 1_000_000 ) - player.seek(to: parsedTime) + player.seek(to: parsedTime) { [weak self] finished in + self?.onSeekComplete?(["success": finished]) + } } public func setOrigin(origin: NSString) { @@ -348,7 +374,7 @@ import UIKit } } - @objc public func loadSource(id: Int) { + public func loadSource(id: Int) { if let source = preloadSourceMap[id] { player.load(source.uri) } @@ -425,6 +451,29 @@ import UIKit } } + private func findNetworkRecoveryMode(mode: String?) + -> IVSPlayer.NetworkRecoveryMode + { + switch mode { + case "none": + return IVSPlayer.NetworkRecoveryMode.none + case "resume": + return IVSPlayer.NetworkRecoveryMode.resume + default: + return IVSPlayer.NetworkRecoveryMode.none + } + } + + private func findSize(size: NSDictionary?) -> CGSize? { + guard let width = (size?["width"] as? NSNumber)?.doubleValue, + let height = (size?["height"] as? NSNumber)?.doubleValue + else { + return nil + } + + return CGSize(width: width, height: height) + } + func addProgressObserver() { progressObserverToken = player.addPeriodicTimeObserver( forInterval: CMTime( @@ -570,13 +619,21 @@ import UIKit "type": cue.type.rawValue, "text": cue.text, "textDescription": cue.textDescription, - ] onTextMetadataCue?(["textMetadataCue": textMetadataCue]) } } + public func player(_ player: IVSPlayer, didChangeVideoSize videoSize: CGSize) + { + let size = [ + "width": Int(videoSize.width), + "height": Int(videoSize.height), + ] + onVideoSizeChange?(["size": size]) + } + public func playerWillRebuffer(_ player: IVSPlayer) { onRebuffering?() } diff --git a/src/components/AmazonIvsViewNativeComponent.ts b/src/components/AmazonIvsViewNativeComponent.ts index d07e550..f3da904 100644 --- a/src/components/AmazonIvsViewNativeComponent.ts +++ b/src/components/AmazonIvsViewNativeComponent.ts @@ -29,7 +29,10 @@ export interface NativeProps extends ViewProps { logLevel?: Int32; resizeMode?: string; volume?: Double; - quality?: Quality | null; + quality?: { + target: Quality | null; + adaptive?: boolean; + }; autoMaxQuality?: Quality | null; autoQualityMode?: boolean; breakpoints?: Int32[]; @@ -37,6 +40,8 @@ export interface NativeProps extends ViewProps { initialBufferDuration?: Double; pipEnabled?: boolean; progressInterval?: Double; + maxVideoSize?: { size: { width: Int32; height: Int32 } }; + networkRecoveryMode?: string; playInBackground?: boolean; notificationTitle?: string; notificationText?: string; @@ -102,6 +107,10 @@ export interface NativeProps extends ViewProps { onLoad?: DirectEventHandler<{ duration?: Double }>; onRebuffering?: DirectEventHandler<{}>; onTimePoint?: DirectEventHandler<{ position?: Double }>; + onVideoSizeChange?: DirectEventHandler<{ + size: { width: Int32; height: Int32 }; + }>; + onSeekComplete?: DirectEventHandler<{ success: boolean }>; } interface NativeCommands { diff --git a/src/components/IVSPlayer.tsx b/src/components/IVSPlayer.tsx index 8e2694d..af19dea 100644 --- a/src/components/IVSPlayer.tsx +++ b/src/components/IVSPlayer.tsx @@ -14,6 +14,7 @@ import { } from 'react-native'; import type { IVSPlayerRef, + NetworkRecoveryMode, PlayerData, Quality, ResizeMode, @@ -46,13 +47,18 @@ export type Props = { resizeMode?: ResizeMode; progressInterval?: number; volume?: number; - quality?: Quality | null; + quality?: { + target: Quality | null; + adaptive?: boolean; + }; autoMaxQuality?: Quality | null; autoQualityMode?: boolean; breakpoints?: number[]; maxBitrate?: number; initialBufferDuration?: number; pipEnabled?: boolean; + networkRecoveryMode?: NetworkRecoveryMode; + maxVideoSize?: { size: { width: number; height: number } }; showErrorMessage?: boolean; playInBackground?: boolean; notificationTitle?: string; @@ -73,6 +79,7 @@ export type Props = { onProgress?(progress: number): void; onError?(error: string): void; onTimePoint?(position: number): void; + onVideoSizeChange?(size: { width: number; height: number }): void; children?: React.ReactNode; }; @@ -106,6 +113,8 @@ const IVSPlayerContainer = React.forwardRef( breakpoints = [], maxBitrate, initialBufferDuration, + networkRecoveryMode, + maxVideoSize, showErrorMessage, playInBackground = false, notificationTitle, @@ -126,12 +135,15 @@ const IVSPlayerContainer = React.forwardRef( onProgress, onError, onTimePoint, + onVideoSizeChange, children, }, ref ) => { const mediaPlayerRef = useRef(null); const initialized = useRef(false); + const liveLatencyRef = useRef(null); + const seekCallbackRef = useRef<((success: boolean) => void) | null>(null); const [errorMessage, setErrorMessage] = useState(); const preload = (url: string) => { @@ -167,8 +179,11 @@ const IVSPlayerContainer = React.forwardRef( Commands.pause(mediaPlayerRef.current); }; - const seekTo = (value: number) => { + const seekTo = (value: number, callback?: (success: boolean) => void) => { if (!mediaPlayerRef.current) return; + if (callback) { + seekCallbackRef.current = callback; + } Commands.seekTo(mediaPlayerRef.current, value); }; @@ -182,6 +197,10 @@ const IVSPlayerContainer = React.forwardRef( Commands.togglePip(mediaPlayerRef.current); }; + const getLiveLatency = () => { + return liveLatencyRef.current; + }; + useEffect(() => { if (initialized.current || autoplay) { if (paused) { @@ -204,6 +223,7 @@ const IVSPlayerContainer = React.forwardRef( seekTo, setOrigin, togglePip, + getLiveLatency, }), [ preload, @@ -214,6 +234,7 @@ const IVSPlayerContainer = React.forwardRef( seekTo, setOrigin, togglePip, + getLiveLatency, ] ); @@ -272,6 +293,7 @@ const IVSPlayerContainer = React.forwardRef( event: NativeSyntheticEvent<{ liveLatency: number }> ) => { const { liveLatency } = event.nativeEvent; + liveLatencyRef.current = liveLatency; onLiveLatencyChange?.(liveLatency); }; @@ -333,6 +355,24 @@ const IVSPlayerContainer = React.forwardRef( onTimePoint?.(position); }; + const onVideoSizeChangeHandler = ( + event: NativeSyntheticEvent<{ size: { width: number; height: number } }> + ) => { + const { size } = event.nativeEvent; + onVideoSizeChange?.(size); + }; + + const onSeekCompleteHandler = ( + event: NativeSyntheticEvent<{ success: boolean }> + ) => { + const { success } = event.nativeEvent; + + if (seekCallbackRef.current) { + seekCallbackRef.current(success); + seekCallbackRef.current = null; + } + }; + const constrainedProgressInterval = useMemo(() => { if (!progressInterval || progressInterval <= 0.1) { return 0.1; @@ -368,6 +408,8 @@ const IVSPlayerContainer = React.forwardRef( playInBackground={playInBackground} notificationTitle={notificationTitle} notificationText={notificationText} + networkRecoveryMode={networkRecoveryMode} + maxVideoSize={maxVideoSize} onVideoStatistics={onVideoStatisticsHandler} onData={onDataHandler} onSeek={onSeekHandler} @@ -381,11 +423,11 @@ const IVSPlayerContainer = React.forwardRef( onTextCue={onTextCueHandler} onTextMetadataCue={onTextMetadataCueHandler} onProgress={onProgressHandler} - onLiveLatencyChange={ - onLiveLatencyChange ? onLiveLatencyChangeHandler : undefined - } + onLiveLatencyChange={onLiveLatencyChangeHandler} onError={onErrorHandler} onTimePoint={onTimePointHandler} + onVideoSizeChange={onVideoSizeChangeHandler} + onSeekComplete={onSeekCompleteHandler} /> {children} diff --git a/src/types/index.ts b/src/types/index.ts index d443edc..279c85c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -48,9 +48,12 @@ export type IVSPlayerRef = { releaseSource: (source: Source) => void; play: () => void; pause: () => void; - seekTo: (position: number) => void; + seekTo: (value: number, callback?: (success: boolean) => void) => void; setOrigin: (origin: string) => void; togglePip: () => void; + getLiveLatency: () => number; }; export type ResizeMode = 'aspectFill' | 'aspectFit' | 'aspectZoom'; + +export type NetworkRecoveryMode = 'none' | 'resume';