refactor: internal refactor for prepare new arch (#3980)
* chore(js): fix typo * refactor(js): refactor type code for codegen * refactor(js): refactor Video component - parse shutterColor value within JS - remove internal fullscreen state * chore(js): add deprecation warning comment * fix(js): fix return type * fix(js): fix import path * refactor(android): apply changed API for new arch * refactor(ios): apply changed API for new arch * fix(ios): fix wrong name * refactor: refactor VideoDecoderProperties - rename and add wrapper * refactor(android): Code fixes for backward compatibility with Kotlin
This commit is contained in:
		@@ -47,7 +47,7 @@ enum class EventTypes(val eventName: String) {
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun toMap() =
 | 
			
		||||
            mutableMapOf<String, Any>().apply {
 | 
			
		||||
                EventTypes.entries.forEach { eventType ->
 | 
			
		||||
                EventTypes.values().toList().forEach { eventType ->
 | 
			
		||||
                    put("top${eventType.eventName.removePrefix("on")}", mapOf("registrationName" to eventType.eventName))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ import android.text.TextUtils;
 | 
			
		||||
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;
 | 
			
		||||
@@ -203,7 +202,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactProp(name = PROP_TEXT_TRACKS)
 | 
			
		||||
    public void setPropTextTracks(final ReactExoplayerView videoView,
 | 
			
		||||
    public void setTextTracks(final ReactExoplayerView videoView,
 | 
			
		||||
                                  @Nullable ReadableArray textTracks) {
 | 
			
		||||
        SideLoadedTextTrackList sideLoadedTextTracks = SideLoadedTextTrackList.Companion.parse(textTracks);
 | 
			
		||||
        videoView.setTextTracks(sideLoadedTextTracks);
 | 
			
		||||
@@ -245,12 +244,12 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactProp(name = PROP_MAXIMUM_BIT_RATE)
 | 
			
		||||
    public void setMaxBitRate(final ReactExoplayerView videoView, final int maxBitRate) {
 | 
			
		||||
        videoView.setMaxBitRateModifier(maxBitRate);
 | 
			
		||||
    public void setMaxBitRate(final ReactExoplayerView videoView, final float maxBitRate) {
 | 
			
		||||
        videoView.setMaxBitRateModifier((int)maxBitRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactProp(name = PROP_MIN_LOAD_RETRY_COUNT)
 | 
			
		||||
    public void minLoadRetryCount(final ReactExoplayerView videoView, final int minLoadRetryCount) {
 | 
			
		||||
    public void setMinLoadRetryCount(final ReactExoplayerView videoView, final int minLoadRetryCount) {
 | 
			
		||||
        videoView.setMinLoadRetryCountModifier(minLoadRetryCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -310,12 +309,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
 | 
			
		||||
        videoView.setSubtitleStyle(SubtitleStyle.parse(src));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactProp(name = PROP_SHUTTER_COLOR, customType = "Color")
 | 
			
		||||
    public void setShutterColor(final ReactExoplayerView videoView, final Integer color) {
 | 
			
		||||
        videoView.setShutterColor(color == null ? Color.BLACK : color);
 | 
			
		||||
    @ReactProp(name = PROP_SHUTTER_COLOR, defaultInt = 0)
 | 
			
		||||
    public void setShutterColor(final ReactExoplayerView videoView, final int color) {
 | 
			
		||||
        videoView.setShutterColor(color == 0 ? Color.BLACK : color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @ReactProp(name = PROP_BUFFER_CONFIG)
 | 
			
		||||
    public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
 | 
			
		||||
        BufferConfig config = BufferConfig.parse(bufferConfig);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ class ReactVideoPackage(private val config: ReactExoplayerConfig? = null) : Reac
 | 
			
		||||
 | 
			
		||||
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
 | 
			
		||||
        listOf(
 | 
			
		||||
            VideoDecoderPropertiesModule(reactContext),
 | 
			
		||||
            VideoDecoderInfoModule(reactContext),
 | 
			
		||||
            VideoManagerModule(reactContext)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
 | 
			
		||||
import com.facebook.react.bridge.ReactMethod
 | 
			
		||||
import java.util.UUID
 | 
			
		||||
 | 
			
		||||
class VideoDecoderPropertiesModule(reactContext: ReactApplicationContext?) : ReactContextBaseJavaModule(reactContext) {
 | 
			
		||||
class VideoDecoderInfoModule(reactContext: ReactApplicationContext?) : ReactContextBaseJavaModule(reactContext) {
 | 
			
		||||
    override fun getName(): String = REACT_CLASS
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
@@ -37,36 +37,36 @@ class VideoDecoderPropertiesModule(reactContext: ReactApplicationContext?) : Rea
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun isCodecSupported(mimeType: String?, width: Int, height: Int, p: Promise) {
 | 
			
		||||
    fun isCodecSupported(mimeType: String?, width: Double, height: Double, p: Promise?) {
 | 
			
		||||
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 | 
			
		||||
            p.resolve("unsupported")
 | 
			
		||||
            p?.resolve("unsupported")
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        val mRegularCodecs = MediaCodecList(MediaCodecList.REGULAR_CODECS)
 | 
			
		||||
        val format = MediaFormat.createVideoFormat(mimeType!!, width, height)
 | 
			
		||||
        val format = MediaFormat.createVideoFormat(mimeType!!, width.toInt(), height.toInt())
 | 
			
		||||
        val codecName = mRegularCodecs.findDecoderForFormat(format)
 | 
			
		||||
        if (codecName == null) {
 | 
			
		||||
            p.resolve("unsupported")
 | 
			
		||||
            p?.resolve("unsupported")
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Fallback for android < 10
 | 
			
		||||
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
 | 
			
		||||
            p.resolve("software")
 | 
			
		||||
            p?.resolve("software")
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        val isHardwareAccelerated = mRegularCodecs.codecInfos.any {
 | 
			
		||||
            it.name.equals(codecName, ignoreCase = true) && it.isHardwareAccelerated
 | 
			
		||||
        }
 | 
			
		||||
        p.resolve(if (isHardwareAccelerated) "software" else "hardware")
 | 
			
		||||
        p?.resolve(if (isHardwareAccelerated) "software" else "hardware")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun isHEVCSupported(p: Promise) = isCodecSupported("video/hevc", 1920, 1080, p)
 | 
			
		||||
    fun isHEVCSupported(p: Promise) = isCodecSupported("video/hevc", 1920.0, 1080.0, p)
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L)
 | 
			
		||||
        private const val SECURITY_LEVEL_PROPERTY = "securityLevel"
 | 
			
		||||
        private const val REACT_CLASS = "VideoDecoderProperties"
 | 
			
		||||
        private const val REACT_CLASS = "VideoDecoderInfoModule"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +1,10 @@
 | 
			
		||||
package com.brentvatne.react
 | 
			
		||||
 | 
			
		||||
import com.brentvatne.common.toolbox.ReactBridgeUtils
 | 
			
		||||
import com.brentvatne.exoplayer.ReactExoplayerView
 | 
			
		||||
import com.facebook.react.bridge.Promise
 | 
			
		||||
import com.facebook.react.bridge.ReactApplicationContext
 | 
			
		||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
 | 
			
		||||
import com.facebook.react.bridge.ReactMethod
 | 
			
		||||
import com.facebook.react.bridge.ReadableMap
 | 
			
		||||
import com.facebook.react.bridge.UiThreadUtil
 | 
			
		||||
import com.facebook.react.uimanager.UIManagerHelper
 | 
			
		||||
import com.facebook.react.uimanager.common.UIManagerType
 | 
			
		||||
@@ -37,31 +35,33 @@ class VideoManagerModule(reactContext: ReactApplicationContext?) : ReactContextB
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun setPlayerPauseState(paused: Boolean?, reactTag: Int) {
 | 
			
		||||
    fun setPlayerPauseStateCmd(reactTag: Int, paused: Boolean?) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
            it?.setPausedModifier(paused!!)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun seek(info: ReadableMap, reactTag: Int) {
 | 
			
		||||
        if (!info.hasKey("time")) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val time = ReactBridgeUtils.safeGetInt(info, "time")
 | 
			
		||||
    fun seekCmd(reactTag: Int, time: Float, tolerance: Float) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
            it?.seekTo((time * 1000f).roundToInt().toLong())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun setVolume(volume: Float, reactTag: Int) {
 | 
			
		||||
    fun setVolumeCmd(reactTag: Int, volume: Float) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
            it?.setVolumeModifier(volume)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun setFullScreenCmd(reactTag: Int, fullScreen: Boolean) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
            it?.setFullscreen(fullScreen)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun getCurrentPosition(reactTag: Int, promise: Promise) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
@@ -69,13 +69,6 @@ class VideoManagerModule(reactContext: ReactApplicationContext?) : ReactContextB
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun setFullScreen(fullScreen: Boolean, reactTag: Int) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
            it?.setFullscreen(fullScreen)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private const val REACT_CLASS = "VideoManager"
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -762,15 +762,13 @@ 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
 | 
			
		||||
 | 
			
		||||
        _pendingSeek = true
 | 
			
		||||
 | 
			
		||||
        guard item != nil, let player = _player, let item, item.status == AVPlayerItem.Status.readyToPlay else {
 | 
			
		||||
            _pendingSeekTime = seekTime.floatValue
 | 
			
		||||
            _pendingSeekTime = time.floatValue
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -778,15 +776,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])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1303,7 +1301,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,
 | 
			
		||||
@@ -1320,14 +1318,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
			
		||||
        _resouceLoaderDelegate?.setLicenseResultError(error, licenseUrl)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func dismissFullscreenPlayer() {
 | 
			
		||||
        setFullscreen(false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func presentFullscreenPlayer() {
 | 
			
		||||
        setFullscreen(true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // MARK: - RCTPlayerObserverHandler
 | 
			
		||||
 | 
			
		||||
    func handleTimeUpdate(time _: CMTime) {
 | 
			
		||||
@@ -1381,18 +1371,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
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#import "React/RCTViewManager.h"
 | 
			
		||||
#import <React/RCTBridge.h>
 | 
			
		||||
#import <React/RCTBridgeModule.h>
 | 
			
		||||
 | 
			
		||||
@interface RCT_EXTERN_MODULE (RCTVideoManager, RCTViewManager)
 | 
			
		||||
 | 
			
		||||
@@ -37,7 +37,7 @@ RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL);
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(localSourceEncryptionKeyScheme, NSString);
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(subtitleStyle, NSDictionary);
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(showNotificationControls, BOOL)
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(showNotificationControls, BOOL);
 | 
			
		||||
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock);
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock);
 | 
			
		||||
@@ -68,31 +68,21 @@ RCT_EXPORT_VIEW_PROPERTY(onTextTracks, RCTDirectEventBlock);
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(onAudioTracks, RCTDirectEventBlock);
 | 
			
		||||
RCT_EXPORT_VIEW_PROPERTY(onTextTrackDataChanged, RCTDirectEventBlock);
 | 
			
		||||
 | 
			
		||||
RCT_EXTERN_METHOD(seekCmd : (nonnull NSNumber*)reactTag time : (nonnull NSNumber*)time tolerance : (nonnull NSNumber*)tolerance)
 | 
			
		||||
RCT_EXTERN_METHOD(setLicenseResultCmd : (nonnull NSNumber*)reactTag lisence : (NSString*)license licenseUrl : (NSString*)licenseUrl)
 | 
			
		||||
RCT_EXTERN_METHOD(setLicenseResultErrorCmd : (nonnull NSNumber*)reactTag error : (NSString*)error licenseUrl : (NSString*)licenseUrl)
 | 
			
		||||
RCT_EXTERN_METHOD(setPlayerPauseStateCmd : (nonnull NSNumber*)reactTag paused : (nonnull BOOL)paused)
 | 
			
		||||
RCT_EXTERN_METHOD(setVolumeCmd : (nonnull NSNumber*)reactTag volume : (nonnull float*)volume)
 | 
			
		||||
RCT_EXTERN_METHOD(setFullScreenCmd : (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
 | 
			
		||||
 
 | 
			
		||||
@@ -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(seekCmd:time:tolerance:)
 | 
			
		||||
    func seekCmd(_ 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(setLicenseResultCmd:license:licenseUrl:)
 | 
			
		||||
    func setLicenseResultCmd(_ 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(setLicenseResultErrorCmd:error:licenseUrl:)
 | 
			
		||||
    func setLicenseResultErrorCmd(_ 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(setPlayerPauseStateCmd:paused:)
 | 
			
		||||
    func setPlayerPauseStateCmd(_ 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) {
 | 
			
		||||
        performOnVideoView(withReactTag: reactTag, callback: { videoView in
 | 
			
		||||
            videoView?.setPaused(paused.boolValue)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @objc(setVolume:reactTag:)
 | 
			
		||||
    func setVolume(value: Float, reactTag: NSNumber) {
 | 
			
		||||
        performOnVideoView(withReactTag: reactTag, callback: { videoView in
 | 
			
		||||
            videoView?.setVolume(value)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @objc(getCurrentPosition:resolver:rejecter:)
 | 
			
		||||
    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) {
 | 
			
		||||
    @objc(setFullScreenCmd:fullscreen:)
 | 
			
		||||
    func setFullScreenCmd(_ reactTag: NSNumber, fullScreen: Bool) {
 | 
			
		||||
        performOnVideoView(withReactTag: reactTag, callback: { videoView in
 | 
			
		||||
            videoView?.setFullscreen(fullScreen)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @objc(save:options:resolve:reject:)
 | 
			
		||||
    func save(_ reactTag: NSNumber, options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
 | 
			
		||||
        performOnVideoView(withReactTag: reactTag, callback: { videoView in
 | 
			
		||||
            videoView?.save(options, resolve, reject)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @objc(getCurrentPosition:resolve:reject:)
 | 
			
		||||
    func getCurrentPosition(_ reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
 | 
			
		||||
        performOnVideoView(withReactTag: reactTag, callback: { videoView in
 | 
			
		||||
            videoView?.getCurrentPlaybackTime(resolve, reject)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override class func requiresMainQueueSetup() -> Bool {
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										229
									
								
								src/Video.tsx
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								src/Video.tsx
									
									
									
									
									
								
							@@ -5,57 +5,47 @@ import React, {
 | 
			
		||||
  useRef,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  useImperativeHandle,
 | 
			
		||||
  type ComponentRef,
 | 
			
		||||
} from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  View,
 | 
			
		||||
  StyleSheet,
 | 
			
		||||
  Image,
 | 
			
		||||
  Platform,
 | 
			
		||||
  type StyleProp,
 | 
			
		||||
  type ImageStyle,
 | 
			
		||||
  type NativeSyntheticEvent,
 | 
			
		||||
} from 'react-native';
 | 
			
		||||
import type {ElementRef} from 'react';
 | 
			
		||||
import {View, StyleSheet, Image, Platform, processColor} from 'react-native';
 | 
			
		||||
import type {StyleProp, ImageStyle, 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 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 NativeVideoManager from './specs/NativeVideoManager';
 | 
			
		||||
import type {VideoSaveData} from './specs/NativeVideoManager';
 | 
			
		||||
import {ViewType} from './types';
 | 
			
		||||
import type {
 | 
			
		||||
  OnLoadData,
 | 
			
		||||
  OnTextTracksData,
 | 
			
		||||
  OnReceiveAdEventData,
 | 
			
		||||
  ReactVideoProps,
 | 
			
		||||
} from './types';
 | 
			
		||||
 | 
			
		||||
export type VideoSaveData = {
 | 
			
		||||
  uri: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export interface VideoRef {
 | 
			
		||||
  seek: (time: number, tolerance?: number) => void;
 | 
			
		||||
  resume: () => void;
 | 
			
		||||
@@ -65,10 +55,10 @@ export interface VideoRef {
 | 
			
		||||
  restoreUserInterfaceForPictureInPictureStopCompleted: (
 | 
			
		||||
    restore: boolean,
 | 
			
		||||
  ) => void;
 | 
			
		||||
  save: (options: object) => Promise<VideoSaveData>;
 | 
			
		||||
  setVolume: (volume: number) => void;
 | 
			
		||||
  getCurrentPosition: () => Promise<number>;
 | 
			
		||||
  setFullScreen: (fullScreen: boolean) => void;
 | 
			
		||||
  save: (options: object) => Promise<VideoSaveData> | void;
 | 
			
		||||
  getCurrentPosition: () => Promise<number>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
@@ -79,7 +69,6 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      resizeMode,
 | 
			
		||||
      posterResizeMode,
 | 
			
		||||
      poster,
 | 
			
		||||
      fullscreen,
 | 
			
		||||
      drm,
 | 
			
		||||
      textTracks,
 | 
			
		||||
      selectedVideoTrack,
 | 
			
		||||
@@ -88,6 +77,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      useTextureView,
 | 
			
		||||
      useSecureView,
 | 
			
		||||
      viewType,
 | 
			
		||||
      shutterColor,
 | 
			
		||||
      onLoadStart,
 | 
			
		||||
      onLoad,
 | 
			
		||||
      onError,
 | 
			
		||||
@@ -122,9 +112,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
    },
 | 
			
		||||
    ref,
 | 
			
		||||
  ) => {
 | 
			
		||||
    const nativeRef = useRef<ComponentRef<VideoComponentType>>(null);
 | 
			
		||||
    const nativeRef = useRef<ElementRef<typeof NativeVideoComponent>>(null);
 | 
			
		||||
    const [showPoster, setShowPoster] = useState(!!poster);
 | 
			
		||||
    const [isFullscreen, setIsFullscreen] = useState(fullscreen);
 | 
			
		||||
    const [
 | 
			
		||||
      _restoreUserInterfaceForPIPStopCompletionHandler,
 | 
			
		||||
      setRestoreUserInterfaceForPIPStopCompletionHandler,
 | 
			
		||||
@@ -274,12 +263,10 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const callSeekFunction = () => {
 | 
			
		||||
        VideoManager.seek(
 | 
			
		||||
          {
 | 
			
		||||
            time,
 | 
			
		||||
            tolerance: tolerance || 0,
 | 
			
		||||
          },
 | 
			
		||||
        NativeVideoManager.seekCmd(
 | 
			
		||||
          getReactTag(nativeRef),
 | 
			
		||||
          time,
 | 
			
		||||
          tolerance || 0,
 | 
			
		||||
        );
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
@@ -287,31 +274,59 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
        ios: callSeekFunction,
 | 
			
		||||
        android: callSeekFunction,
 | 
			
		||||
        default: () => {
 | 
			
		||||
          // TODO: Implement VideoManager.seek for windows
 | 
			
		||||
          // TODO: Implement VideoManager.seekCmd 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 NativeVideoManager.setPlayerPauseStateCmd(
 | 
			
		||||
        getReactTag(nativeRef),
 | 
			
		||||
        true,
 | 
			
		||||
      );
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const resume = useCallback(() => {
 | 
			
		||||
      return VideoManager.setPlayerPauseState(false, getReactTag(nativeRef));
 | 
			
		||||
      return NativeVideoManager.setPlayerPauseStateCmd(
 | 
			
		||||
        getReactTag(nativeRef),
 | 
			
		||||
        false,
 | 
			
		||||
      );
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const setVolume = useCallback((volume: number) => {
 | 
			
		||||
      return NativeVideoManager.setVolumeCmd(getReactTag(nativeRef), volume);
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const setFullScreen = useCallback((fullScreen: boolean) => {
 | 
			
		||||
      return NativeVideoManager.setFullScreenCmd(
 | 
			
		||||
        getReactTag(nativeRef),
 | 
			
		||||
        fullScreen,
 | 
			
		||||
      );
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    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 NativeVideoManager.save?.(getReactTag(nativeRef), options);
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const getCurrentPosition = useCallback(() => {
 | 
			
		||||
      // @todo Must implement it in a different way.
 | 
			
		||||
      return NativeVideoManager.getCurrentPosition(getReactTag(nativeRef));
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const restoreUserInterfaceForPictureInPictureStopCompleted = useCallback(
 | 
			
		||||
@@ -321,18 +336,6 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      [setRestoreUserInterfaceForPIPStopCompletionHandler],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const setVolume = useCallback((volume: number) => {
 | 
			
		||||
      return VideoManager.setVolume(volume, getReactTag(nativeRef));
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const getCurrentPosition = useCallback(() => {
 | 
			
		||||
      return VideoManager.getCurrentPosition(getReactTag(nativeRef));
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const setFullScreen = useCallback((fullScreen: boolean) => {
 | 
			
		||||
      return VideoManager.setFullScreen(fullScreen, getReactTag(nativeRef));
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const onVideoLoadStart = useCallback(
 | 
			
		||||
      (e: NativeSyntheticEvent<OnLoadStartData>) => {
 | 
			
		||||
        hasPoster && setShowPoster(true);
 | 
			
		||||
@@ -379,6 +382,11 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      [onPlaybackStateChanged],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const _shutterColor = useMemo(() => {
 | 
			
		||||
      const color = processColor(shutterColor);
 | 
			
		||||
      return typeof color === 'number' ? color : undefined;
 | 
			
		||||
    }, [shutterColor]);
 | 
			
		||||
 | 
			
		||||
    // android only
 | 
			
		||||
    const _onTimedMetadata = useCallback(
 | 
			
		||||
      (e: NativeSyntheticEvent<OnTimedMetadataData>) => {
 | 
			
		||||
@@ -494,56 +502,41 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      [onControlsVisibilityChange],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const useExternalGetLicense = drm?.getLicense instanceof Function;
 | 
			
		||||
    const usingExternalGetLicense = drm?.getLicense instanceof Function;
 | 
			
		||||
 | 
			
		||||
    const onGetLicense = useCallback(
 | 
			
		||||
      (event: NativeSyntheticEvent<OnGetLicenseData>) => {
 | 
			
		||||
        if (useExternalGetLicense) {
 | 
			
		||||
      async (event: NativeSyntheticEvent<OnGetLicenseData>) => {
 | 
			
		||||
        if (!usingExternalGetLicense) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        const data = event.nativeEvent;
 | 
			
		||||
          if (data && data.spcBase64) {
 | 
			
		||||
            const getLicenseOverride = drm.getLicense(
 | 
			
		||||
        let result;
 | 
			
		||||
        if (data?.spcBase64) {
 | 
			
		||||
          try {
 | 
			
		||||
            // Handles both scenarios, getLicenseOverride being a promise and not.
 | 
			
		||||
            const license = await 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 =
 | 
			
		||||
              typeof license === 'string' ? license : 'Empty license result';
 | 
			
		||||
          } catch {
 | 
			
		||||
            result = 'fetch error';
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          result = 'No spc received';
 | 
			
		||||
        }
 | 
			
		||||
        if (nativeRef.current) {
 | 
			
		||||
          NativeVideoManager.setLicenseResultErrorCmd(
 | 
			
		||||
            getReactTag(nativeRef),
 | 
			
		||||
            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),
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      [drm, useExternalGetLicense],
 | 
			
		||||
      [drm, usingExternalGetLicense],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    useImperativeHandle(
 | 
			
		||||
@@ -613,7 +606,6 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
          src={src}
 | 
			
		||||
          style={StyleSheet.absoluteFill}
 | 
			
		||||
          resizeMode={resizeMode}
 | 
			
		||||
          fullscreen={isFullscreen}
 | 
			
		||||
          restoreUserInterfaceForPIPStopCompletionHandler={
 | 
			
		||||
            _restoreUserInterfaceForPIPStopCompletionHandler
 | 
			
		||||
          }
 | 
			
		||||
@@ -621,7 +613,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
          selectedTextTrack={_selectedTextTrack}
 | 
			
		||||
          selectedAudioTrack={_selectedAudioTrack}
 | 
			
		||||
          selectedVideoTrack={_selectedVideoTrack}
 | 
			
		||||
          onGetLicense={useExternalGetLicense ? onGetLicense : undefined}
 | 
			
		||||
          shutterColor={_shutterColor}
 | 
			
		||||
          onGetLicense={usingExternalGetLicense ? onGetLicense : undefined}
 | 
			
		||||
          onVideoLoad={
 | 
			
		||||
            onLoad || hasPoster
 | 
			
		||||
              ? (onVideoLoad as (e: NativeSyntheticEvent<object>) => void)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/VideoDecoderProperties.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/VideoDecoderProperties.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
import {Platform} from 'react-native';
 | 
			
		||||
 | 
			
		||||
import NativeVideoDecoderInfoModule from './specs/NativeVideoDecoderInfoModule';
 | 
			
		||||
 | 
			
		||||
const errMsgGen = (moduleName: string, propertyName: string) =>
 | 
			
		||||
  `The method or property ${moduleName}.${propertyName} is not available on ${Platform.OS}.`;
 | 
			
		||||
 | 
			
		||||
export const VideoDecoderProperties = {
 | 
			
		||||
  async getWidevineLevel() {
 | 
			
		||||
    if (Platform.OS !== 'android') {
 | 
			
		||||
      throw new Error(errMsgGen('VideoDecoderProperties', 'getWidevineLevel'));
 | 
			
		||||
    }
 | 
			
		||||
    return NativeVideoDecoderInfoModule.getWidevineLevel();
 | 
			
		||||
  },
 | 
			
		||||
  async isCodecSupported(
 | 
			
		||||
    ...args: Parameters<typeof NativeVideoDecoderInfoModule.isCodecSupported>
 | 
			
		||||
  ) {
 | 
			
		||||
    if (Platform.OS !== 'android') {
 | 
			
		||||
      throw new Error(errMsgGen('VideoDecoderProperties', 'isCodecSupported'));
 | 
			
		||||
    }
 | 
			
		||||
    return NativeVideoDecoderInfoModule.isCodecSupported(...args);
 | 
			
		||||
  },
 | 
			
		||||
  async isHEVCSupported() {
 | 
			
		||||
    if (Platform.OS !== 'android') {
 | 
			
		||||
      throw new Error(errMsgGen('VideoDecoderProperties', 'isHEVCSupported'));
 | 
			
		||||
    }
 | 
			
		||||
    return NativeVideoDecoderInfoModule.isHEVCSupported();
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import Video from './Video';
 | 
			
		||||
export {VideoDecoderProperties} from './specs/VideoNativeComponent';
 | 
			
		||||
export {VideoDecoderProperties} from './VideoDecoderProperties';
 | 
			
		||||
export * from './types';
 | 
			
		||||
export type {VideoRef} from './Video';
 | 
			
		||||
export default Video;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/specs/NativeVideoDecoderInfoModule.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/specs/NativeVideoDecoderInfoModule.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
import {NativeModules} from 'react-native';
 | 
			
		||||
import type {Int32} from 'react-native/Libraries/Types/CodegenTypes';
 | 
			
		||||
 | 
			
		||||
// @TODO rename to "Spec" when applying new arch
 | 
			
		||||
interface VideoDecoderInfoModuleType {
 | 
			
		||||
  getWidevineLevel: () => Promise<Int32>;
 | 
			
		||||
  isCodecSupported: (
 | 
			
		||||
    mimeType: string,
 | 
			
		||||
    width: Int32,
 | 
			
		||||
    height: Int32,
 | 
			
		||||
  ) => Promise<'unsupported' | 'hardware' | 'software'>;
 | 
			
		||||
  isHEVCSupported: () => Promise<'unsupported' | 'hardware' | 'software'>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default NativeModules.VideoDecoderInfoModule as VideoDecoderInfoModuleType;
 | 
			
		||||
							
								
								
									
										32
									
								
								src/specs/NativeVideoManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/specs/NativeVideoManager.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
import {NativeModules} from 'react-native';
 | 
			
		||||
import type {
 | 
			
		||||
  Int32,
 | 
			
		||||
  Float,
 | 
			
		||||
  UnsafeObject,
 | 
			
		||||
} from 'react-native/Libraries/Types/CodegenTypes';
 | 
			
		||||
 | 
			
		||||
export type VideoSaveData = {
 | 
			
		||||
  uri: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// @TODO rename to "Spec" when applying new arch
 | 
			
		||||
export interface VideoManagerType {
 | 
			
		||||
  seekCmd: (reactTag: Int32, time: Float, tolerance?: Float) => Promise<void>;
 | 
			
		||||
  setPlayerPauseStateCmd: (reactTag: Int32, paused: boolean) => Promise<void>;
 | 
			
		||||
  setLicenseResultCmd: (
 | 
			
		||||
    reactTag: Int32,
 | 
			
		||||
    result: string,
 | 
			
		||||
    licenseUrl: string,
 | 
			
		||||
  ) => Promise<void>;
 | 
			
		||||
  setLicenseResultErrorCmd: (
 | 
			
		||||
    reactTag: Int32,
 | 
			
		||||
    error: string,
 | 
			
		||||
    licenseUrl: string,
 | 
			
		||||
  ) => Promise<void>;
 | 
			
		||||
  setFullScreenCmd: (reactTag: Int32, fullScreen: boolean) => Promise<void>;
 | 
			
		||||
  setVolumeCmd: (reactTag: Int32, volume: number) => Promise<void>;
 | 
			
		||||
  save: (reactTag: Int32, option: UnsafeObject) => Promise<VideoSaveData>;
 | 
			
		||||
  getCurrentPosition: (reactTag: Int32) => Promise<Int32>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default NativeModules.VideoManager as VideoManagerType;
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/ban-types */
 | 
			
		||||
import type {HostComponent, ViewProps} from 'react-native';
 | 
			
		||||
import {NativeModules, requireNativeComponent} from 'react-native';
 | 
			
		||||
import {requireNativeComponent} from 'react-native';
 | 
			
		||||
import type {
 | 
			
		||||
  DirectEventHandler,
 | 
			
		||||
  Double,
 | 
			
		||||
@@ -91,11 +91,6 @@ type SelectedVideoTrack = Readonly<{
 | 
			
		||||
  value?: string;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
export type Seek = Readonly<{
 | 
			
		||||
  time: Float;
 | 
			
		||||
  tolerance?: Float;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
type BufferConfigLive = Readonly<{
 | 
			
		||||
  maxPlaybackSpeed?: Float;
 | 
			
		||||
  minPlaybackSpeed?: Float;
 | 
			
		||||
@@ -289,7 +284,7 @@ export type OnAudioFocusChangedData = Readonly<{
 | 
			
		||||
 | 
			
		||||
type ControlsStyles = Readonly<{
 | 
			
		||||
  hideSeekBar?: boolean;
 | 
			
		||||
  seekIncrementMS?: number;
 | 
			
		||||
  seekIncrementMS?: Int32;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
export type OnControlsVisibilityChange = Readonly<{
 | 
			
		||||
@@ -300,10 +295,13 @@ export interface VideoNativeProps extends ViewProps {
 | 
			
		||||
  src?: VideoSrc;
 | 
			
		||||
  adTagUrl?: string;
 | 
			
		||||
  allowsExternalPlayback?: boolean; // ios, true
 | 
			
		||||
  disableFocus?: boolean; // android
 | 
			
		||||
  maxBitRate?: Float;
 | 
			
		||||
  resizeMode?: WithDefault<string, 'none'>;
 | 
			
		||||
  repeat?: boolean;
 | 
			
		||||
  automaticallyWaitsToMinimizeStalling?: boolean;
 | 
			
		||||
  shutterColor?: Int32;
 | 
			
		||||
  audioOutput?: WithDefault<string, 'speaker'>;
 | 
			
		||||
  textTracks?: TextTracks;
 | 
			
		||||
  selectedTextTrack?: SelectedTextTrack;
 | 
			
		||||
  selectedAudioTrack?: SelectedAudioTrack;
 | 
			
		||||
@@ -375,45 +373,8 @@ export interface VideoNativeProps extends ViewProps {
 | 
			
		||||
  onVideoTracks?: DirectEventHandler<OnVideoTracksData>; // android
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type VideoComponentType = HostComponent<VideoNativeProps>;
 | 
			
		||||
 | 
			
		||||
export type VideoSaveData = {
 | 
			
		||||
  uri: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export interface VideoManagerType {
 | 
			
		||||
  save: (option: object, reactTag: number) => Promise<VideoSaveData>;
 | 
			
		||||
  seek: (option: Seek, reactTag: number) => Promise<void>;
 | 
			
		||||
  setPlayerPauseState: (paused: boolean, reactTag: number) => Promise<void>;
 | 
			
		||||
  setLicenseResult: (
 | 
			
		||||
    result: string,
 | 
			
		||||
    licenseUrl: string,
 | 
			
		||||
    reactTag: number,
 | 
			
		||||
  ) => Promise<void>;
 | 
			
		||||
  setLicenseResultError: (
 | 
			
		||||
    error: string,
 | 
			
		||||
    licenseUrl: string,
 | 
			
		||||
    reactTag: number,
 | 
			
		||||
  ) => Promise<void>;
 | 
			
		||||
  setVolume: (volume: number, reactTag: number) => Promise<void>;
 | 
			
		||||
  getCurrentPosition: (reactTag: number) => Promise<number>;
 | 
			
		||||
  setFullScreen: (fullScreen: boolean, reactTag: number) => Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface VideoDecoderPropertiesType {
 | 
			
		||||
  getWidevineLevel: () => Promise<number>;
 | 
			
		||||
  isCodecSupported: (
 | 
			
		||||
    mimeType: string,
 | 
			
		||||
    width: number,
 | 
			
		||||
    height: number,
 | 
			
		||||
  ) => Promise<'unsupported' | 'hardware' | 'software'>;
 | 
			
		||||
  isHEVCSupported: () => Promise<'unsupported' | 'hardware' | 'software'>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const VideoManager = NativeModules.VideoManager as VideoManagerType;
 | 
			
		||||
export const VideoDecoderProperties =
 | 
			
		||||
  NativeModules.VideoDecoderProperties as VideoDecoderPropertiesType;
 | 
			
		||||
type NativeVideoComponentType = HostComponent<VideoNativeProps>;
 | 
			
		||||
 | 
			
		||||
export default requireNativeComponent<VideoNativeProps>(
 | 
			
		||||
  'RCTVideo',
 | 
			
		||||
) as VideoComponentType;
 | 
			
		||||
) as NativeVideoComponentType;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,7 @@ export type Drm = Readonly<{
 | 
			
		||||
    contentId: string,
 | 
			
		||||
    licenseUrl: string,
 | 
			
		||||
    loadedLicenseUrl: string,
 | 
			
		||||
  ) => void; // ios
 | 
			
		||||
  ) => string | Promise<string>; // ios
 | 
			
		||||
  /* eslint-enable @typescript-eslint/no-unused-vars */
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<unknown, unknown, unknown>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user