feat: add setSource API function fix ads playback (#4185)
* feat: add setSource API function fix ads playback
This commit is contained in:
		@@ -93,6 +93,13 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void showAds() {
 | 
			
		||||
        adOverlayFrameLayout.setVisibility(View.GONE);
 | 
			
		||||
    }
 | 
			
		||||
    public void hideAds() {
 | 
			
		||||
        adOverlayFrameLayout.setVisibility(View.VISIBLE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void clearVideoView() {
 | 
			
		||||
        if (surfaceView instanceof TextureView) {
 | 
			
		||||
            player.clearVideoTextureView((TextureView) surfaceView);
 | 
			
		||||
@@ -189,7 +196,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
 | 
			
		||||
        surfaceView.setAlpha(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateShutterViewVisibility() {
 | 
			
		||||
    public void updateShutterViewVisibility() {
 | 
			
		||||
        if (this.hideShutterView) {
 | 
			
		||||
            hideShutterView();
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -735,22 +735,28 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
        ReactExoplayerView self = this;
 | 
			
		||||
        Activity activity = themedReactContext.getCurrentActivity();
 | 
			
		||||
        // This ensures all props have been settled, to avoid async racing conditions.
 | 
			
		||||
        Source runningSource = source;
 | 
			
		||||
        mainRunnable = () -> {
 | 
			
		||||
            if (viewHasDropped) {
 | 
			
		||||
            if (viewHasDropped && runningSource == source) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            try {
 | 
			
		||||
                if (runningSource.getUri() == null) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (player == null) {
 | 
			
		||||
                    // Initialize core configuration and listeners
 | 
			
		||||
                    initializePlayerCore(self);
 | 
			
		||||
                }
 | 
			
		||||
                if (playerNeedsSource && source.getUri() != null) {
 | 
			
		||||
                if (playerNeedsSource) {
 | 
			
		||||
                    // Will force display of shutter view if needed
 | 
			
		||||
                    exoPlayerView.updateShutterViewVisibility();
 | 
			
		||||
                    exoPlayerView.invalidateAspectRatio();
 | 
			
		||||
                    // DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread
 | 
			
		||||
                    ExecutorService es = Executors.newSingleThreadExecutor();
 | 
			
		||||
                    es.execute(() -> {
 | 
			
		||||
                        // DRM initialization must run on a different thread
 | 
			
		||||
                        if (viewHasDropped) {
 | 
			
		||||
                        if (viewHasDropped && runningSource == source) {
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        if (activity == null) {
 | 
			
		||||
@@ -761,12 +767,12 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
 | 
			
		||||
                        // Initialize handler to run on the main thread
 | 
			
		||||
                        activity.runOnUiThread(() -> {
 | 
			
		||||
                            if (viewHasDropped) {
 | 
			
		||||
                            if (viewHasDropped && runningSource == source) {
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                            try {
 | 
			
		||||
                                // Source initialization must run on the main thread
 | 
			
		||||
                                initializePlayerSource();
 | 
			
		||||
                                initializePlayerSource(runningSource);
 | 
			
		||||
                            } catch (Exception ex) {
 | 
			
		||||
                                self.playerNeedsSource = true;
 | 
			
		||||
                                DebugLog.e(TAG, "Failed to initialize Player! 1");
 | 
			
		||||
@@ -776,8 +782,8 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                } else if (source.getUri() != null) {
 | 
			
		||||
                    initializePlayerSource();
 | 
			
		||||
                } else if (runningSource == source) {
 | 
			
		||||
                    initializePlayerSource(runningSource);
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception ex) {
 | 
			
		||||
                self.playerNeedsSource = true;
 | 
			
		||||
@@ -816,6 +822,11 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
                        .setEnableDecoderFallback(true)
 | 
			
		||||
                        .forceEnableMediaCodecAsynchronousQueueing();
 | 
			
		||||
 | 
			
		||||
        DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory);
 | 
			
		||||
        if (useCache) {
 | 
			
		||||
            mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ImaSdkSettings imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
 | 
			
		||||
        imaSdkSettings.setLanguage(adLanguage);
 | 
			
		||||
 | 
			
		||||
@@ -826,14 +837,7 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
                .setAdEventListener(this)
 | 
			
		||||
                .setAdErrorListener(this)
 | 
			
		||||
                .build();
 | 
			
		||||
        DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory);
 | 
			
		||||
        if (useCache) {
 | 
			
		||||
            mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (adsLoader != null) {
 | 
			
		||||
            mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
 | 
			
		||||
        }
 | 
			
		||||
        mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
 | 
			
		||||
 | 
			
		||||
        player = new ExoPlayer.Builder(getContext(), renderersFactory)
 | 
			
		||||
                .setTrackSelector(self.trackSelector)
 | 
			
		||||
@@ -846,6 +850,7 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
        player.addListener(self);
 | 
			
		||||
        player.setVolume(muted ? 0.f : audioVolume * 1);
 | 
			
		||||
        exoPlayerView.setPlayer(player);
 | 
			
		||||
 | 
			
		||||
        if (adsLoader != null) {
 | 
			
		||||
            adsLoader.setPlayer(player);
 | 
			
		||||
        }
 | 
			
		||||
@@ -884,31 +889,28 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
        return drmSessionManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void initializePlayerSource() {
 | 
			
		||||
        if (source.getUri() == null) {
 | 
			
		||||
    private void initializePlayerSource(Source runningSource) {
 | 
			
		||||
        if (runningSource.getUri() == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        /// init DRM
 | 
			
		||||
        DrmSessionManager drmSessionManager = initializePlayerDrm();
 | 
			
		||||
        if (drmSessionManager == null && source.getDrmProps() != null && source.getDrmProps().getDrmType() != null) {
 | 
			
		||||
        if (drmSessionManager == null && runningSource.getDrmProps() != null && runningSource.getDrmProps().getDrmType() != null) {
 | 
			
		||||
            // Failed to initialize DRM session manager - cannot continue
 | 
			
		||||
            DebugLog.e(TAG, "Failed to initialize DRM Session Manager Framework!");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // init source to manage ads and external text tracks
 | 
			
		||||
        ArrayList<MediaSource> mediaSourceList = buildTextSources();
 | 
			
		||||
        MediaSource videoSource = buildMediaSource(source.getUri(), source.getExtension(), drmSessionManager, source.getCropStartMs(), source.getCropEndMs());
 | 
			
		||||
        MediaSource videoSource = buildMediaSource(runningSource.getUri(), runningSource.getExtension(), drmSessionManager, runningSource.getCropStartMs(), runningSource.getCropEndMs());
 | 
			
		||||
        MediaSource mediaSourceWithAds = null;
 | 
			
		||||
        if (adTagUrl != null && adsLoader != null) {
 | 
			
		||||
        if (adTagUrl != null && BuildConfig.USE_EXOPLAYER_IMA) {
 | 
			
		||||
            DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
 | 
			
		||||
                    .setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
 | 
			
		||||
            DataSpec adTagDataSpec = new DataSpec(adTagUrl);
 | 
			
		||||
            mediaSourceWithAds = new AdsMediaSource(videoSource, adTagDataSpec, ImmutableList.of(source.getUri(), adTagUrl), mediaSourceFactory, adsLoader, exoPlayerView);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (adTagUrl == null && adsLoader != null) {
 | 
			
		||||
                adsLoader.release();
 | 
			
		||||
                adsLoader = null;
 | 
			
		||||
            }
 | 
			
		||||
            DebugLog.w(TAG, "ads " + adTagUrl);
 | 
			
		||||
            mediaSourceWithAds = new AdsMediaSource(videoSource, adTagDataSpec, ImmutableList.of(runningSource.getUri(), adTagUrl), mediaSourceFactory, adsLoader, exoPlayerView);
 | 
			
		||||
            exoPlayerView.showAds();
 | 
			
		||||
        }
 | 
			
		||||
        MediaSource mediaSource;
 | 
			
		||||
        if (mediaSourceList.isEmpty()) {
 | 
			
		||||
@@ -943,8 +945,8 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
        if (haveResumePosition) {
 | 
			
		||||
            player.seekTo(resumeWindow, resumePosition);
 | 
			
		||||
            player.setMediaSource(mediaSource, false);
 | 
			
		||||
        } else if (source.getStartPositionMs() > 0) {
 | 
			
		||||
            player.setMediaSource(mediaSource, source.getStartPositionMs());
 | 
			
		||||
        } else if (runningSource.getStartPositionMs() > 0) {
 | 
			
		||||
            player.setMediaSource(mediaSource, runningSource.getStartPositionMs());
 | 
			
		||||
        } else {
 | 
			
		||||
            player.setMediaSource(mediaSource, true);
 | 
			
		||||
        }
 | 
			
		||||
@@ -1243,10 +1245,6 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
 | 
			
		||||
    private void releasePlayer() {
 | 
			
		||||
        if (player != null) {
 | 
			
		||||
            if (adsLoader != null) {
 | 
			
		||||
                adsLoader.setPlayer(null);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if(playbackServiceBinder != null) {
 | 
			
		||||
                playbackServiceBinder.getService().unregisterPlayer(player);
 | 
			
		||||
                themedReactContext.unbindService(playbackServiceConnection);
 | 
			
		||||
@@ -1903,19 +1901,21 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
            if (!isSourceEqual) {
 | 
			
		||||
                reloadSource();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            clearSrc();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void clearSrc() {
 | 
			
		||||
        if (source.getUri() != null) {
 | 
			
		||||
            if (player != null) {
 | 
			
		||||
                player.stop();
 | 
			
		||||
                player.clearMediaItems();
 | 
			
		||||
            }
 | 
			
		||||
            this.source = new Source();
 | 
			
		||||
            this.mediaDataSourceFactory = null;
 | 
			
		||||
            clearResumePosition();
 | 
			
		||||
        }
 | 
			
		||||
        exoPlayerView.hideAds();
 | 
			
		||||
        this.source = new Source();
 | 
			
		||||
        this.mediaDataSourceFactory = null;
 | 
			
		||||
        clearResumePosition();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setProgressUpdateInterval(final float progressUpdateInterval) {
 | 
			
		||||
@@ -1927,6 +1927,7 @@ public class ReactExoplayerView extends FrameLayout implements
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAdTagUrl(final Uri uri) {
 | 
			
		||||
        DebugLog.w(TAG, "setAdTagUrl" + uri);
 | 
			
		||||
        adTagUrl = uri;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,12 +89,7 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
 | 
			
		||||
    @ReactProp(name = PROP_SRC)
 | 
			
		||||
    fun setSrc(videoView: ReactExoplayerView, src: ReadableMap?) {
 | 
			
		||||
        val context = videoView.context.applicationContext
 | 
			
		||||
        val source = Source.parse(src, context)
 | 
			
		||||
        if (source.uri == null) {
 | 
			
		||||
            videoView.clearSrc()
 | 
			
		||||
        } else {
 | 
			
		||||
            videoView.setSrc(source)
 | 
			
		||||
        }
 | 
			
		||||
        videoView.setSrc(Source.parse(src, context))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactProp(name = PROP_AD_TAG_URL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
package com.brentvatne.react
 | 
			
		||||
 | 
			
		||||
import com.brentvatne.common.api.Source
 | 
			
		||||
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
 | 
			
		||||
@@ -63,6 +65,13 @@ class VideoManagerModule(reactContext: ReactApplicationContext?) : ReactContextB
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun setSourceCmd(reactTag: Int, source: ReadableMap?) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
            it?.setSrc(Source.parse(source, reactApplicationContext))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    fun getCurrentPosition(reactTag: Int, promise: Promise) {
 | 
			
		||||
        performOnPlayerView(reactTag) {
 | 
			
		||||
 
 | 
			
		||||
@@ -115,6 +115,16 @@ This function will change the volume exactly like [volume](./props#volume) prope
 | 
			
		||||
This function retrieves and returns the precise current position of the video playback, measured in seconds.
 | 
			
		||||
This function will throw an error if player is not initialized.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### `setSource`
 | 
			
		||||
 | 
			
		||||
<PlatformsList types={['Android', 'iOS']} />
 | 
			
		||||
 | 
			
		||||
`setSource(source: ReactVideoSource): Promise<void>`
 | 
			
		||||
 | 
			
		||||
This function will change the source exactly like [source](./props#source) property.
 | 
			
		||||
Changing source with this function will overide source provided as props. 
 | 
			
		||||
 | 
			
		||||
### `setFullScreen`
 | 
			
		||||
 | 
			
		||||
<PlatformsList types={['Android', 'iOS']} />
 | 
			
		||||
 
 | 
			
		||||
@@ -252,6 +252,10 @@ const VideoPlayer: FC<Props> = ({}) => {
 | 
			
		||||
    cacheSizeMB: useCache ? 200 : 0,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    videoRef.current?.setSource(currentSrc)
 | 
			
		||||
  }, [currentSrc])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <View style={styles.container}>
 | 
			
		||||
      <StatusBar animated={true} backgroundColor="black" hidden={false} />
 | 
			
		||||
@@ -261,7 +265,7 @@ const VideoPlayer: FC<Props> = ({}) => {
 | 
			
		||||
          <Video
 | 
			
		||||
            showNotificationControls={showNotificationControls}
 | 
			
		||||
            ref={videoRef}
 | 
			
		||||
            source={currentSrc as ReactVideoSource}
 | 
			
		||||
            // source={currentSrc as ReactVideoSource}
 | 
			
		||||
            adTagUrl={additional?.adTagUrl}
 | 
			
		||||
            drm={additional?.drm}
 | 
			
		||||
            style={viewStyle}
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,7 @@ RCT_EXTERN_METHOD(setLicenseResultErrorCmd : (nonnull NSNumber*)reactTag error :
 | 
			
		||||
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(setSourceCmd : (nonnull NSNumber*)reactTag source : (NSDictionary*)source)
 | 
			
		||||
 | 
			
		||||
RCT_EXTERN_METHOD(save
 | 
			
		||||
                  : (nonnull NSNumber*)reactTag options
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,13 @@ class RCTVideoManager: RCTViewManager {
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @objc(setSourceCmd:source:)
 | 
			
		||||
    func setSourceCmd(_ reactTag: NSNumber, source: NSDictionary) {
 | 
			
		||||
        performOnVideoView(withReactTag: reactTag, callback: { videoView in
 | 
			
		||||
            videoView?.setSrc(source)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @objc(save:options:resolve:reject:)
 | 
			
		||||
    func save(_ reactTag: NSNumber, options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
 | 
			
		||||
        performOnVideoView(withReactTag: reactTag, callback: { videoView in
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										199
									
								
								src/Video.tsx
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								src/Video.tsx
									
									
									
									
									
								
							@@ -53,6 +53,7 @@ import type {
 | 
			
		||||
  OnReceiveAdEventData,
 | 
			
		||||
  ReactVideoProps,
 | 
			
		||||
  CmcdData,
 | 
			
		||||
  ReactVideoSource,
 | 
			
		||||
} from './types';
 | 
			
		||||
 | 
			
		||||
export interface VideoRef {
 | 
			
		||||
@@ -66,6 +67,7 @@ export interface VideoRef {
 | 
			
		||||
  ) => void;
 | 
			
		||||
  setVolume: (volume: number) => void;
 | 
			
		||||
  setFullScreen: (fullScreen: boolean) => void;
 | 
			
		||||
  setSource: (source?: ReactVideoSource) => void;
 | 
			
		||||
  save: (options: object) => Promise<VideoSaveData> | void;
 | 
			
		||||
  getCurrentPosition: () => Promise<number>;
 | 
			
		||||
}
 | 
			
		||||
@@ -157,98 +159,105 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      setRestoreUserInterfaceForPIPStopCompletionHandler,
 | 
			
		||||
    ] = useState<boolean | undefined>();
 | 
			
		||||
 | 
			
		||||
    const src = useMemo<VideoSrc | undefined>(() => {
 | 
			
		||||
      if (!source) {
 | 
			
		||||
        return undefined;
 | 
			
		||||
      }
 | 
			
		||||
      const resolvedSource = resolveAssetSourceForVideo(source);
 | 
			
		||||
      let uri = resolvedSource.uri || '';
 | 
			
		||||
      if (uri && uri.match(/^\//)) {
 | 
			
		||||
        uri = `file://${uri}`;
 | 
			
		||||
      }
 | 
			
		||||
      if (!uri) {
 | 
			
		||||
        console.log('Trying to load empty source');
 | 
			
		||||
      }
 | 
			
		||||
      const isNetwork = !!(uri && uri.match(/^(rtp|rtsp|http|https):/));
 | 
			
		||||
      const isAsset = !!(
 | 
			
		||||
        uri &&
 | 
			
		||||
        uri.match(
 | 
			
		||||
          /^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/,
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const selectedDrm = source.drm || drm;
 | 
			
		||||
      const _textTracks = source.textTracks || textTracks;
 | 
			
		||||
      const _drm = !selectedDrm
 | 
			
		||||
        ? undefined
 | 
			
		||||
        : {
 | 
			
		||||
            type: selectedDrm.type,
 | 
			
		||||
            licenseServer: selectedDrm.licenseServer,
 | 
			
		||||
            headers: generateHeaderForNative(selectedDrm.headers),
 | 
			
		||||
            contentId: selectedDrm.contentId,
 | 
			
		||||
            certificateUrl: selectedDrm.certificateUrl,
 | 
			
		||||
            base64Certificate: selectedDrm.base64Certificate,
 | 
			
		||||
            useExternalGetLicense: !!selectedDrm.getLicense,
 | 
			
		||||
            multiDrm: selectedDrm.multiDrm,
 | 
			
		||||
            localSourceEncryptionKeyScheme:
 | 
			
		||||
              selectedDrm.localSourceEncryptionKeyScheme ||
 | 
			
		||||
              localSourceEncryptionKeyScheme,
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
      let _cmcd: NativeCmcdConfiguration | undefined;
 | 
			
		||||
      if (Platform.OS === 'android' && source?.cmcd) {
 | 
			
		||||
        const cmcd = source.cmcd;
 | 
			
		||||
 | 
			
		||||
        if (typeof cmcd === 'boolean') {
 | 
			
		||||
          _cmcd = cmcd ? {mode: CmcdMode.MODE_QUERY_PARAMETER} : undefined;
 | 
			
		||||
        } else if (typeof cmcd === 'object' && !Array.isArray(cmcd)) {
 | 
			
		||||
          const createCmcdHeader = (property?: CmcdData) =>
 | 
			
		||||
            property ? generateHeaderForNative(property) : undefined;
 | 
			
		||||
 | 
			
		||||
          _cmcd = {
 | 
			
		||||
            mode: cmcd.mode ?? CmcdMode.MODE_QUERY_PARAMETER,
 | 
			
		||||
            request: createCmcdHeader(cmcd.request),
 | 
			
		||||
            session: createCmcdHeader(cmcd.session),
 | 
			
		||||
            object: createCmcdHeader(cmcd.object),
 | 
			
		||||
            status: createCmcdHeader(cmcd.status),
 | 
			
		||||
          };
 | 
			
		||||
        } else {
 | 
			
		||||
          throw new Error(
 | 
			
		||||
            'Invalid CMCD configuration: Expected a boolean or an object.',
 | 
			
		||||
          );
 | 
			
		||||
    const sourceToUnternalSource = useCallback(
 | 
			
		||||
      (_source?: ReactVideoSource) => {
 | 
			
		||||
        if (!_source) {
 | 
			
		||||
          return undefined;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
        const resolvedSource = resolveAssetSourceForVideo(_source);
 | 
			
		||||
        let uri = resolvedSource.uri || '';
 | 
			
		||||
        if (uri && uri.match(/^\//)) {
 | 
			
		||||
          uri = `file://${uri}`;
 | 
			
		||||
        }
 | 
			
		||||
        if (!uri) {
 | 
			
		||||
          console.log('Trying to load empty source');
 | 
			
		||||
        }
 | 
			
		||||
        const isNetwork = !!(uri && uri.match(/^(rtp|rtsp|http|https):/));
 | 
			
		||||
        const isAsset = !!(
 | 
			
		||||
          uri &&
 | 
			
		||||
          uri.match(
 | 
			
		||||
            /^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/,
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
      const selectedContentStartTime =
 | 
			
		||||
        source.contentStartTime || contentStartTime;
 | 
			
		||||
        const selectedDrm = _source.drm || drm;
 | 
			
		||||
        const _textTracks = _source.textTracks || textTracks;
 | 
			
		||||
        const _drm = !selectedDrm
 | 
			
		||||
          ? undefined
 | 
			
		||||
          : {
 | 
			
		||||
              type: selectedDrm.type,
 | 
			
		||||
              licenseServer: selectedDrm.licenseServer,
 | 
			
		||||
              headers: generateHeaderForNative(selectedDrm.headers),
 | 
			
		||||
              contentId: selectedDrm.contentId,
 | 
			
		||||
              certificateUrl: selectedDrm.certificateUrl,
 | 
			
		||||
              base64Certificate: selectedDrm.base64Certificate,
 | 
			
		||||
              useExternalGetLicense: !!selectedDrm.getLicense,
 | 
			
		||||
              multiDrm: selectedDrm.multiDrm,
 | 
			
		||||
              localSourceEncryptionKeyScheme:
 | 
			
		||||
                selectedDrm.localSourceEncryptionKeyScheme ||
 | 
			
		||||
                localSourceEncryptionKeyScheme,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        uri,
 | 
			
		||||
        isNetwork,
 | 
			
		||||
        isAsset,
 | 
			
		||||
        shouldCache: resolvedSource.shouldCache || false,
 | 
			
		||||
        type: resolvedSource.type || '',
 | 
			
		||||
        mainVer: resolvedSource.mainVer || 0,
 | 
			
		||||
        patchVer: resolvedSource.patchVer || 0,
 | 
			
		||||
        requestHeaders: generateHeaderForNative(resolvedSource.headers),
 | 
			
		||||
        startPosition: resolvedSource.startPosition ?? -1,
 | 
			
		||||
        cropStart: resolvedSource.cropStart || 0,
 | 
			
		||||
        cropEnd: resolvedSource.cropEnd,
 | 
			
		||||
        contentStartTime: selectedContentStartTime,
 | 
			
		||||
        metadata: resolvedSource.metadata,
 | 
			
		||||
        drm: _drm,
 | 
			
		||||
        cmcd: _cmcd,
 | 
			
		||||
        textTracks: _textTracks,
 | 
			
		||||
        textTracksAllowChunklessPreparation:
 | 
			
		||||
          resolvedSource.textTracksAllowChunklessPreparation,
 | 
			
		||||
      };
 | 
			
		||||
    }, [
 | 
			
		||||
      drm,
 | 
			
		||||
      source,
 | 
			
		||||
      textTracks,
 | 
			
		||||
      contentStartTime,
 | 
			
		||||
      localSourceEncryptionKeyScheme,
 | 
			
		||||
    ]);
 | 
			
		||||
        let _cmcd: NativeCmcdConfiguration | undefined;
 | 
			
		||||
        if (Platform.OS === 'android' && source?.cmcd) {
 | 
			
		||||
          const cmcd = source.cmcd;
 | 
			
		||||
 | 
			
		||||
          if (typeof cmcd === 'boolean') {
 | 
			
		||||
            _cmcd = cmcd ? {mode: CmcdMode.MODE_QUERY_PARAMETER} : undefined;
 | 
			
		||||
          } else if (typeof cmcd === 'object' && !Array.isArray(cmcd)) {
 | 
			
		||||
            const createCmcdHeader = (property?: CmcdData) =>
 | 
			
		||||
              property ? generateHeaderForNative(property) : undefined;
 | 
			
		||||
 | 
			
		||||
            _cmcd = {
 | 
			
		||||
              mode: cmcd.mode ?? CmcdMode.MODE_QUERY_PARAMETER,
 | 
			
		||||
              request: createCmcdHeader(cmcd.request),
 | 
			
		||||
              session: createCmcdHeader(cmcd.session),
 | 
			
		||||
              object: createCmcdHeader(cmcd.object),
 | 
			
		||||
              status: createCmcdHeader(cmcd.status),
 | 
			
		||||
            };
 | 
			
		||||
          } else {
 | 
			
		||||
            throw new Error(
 | 
			
		||||
              'Invalid CMCD configuration: Expected a boolean or an object.',
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const selectedContentStartTime =
 | 
			
		||||
          _source.contentStartTime || contentStartTime;
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
          uri,
 | 
			
		||||
          isNetwork,
 | 
			
		||||
          isAsset,
 | 
			
		||||
          shouldCache: resolvedSource.shouldCache || false,
 | 
			
		||||
          type: resolvedSource.type || '',
 | 
			
		||||
          mainVer: resolvedSource.mainVer || 0,
 | 
			
		||||
          patchVer: resolvedSource.patchVer || 0,
 | 
			
		||||
          requestHeaders: generateHeaderForNative(resolvedSource.headers),
 | 
			
		||||
          startPosition: resolvedSource.startPosition ?? -1,
 | 
			
		||||
          cropStart: resolvedSource.cropStart || 0,
 | 
			
		||||
          cropEnd: resolvedSource.cropEnd,
 | 
			
		||||
          contentStartTime: selectedContentStartTime,
 | 
			
		||||
          metadata: resolvedSource.metadata,
 | 
			
		||||
          drm: _drm,
 | 
			
		||||
          cmcd: _cmcd,
 | 
			
		||||
          textTracks: _textTracks,
 | 
			
		||||
          textTracksAllowChunklessPreparation:
 | 
			
		||||
            resolvedSource.textTracksAllowChunklessPreparation,
 | 
			
		||||
        };
 | 
			
		||||
      },
 | 
			
		||||
      [
 | 
			
		||||
        contentStartTime,
 | 
			
		||||
        drm,
 | 
			
		||||
        localSourceEncryptionKeyScheme,
 | 
			
		||||
        source,
 | 
			
		||||
        textTracks,
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const src = useMemo<VideoSrc | undefined>(() => {
 | 
			
		||||
      return sourceToUnternalSource(source);
 | 
			
		||||
    }, [sourceToUnternalSource, source]);
 | 
			
		||||
 | 
			
		||||
    const _selectedTextTrack = useMemo(() => {
 | 
			
		||||
      if (!selectedTextTrack) {
 | 
			
		||||
@@ -370,6 +379,16 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
      );
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const setSource = useCallback(
 | 
			
		||||
      (_source?: ReactVideoSource) => {
 | 
			
		||||
        return NativeVideoManager.setSourceCmd(
 | 
			
		||||
          getReactTag(nativeRef),
 | 
			
		||||
          sourceToUnternalSource(_source),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
      [sourceToUnternalSource],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const presentFullscreenPlayer = useCallback(
 | 
			
		||||
      () => setFullScreen(true),
 | 
			
		||||
      [setFullScreen],
 | 
			
		||||
@@ -628,6 +647,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
        setVolume,
 | 
			
		||||
        getCurrentPosition,
 | 
			
		||||
        setFullScreen,
 | 
			
		||||
        setSource,
 | 
			
		||||
      }),
 | 
			
		||||
      [
 | 
			
		||||
        seek,
 | 
			
		||||
@@ -640,6 +660,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
 | 
			
		||||
        setVolume,
 | 
			
		||||
        getCurrentPosition,
 | 
			
		||||
        setFullScreen,
 | 
			
		||||
        setSource,
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ export interface VideoManagerType {
 | 
			
		||||
    licenseUrl: string,
 | 
			
		||||
  ) => Promise<void>;
 | 
			
		||||
  setFullScreenCmd: (reactTag: Int32, fullScreen: boolean) => Promise<void>;
 | 
			
		||||
  setSourceCmd: (reactTag: Int32, source?: UnsafeObject) => Promise<void>;
 | 
			
		||||
  setVolumeCmd: (reactTag: Int32, volume: number) => Promise<void>;
 | 
			
		||||
  save: (reactTag: Int32, option: UnsafeObject) => Promise<VideoSaveData>;
 | 
			
		||||
  getCurrentPosition: (reactTag: Int32) => Promise<Int32>;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user