diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index ee257ddf29..6529820efe 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -7,7 +7,6 @@ import android.util.Log; import androidx.annotation.NonNull; -import androidx.media3.common.util.Util; import com.brentvatne.common.api.BufferConfig; import com.brentvatne.common.api.BufferingStrategy; @@ -22,15 +21,14 @@ import com.brentvatne.common.toolbox.DebugLog; import com.brentvatne.common.toolbox.ReactBridgeUtils; import com.brentvatne.react.ReactNativeVideoManager; +import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.annotations.ReactProp; -import java.util.ArrayList; import java.util.Map; -import java.util.UUID; import javax.annotation.Nullable; @@ -41,7 +39,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager +@interface RCTDecoderPropertiesModule : NSObject +@end diff --git a/ios/Video/RCTDecoderPropertiesModule.m b/ios/Video/RCTDecoderPropertiesModule.m new file mode 100644 index 0000000000..fddf8b424b --- /dev/null +++ b/ios/Video/RCTDecoderPropertiesModule.m @@ -0,0 +1,13 @@ +#import "RCTDecoderPropertiesModule.h" + +@implementation RCTDecoderPropertiesModule + +RCT_EXPORT_MODULE(RNVDecoderPropertiesModule); + +RCT_EXPORT_METHOD(getWidevineLevel : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) {} + +RCT_EXPORT_METHOD(isCodecSupported : (NSString*)mimeType width : (NSInteger*)width height : (NSInteger*)height) {} + +RCT_EXPORT_METHOD(isHEVCSupported : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) {} + +@end diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index e8c91d84af..0ea5b48a7e 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -754,13 +754,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } @objc - func setSeek(_ info: NSDictionary!) { - let seekTime: NSNumber! = info["time"] as! NSNumber - let seekTolerance: NSNumber! = info["tolerance"] as! NSNumber + func setSeek(_ time: NSNumber, _ tolerance: NSNumber) { let item: AVPlayerItem? = _player?.currentItem guard item != nil, let player = _player, let item, item.status == AVPlayerItem.Status.readyToPlay else { _pendingSeek = true - _pendingSeekTime = seekTime.floatValue + _pendingSeekTime = time.floatValue return } @@ -768,15 +766,15 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH player: player, playerItem: item, paused: _paused, - seekTime: seekTime.floatValue, - seekTolerance: seekTolerance.floatValue + seekTime: time.floatValue, + seekTolerance: tolerance.floatValue ) { [weak self] (_: Bool) in guard let self else { return } self._playerObserver.addTimeObserverIfNotSet() self.setPaused(self._paused) self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))), - "seekTime": seekTime, + "seekTime": time, "target": self.reactTag]) } @@ -1294,7 +1292,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH // MARK: - Export @objc - func save(options: NSDictionary!, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + func save(_ options: NSDictionary!, _ resolve: @escaping RCTPromiseResolveBlock, _ reject: @escaping RCTPromiseRejectBlock) { RCTVideoSave.save( options: options, resolve: resolve, @@ -1311,14 +1309,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _resouceLoaderDelegate?.setLicenseResultError(error, licenseUrl) } - func dismissFullscreenPlayer() { - setFullscreen(false) - } - - func presentFullscreenPlayer() { - setFullscreen(true) - } - // MARK: - RCTPlayerObserverHandler func handleTimeUpdate(time _: CMTime) { @@ -1372,18 +1362,12 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH Task { if self._pendingSeek { - self.setSeek([ - "time": NSNumber(value: self._pendingSeekTime), - "tolerance": NSNumber(value: 100), - ]) + self.setSeek(NSNumber(value: self._pendingSeekTime), NSNumber(value: 100)) self._pendingSeek = false } if self._startPosition >= 0 { - self.setSeek([ - "time": NSNumber(value: self._startPosition), - "tolerance": NSNumber(value: 100), - ]) + self.setSeek(NSNumber(value: self._startPosition), NSNumber(value: 100)) self._startPosition = -1 } diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 8c4bad606d..842b27b15b 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -1,5 +1,5 @@ #import "React/RCTViewManager.h" -#import +#import @interface RCT_EXTERN_MODULE (RCTVideoManager, RCTViewManager) @@ -68,31 +68,21 @@ @interface RCT_EXTERN_MODULE (RCTVideoManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(onAudioTracks, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onTextTrackDataChanged, RCTDirectEventBlock); +RCT_EXTERN_METHOD(seek : (nonnull NSNumber*)reactTag time : (nonnull NSNumber*)time tolerance : (nonnull NSNumber*)tolerance) +RCT_EXTERN_METHOD(setLicenseResult : (nonnull NSNumber*)reactTag lisence : (NSString*)license licenseUrl : (NSString*)licenseUrl) +RCT_EXTERN_METHOD(setLicenseResultError : (nonnull NSNumber*)reactTag error : (NSString*)error licenseUrl : (NSString*)licenseUrl) +RCT_EXTERN_METHOD(setPlayerPauseState : (nonnull NSNumber*)reactTag paused : (nonnull BOOL)paused) +RCT_EXTERN_METHOD(setVolumeCMD : (nonnull NSNumber*)reactTag volume : (nonnull float*)volume) +RCT_EXTERN_METHOD(setFullScreen : (nonnull NSNumber*)reactTag fullscreen : (nonnull BOOL)fullScreen) + RCT_EXTERN_METHOD(save - : (NSDictionary*)options reactTag - : (nonnull NSNumber*)reactTag resolver - : (RCTPromiseResolveBlock)resolve rejecter + : (nonnull NSNumber*)reactTag options + : (NSDictionary*)options resolve + : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) - -RCT_EXTERN_METHOD(seek : (NSDictionary*)info reactTag : (nonnull NSNumber*)reactTag) - -RCT_EXTERN_METHOD(setLicenseResult : (NSString*)license licenseUrl : (NSString*)licenseUrl reactTag : (nonnull NSNumber*)reactTag) - -RCT_EXTERN_METHOD(setLicenseResultError : (NSString*)error licenseUrl : (NSString*)licenseUrl reactTag : (nonnull NSNumber*)reactTag) - -RCT_EXTERN_METHOD(setPlayerPauseState : (nonnull NSNumber*)paused reactTag : (nonnull NSNumber*)reactTag) - -RCT_EXTERN_METHOD(presentFullscreenPlayer : (nonnull NSNumber*)reactTag) - -RCT_EXTERN_METHOD(dismissFullscreenPlayer : (nonnull NSNumber*)reactTag) - -RCT_EXTERN_METHOD(setVolume : (nonnull float*)volume reactTag : (nonnull NSNumber*)reactTag) - RCT_EXTERN_METHOD(getCurrentPosition - : (nonnull NSNumber*)reactTag resolver - : (RCTPromiseResolveBlock)resolve rejecter + : (nonnull NSNumber*)reactTag resolve + : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(setFullScreen : (BOOL)fullScreen reactTag : (nonnull NSNumber*)reactTag) - @end diff --git a/ios/Video/RCTVideoManager.swift b/ios/Video/RCTVideoManager.swift index 12ac422207..ad64f8c2a7 100644 --- a/ios/Video/RCTVideoManager.swift +++ b/ios/Video/RCTVideoManager.swift @@ -30,76 +30,62 @@ class RCTVideoManager: RCTViewManager { } } - @objc(save:reactTag:resolver:rejecter:) - func save(options: NSDictionary, reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(seek:time:tolerance:) + func seek(_ reactTag: NSNumber, time: NSNumber, tolerance: NSNumber) { performOnVideoView(withReactTag: reactTag, callback: { videoView in - videoView?.save(options: options, resolve: resolve, reject: reject) + videoView?.setSeek(time, tolerance) }) } - @objc(seek:reactTag:) - func seek(info: NSDictionary, reactTag: NSNumber) { - performOnVideoView(withReactTag: reactTag, callback: { videoView in - videoView?.setSeek(info) - }) - } - - @objc(setLicenseResult:licenseUrl:reactTag:) - func setLicenseResult(license: NSString, licenseUrl: NSString, reactTag: NSNumber) { + @objc(setLicenseResult:license:licenseUrl:) + func setLicenseResult(_ reactTag: NSNumber, license: NSString, licenseUrl: NSString) { performOnVideoView(withReactTag: reactTag, callback: { videoView in videoView?.setLicenseResult(license as String, licenseUrl as String) }) } - @objc(setLicenseResultError:licenseUrl:reactTag:) - func setLicenseResultError(error: NSString, licenseUrl: NSString, reactTag: NSNumber) { + @objc(setLicenseResultError:error:licenseUrl:) + func setLicenseResultError(_ reactTag: NSNumber, error: NSString, licenseUrl: NSString) { performOnVideoView(withReactTag: reactTag, callback: { videoView in videoView?.setLicenseResultError(error as String, licenseUrl as String) }) } - @objc(dismissFullscreenPlayer:) - func dismissFullscreenPlayer(_ reactTag: NSNumber) { + @objc(setPlayerPauseState:paused:) + func setPlayerPauseState(_ reactTag: NSNumber, paused: Bool) { performOnVideoView(withReactTag: reactTag, callback: { videoView in - videoView?.dismissFullscreenPlayer() + videoView?.setPaused(paused) }) } - @objc(presentFullscreenPlayer:) - func presentFullscreenPlayer(_ reactTag: NSNumber) { + @objc(setVolumeCMD:volume:) + func setVolumeCMD(_ reactTag: NSNumber, volume: Float) { performOnVideoView(withReactTag: reactTag, callback: { videoView in - videoView?.presentFullscreenPlayer() + videoView?.setVolume(volume) }) } - @objc(setPlayerPauseState:reactTag:) - func setPlayerPauseState(paused: NSNumber, reactTag: NSNumber) { + @objc(setFullScreen:fullscreen:) + func setFullScreen(_ reactTag: NSNumber, fullScreen: Bool) { performOnVideoView(withReactTag: reactTag, callback: { videoView in - videoView?.setPaused(paused.boolValue) + videoView?.setFullscreen(fullScreen) }) } - @objc(setVolume:reactTag:) - func setVolume(value: Float, reactTag: NSNumber) { + @objc(save:options:resolve:reject:) + func save(_ reactTag: NSNumber, options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { performOnVideoView(withReactTag: reactTag, callback: { videoView in - videoView?.setVolume(value) + videoView?.save(options, resolve, reject) }) } - @objc(getCurrentPosition:resolver:rejecter:) - func getCurrentPosition(reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(getCurrentPosition:resolve:reject:) + func getCurrentPosition(_ reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { performOnVideoView(withReactTag: reactTag, callback: { videoView in videoView?.getCurrentPlaybackTime(resolve, reject) }) } - @objc(setFullScreen:reactTag:) - func setFullScreen(fullScreen: Bool, reactTag: NSNumber) { - performOnVideoView(withReactTag: reactTag, callback: { videoView in - videoView?.setFullscreen(fullScreen) - }) - } - override class func requiresMainQueueSetup() -> Bool { return true } diff --git a/src/Video.tsx b/src/Video.tsx index f6a0016d0b..c9dec1d3dc 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -5,7 +5,7 @@ import React, { useRef, forwardRef, useImperativeHandle, - type ComponentRef, + type ElementRef, } from 'react'; import { View, @@ -17,44 +17,41 @@ import { type NativeSyntheticEvent, } from 'react-native'; -import NativeVideoComponent, { - type OnAudioFocusChangedData, - type OnAudioTracksData, - type OnBandwidthUpdateData, - type OnBufferData, - type OnControlsVisibilityChange, - type OnExternalPlaybackChangeData, - type OnGetLicenseData, - type OnLoadStartData, - type OnPictureInPictureStatusChangedData, - type OnPlaybackStateChangedData, - type OnProgressData, - type OnSeekData, - type OnTextTrackDataChangedData, - type OnTimedMetadataData, - type OnVideoAspectRatioData, - type OnVideoErrorData, - type OnVideoTracksData, - type VideoComponentType, - type VideoSrc, +import NativeVideoComponent, {Commands} from './specs/VideoNativeComponent'; +import type { + OnAudioFocusChangedData, + OnAudioTracksData, + OnBandwidthUpdateData, + OnBufferData, + OnControlsVisibilityChange, + OnExternalPlaybackChangeData, + OnGetLicenseData, + OnLoadStartData, + OnPictureInPictureStatusChangedData, + OnPlaybackStateChangedData, + OnProgressData, + OnSeekData, + OnTextTrackDataChangedData, + OnTimedMetadataData, + OnVideoAspectRatioData, + OnVideoErrorData, + OnVideoTracksData, + VideoSrc, } from './specs/VideoNativeComponent'; import { generateHeaderForNative, getReactTag, resolveAssetSourceForVideo, } from './utils'; -import {VideoManager} from './specs/VideoNativeComponent'; -import { - type OnLoadData, - type OnTextTracksData, - type OnReceiveAdEventData, - type ReactVideoProps, - ViewType, +import type { + OnLoadData, + OnTextTracksData, + OnReceiveAdEventData, + ReactVideoProps, } from './types'; - -export type VideoSaveData = { - uri: string; -}; +import {ViewType} from './types'; +import type {VideoSaveData} from './specs/NativeVideoManagerModule'; +import NativeVideoManagerModule from './specs/NativeVideoManagerModule'; export interface VideoRef { seek: (time: number, tolerance?: number) => void; @@ -65,10 +62,10 @@ export interface VideoRef { restoreUserInterfaceForPictureInPictureStopCompleted: ( restore: boolean, ) => void; - save: (options: object) => Promise; setVolume: (volume: number) => void; - getCurrentPosition: () => Promise; setFullScreen: (fullScreen: boolean) => void; + save: (options: object) => Promise | void; + getCurrentPosition: () => Promise; } const Video = forwardRef( @@ -79,7 +76,6 @@ const Video = forwardRef( resizeMode, posterResizeMode, poster, - fullscreen, drm, textTracks, selectedVideoTrack, @@ -122,9 +118,8 @@ const Video = forwardRef( }, ref, ) => { - const nativeRef = useRef>(null); + const nativeRef = useRef>(null); const [showPoster, setShowPoster] = useState(!!poster); - const [isFullscreen, setIsFullscreen] = useState(fullscreen); const [ _restoreUserInterfaceForPIPStopCompletionHandler, setRestoreUserInterfaceForPIPStopCompletionHandler, @@ -275,44 +270,32 @@ const Video = forwardRef( } const callSeekFunction = () => { - VideoManager.seek( - { - time, - tolerance: tolerance || 0, - }, - getReactTag(nativeRef), - ); + nativeRef.current && + Commands.seek(nativeRef.current, time, tolerance || 0); }; Platform.select({ ios: callSeekFunction, android: callSeekFunction, default: () => { - // TODO: Implement VideoManager.seek for windows + // TODO: Implement Commands.seek for windows nativeRef.current?.setNativeProps({seek: time}); }, })(); }, []); - const presentFullscreenPlayer = useCallback(() => { - setIsFullscreen(true); - }, [setIsFullscreen]); - - const dismissFullscreenPlayer = useCallback(() => { - setIsFullscreen(false); - }, [setIsFullscreen]); - - const save = useCallback((options: object) => { - // VideoManager.save can be null on android & windows - return VideoManager.save?.(options, getReactTag(nativeRef)); - }, []); - const pause = useCallback(() => { - return VideoManager.setPlayerPauseState(true, getReactTag(nativeRef)); + return ( + nativeRef.current && + Commands.setPlayerPauseState(nativeRef.current, true) + ); }, []); const resume = useCallback(() => { - return VideoManager.setPlayerPauseState(false, getReactTag(nativeRef)); + return ( + nativeRef.current && + Commands.setPlayerPauseState(nativeRef.current, false) + ); }, []); const restoreUserInterfaceForPictureInPictureStopCompleted = useCallback( @@ -323,15 +306,42 @@ const Video = forwardRef( ); const setVolume = useCallback((volume: number) => { - return VideoManager.setVolume(volume, getReactTag(nativeRef)); + return ( + nativeRef.current && Commands.setVolumeCMD(nativeRef.current, volume) + ); }, []); - const getCurrentPosition = useCallback(() => { - return VideoManager.getCurrentPosition(getReactTag(nativeRef)); + const setFullScreen = useCallback((fullScreen: boolean) => { + return ( + nativeRef.current && + Commands.setFullScreen(nativeRef.current, fullScreen) + ); }, []); - const setFullScreen = useCallback((fullScreen: boolean) => { - return VideoManager.setFullScreen(fullScreen, getReactTag(nativeRef)); + const presentFullscreenPlayer = useCallback( + () => setFullScreen(true), + [setFullScreen], + ); + + const dismissFullscreenPlayer = useCallback( + () => setFullScreen(false), + [setFullScreen], + ); + + const save = useCallback((options: object) => { + // VideoManager.save can be null on android & windows + if (Platform.OS !== 'ios') { + return; + } + // @todo Must implement it in a different way. + return NativeVideoManagerModule.save?.(getReactTag(nativeRef), options); + }, []); + + const getCurrentPosition = useCallback(() => { + // @todo Must implement it in a different way. + return NativeVideoManagerModule.getCurrentPosition( + getReactTag(nativeRef), + ); }, []); const onVideoLoadStart = useCallback( @@ -495,56 +505,42 @@ const Video = forwardRef( [onControlsVisibilityChange], ); - const useExternalGetLicense = drm?.getLicense instanceof Function; + const usingExternalGetLicense = drm?.getLicense instanceof Function; const onGetLicense = useCallback( - (event: NativeSyntheticEvent) => { - if (useExternalGetLicense) { - const data = event.nativeEvent; - if (data && data.spcBase64) { - const getLicenseOverride = drm.getLicense( - data.spcBase64, - data.contentId, - data.licenseUrl, - data.loadedLicenseUrl, - ); - const getLicensePromise = Promise.resolve(getLicenseOverride); // Handles both scenarios, getLicenseOverride being a promise and not. - getLicensePromise - .then((result) => { - if (result !== undefined) { - nativeRef.current && - VideoManager.setLicenseResult( - result, - data.loadedLicenseUrl, - getReactTag(nativeRef), - ); - } else { - nativeRef.current && - VideoManager.setLicenseResultError( - 'Empty license result', - data.loadedLicenseUrl, - getReactTag(nativeRef), - ); - } - }) - .catch(() => { - nativeRef.current && - VideoManager.setLicenseResultError( - 'fetch error', - data.loadedLicenseUrl, - getReactTag(nativeRef), - ); - }); - } else { - VideoManager.setLicenseResultError( - 'No spc received', - data.loadedLicenseUrl, - getReactTag(nativeRef), - ); + async (event: NativeSyntheticEvent) => { + if (!usingExternalGetLicense) { + return; + } + + const data = event.nativeEvent; + let result; + if (data?.spcBase64) { + const getLicenseOverride = drm.getLicense( + data.spcBase64, + data.contentId, + data.licenseUrl, + data.loadedLicenseUrl, + ); + const getLicensePromise = Promise.resolve(getLicenseOverride); // Handles both scenarios, getLicenseOverride being a promise and not. + try { + result = await getLicensePromise; + result ??= 'Empty license result'; + } catch { + result = 'fetch error'; } + } else { + result = 'No spc received'; + } + if (nativeRef.current) { + Commands.setLicenseResultError( + nativeRef.current, + result, + data.loadedLicenseUrl, + ); } }, - [drm, useExternalGetLicense], + [drm, usingExternalGetLicense], ); useImperativeHandle( @@ -615,7 +611,6 @@ const Video = forwardRef( drm={_drm} style={StyleSheet.absoluteFill} resizeMode={resizeMode} - fullscreen={isFullscreen} restoreUserInterfaceForPIPStopCompletionHandler={ _restoreUserInterfaceForPIPStopCompletionHandler } @@ -623,7 +618,7 @@ const Video = forwardRef( selectedTextTrack={_selectedTextTrack} selectedAudioTrack={_selectedAudioTrack} selectedVideoTrack={_selectedVideoTrack} - onGetLicense={useExternalGetLicense ? onGetLicense : undefined} + onGetLicense={usingExternalGetLicense ? onGetLicense : undefined} onVideoLoad={ onLoad || hasPoster ? (onVideoLoad as (e: NativeSyntheticEvent) => void) diff --git a/src/index.ts b/src/index.ts index 56fd87d241..d2601ae86d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import Video from './Video'; -export {VideoDecoderProperties} from './specs/VideoNativeComponent'; +export {default as VideoDecoderProperties} from './specs/NativeVideoDecoderProperties'; export * from './types'; export type {VideoRef} from './Video'; export default Video; diff --git a/src/specs/NativeVideoDecoderProperties.ts b/src/specs/NativeVideoDecoderProperties.ts new file mode 100644 index 0000000000..7bfbf021f2 --- /dev/null +++ b/src/specs/NativeVideoDecoderProperties.ts @@ -0,0 +1,17 @@ +import type {TurboModule} from 'react-native'; +import {TurboModuleRegistry} from 'react-native'; +import type {Int32} from 'react-native/Libraries/Types/CodegenTypes'; + +interface Spec extends TurboModule { + getWidevineLevel: () => Promise; + isCodecSupported: ( + mimeType: string, + width: Int32, + height: Int32, + ) => Promise<'unsupported' | 'hardware' | 'software'>; + isHEVCSupported: () => Promise<'unsupported' | 'hardware' | 'software'>; +} + +export default TurboModuleRegistry.getEnforcing( + 'RNVDecoderPropertiesModule', +); diff --git a/src/specs/NativeVideoManagerModule.ts b/src/specs/NativeVideoManagerModule.ts new file mode 100644 index 0000000000..b5e73fd97b --- /dev/null +++ b/src/specs/NativeVideoManagerModule.ts @@ -0,0 +1,17 @@ +import type {TurboModule} from 'react-native'; +import {TurboModuleRegistry} from 'react-native'; +import type { + Int32, + UnsafeObject, +} from 'react-native/Libraries/Types/CodegenTypes'; + +export type VideoSaveData = { + uri: string; +}; + +interface Spec extends TurboModule { + save: (reactTag: Int32, option: UnsafeObject) => Promise; + getCurrentPosition: (reactTag: Int32) => Promise; +} + +export default TurboModuleRegistry.getEnforcing('VideoManager'); diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 107b717daa..eb9149cabd 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/ban-types */ import type {HostComponent, ViewProps} from 'react-native'; -import {NativeModules, requireNativeComponent} from 'react-native'; import type { DirectEventHandler, Double, @@ -8,6 +7,8 @@ import type { Int32, WithDefault, } from 'react-native/Libraries/Types/CodegenTypes'; +import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; // -------- There are types for native component (future codegen) -------- // if you are looking for types for react component, see src/types/video.ts @@ -89,11 +90,6 @@ type SelectedVideoTrack = Readonly<{ value?: string; }>; -export type Seek = Readonly<{ - time: Float; - tolerance?: Float; -}>; - type BufferConfigLive = Readonly<{ maxPlaybackSpeed?: Float; minPlaybackSpeed?: Float; @@ -286,7 +282,7 @@ export type OnAudioFocusChangedData = Readonly<{ type ControlsStyles = Readonly<{ hideSeekBar?: boolean; - seekIncrementMS?: number; + seekIncrementMS?: Int32; }>; export type OnControlsVisibilityChange = Readonly<{ @@ -373,45 +369,47 @@ export interface VideoNativeProps extends ViewProps { onVideoTracks?: DirectEventHandler; // android } -export type VideoComponentType = HostComponent; - -export type VideoSaveData = { - uri: string; -}; - -export interface VideoManagerType { - save: (option: object, reactTag: number) => Promise; - seek: (option: Seek, reactTag: number) => Promise; - setPlayerPauseState: (paused: boolean, reactTag: number) => Promise; +type NativeVideoComponentType = HostComponent; + +export interface NativeVideoCommands { + seek: ( + viewRef: React.ElementRef, + time: Float, + tolerance?: Float, + ) => void; + setPlayerPauseState: ( + viewRef: React.ElementRef, + paused: boolean, + ) => void; + setVolumeCMD: ( + viewRef: React.ElementRef, + volume: Float, + ) => void; + setFullScreen: ( + viewRef: React.ElementRef, + fullScreen: boolean, + ) => void; setLicenseResult: ( + viewRef: React.ElementRef, result: string, licenseUrl: string, - reactTag: number, - ) => Promise; + ) => void; setLicenseResultError: ( + viewRef: React.ElementRef, error: string, licenseUrl: string, - reactTag: number, - ) => Promise; - setVolume: (volume: number, reactTag: number) => Promise; - getCurrentPosition: (reactTag: number) => Promise; - setFullScreen: (fullScreen: boolean, reactTag: number) => Promise; -} - -export interface VideoDecoderPropertiesType { - getWidevineLevel: () => Promise; - isCodecSupported: ( - mimeType: string, - width: number, - height: number, - ) => Promise<'unsupported' | 'hardware' | 'software'>; - isHEVCSupported: () => Promise<'unsupported' | 'hardware' | 'software'>; + ) => void; } -export const VideoManager = NativeModules.VideoManager as VideoManagerType; -export const VideoDecoderProperties = - NativeModules.VideoDecoderProperties as VideoDecoderPropertiesType; - -export default requireNativeComponent( - 'RCTVideo', -) as VideoComponentType; +export const Commands = codegenNativeCommands({ + supportedCommands: [ + 'seek', + 'setPlayerPauseState', + 'setVolumeCMD', + 'setFullScreen', + 'setLicenseResult', + 'setLicenseResultError', + ], +}); + +export default codegenNativeComponent('RCTVideo'); diff --git a/src/types/ViewType.ts b/src/types/ViewType.ts index 34e6a419b4..e4159a42ed 100644 --- a/src/types/ViewType.ts +++ b/src/types/ViewType.ts @@ -2,10 +2,10 @@ * Define Available view type for android * these values shall match android spec, see ViewType.kt */ -enum ResizeMode { +enum ViewType { TEXTURE = 0, SURFACE = 1, SURFACE_SECURE = 2, } -export default ResizeMode; +export default ViewType; diff --git a/src/types/video.ts b/src/types/video.ts index c48053905a..cd170df61d 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -66,7 +66,7 @@ export type Drm = Readonly<{ contentId: string, licenseUrl: string, loadedLicenseUrl: string, - ) => void; // ios + ) => string | Promise; // ios /* eslint-enable @typescript-eslint/no-unused-vars */ }>; diff --git a/src/utils.ts b/src/utils.ts index 4d5e29c8f5..149844b15f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -43,6 +43,10 @@ export function resolveAssetSourceForVideo( return source as ReactVideoSourceProperties; } +/** + * @deprecated + * Do not use this fn anymore. "findNodeHandle" will be deprecated. + * */ export function getReactTag( ref: RefObject< | Component