Repository: TheWidlarzGroup/react-native-video
Branch: master
Commit: a01d203bcbaf
Files: 616
Total size: 1.7 MB
Directory structure:
gitextract_c7gy42g0/
├── .github/
│ ├── actions/
│ │ └── setup-bun/
│ │ └── action.yml
│ └── workflows/
│ ├── deploy-docs.yml
│ └── test-docs-build.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bunfig.toml
├── config/
│ ├── .editorconfig
│ ├── .eslintrc.js
│ └── tsconfig.json
├── docs/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── README.md
│ ├── docs/
│ │ ├── fundamentals/
│ │ │ ├── _category_.json
│ │ │ ├── configuration/
│ │ │ │ ├── _category_.json
│ │ │ │ ├── with-expo.md
│ │ │ │ └── without-expo.md
│ │ │ ├── installation.md
│ │ │ └── intro.mdx
│ │ ├── offer.mdx
│ │ ├── player/
│ │ │ ├── _category_.json
│ │ │ ├── analytics/
│ │ │ │ ├── _category_.json
│ │ │ │ ├── manual.md
│ │ │ │ └── simple.md
│ │ │ ├── downloading/
│ │ │ │ ├── _category_.json
│ │ │ │ ├── downloading.md
│ │ │ │ ├── drm-downloading.md
│ │ │ │ ├── events-downloading.md
│ │ │ │ ├── getting-started.md
│ │ │ │ └── track-selection.md
│ │ │ ├── drm.md
│ │ │ ├── events.md
│ │ │ ├── player.md
│ │ │ ├── use-video-player.md
│ │ │ └── video-player.md
│ │ ├── plugins/
│ │ │ ├── _category_.json
│ │ │ ├── ask-for-plugin.md
│ │ │ ├── examples.md
│ │ │ ├── interface.md
│ │ │ ├── plugins.mdx
│ │ │ ├── registry.md
│ │ │ └── use-in-third-party-library.md
│ │ ├── projects.md
│ │ ├── updating.md
│ │ └── video-view/
│ │ ├── _category_.json
│ │ ├── chapters.md
│ │ ├── events.md
│ │ ├── methods.md
│ │ ├── props.md
│ │ └── video-view.md
│ ├── docusaurus.config.ts
│ ├── package.json
│ ├── sidebars.ts
│ ├── src/
│ │ ├── components/
│ │ │ ├── Homepage/
│ │ │ │ ├── Enterprise/
│ │ │ │ │ ├── Enterprise.module.css
│ │ │ │ │ ├── Enterprise.tsx
│ │ │ │ │ └── FeatureCard/
│ │ │ │ │ ├── FeatureCard.module.css
│ │ │ │ │ └── FeatureCard.tsx
│ │ │ │ ├── Features/
│ │ │ │ │ ├── Feature/
│ │ │ │ │ │ ├── Feature.module.css
│ │ │ │ │ │ └── Feature.tsx
│ │ │ │ │ ├── Features.module.css
│ │ │ │ │ └── Features.tsx
│ │ │ │ └── Hero/
│ │ │ │ ├── Badge/
│ │ │ │ │ ├── Badge.module.css
│ │ │ │ │ └── Badge.tsx
│ │ │ │ ├── Buttons/
│ │ │ │ │ ├── Buttons.module.css
│ │ │ │ │ └── Buttons.tsx
│ │ │ │ ├── Hero.module.css
│ │ │ │ ├── Hero.tsx
│ │ │ │ ├── ScrollIndicator/
│ │ │ │ │ ├── ScrollIndicator.module.css
│ │ │ │ │ └── ScrollIndicator.tsx
│ │ │ │ └── Stats/
│ │ │ │ ├── Stats.module.css
│ │ │ │ └── Stats.tsx
│ │ │ ├── Intro/
│ │ │ │ ├── V7ApiModel/
│ │ │ │ │ ├── V7ApiModel.module.css
│ │ │ │ │ └── V7ApiModel.tsx
│ │ │ │ ├── V7FeatureCards/
│ │ │ │ │ ├── V7FeatureCards.module.css
│ │ │ │ │ └── V7FeatureCards.tsx
│ │ │ │ ├── V7Lead/
│ │ │ │ │ ├── V7Lead.module.css
│ │ │ │ │ └── V7Lead.tsx
│ │ │ │ ├── V7NitroSection/
│ │ │ │ │ ├── V7NitroSection.module.css
│ │ │ │ │ └── V7NitroSection.tsx
│ │ │ │ ├── V7OpenSource/
│ │ │ │ │ ├── V7OpenSource.module.css
│ │ │ │ │ └── V7OpenSource.tsx
│ │ │ │ ├── V7ProPlugins/
│ │ │ │ │ ├── V7ProPlugins.module.css
│ │ │ │ │ └── V7ProPlugins.tsx
│ │ │ │ ├── V7StatusTimeline/
│ │ │ │ │ ├── V7StatusTimeline.module.css
│ │ │ │ │ └── V7StatusTimeline.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Offer/
│ │ │ │ ├── Contact/
│ │ │ │ │ ├── Contact.module.css
│ │ │ │ │ └── Contact.tsx
│ │ │ │ ├── DecisionTable/
│ │ │ │ │ ├── DecisionTable.module.css
│ │ │ │ │ └── DecisionTable.tsx
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── Extensions.module.css
│ │ │ │ │ └── Extensions.tsx
│ │ │ │ ├── Hero/
│ │ │ │ │ ├── IntroHero.module.css
│ │ │ │ │ └── IntroHero.tsx
│ │ │ │ ├── HowItWorks/
│ │ │ │ │ ├── HowItWorks.module.css
│ │ │ │ │ └── HowItWorks.tsx
│ │ │ │ ├── Migration/
│ │ │ │ │ ├── Migration.module.css
│ │ │ │ │ └── Migration.tsx
│ │ │ │ ├── OfferCards/
│ │ │ │ │ ├── OfferCards.module.css
│ │ │ │ │ └── OfferCards.tsx
│ │ │ │ ├── Services/
│ │ │ │ │ ├── Services.module.css
│ │ │ │ │ └── Services.tsx
│ │ │ │ └── index.ts
│ │ │ └── PlatformsList/
│ │ │ ├── PlatformsList.module.css
│ │ │ └── PlatformsList.tsx
│ │ ├── css/
│ │ │ └── custom.css
│ │ └── pages/
│ │ ├── index.module.css
│ │ └── index.tsx
│ ├── static/
│ │ └── .nojekyll
│ ├── tsconfig.json
│ ├── versioned_docs/
│ │ └── version-6.x/
│ │ ├── component/
│ │ │ ├── _category_.json
│ │ │ ├── ads.md
│ │ │ ├── drm.mdx
│ │ │ ├── events.mdx
│ │ │ ├── methods.mdx
│ │ │ └── props.mdx
│ │ ├── installation.md
│ │ ├── intro.md
│ │ ├── other/
│ │ │ ├── _category_.json
│ │ │ ├── caching.md
│ │ │ ├── debug.md
│ │ │ ├── downloading.md
│ │ │ ├── expo.md
│ │ │ ├── misc.md
│ │ │ ├── new-arch.md
│ │ │ └── plugin.md
│ │ ├── projects.md
│ │ └── updating.md
│ ├── versioned_sidebars/
│ │ └── version-6.x-sidebars.json
│ └── versions.json
├── example/
│ ├── .bundle/
│ │ └── config
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── Gemfile
│ ├── android/
│ │ ├── app/
│ │ │ ├── build.gradle
│ │ │ ├── debug.keystore
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ ├── debug/
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── videoexample/
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ └── rn_edit_text_material.xml
│ │ │ └── values/
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── app.json
│ ├── babel.config.js
│ ├── index.js
│ ├── ios/
│ │ ├── .xcode.env
│ │ ├── Podfile
│ │ ├── VideoExample/
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Images.xcassets/
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Info.plist
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── PrivacyInfo.xcprivacy
│ │ ├── VideoExample-Bridging-Header.h
│ │ ├── VideoExample.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ └── VideoExample.xcscheme
│ │ ├── VideoExample.xcworkspace/
│ │ │ └── contents.xcworkspacedata
│ │ └── VideoExampleTests/
│ │ ├── Info.plist
│ │ └── VideoExampleTests.m
│ ├── metro.config.js
│ ├── package.json
│ ├── src/
│ │ ├── App.tsx
│ │ ├── components/
│ │ │ ├── Controls.tsx
│ │ │ └── TextTrackManager.tsx
│ │ ├── styles.ts
│ │ ├── types/
│ │ │ └── videoSettings.ts
│ │ └── utils/
│ │ ├── time.ts
│ │ └── videoSource.ts
│ └── tsconfig.json
├── lefthook.yml
├── package.json
├── packages/
│ ├── drm-plugin/
│ │ ├── .eslintrc.js
│ │ ├── .gitattributes
│ │ ├── .gitignore
│ │ ├── .watchmanconfig
│ │ ├── README.md
│ │ ├── ReactNativeVideoDrm.podspec
│ │ ├── android/
│ │ │ ├── CMakeLists.txt
│ │ │ ├── build.gradle
│ │ │ ├── gradle.properties
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── cpp/
│ │ │ │ └── cpp-adapter.cpp
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── twg/
│ │ │ └── videodrm/
│ │ │ ├── DRMManager/
│ │ │ │ └── DRMManager.kt
│ │ │ ├── DRMPlugin.kt
│ │ │ ├── PluginManager.kt
│ │ │ └── VideoDrmPackage.kt
│ │ ├── babel.config.js
│ │ ├── ios/
│ │ │ ├── DRMManager/
│ │ │ │ ├── DRMManager+AVContentKeySessionDelegate.swift
│ │ │ │ └── DRMManager.swift
│ │ │ ├── DRMPlugin.swift
│ │ │ └── PluginManager.swift
│ │ ├── nitro.json
│ │ ├── nitrogen/
│ │ │ └── generated/
│ │ │ ├── .gitattributes
│ │ │ ├── android/
│ │ │ │ ├── ReactNativeVideoDrm+autolinking.cmake
│ │ │ │ ├── ReactNativeVideoDrm+autolinking.gradle
│ │ │ │ ├── ReactNativeVideoDrmOnLoad.cpp
│ │ │ │ ├── ReactNativeVideoDrmOnLoad.hpp
│ │ │ │ ├── c++/
│ │ │ │ │ ├── JHybridPluginManagerSpec.cpp
│ │ │ │ │ └── JHybridPluginManagerSpec.hpp
│ │ │ │ └── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── margelo/
│ │ │ │ └── nitro/
│ │ │ │ └── videodrm/
│ │ │ │ ├── HybridPluginManagerSpec.kt
│ │ │ │ └── ReactNativeVideoDrmOnLoad.kt
│ │ │ ├── ios/
│ │ │ │ ├── ReactNativeVideoDrm+autolinking.rb
│ │ │ │ ├── ReactNativeVideoDrm-Swift-Cxx-Bridge.cpp
│ │ │ │ ├── ReactNativeVideoDrm-Swift-Cxx-Bridge.hpp
│ │ │ │ ├── ReactNativeVideoDrm-Swift-Cxx-Umbrella.hpp
│ │ │ │ ├── ReactNativeVideoDrmAutolinking.mm
│ │ │ │ ├── ReactNativeVideoDrmAutolinking.swift
│ │ │ │ ├── c++/
│ │ │ │ │ ├── HybridPluginManagerSpecSwift.cpp
│ │ │ │ │ └── HybridPluginManagerSpecSwift.hpp
│ │ │ │ └── swift/
│ │ │ │ ├── HybridPluginManagerSpec.swift
│ │ │ │ └── HybridPluginManagerSpec_cxx.swift
│ │ │ └── shared/
│ │ │ └── c++/
│ │ │ ├── HybridPluginManagerSpec.cpp
│ │ │ └── HybridPluginManagerSpec.hpp
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── PluginManager.nitro.ts
│ │ │ └── index.tsx
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── react-native-video/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── ReactNativeVideo.podspec
│ ├── android/
│ │ ├── CMakeLists.txt
│ │ ├── build.gradle
│ │ ├── fix-prefab.gradle
│ │ ├── gradle.properties
│ │ └── src/
│ │ ├── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── AndroidManifestNew.xml
│ │ │ ├── cpp/
│ │ │ │ └── cpp-adapter.cpp
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── twg/
│ │ │ │ └── video/
│ │ │ │ ├── core/
│ │ │ │ │ ├── AudioFocusManager.kt
│ │ │ │ │ ├── VideoError.kt
│ │ │ │ │ ├── VideoManager.kt
│ │ │ │ │ ├── extensions/
│ │ │ │ │ │ ├── ResizeMode+AspectRatioFrameLayout.kt
│ │ │ │ │ │ ├── SubtitleType+toString.kt
│ │ │ │ │ │ └── VideoPlaybackService+ServiceManagment.kt
│ │ │ │ │ ├── fragments/
│ │ │ │ │ │ ├── FullscreenVideoFragment.kt
│ │ │ │ │ │ └── PictureInPictureHelperFragment.kt
│ │ │ │ │ ├── player/
│ │ │ │ │ │ ├── DRMManagerSpec.kt
│ │ │ │ │ │ ├── DataSourceFactoryUtils.kt
│ │ │ │ │ │ ├── MediaItemUtils.kt
│ │ │ │ │ │ ├── MediaSourceUtils.kt
│ │ │ │ │ │ └── OnAudioFocusChangedListener.kt
│ │ │ │ │ ├── plugins/
│ │ │ │ │ │ ├── PluginsRegistry.kt
│ │ │ │ │ │ └── ReactNativeVideoPlugin.kt
│ │ │ │ │ ├── recivers/
│ │ │ │ │ │ └── AudioBecomingNoisyReceiver.kt
│ │ │ │ │ ├── services/
│ │ │ │ │ │ └── playback/
│ │ │ │ │ │ ├── CustomMediaNotificationProvider.kt
│ │ │ │ │ │ ├── VideoPlaybackCallback.kt
│ │ │ │ │ │ ├── VideoPlaybackService.kt
│ │ │ │ │ │ └── VideoPlaybackServiceConnection.kt
│ │ │ │ │ └── utils/
│ │ │ │ │ ├── PictureInPictureUtils.kt
│ │ │ │ │ ├── SmallVideoPlayerOptimizer.kt
│ │ │ │ │ ├── SourceLoader.kt
│ │ │ │ │ ├── TextTrackUtils.kt
│ │ │ │ │ ├── Threading.kt
│ │ │ │ │ ├── VideoFileHelper.kt
│ │ │ │ │ ├── VideoInformationUtils.kt
│ │ │ │ │ └── VideoOrientationUtils.kt
│ │ │ │ ├── hybrids/
│ │ │ │ │ ├── videoplayer/
│ │ │ │ │ │ ├── HybridVideoPlayer.kt
│ │ │ │ │ │ └── HybridVideoPlayerFactory.kt
│ │ │ │ │ ├── videoplayereventemitter/
│ │ │ │ │ │ └── HybridVideoPlayerEventEmitter.kt
│ │ │ │ │ ├── videoplayersource/
│ │ │ │ │ │ ├── HybridVideoPlayerSource.kt
│ │ │ │ │ │ └── HybridVideoPlayerSourceFactory.kt
│ │ │ │ │ └── videoviewviewmanager/
│ │ │ │ │ ├── HybridVideoViewViewManager.kt
│ │ │ │ │ └── HybridVideoViewViewManagerFactory.kt
│ │ │ │ ├── react/
│ │ │ │ │ ├── VideoPackage.kt
│ │ │ │ │ └── VideoViewViewManager.kt
│ │ │ │ └── view/
│ │ │ │ └── VideoView.kt
│ │ │ └── res/
│ │ │ └── layout/
│ │ │ ├── player_view_surface.xml
│ │ │ └── player_view_texture.xml
│ │ ├── paper/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── facebook/
│ │ │ └── react/
│ │ │ └── viewmanagers/
│ │ │ ├── RNCVideoViewManagerDelegate.java
│ │ │ └── RNCVideoViewManagerInterface.java
│ │ └── stubs/
│ │ ├── dash/
│ │ │ └── androidx/
│ │ │ └── media3/
│ │ │ └── exoplayer/
│ │ │ └── dash/
│ │ │ ├── DashMediaSource.kt
│ │ │ ├── DashUtil.kt
│ │ │ └── manifest/
│ │ │ ├── AdaptationSet.kt
│ │ │ ├── DashManifest.kt
│ │ │ ├── Period.kt
│ │ │ └── Representation.kt
│ │ └── hls/
│ │ └── androidx/
│ │ └── media3/
│ │ └── exoplayer/
│ │ └── hls/
│ │ └── HlsMediaSource.kt
│ ├── app.plugin.js
│ ├── babel.config.js
│ ├── ios/
│ │ ├── Video-Bridging-Header.h
│ │ ├── core/
│ │ │ ├── Extensions/
│ │ │ │ ├── AVAsset+estimatedMemoryUsage.swift
│ │ │ │ ├── AVAssetTrack+orientation.swift
│ │ │ │ ├── AVMetadataItem+make.swift
│ │ │ │ ├── AVPlayerItem+externalSubtitles.swift
│ │ │ │ ├── AVPlayerItem+getBufferedDurration.swift
│ │ │ │ ├── AVPlayerItem+setBufferConfig.swift
│ │ │ │ ├── AVPlayerViewController+Fullscreen.swift
│ │ │ │ ├── AVPlayerViewController+PictureInPicture.swift
│ │ │ │ ├── AVURLAsset+getAssetInformation.swift
│ │ │ │ ├── NSObject+PerformIfResponds.swift
│ │ │ │ └── ResizeMode+VideoGravity.swift
│ │ │ ├── HLSSubtitleInjector.swift
│ │ │ ├── NowPlayingInfoCenterManager.swift
│ │ │ ├── Plugins/
│ │ │ │ ├── PluginsRegistry.swift
│ │ │ │ └── ReactNativeVideoPlugin.swift
│ │ │ ├── Spec/
│ │ │ │ ├── DRMManagerSpec.swift
│ │ │ │ ├── NativeVideoPlayerSourceSpec.swift
│ │ │ │ └── NativeVideoPlayerSpec.swift
│ │ │ ├── Utils/
│ │ │ │ ├── ExternalSubtitlesUtils.swift
│ │ │ │ ├── HLSManifestParser.swift
│ │ │ │ └── Weak.swift
│ │ │ ├── VideoError.swift
│ │ │ ├── VideoFileHelper.swift
│ │ │ ├── VideoManager.swift
│ │ │ └── VideoPlayerObserver.swift
│ │ ├── hybrids/
│ │ │ ├── VideoPlayer/
│ │ │ │ ├── HybridVideoPlayer+Events.swift
│ │ │ │ ├── HybridVideoPlayer.swift
│ │ │ │ └── HybridVideoPlayerFactory.swift
│ │ │ ├── VideoPlayerEmitter/
│ │ │ │ └── HybridVideoPlayerEventEmitter.swift
│ │ │ ├── VideoPlayerSource/
│ │ │ │ ├── HybridVideoPlayerSource.swift
│ │ │ │ ├── HybridVideoPlayerSourceFactory.swift
│ │ │ │ └── SourceLoader.swift
│ │ │ └── VideoViewViewManager/
│ │ │ ├── HybridVideoViewViewManager.swift
│ │ │ └── HybridVideoViewViewManagerFactory.swift
│ │ └── view/
│ │ ├── VideoComponentView.swift
│ │ ├── VideoComponentViewObserver.swift
│ │ ├── fabric/
│ │ │ ├── RCTVideoViewComponentView.h
│ │ │ ├── RCTVideoViewComponentView.mm
│ │ │ └── RCTVideoViewViewManager.mm
│ │ └── paper/
│ │ ├── RCTVideoViewComponentView.h
│ │ ├── RCTVideoViewComponentView.mm
│ │ └── RCTVideoViewViewManager.m
│ ├── nitro.json
│ ├── nitrogen/
│ │ └── generated/
│ │ ├── .gitattributes
│ │ ├── android/
│ │ │ ├── ReactNativeVideo+autolinking.cmake
│ │ │ ├── ReactNativeVideo+autolinking.gradle
│ │ │ ├── ReactNativeVideoOnLoad.cpp
│ │ │ ├── ReactNativeVideoOnLoad.hpp
│ │ │ ├── c++/
│ │ │ │ ├── JBandwidthData.hpp
│ │ │ │ ├── JBufferConfig.hpp
│ │ │ │ ├── JCustomVideoMetadata.hpp
│ │ │ │ ├── JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__string_____OnGetLicensePayload.hpp
│ │ │ │ ├── JFunc_void.hpp
│ │ │ │ ├── JFunc_void_BandwidthData.hpp
│ │ │ │ ├── JFunc_void_TimedMetadata.hpp
│ │ │ │ ├── JFunc_void_VideoPlayerStatus.hpp
│ │ │ │ ├── JFunc_void_bool.hpp
│ │ │ │ ├── JFunc_void_double.hpp
│ │ │ │ ├── JFunc_void_onLoadData.hpp
│ │ │ │ ├── JFunc_void_onLoadStartData.hpp
│ │ │ │ ├── JFunc_void_onPlaybackStateChangeData.hpp
│ │ │ │ ├── JFunc_void_onProgressData.hpp
│ │ │ │ ├── JFunc_void_onVolumeChangeData.hpp
│ │ │ │ ├── JFunc_void_std__optional_std__variant_nitro__NullType__TextTrack__.hpp
│ │ │ │ ├── JFunc_void_std__vector_std__string_.hpp
│ │ │ │ ├── JHybridVideoPlayerEventEmitterSpec.cpp
│ │ │ │ ├── JHybridVideoPlayerEventEmitterSpec.hpp
│ │ │ │ ├── JHybridVideoPlayerFactorySpec.cpp
│ │ │ │ ├── JHybridVideoPlayerFactorySpec.hpp
│ │ │ │ ├── JHybridVideoPlayerSourceFactorySpec.cpp
│ │ │ │ ├── JHybridVideoPlayerSourceFactorySpec.hpp
│ │ │ │ ├── JHybridVideoPlayerSourceSpec.cpp
│ │ │ │ ├── JHybridVideoPlayerSourceSpec.hpp
│ │ │ │ ├── JHybridVideoPlayerSpec.cpp
│ │ │ │ ├── JHybridVideoPlayerSpec.hpp
│ │ │ │ ├── JHybridVideoViewViewManagerFactorySpec.cpp
│ │ │ │ ├── JHybridVideoViewViewManagerFactorySpec.hpp
│ │ │ │ ├── JHybridVideoViewViewManagerSpec.cpp
│ │ │ │ ├── JHybridVideoViewViewManagerSpec.hpp
│ │ │ │ ├── JIgnoreSilentSwitchMode.hpp
│ │ │ │ ├── JListenerSubscription.hpp
│ │ │ │ ├── JLivePlaybackParams.hpp
│ │ │ │ ├── JMixAudioMode.hpp
│ │ │ │ ├── JNativeDrmParams.hpp
│ │ │ │ ├── JNativeExternalSubtitle.hpp
│ │ │ │ ├── JNativeVideoConfig.hpp
│ │ │ │ ├── JOnGetLicensePayload.hpp
│ │ │ │ ├── JResizeMode.hpp
│ │ │ │ ├── JResolution.hpp
│ │ │ │ ├── JSourceType.hpp
│ │ │ │ ├── JSubtitleType.hpp
│ │ │ │ ├── JSurfaceType.hpp
│ │ │ │ ├── JTextTrack.hpp
│ │ │ │ ├── JTimedMetadata.hpp
│ │ │ │ ├── JTimedMetadataObject.hpp
│ │ │ │ ├── JVariant_NullType_HybridVideoPlayerSourceSpec.cpp
│ │ │ │ ├── JVariant_NullType_HybridVideoPlayerSourceSpec.hpp
│ │ │ │ ├── JVariant_NullType_TextTrack.cpp
│ │ │ │ ├── JVariant_NullType_TextTrack.hpp
│ │ │ │ ├── JVideoInformation.hpp
│ │ │ │ ├── JVideoOrientation.hpp
│ │ │ │ ├── JVideoPlayerStatus.hpp
│ │ │ │ ├── JonLoadData.hpp
│ │ │ │ ├── JonLoadStartData.hpp
│ │ │ │ ├── JonPlaybackStateChangeData.hpp
│ │ │ │ ├── JonProgressData.hpp
│ │ │ │ └── JonVolumeChangeData.hpp
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── margelo/
│ │ │ └── nitro/
│ │ │ └── video/
│ │ │ ├── BandwidthData.kt
│ │ │ ├── BufferConfig.kt
│ │ │ ├── CustomVideoMetadata.kt
│ │ │ ├── Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__string_____OnGetLicensePayload.kt
│ │ │ ├── Func_void.kt
│ │ │ ├── Func_void_BandwidthData.kt
│ │ │ ├── Func_void_TimedMetadata.kt
│ │ │ ├── Func_void_VideoPlayerStatus.kt
│ │ │ ├── Func_void_bool.kt
│ │ │ ├── Func_void_double.kt
│ │ │ ├── Func_void_onLoadData.kt
│ │ │ ├── Func_void_onLoadStartData.kt
│ │ │ ├── Func_void_onPlaybackStateChangeData.kt
│ │ │ ├── Func_void_onProgressData.kt
│ │ │ ├── Func_void_onVolumeChangeData.kt
│ │ │ ├── Func_void_std__optional_std__variant_nitro__NullType__TextTrack__.kt
│ │ │ ├── Func_void_std__vector_std__string_.kt
│ │ │ ├── HybridVideoPlayerEventEmitterSpec.kt
│ │ │ ├── HybridVideoPlayerFactorySpec.kt
│ │ │ ├── HybridVideoPlayerSourceFactorySpec.kt
│ │ │ ├── HybridVideoPlayerSourceSpec.kt
│ │ │ ├── HybridVideoPlayerSpec.kt
│ │ │ ├── HybridVideoViewViewManagerFactorySpec.kt
│ │ │ ├── HybridVideoViewViewManagerSpec.kt
│ │ │ ├── IgnoreSilentSwitchMode.kt
│ │ │ ├── ListenerSubscription.kt
│ │ │ ├── LivePlaybackParams.kt
│ │ │ ├── MixAudioMode.kt
│ │ │ ├── NativeDrmParams.kt
│ │ │ ├── NativeExternalSubtitle.kt
│ │ │ ├── NativeVideoConfig.kt
│ │ │ ├── OnGetLicensePayload.kt
│ │ │ ├── ReactNativeVideoOnLoad.kt
│ │ │ ├── ResizeMode.kt
│ │ │ ├── Resolution.kt
│ │ │ ├── SourceType.kt
│ │ │ ├── SubtitleType.kt
│ │ │ ├── SurfaceType.kt
│ │ │ ├── TextTrack.kt
│ │ │ ├── TimedMetadata.kt
│ │ │ ├── TimedMetadataObject.kt
│ │ │ ├── Variant_NullType_HybridVideoPlayerSourceSpec.kt
│ │ │ ├── Variant_NullType_TextTrack.kt
│ │ │ ├── VideoInformation.kt
│ │ │ ├── VideoOrientation.kt
│ │ │ ├── VideoPlayerStatus.kt
│ │ │ ├── onLoadData.kt
│ │ │ ├── onLoadStartData.kt
│ │ │ ├── onPlaybackStateChangeData.kt
│ │ │ ├── onProgressData.kt
│ │ │ └── onVolumeChangeData.kt
│ │ ├── ios/
│ │ │ ├── ReactNativeVideo+autolinking.rb
│ │ │ ├── ReactNativeVideo-Swift-Cxx-Bridge.cpp
│ │ │ ├── ReactNativeVideo-Swift-Cxx-Bridge.hpp
│ │ │ ├── ReactNativeVideo-Swift-Cxx-Umbrella.hpp
│ │ │ ├── ReactNativeVideoAutolinking.mm
│ │ │ ├── ReactNativeVideoAutolinking.swift
│ │ │ ├── c++/
│ │ │ │ ├── HybridVideoPlayerEventEmitterSpecSwift.cpp
│ │ │ │ ├── HybridVideoPlayerEventEmitterSpecSwift.hpp
│ │ │ │ ├── HybridVideoPlayerFactorySpecSwift.cpp
│ │ │ │ ├── HybridVideoPlayerFactorySpecSwift.hpp
│ │ │ │ ├── HybridVideoPlayerSourceFactorySpecSwift.cpp
│ │ │ │ ├── HybridVideoPlayerSourceFactorySpecSwift.hpp
│ │ │ │ ├── HybridVideoPlayerSourceSpecSwift.cpp
│ │ │ │ ├── HybridVideoPlayerSourceSpecSwift.hpp
│ │ │ │ ├── HybridVideoPlayerSpecSwift.cpp
│ │ │ │ ├── HybridVideoPlayerSpecSwift.hpp
│ │ │ │ ├── HybridVideoViewViewManagerFactorySpecSwift.cpp
│ │ │ │ ├── HybridVideoViewViewManagerFactorySpecSwift.hpp
│ │ │ │ ├── HybridVideoViewViewManagerSpecSwift.cpp
│ │ │ │ └── HybridVideoViewViewManagerSpecSwift.hpp
│ │ │ └── swift/
│ │ │ ├── BandwidthData.swift
│ │ │ ├── BufferConfig.swift
│ │ │ ├── CustomVideoMetadata.swift
│ │ │ ├── Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__string_____OnGetLicensePayload.swift
│ │ │ ├── Func_void.swift
│ │ │ ├── Func_void_BandwidthData.swift
│ │ │ ├── Func_void_TimedMetadata.swift
│ │ │ ├── Func_void_VideoInformation.swift
│ │ │ ├── Func_void_VideoPlayerStatus.swift
│ │ │ ├── Func_void_bool.swift
│ │ │ ├── Func_void_double.swift
│ │ │ ├── Func_void_onLoadData.swift
│ │ │ ├── Func_void_onLoadStartData.swift
│ │ │ ├── Func_void_onPlaybackStateChangeData.swift
│ │ │ ├── Func_void_onProgressData.swift
│ │ │ ├── Func_void_onVolumeChangeData.swift
│ │ │ ├── Func_void_std__exception_ptr.swift
│ │ │ ├── Func_void_std__optional_std__variant_nitro__NullType__TextTrack__.swift
│ │ │ ├── Func_void_std__shared_ptr_Promise_std__string__.swift
│ │ │ ├── Func_void_std__string.swift
│ │ │ ├── Func_void_std__vector_std__string_.swift
│ │ │ ├── HybridVideoPlayerEventEmitterSpec.swift
│ │ │ ├── HybridVideoPlayerEventEmitterSpec_cxx.swift
│ │ │ ├── HybridVideoPlayerFactorySpec.swift
│ │ │ ├── HybridVideoPlayerFactorySpec_cxx.swift
│ │ │ ├── HybridVideoPlayerSourceFactorySpec.swift
│ │ │ ├── HybridVideoPlayerSourceFactorySpec_cxx.swift
│ │ │ ├── HybridVideoPlayerSourceSpec.swift
│ │ │ ├── HybridVideoPlayerSourceSpec_cxx.swift
│ │ │ ├── HybridVideoPlayerSpec.swift
│ │ │ ├── HybridVideoPlayerSpec_cxx.swift
│ │ │ ├── HybridVideoViewViewManagerFactorySpec.swift
│ │ │ ├── HybridVideoViewViewManagerFactorySpec_cxx.swift
│ │ │ ├── HybridVideoViewViewManagerSpec.swift
│ │ │ ├── HybridVideoViewViewManagerSpec_cxx.swift
│ │ │ ├── IgnoreSilentSwitchMode.swift
│ │ │ ├── ListenerSubscription.swift
│ │ │ ├── LivePlaybackParams.swift
│ │ │ ├── MixAudioMode.swift
│ │ │ ├── NativeDrmParams.swift
│ │ │ ├── NativeExternalSubtitle.swift
│ │ │ ├── NativeVideoConfig.swift
│ │ │ ├── OnGetLicensePayload.swift
│ │ │ ├── ResizeMode.swift
│ │ │ ├── Resolution.swift
│ │ │ ├── SourceType.swift
│ │ │ ├── SubtitleType.swift
│ │ │ ├── SurfaceType.swift
│ │ │ ├── TextTrack.swift
│ │ │ ├── TimedMetadata.swift
│ │ │ ├── TimedMetadataObject.swift
│ │ │ ├── Variant_NullType_TextTrack.swift
│ │ │ ├── Variant_NullType__any_HybridVideoPlayerSourceSpec_.swift
│ │ │ ├── VideoInformation.swift
│ │ │ ├── VideoOrientation.swift
│ │ │ ├── VideoPlayerStatus.swift
│ │ │ ├── onLoadData.swift
│ │ │ ├── onLoadStartData.swift
│ │ │ ├── onPlaybackStateChangeData.swift
│ │ │ ├── onProgressData.swift
│ │ │ └── onVolumeChangeData.swift
│ │ └── shared/
│ │ └── c++/
│ │ ├── BandwidthData.hpp
│ │ ├── BufferConfig.hpp
│ │ ├── CustomVideoMetadata.hpp
│ │ ├── HybridVideoPlayerEventEmitterSpec.cpp
│ │ ├── HybridVideoPlayerEventEmitterSpec.hpp
│ │ ├── HybridVideoPlayerFactorySpec.cpp
│ │ ├── HybridVideoPlayerFactorySpec.hpp
│ │ ├── HybridVideoPlayerSourceFactorySpec.cpp
│ │ ├── HybridVideoPlayerSourceFactorySpec.hpp
│ │ ├── HybridVideoPlayerSourceSpec.cpp
│ │ ├── HybridVideoPlayerSourceSpec.hpp
│ │ ├── HybridVideoPlayerSpec.cpp
│ │ ├── HybridVideoPlayerSpec.hpp
│ │ ├── HybridVideoViewViewManagerFactorySpec.cpp
│ │ ├── HybridVideoViewViewManagerFactorySpec.hpp
│ │ ├── HybridVideoViewViewManagerSpec.cpp
│ │ ├── HybridVideoViewViewManagerSpec.hpp
│ │ ├── IgnoreSilentSwitchMode.hpp
│ │ ├── ListenerSubscription.hpp
│ │ ├── LivePlaybackParams.hpp
│ │ ├── MixAudioMode.hpp
│ │ ├── NativeDrmParams.hpp
│ │ ├── NativeExternalSubtitle.hpp
│ │ ├── NativeVideoConfig.hpp
│ │ ├── OnGetLicensePayload.hpp
│ │ ├── ResizeMode.hpp
│ │ ├── Resolution.hpp
│ │ ├── SourceType.hpp
│ │ ├── SubtitleType.hpp
│ │ ├── SurfaceType.hpp
│ │ ├── TextTrack.hpp
│ │ ├── TimedMetadata.hpp
│ │ ├── TimedMetadataObject.hpp
│ │ ├── VideoInformation.hpp
│ │ ├── VideoOrientation.hpp
│ │ ├── VideoPlayerStatus.hpp
│ │ ├── onLoadData.hpp
│ │ ├── onLoadStartData.hpp
│ │ ├── onPlaybackStateChangeData.hpp
│ │ ├── onProgressData.hpp
│ │ └── onVolumeChangeData.hpp
│ ├── package.json
│ ├── react-native.config.js
│ ├── src/
│ │ ├── core/
│ │ │ ├── VideoPlayer.ts
│ │ │ ├── VideoPlayerEvents.ts
│ │ │ ├── hooks/
│ │ │ │ ├── useEvent.ts
│ │ │ │ ├── useManagedInstance.ts
│ │ │ │ └── useVideoPlayer.ts
│ │ │ ├── types/
│ │ │ │ ├── BufferConfig.ts
│ │ │ │ ├── DrmParams.ts
│ │ │ │ ├── Events.ts
│ │ │ │ ├── IgnoreSilentSwitchMode.ts
│ │ │ │ ├── MixAudioMode.ts
│ │ │ │ ├── ResizeMode.ts
│ │ │ │ ├── TextTrack.ts
│ │ │ │ ├── Utils.ts
│ │ │ │ ├── VideoConfig.ts
│ │ │ │ ├── VideoError.ts
│ │ │ │ ├── VideoInformation.ts
│ │ │ │ ├── VideoOrientation.ts
│ │ │ │ ├── VideoPlayerBase.ts
│ │ │ │ ├── VideoPlayerSourceBase.ts
│ │ │ │ └── VideoPlayerStatus.ts
│ │ │ ├── utils/
│ │ │ │ ├── playerFactory.ts
│ │ │ │ └── sourceFactory.ts
│ │ │ └── video-view/
│ │ │ ├── NativeVideoView.tsx
│ │ │ └── VideoView.tsx
│ │ ├── expo-plugins/
│ │ │ ├── @types.ts
│ │ │ ├── getPackageInfo.ts
│ │ │ ├── withAndroidExtensions.ts
│ │ │ ├── withAndroidNotificationControls.ts
│ │ │ ├── withAndroidPictureInPicture.ts
│ │ │ ├── withBackgroundAudio.ts
│ │ │ ├── withReactNativeVideo.ts
│ │ │ └── writeToPodfile.ts
│ │ ├── index.tsx
│ │ └── spec/
│ │ ├── fabric/
│ │ │ └── VideoViewNativeComponent.ts
│ │ └── nitro/
│ │ ├── VideoPlayer.nitro.ts
│ │ ├── VideoPlayerEventEmitter.nitro.ts
│ │ ├── VideoPlayerSource.nitro.ts
│ │ └── VideoViewViewManager.nitro.ts
│ ├── tsconfig.build.json
│ └── tsconfig.json
└── scripts/
└── release.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/actions/setup-bun/action.yml
================================================
name: setup bun
description: Setup bun and install dependencies
inputs:
working-directory:
description: 'working directory for bun install'
default: ./
required: false
runs:
using: composite
steps:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.1
- name: Cache dependencies
id: bun-cache
uses: actions/cache@v4
with:
path: |
**/node_modules
key: ${{ runner.os }}-v7-bun-${{ hashFiles('**/bun.lock') }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-v7-bun-${{ hashFiles('**/bun.lock') }}
${{ runner.os }}-v7-bun-
- name: Install dependencies
working-directory: ${{ inputs.working-directory }}
run: bun install
shell: bash
================================================
FILE: .github/workflows/deploy-docs.yml
================================================
name: Deploy Documentation
on:
workflow_dispatch:
push:
branches:
- master
paths:
# Update on workflow change
- ".github/workflows/deploy-docs.yml"
# Update on docs change
- "docs/**"
# Update on code change (Api Reference)
- "packages/react-native-video/src/**"
jobs:
build:
name: Build Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ./.github/actions/setup-bun
- name: Build Documentation
run: bun run --cwd docs build
- name: Upload Build Artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/build
deploy:
name: Deploy Documentation to GitHub Pages
needs: build
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .github/workflows/test-docs-build.yml
================================================
name: Test Documentation Build
on:
pull_request:
branches:
- master
paths:
# Update on workflow change
- ".github/workflows/test-docs-build.yml"
# Update on docs change
- "docs/**"
# Update on code change (Api Reference)
- "packages/react-native-video/src/**"
jobs:
test-docs-deploy:
name: Test Documentation Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ./.github/actions/setup-bun
- name: Test build website
run: bun run --cwd docs build
================================================
FILE: .gitignore
================================================
# OSX
#
.DS_Store
**/.xcode.env.local
# XDE
.expo/
# VSCode
.vscode/
jsconfig.json
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IJ
#
.classpath
.cxx
.gradle
.idea
.project
.settings
local.properties
android.iml
# Cocoapods
#
example/ios/Pods
# Ruby
example/vendor/
# node.js
#
node_modules/
npm-debug.log
yarn-debug.log
yarn-error.log
# Bun
package-lock.json
**/*.bun
# BUCK
buck-out/
\.buckd/
android/app/libs
android/keystores/debug.keystore
# Yarn
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Expo
.expo/
# Turborepo
.turbo/
# generated by bob
lib/
# TypeScript
tsconfig.tsbuildinfo
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[INSERT CONTACT METHOD].
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Contributions are always welcome, no matter how large or small!
We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md).
## Development workflow
This project is a monorepo managed using [Bun workspaces](https://bun.sh/docs/install/workspaces). It contains the following packages:
- An Library package in the `packages/react-native-video` directory.
- An example app in the `example/` directory.
To get started with the project, run `bun install` in the root directory to install the required dependencies for each package:
```sh
bun install
```
> Since the project relies on Bun workspaces, you cannot use [`npm`](https://github.com/npm/cli) or [`yarn`](https://yarnpkg.com/) for development.
The [example app](/example/) demonstrates usage of the library. You need to run it to test any changes you make.
It is configured to use the local version of the library, so any changes you make to the library's source code will be reflected in the example app. Changes to the library's JavaScript code will be reflected in the example app without a rebuild, but native code changes will require a rebuild of the example app.
If you want to use Android Studio or XCode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/VideoExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-video`.
To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-video` under `Android`.
You can use various commands from the root directory to work with the project.
To start the packager:
```sh
bun example start
```
To run the example app on Android:
```sh
bun example android
```
To run the example app on iOS:
```sh
bun example ios
```
Make sure your code passes TypeScript and ESLint. Run the following to verify:
```sh
bun typecheck
bun lint
```
To fix formatting errors, run the following:
```sh
bun lint --fix
```
### Commit message convention
We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:
- `fix`: bug fixes, e.g. fix crash due to deprecated method.
- `feat`: new features, e.g. add new method to the module.
- `refactor`: code refactor, e.g. migrate from class components to hooks.
- `docs`: changes into documentation, e.g. add usage example for the module..
- `test`: adding or updating tests, e.g. add integration tests using detox.
- `chore`: tooling changes, e.g. change CI config.
Our pre-commit hooks verify that your commit message matches this format when committing.
### Linting and tests
[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
Our pre-commit hooks verify that the linter and tests pass when committing.
### Publishing to npm
We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
To publish new versions, run the following:
```sh
bun release
```
### Scripts
The `package.json` file contains various scripts for common tasks:
- `bun install`: setup project by installing dependencies.
- `bun typecheck`: type-check files with TypeScript.
- `bun lint`: lint files with ESLint.
- `bun example start`: start the Metro server for the example app.
- `bun example android`: run the example app on Android.
- `bun example ios`: run the example app on iOS.
### Sending a pull request
> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
When you're sending a pull request:
- Prefer small pull requests focused on one change.
- Verify that linters and tests are passing.
- Review the documentation to make sure it looks good.
- Follow the pull request template when opening a pull request.
- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.
### License
By contributing to this project, you agree that your contributions will be licensed under the [MIT License](LICENSE).
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024-2025 TheWidlarzGroup
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
[](https://thewidlarzgroup.com/?utm_source=rnv&utm_medium=readme&utm_id=banner)
The most battle-tested open-source video player component for React Native with support for DRM, offline playback, HLS/DASH streaming, and more.
> [!IMPORTANT]
> This is a new version (v7) of `react-native-video` that is currently in active development.
> You can expect breaking changes and missing features.
>
> If you have any questions, please contact us at [hi@thewidlarzgroup.com](mailto:hi@thewidlarzgroup.com).
## 🔍 Features
| Feature | Status |
|---------|--------|
| 📱 Plays all video formats natively supported by iOS/Android | ✅ Available |
| ▶️ Local and remote playback | ✅ Available |
| 🔁 Streaming: HLS • DASH • SmoothStreaming | ✅ Available |
| 🧩 Expo plugin support | ✅ Available |
| 📴 Offline playback, video download, support for side-tracks and side-captions (via [optional SDK](https://docs.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=readme&utm_id=features-text)) | ✅ Available |
| 📱 Picture in Picture | ✅ Available |
| 🎚️ Fine-grained control over tracks, buffering & events | 🏗️ In Development |
| 🧠 Advanced control over playback and buffering | ✅ Available |
| 🔐 DRM: Widevine & FairPlay ([See free DRM stream example](https://www.thewidlarzgroup.com/services/free-drm-token-generator-for-video?utm_source=rnv&utm_medium=readme&utm_id=free-drm)) | ✅ Available |
| 🌐 Basic Web Support | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4605) |
| 📺 TV Support | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4607) |
| 🥽 VisionOS Support | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4608) |
## ✨ Project Status
| Version | State | Architecture |
|---------|-------|--------------|
| **v5 and lower** | ❌ End-of-life [Commercial Support Available](https://www.thewidlarzgroup.com/blog/react-native-video-upgrade-challenges-custom-maintenance-support#how-we-can-help?utm_source=rnv&utm_medium=readme&utm_id=upgradev5) | Old Architecture |
| **v6** | 🛠 Maintained (community + TWG) | Old + New (Interop Layer) |
| **v7** | 🚀 Active Development | Old + New (Full Support) |
`react-native-video` v7 introduces full support for the new React Native architecture, unlocking better performance, improved consistency, and modern native modules.
---
## 📚 Documentation & Examples
- 📖 [Documentation](https://docs.thewidlarzgroup.com/react-native-video/docs/v7/intro)
- 📦 [Example: Basic Usage](https://github.com/TheWidlarzGroup/react-native-video/tree/v7/example)
- 📦 [Example: Free DRM Stream](https://www.thewidlarzgroup.com/services/free-drm-token-generator-for-video?utm_source=rnv&utm_medium=readme&utm_id=free-drm)
## 🚀 Quick Start
### Requirements
- React Native 0.75 or higher
- `react-native-nitro-modules` (>=0.31.10) - Please see [nitro requirements](https://nitro.margelo.com/docs/minimum-requirements)
### Install
`react-native-video` requires `react-native-nitro-modules` (>=0.31.10) in your project.
```bash
npm install react-native-nitro-modules
```
Then install `react-native-video`
```bash
# Install the beta version of react-native-video v7
npm install react-native-video@beta
# Install pods
cd ios && pod install
```
For react-native < 0.80
`react-native` < 0.80 have bug that prevents to properly handle errors by nitro modules on Android.
We highly recommend to apply bellow patch for `react-native-nitro-modules` to fix this issue.
You can apply it using `patch-package`.
Without this patch you won't be able "recognize" errors, all will be thrown as unknown errors.
see [installation guide](https://docs.thewidlarzgroup.com/react-native-video/docs/v7/installation#patch-for-react-native--080)
### Usage
```tsx
import { useVideoPlayer, VideoView } from 'react-native-video';
export default () => (
const player = useVideoPlayer(
'https://www.w3schools.com/html/mov_bbb.mp4',
(_player) => {
_player.play();
}
);
);
```
---
## :inbox_tray: We're building a Pro Player!
We see the need for a more feature-rich video player. There is a gap between open source and commercial players, and we want to fill that gap with plugins.
**Are you using a commercial player just for 1-2 features?** Maybe you are paying for a license just to get **Caching**, **Ads**, or **Analytics**? Let us know. We want to identify these missing pieces and build them, so you can switch back to open source.
**This is what we have already. Check out!**
* [Offline Video](https://sdk.thewidlarzgroup.com/offline-video): Logic for downloading streams (HLS/DASH) and standard video files to enable offline playback.
* [Background Uploader](https://sdk.thewidlarzgroup.com/background-uploader): Handles uploads even if the app is minimized (not strictly a player plugin, but super useful).
* [Chapter Markers](https://sdk.thewidlarzgroup.com/chapters): Visual markers on the timeline to navigate content.
[-> Tell us what to build next ←](https://sdk.thewidlarzgroup.com/ask-for-plugin) or reach out to us sdk@thewidlarzgroup.com
---
## 💼 TWG Services & Products
| Offering | Description |
|----------|-------------|
| [**Professional Support Packages**](https://www.thewidlarzgroup.com/issue-boost?utm_source=rnv&utm_medium=readme&utm_campaign=professional-support-packages#Contact) | Priority bug-fixes, guaranteed SLAs, [roadmap influence](https://github.com/orgs/TheWidlarzGroup/projects/6) |
| [**Issue Booster**](https://www.thewidlarzgroup.com/issue-boost?utm_source=rnv&utm_medium=readme) | Fast-track urgent fixes with a pay‑per‑issue model |
| [**Offline Video SDK**](https://www.thewidlarzgroup.com/offline-video-sdk/?utm_source=rnv&utm_medium=readme&utm_campaign=downloading&utm_id=offline-video-sdk-link) | Plug‑and‑play secure download solution for iOS & Android |
| [**Integration Support**](https://www.thewidlarzgroup.com/?utm_source=rnv&utm_medium=readme&utm_campaign=integration-support#Contact) | Hands‑on help integrating video, DRM & offline into your app |
| [**Free DRM Token Generator**](https://www.thewidlarzgroup.com/services/free-drm-token-generator-for-video?utm_source=rnv&utm_medium=readme&utm_id=free-drm) | Generate Widevine / FairPlay tokens for testing |
| [**Ready Boilerplates**](https://www.thewidlarzgroup.com/showcases?utm_source=rnv&utm_medium=readme) | Ready-to-use apps with offline HLS/DASH DRM, video frame scrubbing, TikTok-style video feed, background uploads, Skia-based frame processor (R&D phase), and more |
| [**React Native Video Upgrade Guide**](https://www.thewidlarzgroup.com/blog/react-native-video-upgrade-challenges-custom-maintenance-support?utm_source=rnv&utm_medium=readme&utm_id=upgrade-blog&utm_campaign=v7) | Common upgrade pitfalls & how to solve them |
*See how [TWG](https://www.thewidlarzgroup.com/?utm_source=rnv&utm_medium=readme&utm_id=services-text) helped **Learnn** ship a world‑class player in record time - [case study](https://gitnation.com/contents/a-4-year-retrospective-lessons-learned-from-building-a-video-player-from-scratch-with-react-native).*
Contact us at [hi@thewidlarzgroup.com](mailto:hi@thewidlarzgroup.com)
## 🌍 Social
- 🐦 **X / Twitter** - [follow product & release updates](https://x.com/TheWidlarzGroup)
- 💬 **Discord** - [talk to the community and us](https://discord.gg/9WPq6Yx)
- 💼 **LinkedIn** - [see TWG flexing](https://linkedin.com/company/the-widlarz-group)
## 📰 Community & Media
- 🗽 **React Summit US** – How TWG helped Learnn boost video performance on React Native.
[Watch the talk »](https://gitnation.com/contents/a-4-year-retrospective-lessons-learned-from-building-a-video-player-from-scratch-with-react-native)
- 🧨 **v7 deep dive** – Why we’re building v7 with Nitro Modules
[Watch on X »](https://x.com/krzysztof_moch/status/1854162551946478051)
- 🛠️ **Well-maintained open-source library** - What does it truly mean? - Bart's talk for React Native Warsaw
[Watch here »](https://www.youtube.com/watch?v=RAQQwGCQNqY)
- 📺 **“Over the Top” Panel** - Building Streaming Apps for Mobile, Web, and Smart TVs - Bart giving his insights on the industry
[Watch here »](https://youtu.be/j2b_bG-32JI)
================================================
FILE: bunfig.toml
================================================
[install]
linker = "hoisted"
================================================
FILE: config/.editorconfig
================================================
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
quote_type = single
================================================
FILE: config/.eslintrc.js
================================================
module.exports = {
root: true,
extends: ['@react-native', 'plugin:prettier/recommended'],
ignorePatterns: [
'**/node_modules',
'**/lib',
'**/build',
'**/.eslintrc.js',
'**/.prettierrc.js',
'**/jest.config.js',
'**/babel.config.js',
'**/metro.config.js',
'**/react-native.config.js',
'**/tsconfig.json',
],
plugins: ['@typescript-eslint', 'prettier'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2020,
sourceType: 'module',
},
rules: {
'prettier/prettier': [
'error',
{
quoteProps: 'consistent',
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
useTabs: false,
},
],
quotes: [
'error',
'single',
{ avoidEscape: true, allowTemplateLiterals: true },
],
'@react-native/no-deep-imports': 'off',
},
};
================================================
FILE: config/tsconfig.json
================================================
{
"exclude": [
"**/node_modules",
"**/lib",
"**/.eslintrc.js",
"**/.prettierrc.js",
"**/jest.config.js",
"**/babel.config.js",
"**/metro.config.js",
"**/react-native.config.js",
"**/tsconfig.json"
],
"compilerOptions": {
"composite": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ESNext"],
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noImplicitUseStrict": false,
"noStrictGenericChecks": false,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ESNext",
"verbatimModuleSyntax": true
}
}
================================================
FILE: docs/.eslintrc.js
================================================
module.exports = {
root: true,
extends: ["../config/.eslintrc.js"],
parserOptions: {
tsconfigRootDir: __dirname,
project: true,
},
plugins: ['@widlarzgroup/docusaurus'],
settings: {
'@widlarzgroup/docusaurus': {
extend: ['src/css/custom.css'],
},
},
overrides: [
{
files: ['**/*.css'],
processor: '@widlarzgroup/docusaurus/.css',
},
],
};
================================================
FILE: docs/.gitignore
================================================
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# API Reference generated files
docs/api-reference/
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
================================================
FILE: docs/README.md
================================================
### Instalation
To install the dependencies, run at the root of the repository:
```
$ bun install
```
### Development
To start a local development server with hot-reloading, run:
```
$ bun run start
```
### Deployment
Deployment is handled via GitHub Actions. Upon pushing to the `master` branch content from the `docs` directory is automatically deployed to GitHub Pages.
### Custom Props
Custom props are provided by the `@widlarzgroup/docusaurus-ui` package and allow you to display badges and other UI elements. Custom props can be defined in three places:
#### 1. In `_category_.json` (for entire categories)
```json
{
"label": "Analytics",
"position": 7,
"customProps": {
"badgeType": "new"
}
}
```
#### 2. In markdown frontmatter (for individual pages)
```md
---
sidebar_position: 5
sidebar_label: Chapters
customProps:
plan: pro
---
```
#### 3. In `sidebars.ts` (for sidebar items)
```ts
{
type: 'doc',
id: 'some-doc',
customProps: {
badgeType: 'new'
}
}
```
#### Available Custom Props
| Prop | Values | Description |
|------|--------|-------------|
| `badgeType` | `"new"`, `"preview"` | Displays a "NEW" or "PREVIEW" badge next to the item |
| `plan` | `"pro"` | Displays a "PRO" badge indicating premium/commercial feature |
================================================
FILE: docs/docs/fundamentals/_category_.json
================================================
{
"label": "Fundamentals",
"position": 1,
"collapsed": false
}
================================================
FILE: docs/docs/fundamentals/configuration/_category_.json
================================================
{
"label": "Configuration",
"position": 3,
"collapsed": false
}
================================================
FILE: docs/docs/fundamentals/configuration/with-expo.md
================================================
---
sidebar_position: 1
sidebar_label: With Expo
description: Expo plugin for react-native-video configuration
---
# Expo Plugin
The `react-native-video` library provides an Expo plugin to simplify the integration and configuration of specific features into your Expo project.
## Installation
To use the Expo plugin, you need to add it to your app's configuration file (`app.json` or `app.config.js`).
```json title="app.json"
{
"expo": {
"plugins": [
[
"react-native-video",
{
"enableAndroidPictureInPicture": true,
"enableBackgroundAudio": true,
"androidExtensions": {
"useExoplayerDash": true,
"useExoplayerHls": true
}
}
]
]
}
}
```
```javascript title="app.config.js"
export default {
plugins: [
[
'react-native-video',
{
enableAndroidPictureInPicture: true,
enableBackgroundAudio: true,
androidExtensions: {
useExoplayerDash: true,
useExoplayerHls: true,
},
},
],
],
};
```
## Configuration Options
The plugin accepts an optional configuration object with the following properties:
### `enableAndroidPictureInPicture` (optional)
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables Picture-in-Picture (PiP) mode on Android. This will apply the necessary configurations to your Android project.
### `enableBackgroundAudio` (optional)
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables audio playback to continue when the app is in the background on Android. Ensure you have also configured the necessary background modes capabilities in your app if required by the operating system.
### `androidExtensions` (optional)
- **Type:** `object`
- **Default:** `{ useExoplayerDash: true, useExoplayerHls: true }`
- **Description:** Allows you to specify which Android ExoPlayer extensions to include. This can help reduce the size of your app by only including the extensions you need.
- `useExoplayerDash` (boolean, default: `true`): Whether to include ExoPlayer's Dash extension.
- `useExoplayerHls` (boolean, default: `true`): Whether to include ExoPlayer's HLS extension.
### `reactNativeTestApp` (optional)
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Whether to use `react-native-test-app` compatible mode.
## Usage
Once configured in your `app.json` or `app.config.js`, the plugin will automatically apply the necessary native project changes during the prebuild process (e.g., when running `npx expo prebuild`). No further manual setup is typically required for these features.
================================================
FILE: docs/docs/fundamentals/configuration/without-expo.md
================================================
---
sidebar_position: 2
sidebar_label: Without Expo
description: Manual configuration of react-native-video
---
# Manual Configuration
If you prefer not to use the Expo plugin you can configure **react-native-video** manually by editing the native project files directly. The steps below show the exact changes performed by the plugin so you can reproduce them in a plain React Native or bare Expo project.
---
## iOS
### Enable Background Audio
To allow video sound to continue when the app goes to the background add the `audio` mode to `Info.plist`:
```xml title="ios/YourApp/Info.plist"
UIBackgroundModesaudio
```
## Android
### Configure ExoPlayer extensions
By default the library enables DASH & HLS extensions. You can fine-tune this by adding properties to **gradle.properties**:
```properties title="android/gradle.properties"
# Enable / disable ExoPlayer extensions used by react-native-video
RNVideo_useExoplayerDash=true # DASH playback support
RNVideo_useExoplayerHls=true # HLS playback support
```
Set a value to `false` to exclude the corresponding extension and reduce APK size.
### Enable Picture-in-Picture (PiP)
Add the `android:supportsPictureInPicture` flag to your *main* activity in **AndroidManifest.xml**:
```xml title="android/app/src/main/AndroidManifest.xml"
```
PiP requires **API 26+** (Android 8.0). Make sure `minSdkVersion` is at least `26` when enabling this feature.
## Verification
After the modifications:
1. **iOS** – run `cd ios && pod install` then build the app from Xcode or via `npx react-native run-ios` / `npx expo run:ios`.
2. **Android** – clean & rebuild the project: `./gradlew clean && ./gradlew :app:assembleDebug` or simply run `npx react-native run-android` / `npx expo run:android`.
If the build succeeds your manual configuration is complete.
---
### Need an easier way?
Use the [Expo plugin](./with-expo.md) to apply exactly the same changes automatically during `expo prebuild`.
================================================
FILE: docs/docs/fundamentals/installation.md
================================================
---
title: Installation
sidebar_position: 2
description: React Native Video Installation Guide and Requirements
---
# Installation
React Native Video is a library that allows you to play various kinds of video in a React Native application. It is built on top of the [`react-native-nitro-modules`](https://nitro.margelo.com/docs/what-is-nitro) framework, giving it type-safety and blazing fast communication across Native and JavaScript threads. React Native Video supports both the New Architecture and the Old Architecture.
## Requirements
### System Requirements
- iOS `15.0` or higher
- Android `6.0` or higher
### Minimal Package Requirements
- `react-native` `0.75.0` or higher
- `react-native-nitro-modules` `0.35.0` or higher
## Installation
1. Install dependencies:
```bash
npm install react-native-video@next react-native-nitro-modules
```
2. Configure Library:
You can configure the library in two ways:
- [With Expo](./configuration/with-expo.md)
- [Without Expo](./configuration/without-expo.md)
3. Run the project:
If you are using Expo, you will need to generate native files:
```bash
npx expo prebuild
```
And then run the project:
```bash
npx expo run:ios # run on iOS
npx expo run:android # run on Android
```
If you are using React Native CLI, you will need to install Pods for iOS:
```bash
cd ios && pod install && cd ..
```
And then run the project:
```bash
npx react-native run-ios # run on iOS
npx react-native run-android # run on Android
```
## Patch for react-native < 0.80
Versions of `react-native` < 0.80 have a bug that prevents them from properly handling errors thrown by nitro modules on Android.
If working with these versions of `react-native`, we highly recommend you apply the `react-native-nitro-modules` patch below, to fix this issue.
You can apply it using `patch-package`.
:::warning
Without this patch you won't be able "recognize" errors, all will be thrown as unknown errors.
:::
Patch for `react-native-nitro-modules`
```diff
diff --git a/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp b/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp
index efcea05..ffad3f2 100644
--- a/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp
+++ b/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp
@@ -23,6 +23,10 @@ struct JSIConverter;
#include
#include
+#ifdef ANDROID
+#include
+#endif
+
namespace margelo::nitro {
using namespace facebook;
@@ -109,6 +113,15 @@ public:
std::string funcName = getHybridFuncFullName(kind, name, hybridInstance.get());
std::string message = exception.what();
throw jsi::JSError(runtime, funcName + ": " + message);
+#ifdef ANDROID
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexceptions"
+ } catch (const jni::JniException& exception) {
+ std::string funcName = getHybridFuncFullName(kind, name, hybridInstance.get());
+ std::string message = exception.what();
+ throw jsi::JSError(runtime, funcName + ": " + message);
+#pragma clang diagnostic pop
+#endif
} catch (...) {
// Some unknown exception was thrown - add method name information and re-throw as `JSError`.
std::string funcName = getHybridFuncFullName(kind, name, hybridInstance.get());
```
see [raw](https://github.com/TheWidlarzGroup/react-native-video/blob/v7/example/patches/react-native-nitro-modules%2B0.27.2.patch)
## Usage
```tsx title="App.tsx"
import { VideoView, useVideoPlayer } from 'react-native-video';
export default function App() {
const player = useVideoPlayer({
source: {
uri: 'https://www.w3schools.com/html/mov_bbb.mp4',
},
});
return ;
}
```
================================================
FILE: docs/docs/fundamentals/intro.mdx
================================================
---
title: Intro
sidebar_position: 1
description: Introduction to React Native Video v7
customProps:
badgeType: new
---
import {
V7Lead,
V7FeatureCards,
V7ApiModel,
V7NitroSection,
V7StatusTimeline,
V7OpenSource,
V7ProPlugins,
} from '@site/src/components/Intro';
## What changed in v7 vs v6
## New API model in practice
### New model: you control the player, the view is optional
## Why Nitro Modules?
### Nitro Modules as the foundation of v7
## Status v7
## Open-source + optional Pro features and services
## Pro plugins
================================================
FILE: docs/docs/offer.mdx
================================================
---
title: Offer
description: Introduction to React Native Video library
sidebar_class_name: hidden
---
import {
IntroHero,
OfferCards,
HowItWorks,
Services,
Extensions,
DecisionTable,
Contact,
} from "@site/src/components/Offer";
## What you can use
Three ways to work with react-native-video, depending on your needs and timeline:
## How it works
## Services
When you need guaranteed response times and direct maintainer involvement:
## Extensions
Ready-to-use modules and templates for advanced use cases:
## Which option should I choose?
Quick reference guide based on your situation:
## Contact
Ready to discuss your project? Here's what to include:
================================================
FILE: docs/docs/player/_category_.json
================================================
{
"label": "Player",
"position": 2,
"collapsed": false
}
================================================
FILE: docs/docs/player/analytics/_category_.json
================================================
{
"label": "Analytics",
"position": 7,
"collapsed": false,
"customProps": {
"badgeType": "new"
}
}
================================================
FILE: docs/docs/player/analytics/manual.md
================================================
---
sidebar_position: 2
sidebar_label: Manual (Native)
description: Manual analytics integration guide using native plugins
---
# Manual Analytics
Integrate your own analytics system using native plugins.
## Overview
Most analytics systems that track player data (e.g., bitrate, errors) can be integrated directly with ExoPlayer or AVPlayer. The [plugin system](../../plugins/plugins.mdx) allows for non-intrusive analytics integration with react-native-video. It should be implemented in native languages (Kotlin/Swift) to ensure efficiency.
## Architecture
```
┌─────────────────────────────────────┐
│ React Native App │
│ ┌───────────────────────────────┐ │
│ │ react-native-video │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Plugin System │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
│ │
│ onPlayerCreated │ onPlayerCreated
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Android Plugin │ │ iOS Plugin │
│ (Kotlin) │ │ (Swift) │
│ │ │ │
│ ExoPlayer │ │ AVPlayer │
│ Analytics │ │ KVO Observers │
│ Listener │ │ │
└─────────────────┘ └─────────────────┘
```
## Implementation
### Android (Kotlin)
```kotlin
import androidx.annotation.OptIn
import androidx.media3.common.Format
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.analytics.AnalyticsListener
import com.twg.video.core.plugins.NativeVideoPlayer
import com.twg.video.core.plugins.ReactNativeVideoPlugin
import java.lang.ref.WeakReference
@OptIn(UnstableApi::class)
class AnalyticsPlugin : ReactNativeVideoPlugin("MyAnalytics") {
override fun onPlayerCreated(player: WeakReference) {
val nativePlayer = player.get() ?: return
val exoPlayer = nativePlayer.player
exoPlayer.addAnalyticsListener(object : AnalyticsListener {
override fun onBandwidthEstimate(
eventTime: AnalyticsListener.EventTime,
totalLoadTimeMs: Int,
totalBytesLoaded: Long,
bitrateEstimate: Long
) {
trackMetric("bitrate", bitrateEstimate)
}
override fun onDroppedVideoFrames(
eventTime: AnalyticsListener.EventTime,
droppedFrames: Int,
elapsedMs: Long
) {
trackMetric("dropped_frames", droppedFrames)
}
override fun onVideoInputFormatChanged(
eventTime: AnalyticsListener.EventTime,
format: Format,
decoderReuseDecision: Int?
) {
trackEvent("quality_change", mapOf(
"width" to format.width,
"height" to format.height,
"bitrate" to format.bitrate
))
}
override fun onPlaybackStateChanged(
eventTime: AnalyticsListener.EventTime,
state: Int
) {
when (state) {
Player.STATE_BUFFERING -> trackEvent("buffering_start")
Player.STATE_READY -> trackEvent("buffering_end")
Player.STATE_ENDED -> trackEvent("playback_complete")
}
}
override fun onPlayerError(
eventTime: AnalyticsListener.EventTime,
error: PlaybackException
) {
trackEvent("error", mapOf(
"code" to error.errorCode,
"message" to error.message
))
}
})
}
override fun onPlayerDestroyed(player: WeakReference) {
flushAnalytics()
}
private fun trackEvent(name: String, params: Map = emptyMap()) {
// Send to your analytics backend
}
private fun trackMetric(name: String, value: Number) {
// Send to your analytics backend
}
private fun flushAnalytics() {
// Flush pending analytics
}
}
```
### iOS (Swift)
```swift
import AVFoundation
class AnalyticsPlugin: ReactNativeVideoPlugin {
// MARK: - Properties
private weak var currentPlayer: AVPlayer?
private var rateObserver: NSKeyValueObservation?
private var statusObserver: NSKeyValueObservation?
private var timeObserver: Any?
// MARK: - Init
init() {
super.init(name: "MyAnalytics")
}
// MARK: - Plugin Lifecycle
override func onPlayerCreated(player: Weak) {
guard let nativePlayer = player.value else { return }
currentPlayer = nativePlayer.player
setupPlaybackObservers(for: nativePlayer.player)
setupQualityTracking(for: nativePlayer.playerItem)
}
override func onPlayerDestroyed(player: Weak) {
removeAllObservers()
flushAnalytics()
}
// MARK: - Setup
private func setupPlaybackObservers(for player: AVPlayer) {
rateObserver = player.observe(\.rate) { [weak self] p, _ in
self?.trackEvent(p.rate > 0 ? "play" : "pause")
}
statusObserver = player.observe(\.status) { [weak self] p, _ in
if p.status == .readyToPlay {
self?.trackEvent("ready")
} else if p.status == .failed {
self?.trackEvent("error", params: ["message": p.error?.localizedDescription ?? ""])
}
}
timeObserver = player.addPeriodicTimeObserver(
forInterval: CMTime(seconds: 10, preferredTimescale: 1),
queue: .main
) { [weak self] time in
self?.trackMetric("position", value: time.seconds)
}
}
private func setupQualityTracking(for playerItem: AVPlayerItem?) {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleAccessLog),
name: .AVPlayerItemNewAccessLogEntry,
object: playerItem
)
}
@objc private func handleAccessLog(_ notification: Notification) {
guard let item = notification.object as? AVPlayerItem,
let event = item.accessLog()?.events.last else { return }
trackMetric("bitrate", value: event.indicatedBitrate)
trackMetric("stalls", value: Double(event.numberOfStalls))
trackMetric("dropped_frames", value: Double(event.numberOfDroppedVideoFrames))
}
private func removeAllObservers() {
if let observer = timeObserver {
currentPlayer?.removeTimeObserver(observer)
}
rateObserver?.invalidate()
statusObserver?.invalidate()
NotificationCenter.default.removeObserver(self)
currentPlayer = nil
timeObserver = nil
}
// MARK: - Analytics
private func trackEvent(_ name: String, params: [String: Any] = [:]) {
// Send to your analytics backend
}
private func trackMetric(_ name: String, value: Double) {
// Send to your analytics backend
}
private func flushAnalytics() {
// Flush pending analytics
}
}
```
## Available Metrics
### Android (ExoPlayer AnalyticsListener)
| Event | Method | Data |
|-------|--------|------|
| Bitrate | `onBandwidthEstimate` | `bitrateEstimate` |
| Dropped frames | `onDroppedVideoFrames` | `droppedFrames`, `elapsedMs` |
| Quality change | `onVideoInputFormatChanged` | `format.width`, `format.height` |
| Buffering | `onPlaybackStateChanged` | `Player.STATE_BUFFERING` |
| Stall | `onStallStart` / `onStallEnd` | duration |
| Seek | `onSeekStarted` / `onSeekProcessed` | position |
| Error | `onPlayerError` | `errorCode`, `message` |
### iOS (AVPlayer)
| Metric | Source | Property |
|--------|--------|----------|
| Bitrate | `accessLog` | `indicatedBitrate` |
| Stalls | `accessLog` | `numberOfStalls` |
| Dropped frames | `accessLog` | `numberOfDroppedVideoFrames` |
| Bytes | `accessLog` | `numberOfBytesTransferred` |
| Playback status | KVO | `player.status` |
| Play/Pause | KVO | `player.rate` |
| Buffer | `playerItem` | `isPlaybackLikelyToKeepUp` |
## Registration
Plugins auto-register when instantiated. Create your plugin early in app lifecycle:
**Android** - `MainApplication.kt`:
```kotlin
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
AnalyticsPlugin() // Auto-registers via init block
}
}
```
**iOS** - `AppDelegate.swift`:
```swift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
private var analyticsPlugin: AnalyticsPlugin?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
analyticsPlugin = AnalyticsPlugin() // Auto-registers via init
return true
}
}
```
## See Also
- [Plugins](../../plugins/plugins.mdx) - Full plugin system documentation
- [Plugin Interface](../../plugins/interface.md) - Complete API reference
================================================
FILE: docs/docs/player/analytics/simple.md
================================================
---
sidebar_position: 0
sidebar_label: Simple (JS Events)
description: Simple analytics using JavaScript events
---
# Simple Analytics
The easiest way to track video analytics is by using the JavaScript events provided by `react-native-video`. This approach requires no native code and works across all platforms.
## Overview
Use the `useEvent` hook to subscribe to player events. For a complete list of available events and their data, see the [Events documentation](../events.md).
## Example
```tsx
import { useVideoPlayer, useEvent, VideoView } from 'react-native-video';
function VideoPlayer() {
const player = useVideoPlayer('https://example.com/video.mp4');
useEvent(player, 'onLoad', (data) => {
analytics.track('video_loaded', { duration: data.duration });
});
useEvent(player, 'onProgress', (data) => {
analytics.track('video_progress', { currentTime: data.currentTime });
});
useEvent(player, 'onError', (error) => {
analytics.track('video_error', { error: error.error });
});
useEvent(player, 'onEnd', () => {
analytics.track('video_completed');
});
return ;
}
```
## When to Use
**Use JS Events when:**
- You need basic playback tracking (play, pause, progress, errors)
- You want a quick integration without native code
- Your analytics needs are straightforward
**Consider [Manual Analytics](./manual.md) when:**
- You need low-level metrics (bitrate, dropped frames, buffer health)
- You want to integrate with native analytics SDKs
- You need the most accurate and detailed data
## Tips
1. **Throttle progress events** - `onProgress` fires frequently. Consider throttling before sending to your analytics backend.
2. **Track session data** - Store a session ID to group events from the same viewing session.
3. **Include video metadata** - Add video ID, title, or other metadata to your events for better reporting.
```tsx
const sessionId = useRef(uuid()).current;
const handleProgress = (data) => {
// Throttle to every 10 seconds
if (Math.floor(data.currentTime) % 10 === 0) {
analytics.track('video_progress', {
sessionId,
videoId: 'video-123',
currentTime: data.currentTime,
});
}
};
```
================================================
FILE: docs/docs/player/downloading/_category_.json
================================================
{
"label": "Downloading",
"position": 6,
"link": null,
"collapsed": false,
"customProps": {
"plan": "pro"
}
}
================================================
FILE: docs/docs/player/downloading/downloading.md
================================================
---
sidebar_position: 2
sidebar_label: Downloading
description: Complete API reference for Offline Video SDK
---
# Downloading
## Authorization
### `registerPlugin(apiKey: string): Promise`
Registers and authorizes the plugin with the provided API key. Must be called before using any other functions.
**Parameters:**
- `apiKey`: Your API key obtained from [sdk.thewidlarzgroup.com](https://sdk.thewidlarzgroup.com?utm_source=rnv&utm_medium=docs&utm_campaign=downloading&utm_id=downloading_api-key-param)
**Returns:** `Promise` - `true` if registration was successful, `false` otherwise
```tsx
import { registerPlugin } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const success = await registerPlugin("YOUR_API_KEY");
```
### `disablePlugin(): void`
Disables the plugin. After calling this, you'll need to call `registerPlugin` again to use the SDK.
```tsx
import { disablePlugin } from "@TheWidlarzGroup/react-native-video-stream-downloader";
disablePlugin();
```
### `isRegistered(): boolean`
Checks if the plugin is currently registered and authorized.
**Returns:** `boolean` - `true` if registered, `false` otherwise
```tsx
import { isRegistered } from "@TheWidlarzGroup/react-native-video-stream-downloader";
if (isRegistered()) {
// Plugin is ready to use
}
```
## Configuration
### `setConfig(config: DownloadConfig): void`
Sets global configuration for downloads.
**Parameters:**
- `config`: Configuration with the following properties:
- `updateFrequencyMS?: number` - How often to update download progress (in milliseconds)
- `maxParallelDownloads?: number` - Maximum number of simultaneous downloads
```tsx
import { setConfig } from "@TheWidlarzGroup/react-native-video-stream-downloader";
setConfig({
updateFrequencyMS: 1000,
maxParallelDownloads: 3,
});
```
### `getConfig(): DownloadConfig`
Gets the current global configuration.
**Returns:** `DownloadConfig` with properties:
- `updateFrequencyMS?: number`
- `maxParallelDownloads?: number`
```tsx
import { getConfig } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const config = getConfig();
```
## Download Management
### `downloadStream(url: string, options?: DownloadOptions): Promise` {#downloadstream}
Downloads a video stream for offline playback.
**Parameters:**
- `url`: The URL of the video stream to download
- `options`: Optional download configuration with the following properties:
- `includeAllTracks?: boolean` - Whether to download all available tracks (default: `false`)
- `tracks?: Track[]` - Array of specific tracks to download. Each track has:
- `id: string` - Track identifier
- `type: 'video' | 'audio' | 'text'` - Track type
- `language?: string` - Language code (for audio/text tracks)
- `expiresAt?: number` - Unix timestamp when the download should expire
- `drm?: DRMConfig` - DRM configuration (see [DRM Downloading](./drm-downloading.md) for details)
- `metadata?: Record` - Custom metadata to store with the download
**Returns:** `Promise` - Status immediately after adding to queue (not after completion)
**DownloadStatus properties:**
- `id: string` - Unique download identifier
- `url: string` - Source URL
- `status: 'queued' | 'downloading' | 'paused' | 'completed' | 'failed' | 'cancelled'` - Current status
- `progress: number` - Download progress (0-1)
- `bytesDownloaded: number` - Number of bytes downloaded
- `totalBytes: number` - Total bytes to download (may be `undefined` until known)
```tsx
import { downloadStream } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const status = await downloadStream("https://example.com/video.m3u8", {
tracks: [
{ id: "video-1", type: "video" },
{ id: "audio-en", type: "audio", language: "en" },
],
});
```
### `pauseDownload(id: string): Promise`
Pauses an active download.
**Parameters:**
- `id`: The download identifier returned from `downloadStream`
```tsx
import { pauseDownload } from "@TheWidlarzGroup/react-native-video-stream-downloader";
await pauseDownload(downloadId);
```
### `resumeDownload(id: string): Promise`
Resumes a paused download.
**Parameters:**
- `id`: The download identifier returned from `downloadStream`
```tsx
import { resumeDownload } from "@TheWidlarzGroup/react-native-video-stream-downloader";
await resumeDownload(downloadId);
```
### `cancelDownload(id: string): Promise`
Cancels a download and removes it from the queue. Also deletes any partially downloaded files.
**Parameters:**
- `id`: The download identifier returned from `downloadStream`
```tsx
import { cancelDownload } from "@TheWidlarzGroup/react-native-video-stream-downloader";
await cancelDownload(downloadId);
```
### `cancelAllDownloads(): Promise`
Cancels all active and queued downloads.
```tsx
import { cancelAllDownloads } from "@TheWidlarzGroup/react-native-video-stream-downloader";
await cancelAllDownloads();
```
### `getDownloadStatus(id: string): Promise`
Gets the current status of a specific download.
**Parameters:**
- `id`: The download identifier
**Returns:** `Promise` - Current download status or `null` if not found
```tsx
import { getDownloadStatus } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const status = await getDownloadStatus(downloadId);
```
### `getDownloadsStatus(): Promise`
Gets the status of all downloads (active, queued, completed, etc.).
**Returns:** `Promise` - Array of all download statuses
```tsx
import { getDownloadsStatus } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const allStatuses = await getDownloadsStatus();
```
## Track Inspection
### `getAvailableTracks(url: string): Promise` {#getavailabletracks}
Retrieves information about available audio, video, and subtitle tracks in a stream.
**Parameters:**
- `url`: The URL of the video stream to inspect
**Returns:** `Promise` with the following structure:
- `video: VideoTrack[]` - Available video tracks, each with:
- `id: string` - Track identifier
- `width: number` - Video width in pixels
- `height: number` - Video height in pixels
- `bitrate: number` - Bitrate in bits per second
- `audio: AudioTrack[]` - Available audio tracks, each with:
- `id: string` - Track identifier
- `language: string` - Language code (e.g., "en", "es")
- `bitrate: number` - Bitrate in bits per second
- `text: TextTrack[]` - Available subtitle tracks, each with:
- `id: string` - Track identifier
- `language: string` - Language code
- `type: string` - Subtitle format (e.g., "text/vtt")
```tsx
import { getAvailableTracks } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const tracks = await getAvailableTracks("https://example.com/video.m3u8");
// Use tracks.video, tracks.audio, tracks.text to let user select
// Then pass selected tracks to downloadStream
```
For more details on track selection, see [Track Selection](./track-selection.md).
## Types
### `DownloadStatus` {#downloadstatus}
Status information for a download.
**Properties:**
- `id: string` - Unique download identifier
- `url: string` - Source URL
- `status: 'queued' | 'downloading' | 'paused' | 'completed' | 'failed' | 'cancelled'` - Current status
- `progress: number` - Download progress (0-1)
- `bytesDownloaded: number` - Number of bytes downloaded
- `totalBytes: number | undefined` - Total bytes to download (may be `undefined` until known)
### `DownloadOptions`
Configuration options for `downloadStream`.
**Properties:**
- `includeAllTracks?: boolean` - Whether to download all available tracks (default: `false`)
- `tracks?: Track[]` - Array of specific tracks to download
- `expiresAt?: number` - Unix timestamp when the download should expire
- `drm?: DRMConfig` - DRM configuration (see [DRM Downloading](./drm-downloading.md))
- `metadata?: Record` - Custom metadata to store with the download
### `Track`
A track selection for download.
**Properties:**
- `id: string` - Track identifier
- `type: 'video' | 'audio' | 'text'` - Track type
- `language?: string` - Language code (for audio/text tracks)
### `AvailableTracks`
Result from `getAvailableTracks` containing available tracks.
**Properties:**
- `video: VideoTrack[]` - Available video tracks
- `audio: AudioTrack[]` - Available audio tracks
- `text: TextTrack[]` - Available subtitle tracks
### `VideoTrack`
Information about a video track.
**Properties:**
- `id: string` - Track identifier
- `width: number` - Video width in pixels
- `height: number` - Video height in pixels
- `bitrate: number` - Bitrate in bits per second
### `AudioTrack`
Information about an audio track.
**Properties:**
- `id: string` - Track identifier
- `language: string` - Language code (e.g., "en", "es")
- `bitrate: number` - Bitrate in bits per second
### `TextTrack`
Information about a subtitle track.
**Properties:**
- `id: string` - Track identifier
- `language: string` - Language code
- `type: string` - Subtitle format (e.g., "text/vtt")
### `DownloadConfig`
Global configuration for downloads.
**Properties:**
- `updateFrequencyMS?: number` - How often to update download progress (in milliseconds)
- `maxParallelDownloads?: number` - Maximum number of simultaneous downloads
### `DRMConfig`
DRM configuration for downloading protected content.
**Properties:**
- `licenseServer: string` - URL of the license server
- `certificateUrl?: string` - Certificate URL (required for FairPlay on iOS)
- `headers?: Record` - HTTP headers for license requests
- `getLicense?: (spcData: ArrayBuffer) => Promise` - Custom license acquisition function (iOS only, FairPlay)
================================================
FILE: docs/docs/player/downloading/drm-downloading.md
================================================
---
sidebar_position: 5
sidebar_label: DRM Downloading
description: Downloading and playing DRM-protected content offline
---
# DRM Downloading
The Offline Video SDK supports downloading and playing DRM-protected content offline, including support for persistent licenses.
## Prerequisites
:::warning Critical Requirement
Your DRM provider **must support persistent/offline licenses**. Not all DRM providers support this feature. Contact your DRM provider to confirm offline license support before implementing.
:::
To download DRM-protected content, you need:
- **Encrypted media**: Video content encrypted with DRM (Widevine, FairPlay, PlayReady)
- **License server**: A license server that supports **persistent/offline licenses**
- **Short-lived token**: Authentication token for license acquisition (if required)
## High-level Flow
1. **Content Preparation**: Media is encrypted and packaged with DRM
2. **License Server Configuration**: License server is configured to issue persistent licenses
3. **Download with DRM Config**: Call `downloadStream` with DRM configuration
4. **License Acquisition**: SDK automatically acquires and stores persistent license
5. **Offline Playback**: Downloaded content can be played offline using the stored license
## Configuration
The `drm` property in `downloadStream` accepts a `DRMConfig` with the following properties:
- `licenseServer: string` - URL of the license server
- `certificateUrl?: string` - Certificate URL (required for FairPlay on iOS)
- `headers?: Record` - HTTP headers for license requests
- `getLicense?: (spcData: ArrayBuffer) => Promise` - Custom license acquisition function (iOS only, FairPlay)
See [DRMConfig](./downloading.md#drmconfig) in API Reference for complete type definition.
### Basic DRM Configuration
```tsx
import { downloadStream } from "@TheWidlarzGroup/react-native-video-stream-downloader";
await downloadStream(videoUrl, {
drm: {
licenseServer: "https://license.example.com/acquire",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
},
},
});
```
### FairPlay (iOS)
For FairPlay on iOS, you also need to provide the certificate URL:
```tsx
await downloadStream(videoUrl, {
drm: {
licenseServer: "https://license.example.com/acquire",
certificateUrl: "https://license.example.com/certificate.cer",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
},
},
});
```
### Custom License Acquisition (iOS only)
On iOS, you can provide a custom license acquisition function:
```tsx
await downloadStream(videoUrl, {
drm: {
licenseServer: "https://license.example.com/acquire",
certificateUrl: "https://license.example.com/certificate.cer",
getLicense: async (spcData: ArrayBuffer) => {
// Custom license acquisition logic
const response = await fetch("https://license.example.com/acquire", {
method: "POST",
headers: {
"Content-Type": "application/octet-stream",
"Authorization": "Bearer YOUR_TOKEN",
},
body: spcData,
});
return await response.arrayBuffer();
},
},
});
```
## Platform-Specific Notes
### Android
- Supports Widevine and PlayReady
- License acquisition is handled automatically by the SDK
- Ensure your license server supports persistent licenses
### iOS
- Supports FairPlay
- Requires `certificateUrl` for FairPlay
- Custom license acquisition via `getLicense` is optional but available
- FairPlay configuration should match your `react-native-video` DRM setup
## Testing
:::tip
Always test DRM downloading on a **real device**. Simulators/emulators may not properly handle DRM operations.
:::
When testing:
1. Ensure your test content is properly encrypted
2. Verify your license server supports persistent licenses
3. Test on both iOS and Android devices
4. Verify offline playback works after download completes
5. Test license expiration scenarios
## FairPlay Notes
The FairPlay configuration for offline downloading should match your `react-native-video` DRM configuration exactly. Use the same:
- `licenseServer` URL
- `certificateUrl`
- `headers` (if applicable)
- `getLicense` function (if using custom acquisition)
This ensures consistency between online and offline playback.
================================================
FILE: docs/docs/player/downloading/events-downloading.md
================================================
---
sidebar_position: 3
sidebar_label: Events (downloading)
description: Event system for tracking download progress and status
---
# Downloading Events
The Offline Video SDK emits events that allow you to monitor and react to changes in download status and progress. You can subscribe to these events using the `useEvent` hook.
## Available Events
### `onError`
Fired when an error occurs during download.
**Callback signature:**
```tsx
(error: string) => void
```
**Parameters:**
- `error`: Error message describing what went wrong
```tsx
import { useEvent } from "@TheWidlarzGroup/react-native-video-stream-downloader";
useEvent("onError", (error: string) => {
console.error("Download error:", error);
});
```
### `onDownloadProgress`
Fired periodically during downloads with current progress information.
**Callback signature:**
```tsx
(downloads: DownloadStatus[]) => void
```
**Parameters:**
- `downloads`: Array of `DownloadStatus` - See [DownloadStatus structure](./downloading.md#downloadstatus) in API Reference for complete properties
```tsx
import { useEvent } from "@TheWidlarzGroup/react-native-video-stream-downloader";
useEvent("onDownloadProgress", (downloads: DownloadStatus[]) => {
downloads.forEach((download) => {
console.log(`Download ${download.id}: ${(download.progress * 100).toFixed(1)}%`);
});
});
```
### `onDownloadEnd`
Fired when a download completes (successfully or with failure).
**Callback signature:**
```tsx
(download: DownloadStatus) => void
```
**Parameters:**
- `download`: Final `DownloadStatus` - See [DownloadStatus structure](./downloading.md#downloadstatus) in API Reference for complete properties. When this event fires, `status` will be either `'completed'` or `'failed'`.
```tsx
import { useEvent } from "@TheWidlarzGroup/react-native-video-stream-downloader";
useEvent("onDownloadEnd", (download: DownloadStatus) => {
if (download.status === "completed") {
console.log(`Download ${download.id} completed successfully`);
} else {
console.log(`Download ${download.id} failed`);
}
});
```
## Using Events
Events are automatically typed and can be used with the `useEvent` hook:
```tsx
import { useEvent } from "@TheWidlarzGroup/react-native-video-stream-downloader";
function DownloadManager() {
useEvent("onDownloadProgress", (downloads) => {
// Update UI with progress
});
useEvent("onDownloadEnd", (download) => {
// Handle completion
});
useEvent("onError", (error) => {
// Handle errors
});
// ... rest of component
}
```
The `useEvent` hook automatically manages cleanup when the component unmounts, preventing memory leaks.
================================================
FILE: docs/docs/player/downloading/getting-started.md
================================================
---
sidebar_position: 1
sidebar_label: Getting Started
description: Installation and setup guide for Offline Video SDK
---
# Getting Started
This section covers the **Offline Video SDK** - a commercial extension for `react-native-video` that enables downloading and offline playback.
The Offline Video SDK is a commercial add-on for `react-native-video` (versions 6 and 7) that enables secure offline playback of HLS/DASH streams or static videos like MP4, including support for DRM, multiple audio tracks, and subtitles.
## Requirements
- **React Native Video**: version 6.15.0 or higher (versions 6 and 7 are supported)
- **iOS**: 15.0 or higher
- **Android**: 6.0 or higher
- **Api key**: A valid api key for the Offline Video SDK is required. You can obtain this through the [SDK platform](https://sdk.thewidlarzgroup.com?utm_source=rnv&utm_medium=docs&utm_campaign=downloading&utm_id=getting-started_api-key-requirement).
## Features
- **HLS/DASH/MP4 Downloading**: Download and store video streams for offline playback
- **Asset Management**: Full control over downloaded assets
- **Offline DRM**: Supports offline playback of DRM-protected content with proper rights enforcement and license handling
- **Offline Playback**: Play downloaded content without internet connection
- **Cross-platform**: Works on both iOS and Android
## Installation
### 1. Configure npm for Private Package
Since this is a private package, configure npm to access GitHub Packages by adding the following to your `~/.npmrc` file:
```
@TheWidlarzGroup:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=
```
Replace `` with your GitHub token. To obtain this token, please [contact us](https://www.thewidlarzgroup.com/?utm_source=rnv&utm_medium=docs&utm_campaign=downloading&utm_id=getting-started_github-token#Contact).
:::note
This token is different from the API key used for plugin authorization.
:::
### 2. Install the Package
Install the package using npm:
```bash
npm install @TheWidlarzGroup/react-native-video-stream-downloader
```
### 3. Android Configuration
Add the following line to your `./android/app/build.gradle` file in the `dependencies` block:
```groovy
implementation fileTree(dir: "../../node_modules/@TheWidlarzGroup/react-native-video-stream-downloader/native-libs", include: ["*.aar"])
```
Ensure the path matches your project's `node_modules` location.
### 4. iOS Configuration
No additional configuration is required for iOS; the plugin will be automatically linked.
### 5. Plugin Authorization
After installation, authorize the plugin with an API key:
```tsx
import { registerPlugin } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const success = await registerPlugin("YOUR_API_KEY");
```
Replace `"YOUR_API_KEY"` with your actual API key obtained from [sdk.thewidlarzgroup.com](https://sdk.thewidlarzgroup.com?utm_source=rnv&utm_medium=docs&utm_campaign=downloading&utm_id=getting-started_api-key-link).
## Supported Formats
| Format | iOS | Android |
|--------|-----|---------|
| HLS | ✅ | ✅ |
| MP4 | ✅ | ✅ |
| MPEG-DASH | ❌ | ✅ |
## Licensing & Pricing
The Offline Video SDK is distributed under a commercial license. You can evaluate it for free for 14 days without a credit card. For questions or assistance, contact [hi@thewidlarzgroup.com](mailto:hi@thewidlarzgroup.com).
================================================
FILE: docs/docs/player/downloading/track-selection.md
================================================
---
sidebar_position: 4
sidebar_label: Track Selection
description: How to select specific tracks for download
---
# Track Selection
Track selection allows you to download only the specific audio, video, and subtitle tracks you need, optimizing storage usage and download time.
## Why Select Tracks?
- **Storage Optimization**: Download only the tracks you need (e.g., specific language, resolution)
- **Language Support**: Choose audio tracks in different languages
- **Subtitle Selection**: Download only the subtitle languages your users need
- **Bitrate Control**: Select video tracks with appropriate bitrates for your use case
## How It Works
The track selection process follows a 2-step flow:
1. **Inspect Available Tracks**: Use `getAvailableTracks(url)` to retrieve all available tracks
2. **Select Tracks for Download**: Pass the selected track IDs to `downloadStream` in the `tracks` property
### Step 1: Get Available Tracks
Use `getAvailableTracks` to retrieve all available tracks. See [getAvailableTracks](./downloading.md#getavailabletracks) in API Reference for complete details.
```tsx
import { getAvailableTracks } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const tracks = await getAvailableTracks("https://example.com/video.m3u8");
// tracks.video, tracks.audio, tracks.text contain available tracks
```
### Step 2: Download Selected Tracks
```tsx
import { downloadStream } from "@TheWidlarzGroup/react-native-video-stream-downloader";
// Select specific tracks
const selectedTracks = [
tracks.video[0].id, // First video track (usually highest quality)
tracks.audio.find(t => t.language === "en")?.id, // English audio
tracks.text.find(t => t.language === "en")?.id, // English subtitles
].filter(Boolean); // Remove undefined values
await downloadStream("https://example.com/video.m3u8", {
tracks: selectedTracks.map(id => ({ id, type: "video" })), // You need to specify type
});
```
## Example: Multi-Language Selection
```tsx
import { getAvailableTracks, downloadStream } from "@TheWidlarzGroup/react-native-video-stream-downloader";
const tracks = await getAvailableTracks(videoUrl);
// Select video track (highest quality)
const videoTrack = tracks.video[0];
// Select multiple audio tracks (English and Spanish)
const audioTracks = tracks.audio.filter(t =>
t.language === "en" || t.language === "es"
);
// Select subtitles for both languages
const subtitleTracks = tracks.text.filter(t =>
t.language === "en" || t.language === "es"
);
// Build track selection array
const selectedTracks = [
{ id: videoTrack.id, type: "video" as const },
...audioTracks.map(t => ({ id: t.id, type: "audio" as const })),
...subtitleTracks.map(t => ({ id: t.id, type: "text" as const })),
];
await downloadStream(videoUrl, {
tracks: selectedTracks,
});
```
## Default Behavior
If you don't specify `tracks` in `downloadStream`, or if `includeAllTracks` is `false`, only the default tracks are downloaded (typically the first video track and the first audio track).
To download all available tracks, set `includeAllTracks: true`:
```tsx
await downloadStream(videoUrl, {
includeAllTracks: true,
});
```
================================================
FILE: docs/docs/player/drm.md
================================================
---
sidebar_position: 4
sidebar_label: DRM
---
# DRM
## What is DRM (Digital Rights Management)?
DRM is a set of access control technologies that are used to protect copyrighted content from unauthorized use and distribution. It allows content owners to control how their digital media is used and distributed.
### When do you need it?
If you are working with copyrighted content and want to prevent unauthorized access or distribution, you will need DRM. It is especially important for streaming services, e-learning platforms, and any application that delivers premium content that you want to protect from piracy.
### What next?
This page explains how to play DRM‑protected content with React Native Video using the official DRM plugin. It covers installing and enabling the plugin, configuring sources with DRM, and platform‑specific notes for Android (Widevine) and iOS/visionOS (FairPlay).
## Install and enable the DRM plugin
:::tip Pluginable Architecture
React Native Video uses a plugin architecture. DRM support is provided by the `@react-native-video/drm` plugin and is not built into the core package.
:::
1) Install dependencies in your app:
```sh
npm install @react-native-video/drm
```
2) Enable the plugin at app startup (before creating any players):
```ts
// App.tsx (or any place you want to initialize the plugin)
import { enable } from '@react-native-video/drm';
enable();
```
The plugin autolinks on both Android and iOS. Nitro Modules are required because the plugin uses Nitro under the hood.
## Quick start
You pass DRM configuration via `VideoConfig.drm` when creating a player or using the `useVideoPlayer` hook. If `drm.type` is omitted, the default is inferred per platform (`widevine` on Android, `fairplay` on iOS/visionOS).
```tsx
import { VideoView, useVideoPlayer } from 'react-native-video';
export function Player() {
const player = useVideoPlayer({
source: {
uri: 'https://example.com/manifest.mpd', // or HLS .m3u8 for FairPlay
// On iOS these headers are also used for the default license request
headers: { Authorization: 'Bearer ' },
drm: {
// type: 'widevine' | 'fairplay' // optional; inferred by platform
licenseUrl: 'https://license.example.com/widevine',
},
},
});
return ;
}
```
:::warning
You shouldn't include your authorization token directly in the code. Instead, use a backend method to retrieve it at runtime.
:::
## DRM config reference
All properties are optional unless marked otherwise for a platform. The table below describes each property, the expected type, platforms where it applies, whether it's required, and important notes.
| Property | Type | Platform | Required | Notes |
|---|---:|---|:---:|---|
| `type` | `'widevine' \| 'fairplay'` | Android, iOS, visionOS | No (defaulted) | Default inferred per platform when `drm` is present and `type` omitted (Android → `widevine`, iOS/visionOS → `fairplay`). |
| `licenseUrl` | `string` | Android, iOS, visionOS | Android: Yes; iOS/visionOS: Yes for default/custom flows | URL of the license (CKC) service. Required for license acquisition. |
| `licenseHeaders` | ``Record`` | Android | No | Extra headers sent with the Widevine license request. (On iOS, use `source.headers` for license requests.) |
| `multiSession` | `boolean` | Android | No | Whether to allow multiple Widevine sessions. |
| `certificateUrl` | `string` | iOS, visionOS | Yes (for FairPlay) | URL to fetch the FairPlay application certificate (used to create the SPC). |
| `contentId` | `string` | iOS, visionOS | No | If omitted, derived from the `skd://` key URL. Used when creating the SPC. |
| `getLicense` | ``(payload) => Promise`` | iOS, visionOS | No | Optional hook for custom FairPlay license logic; must resolve to a base64‑encoded CKC string. |
Payload shape passed to `getLicense` (iOS/visionOS):
| Field | Type | Description |
|---|---:|---|
| `contentId` | `string` | Content identifier for the asset. If not provided the player will try to derive it from the `skd://` key URL. |
| `licenseUrl` | `string` | The license server URL that should be used for license acquisition. |
| `keyUrl` | `string` | The key URL/identifier received from the stream (typically an `skd://` URL). |
| `spc` | `string` | The SPC (secure playback context) as a base64‑encoded string. Send raw SPC bytes to your license server (server side may expect raw bytes rather than base64). |
## Android: Widevine
- Set `drm.type` to `'widevine'` or omit it (the library will default to Widevine on Android if `drm` is present).
- `licenseUrl` is required; `licenseHeaders` and `multiSession` are optional.
- Implementation details:
- The plugin uses ExoPlayer’s `DefaultDrmSessionManager` and `HttpMediaDrmCallback` with your `licenseUrl` and `licenseHeaders`.
- If a first attempt fails due to device security level issues, the plugin retries with `L3` security level.
Example:
```tsx
useVideoPlayer({
source: {
uri: 'https://example.com/manifest.mpd',
drm: {
// type: 'widevine', // optional
licenseUrl: 'https://license.example.com/widevine',
licenseHeaders: { 'X-Custom-Header': 'value' },
multiSession: false,
},
},
});
```
## iOS and visionOS: FairPlay
Two ways to get the CKC (license):
1) Default flow (no `getLicense`):
- Required: `certificateUrl`, `licenseUrl`.
- The plugin requests the application certificate, generates the SPC, then POSTs the SPC to `licenseUrl`.
- It uses `source.headers` (not `drm.licenseHeaders`) for the license request.
2) Custom flow (provide `getLicense`):
- Required: `certificateUrl`, `licenseUrl`, and a `getLicense` implementation.
- You receive `{ contentId, licenseUrl, keyUrl, spc }` and must return a base64‑encoded CKC string.
Notes:
- DRM isn’t supported in the iOS Simulator; the plugin returns `null` for DRM in Simulator builds.
- If `contentId` isn’t provided, it is derived from the `skd://` key URL.
Default flow example:
```tsx
useVideoPlayer({
source: {
uri: 'https://example.com/fairplay.m3u8',
headers: { Authorization: 'Bearer ' }, // used for the license request
drm: {
// type: 'fairplay', // optional
certificateUrl: 'https://license.example.com/fps-cert',
licenseUrl: 'https://license.example.com/fps',
// contentId: 'my-content-id', // optional
},
},
});
```
Custom `getLicense` example:
:::tip
This is example code for a custom `getLicense` implementation. it may differ from your actual implementation provided by your DRM provider
:::
```tsx
useVideoPlayer({
source: {
uri: 'https://example.com/fairplay.m3u8',
drm: {
certificateUrl: 'https://license.example.com/fps-cert',
licenseUrl: 'https://license.example.com/fps',
getLicense: async ({ contentId, licenseUrl, keyUrl, spc }) => {
// Example: POST SPC to your license server and return base64 CKC
const res = await fetch(licenseUrl, {
method: 'POST',
body: Buffer.from(spc, 'base64'), // server expects raw SPC bytes
headers: {
'Content-Type': 'application/octet-stream',
'X-Content-ID': contentId,
'X-Asset-Id': keyUrl,
},
});
if (!res.ok) throw new Error(`License request failed: ${res.status}`);
const ckc = await res.arrayBuffer();
// return base64 CKC string
return Buffer.from(ckc).toString('base64');
},
},
},
});
```
## Offline
If you are looking for implementing offline playback with DRM, make sure to checkout our [Offline Video SDK](https://www.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=docs&utm_campaign=drm&utm_id=offline-sdk-link). It provides a comprehensive solution for downloading and playing Streams and DRM-protected content.
## Troubleshooting
- DRMPluginNotFound: Ensure you installed `@react-native-video/drm`, imported it, and called `enable()` before creating any players.
- iOS headers: The default FairPlay flow uses `source.headers` for license requests; `drm.licenseHeaders` are not used on iOS.
- Invalid CKC: `getLicense` must return a base64 string. Returning raw bytes or JSON will fail.
- 403/415 from license server: Verify required auth headers, content type, and whether the server expects raw SPC bytes vs base64.
- Android security level issues: The plugin retries with Widevine L3 if the first attempt fails.
- iOS Simulator: DRM isn’t supported in Simulator. Test on a real device.
## Notes and defaults
- If `drm` is provided without `type`, the library sets a platform default: Android → Widevine, iOS/visionOS → FairPlay.
- For custom DRM systems or advanced pipelines, you can implement your own plugin. See the Plugin Interface docs.
================================================
FILE: docs/docs/player/events.md
================================================
---
sidebar_label: Events
sidebar_position: 5
---
# Handling Player Events
The `VideoPlayer` emits a variety of events that allow you to monitor and react to changes in its state and playback.
## Using the `useEvent` Hook
For React functional components, the `useEvent` hook provides a convenient way to subscribe to player events and automatically manage cleanup.
```typescript
import { useVideoPlayer, useEvent } from 'react-native-video';
import { useEffect } from 'react';
const MyVideoComponent = () => {
const player = useVideoPlayer('https://example.com/video.mp4', (_player) => {
_player.play();
});
useEvent(player, 'onLoad', (data) => {
console.log('Video loaded via useEvent! Duration:', data.duration);
});
useEvent(player, 'onProgress', (data) => {
console.log('Progress via useEvent:', data.currentTime);
});
// For onError, which is a direct property on VideoPlayer, not from VideoPlayerEvents
useEvent(player, 'onError', (error) => {
console.error('Player Error via useEvent:', error.code, error.message);
});
return ;
};
```
## Available Events
The `VideoPlayer` class, through `VideoPlayerEvents`, supports the following events. You can subscribe to these by assigning a callback function to the corresponding property on the `VideoPlayer` instance.
| Event | Callback Signature | Description |
|----------------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------|
| `onAudioBecomingNoisy` | () => void | Fired when audio is about to become noisy (e.g., headphones unplugged). |
| `onAudioFocusChange` | (hasAudioFocus: boolean) => void | Fired when the audio focus changes (e.g., another app starts playing audio). |
| `onBandwidthUpdate` | (data: [BandwidthData](../api-reference/interfaces/BandwidthData.md)) => void | Fired with an estimate of the available bandwidth. |
| `onBuffer` | (buffering: boolean) => void | Fired when the player starts or stops buffering data. |
| `onControlsVisibleChange` | (visible: boolean) => void | Fired when the visibility of native controls changes. |
| `onEnd` | () => void | Fired when the video playback reaches the end. |
| `onExternalPlaybackChange` | (externalPlaybackActive: boolean) => void | Fired when the external playback status changes (e.g., AirPlay). |
| `onLoad` | (data: [onLoadData](../api-reference/interfaces/onLoadData.md)) => void | Fired when the video has loaded and is ready to play. |
| `onLoadStart` | (data: [onLoadStartData](../api-reference/interfaces/onLoadStartData.md)) => void | Fired when the video starts loading. |
| `onPlaybackRateChange` | (rate: number) => void | Fired when the playback rate changes. |
| `onPlaybackStateChange` | (data: [onPlaybackStateChangeData](../api-reference/interfaces/onPlaybackStateChangeData.md)) => void | Fired when the playback state changes (e.g., playing, paused, stopped). |
| `onProgress` | (data: [onProgressData](../api-reference/interfaces/onProgressData.md)) => void | Fired periodically during playback with the current time. |
| `onReadyToDisplay` | () => void | Fired when the player is ready to display the first frame of the video. |
| `onSeek` | (seekTime: number) => void | Fired when a seek operation has completed. |
| `onStatusChange` | (status: [VideoPlayerStatus](../api-reference/type-aliases/VideoPlayerStatus.md)) => void | Fired when the player status changes (detailed status updates). |
| `onTextTrackDataChanged` | (texts: string[]) => void | Fired when text track data (e.g., subtitles) changes. |
| `onTimedMetadata` | (metadata: [TimedMetadata](../api-reference/interfaces/TimedMetadata.md)) => void | Fired when timed metadata is encountered in the video stream. |
| `onTrackChange` | (track: [TextTrack](../api-reference/interfaces/TextTrack.md) \| null) => void | Fired when the selected text track changes. |
| `onVolumeChange` | (data: [onVolumeChangeData](../api-reference/interfaces/onVolumeChangeData.md)) => void | Fired when the volume changes. |
Additionally, the `VideoPlayer` instance itself has an `onError` property:
- `onError: (error: ` [VideoRuntimeError](../api-reference/interfaces/VideoRuntimeError.md) `) => void` - Fired when an error occurs. The callback receives the `VideoRuntimeError` object.
**Benefits of `useEvent`**:
- **Automatic Cleanup**: The event listener is automatically removed when the component unmounts or when the `player`, `event`, or `callback` dependencies change, preventing memory leaks.
- **Type Safety**: Provides better type inference for event callback parameters.
This hook is recommended for managing event subscriptions in a declarative React style.
### Initialization Timing and Events
`onLoadStart` / `onLoad` will fire automatically after construction when `initializeOnCreation` (default `true`) is enabled. If you set `initializeOnCreation: false`, these events will not fire until you call `initialize()` or `preload()`. Attach your event handlers before invoking those methods to avoid missing early events.
## Subscribing to Events
You can subscribe to an event by assigning a function to the player instance's corresponding property:
```typescript
import { VideoPlayer } from 'react-native-video';
const player = new VideoPlayer('https://example.com/video.mp4');
player.addEventListener('onLoad', (data) => {
console.log('Video loaded! Duration:', data.duration);
});
player.addEventListener('onProgress', (data) => {
console.log('Current time:', data.currentTime);
});
player.addEventListener('onError', (error) => {
console.error('Player Error:', error.code, error.message);
});
player.play();
```
## Clearing Events
- The `player.clearEvent(eventName)` method can be used to clear a specific native event handler.
- When a player instance is no longer needed and `player.release()` is called, all event listeners are automatically cleared
================================================
FILE: docs/docs/player/player.md
================================================
---
sidebar_position: 1
sidebar_label: Player
---
# Player
The `VideoPlayer` class is the primary way to control video playback. It provides methods and properties to manage the video source, playback state, volume, and other aspects of the video.
## Initialization
To use the `VideoPlayer`, you first need to create an instance of it with a video source. There are two ways to do this. By default the native media item is initialized asynchronously right after creation (unless you opt out with `initializeOnCreation: false`).
### Using `useVideoPlayer` hook
```tsx
import { useVideoPlayer } from 'react-native-video';
const player = useVideoPlayer({
source: {
uri: 'https://www.w3schools.com/html/mov_bbb.mp4',
},
});
```
:::info
`useVideoPlayer` hook is recommended for most use cases. It automatically manages the player lifecycle between the component mount and unmount.
:::
For detailed information about using the hook, see [useVideoPlayer](./use-video-player.md).
### Using `VideoPlayer` class constructor directly
```typescript
import { VideoPlayer } from 'react-native-video';
// Using a URL string
const player = new VideoPlayer('https://example.com/video.mp4');
// Using a VideoSource object
const playerWithSource = new VideoPlayer({ uri: 'https://example.com/video.mp4' });
// Using a VideoConfig object
const playerWithConfig = new VideoPlayer({
source: { uri: 'https://example.com/video.mp4' },
// other configurations
});
```
:::warning
When using `VideoPlayer` class directly, you need to manually manage the player lifecycle. Once you no longer need the player, you need to call `release()` method to release the player's native resources. See [Player Lifecycle](./video-player.md#player-lifecycle) for more details.
:::
For detailed information about using the class, see [VideoPlayer](./video-player.md).
================================================
FILE: docs/docs/player/use-video-player.md
================================================
---
sidebar_position: 2
sidebar_label: useVideoPlayer
---
# useVideoPlayer
The `useVideoPlayer` hook is the recommended way to create and manage a `VideoPlayer` instance in React components. It automatically handles the player lifecycle, ensuring resources are properly released when the component unmounts.
## Quick Start
The simplest way to use the hook is to pass a video source:
```tsx
import { useVideoPlayer } from 'react-native-video';
const player = useVideoPlayer('https://www.w3schools.com/html/mov_bbb.mp4');
```
You can also pass a configuration object for more control:
```tsx
const player = useVideoPlayer({
source: {
uri: 'https://www.w3schools.com/html/mov_bbb.mp4',
},
});
```
## Why useVideoPlayer?
The `useVideoPlayer` hook provides several advantages over creating a `VideoPlayer` instance directly:
- **Automatic lifecycle management**: The player is automatically released when the component unmounts
- **React-friendly**: Works seamlessly with React's component lifecycle
:::info
For most React components, `useVideoPlayer` is the recommended approach. If you need more control over the player lifecycle, see [VideoPlayer class](./video-player.md) for direct instantiation.
:::
## Using the Player Instance
The `useVideoPlayer` hook returns a `VideoPlayer` instance that you can use to control playback:
```tsx
import { useVideoPlayer } from 'react-native-video';
const player = useVideoPlayer('https://example.com/video.mp4');
// Control playback
player.play();
player.pause();
player.muted = true;
```
### Playback Control
| Method | Description |
|--------|-------------|
| `play()` | Starts or resumes video playback. |
| `pause()` | Pauses video playback. |
| `seekBy(time: number)` | Seeks the video forward or backward by the specified number of seconds. |
| `seekTo(time: number)` | Seeks the video to a specific time in seconds. |
| `replaceSourceAsync(source: VideoSource \| VideoConfig \| null)` | Replaces the current video source with a new one. Pass `null` to release the current source without replacing it. |
| `initialize()` | Manually initialize the underlying native player item when `initializeOnCreation` was set to `false`. No-op if already initialized. |
| `preload()` | Ensures the media source is set and prepared (buffering started) without starting playback. If not yet initialized it will initialize first. |
| `release()` | Releases the player's native resources. The player is no longer usable after calling this method. **Note:** If you intend to reuse the player instance with a different source, use `replaceSourceAsync(null)` to clear resources instead of `release()`. |
### Properties
| Property | Access | Type | Description |
|----------|--------|------|-------------|
| `source` | Read-only | `VideoPlayerSource` | Gets the current `VideoPlayerSource` object. |
| `status` | Read-only | `VideoPlayerStatus` | Gets the current status (e.g., `playing`, `paused`, `buffering`). |
| `duration` | Read-only | `number` | Gets the total duration of the video in seconds. Returns `NaN` until metadata is loaded. |
| `volume` | Read/Write | `number` | Gets or sets the player volume (0.0 to 1.0). |
| `currentTime` | Read/Write | `number` | Gets or sets the current playback time in seconds. |
| `muted` | Read/Write | `boolean` | Gets or sets whether the video is muted. |
| `loop` | Read/Write | `boolean` | Gets or sets whether the video should loop. |
| `rate` | Read/Write | `number` | Gets or sets the playback rate (e.g., 1.0 for normal speed, 0.5 for half speed, 2.0 for double speed). |
| `mixAudioMode` | Read/Write | `MixAudioMode` | Controls how this player's audio mixes with other audio sources (see [MixAudioMode](../api-reference/type-aliases/MixAudioMode.md)). |
| `ignoreSilentSwitchMode` | Read/Write | `IgnoreSilentSwitchMode` | iOS-only. Determines how audio should behave when the hardware mute (silent) switch is on. |
| `playInBackground` | Read/Write | `boolean` | Whether playback should continue when the app goes to the background. |
| `playWhenInactive` | Read/Write | `boolean` | Whether playback should continue when the app is inactive (e.g., during a phone call). |
| `isPlaying` | Read-only | `boolean` | Returns `true` if the video is currently playing. |
| `selectedTrack` | Read-only | `TextTrack \| undefined` | Currently selected text track, or `undefined` when no track is selected. |
### Error Handling
| Property | Type | Description |
|----------|------|-------------|
| `onError?` | `(error: VideoRuntimeError) => void` | A callback function that is invoked when a runtime error occurs in the player. You can use this to catch and handle errors gracefully. |
### Buffer Config
You can fine‑tune buffering via `bufferConfig` on the `VideoConfig` you pass to `useVideoPlayer`/`VideoPlayer`. This controls how much data is buffered, live latency targets, and iOS network constraints.
Example
```ts
const player = useVideoPlayer({
source: {
uri: 'https://example.com/stream.m3u8',
bufferConfig: {
// Android
minBufferMs: 5000,
maxBufferMs: 10000,
// iOS
preferredForwardBufferDurationMs: 3000,
// Live (cross‑platform target)
livePlayback: { targetOffsetMs: 500 },
},
},
});
```
#### Android
Properties below are Android‑only
| Property | Type | Description |
|----------|------|-------------|
| `minBufferMs` | `number` | Minimum media duration the player attempts to keep buffered (ms). Default: 5000. |
| `maxBufferMs` | `number` | Maximum media duration the player attempts to buffer (ms). Default: 10000. |
| `bufferForPlaybackMs` | `number` | Media that must be buffered before playback can start or resume after user action (ms). Default: 1000. |
| `bufferForPlaybackAfterRebufferMs` | `number` | Media that must be buffered to resume after a rebuffer (ms). Default: 2000. |
| `backBufferDurationMs` | `number` | Duration kept behind the current position to allow instant rewind without rebuffer (ms). |
| `livePlayback.minPlaybackSpeed` | `number` | Minimum playback speed used to maintain target live offset. |
| `livePlayback.maxPlaybackSpeed` | `number` | Maximum playback speed used to catch up to target live offset. |
| `livePlayback.minOffsetMs` | `number` | Minimum allowed live offset (ms). |
| `livePlayback.maxOffsetMs` | `number` | Maximum allowed live offset (ms). |
| `livePlayback.targetOffsetMs` | `number` | Target live offset the player tries to maintain (ms). |
#### iOS, visionOS, tvOS
Properties below are Apple platforms‑only
| Property | Type | Description |
|----------|------|-------------|
| `preferredForwardBufferDurationMs` | `number` | Preferred duration the player attempts to retain ahead of the playhead (ms). |
| `preferredPeakBitRate` | `number` | Desired limit of network bandwidth for loading the current item (bits per second). |
| `preferredMaximumResolution` | `{ width: number; height: number }` | Preferred maximum video resolution. |
| `preferredPeakBitRateForExpensiveNetworks` | `number` | Bandwidth limit for expensive networks (e.g., cellular), in bits per second. |
| `preferredMaximumResolutionForExpensiveNetworks` | `{ width: number; height: number }` | Preferred maximum resolution on expensive networks. |
| `livePlayback.targetOffsetMs` | `number` | Target live offset (ms) the player will try to maintain. |
## DRM
Protected content is supported via a plugin. See the full DRM guide: [DRM](./drm.md).
Quick notes:
- Install and enable the official plugin `@react-native-video/drm` and call `enable()` at app startup before creating players.
- Pass DRM configuration on the source using the `drm` property of `VideoConfig` (see the DRM guide for platform specifics and `getLicense` examples).
- If you defer initialization (`initializeOnCreation: false`), be sure to call `await player.initialize()` (or `preload()`) before expecting DRM license acquisition events.
================================================
FILE: docs/docs/player/video-player.md
================================================
---
sidebar_position: 3
sidebar_label: VideoPlayer
---
# VideoPlayer
:::tip When to Use What
- **[`useVideoPlayer`](./use-video-player.md) hook** - Recommended for most cases. Automatically manages player lifecycle (creation, cleanup on unmount, source changes).
- **`VideoPlayer` class** - Use when you need manual lifecycle control, e.g., preloading videos before displaying them, managing multiple players with custom logic, or deferred initialization scenarios.
:::
The `VideoPlayer` class is the primary way to control video playback. It provides methods and properties to manage the video source, playback state, volume, and other aspects of the video.
## Initialization
To use the `VideoPlayer`, you first need to create an instance of it with a video source. By default the native media item is initialized asynchronously right after creation (unless you opt out with `initializeOnCreation: false`).
```typescript
import { VideoPlayer } from 'react-native-video';
// Using a URL string
const player = new VideoPlayer('https://example.com/video.mp4');
// Using a VideoSource object
const playerWithSource = new VideoPlayer({ uri: 'https://example.com/video.mp4' });
// Using a VideoConfig object
const playerWithConfig = new VideoPlayer({
source: { uri: 'https://example.com/video.mp4' },
// other configurations
});
```
:::warning
When using `VideoPlayer` class directly, you need to manually manage the player lifecycle. Once you no longer need the player, you need to call `release()` method to release the player's native resources. See the [Player Lifecycle](#player-lifecycle) section below for more details.
:::
## Core Functionality
The `VideoPlayer` class offers a comprehensive set of methods and properties to control video playback:
### Playback Control
| Method | Description |
|--------|-------------|
| `play()` | Starts or resumes video playback. |
| `pause()` | Pauses video playback. |
| `seekBy(time: number)` | Seeks the video forward or backward by the specified number of seconds. |
| `seekTo(time: number)` | Seeks the video to a specific time in seconds. |
| `replaceSourceAsync(source: VideoSource \| VideoConfig \| null)` | Replaces the current video source with a new one. Pass `null` to release the current source without replacing it. |
| `initialize()` | Manually initialize the underlying native player item when `initializeOnCreation` was set to `false`. No-op if already initialized. |
| `preload()` | Ensures the media source is set and prepared (buffering started) without starting playback. If not yet initialized it will initialize first. |
| `release()` | Releases the player's native resources. The player is no longer usable after calling this method. **Note:** If you intend to reuse the player instance with a different source, use `replaceSourceAsync(null)` to clear resources instead of `release()`. |
### Properties
| Property | Access | Type | Description |
|----------|--------|------|-------------|
| `source` | Read-only | `VideoPlayerSource` | Gets the current `VideoPlayerSource` object. |
| `status` | Read-only | `VideoPlayerStatus` | Gets the current status (e.g., `playing`, `paused`, `buffering`). |
| `duration` | Read-only | `number` | Gets the total duration of the video in seconds. Returns `NaN` until metadata is loaded. |
| `volume` | Read/Write | `number` | Gets or sets the player volume (0.0 to 1.0). |
| `currentTime` | Read/Write | `number` | Gets or sets the current playback time in seconds. |
| `muted` | Read/Write | `boolean` | Gets or sets whether the video is muted. |
| `loop` | Read/Write | `boolean` | Gets or sets whether the video should loop. |
| `rate` | Read/Write | `number` | Gets or sets the playback rate (e.g., 1.0 for normal speed, 0.5 for half speed, 2.0 for double speed). |
| `mixAudioMode` | Read/Write | `MixAudioMode` | Controls how this player's audio mixes with other audio sources (see [MixAudioMode](../api-reference/type-aliases/MixAudioMode.md)). |
| `ignoreSilentSwitchMode` | Read/Write | `IgnoreSilentSwitchMode` | iOS-only. Determines how audio should behave when the hardware mute (silent) switch is on. |
| `playInBackground` | Read/Write | `boolean` | Whether playback should continue when the app goes to the background. |
| `playWhenInactive` | Read/Write | `boolean` | Whether playback should continue when the app is inactive (e.g., during a phone call). |
| `isPlaying` | Read-only | `boolean` | Returns `true` if the video is currently playing. |
| `selectedTrack` | Read-only | `TextTrack \| undefined` | Currently selected text track, or `undefined` when no track is selected. |
### Error Handling
| Property | Type | Description |
|----------|------|-------------|
| `onError?` | `(error: VideoRuntimeError) => void` | A callback function that is invoked when a runtime error occurs in the player. You can use this to catch and handle errors gracefully. |
### Buffer Config
You can fine‑tune buffering via `bufferConfig` on the `VideoConfig` you pass to `useVideoPlayer`/`VideoPlayer`. This controls how much data is buffered, live latency targets, and iOS network constraints.
Example
```ts
const player = new VideoPlayer({
source: {
uri: 'https://example.com/stream.m3u8',
bufferConfig: {
// Android
minBufferMs: 5000,
maxBufferMs: 10000,
// iOS
preferredForwardBufferDurationMs: 3000,
// Live (cross‑platform target)
livePlayback: { targetOffsetMs: 500 },
},
},
});
```
#### Android
Properties below are Android‑only
| Property | Type | Description |
|----------|------|-------------|
| `minBufferMs` | `number` | Minimum media duration the player attempts to keep buffered (ms). Default: 5000. |
| `maxBufferMs` | `number` | Maximum media duration the player attempts to buffer (ms). Default: 10000. |
| `bufferForPlaybackMs` | `number` | Media that must be buffered before playback can start or resume after user action (ms). Default: 1000. |
| `bufferForPlaybackAfterRebufferMs` | `number` | Media that must be buffered to resume after a rebuffer (ms). Default: 2000. |
| `backBufferDurationMs` | `number` | Duration kept behind the current position to allow instant rewind without rebuffer (ms). |
| `livePlayback.minPlaybackSpeed` | `number` | Minimum playback speed used to maintain target live offset. |
| `livePlayback.maxPlaybackSpeed` | `number` | Maximum playback speed used to catch up to target live offset. |
| `livePlayback.minOffsetMs` | `number` | Minimum allowed live offset (ms). |
| `livePlayback.maxOffsetMs` | `number` | Maximum allowed live offset (ms). |
| `livePlayback.targetOffsetMs` | `number` | Target live offset the player tries to maintain (ms). |
#### iOS, visionOS, tvOS
Properties below are Apple platforms‑only
| Property | Type | Description |
|----------|------|-------------|
| `preferredForwardBufferDurationMs` | `number` | Preferred duration the player attempts to retain ahead of the playhead (ms). |
| `preferredPeakBitRate` | `number` | Desired limit of network bandwidth for loading the current item (bits per second). |
| `preferredMaximumResolution` | `{ width: number; height: number }` | Preferred maximum video resolution. |
| `preferredPeakBitRateForExpensiveNetworks` | `number` | Bandwidth limit for expensive networks (e.g., cellular), in bits per second. |
| `preferredMaximumResolutionForExpensiveNetworks` | `{ width: number; height: number }` | Preferred maximum resolution on expensive networks. |
| `livePlayback.targetOffsetMs` | `number` | Target live offset (ms) the player will try to maintain. |
## DRM
Protected content is supported via a plugin. See the full DRM guide: [DRM](./drm.md).
Quick notes:
- Install and enable the official plugin `@react-native-video/drm` and call `enable()` at app startup before creating players.
- Pass DRM configuration on the source using the `drm` property of `VideoConfig` (see the DRM guide for platform specifics and `getLicense` examples).
- If you defer initialization (`initializeOnCreation: false`), be sure to call `await player.initialize()` (or `preload()`) before expecting DRM license acquisition events.
## Player Lifecycle
Understanding the lifecycle of the `VideoPlayer` is crucial for managing resources effectively and ensuring a smooth user experience.
### Creation and Initialization
1. **Instantiation**: A `VideoPlayer` instance is created by calling its constructor with a video source (URL, `VideoSource`, or `VideoConfig`).
```typescript
const player = new VideoPlayer('https://example.com/video.mp4');
```
2. **Native Player Allocation**: A lightweight native player object is allocated immediately.
3. **Asset Initialization**: By default (unless you opt out) the underlying media item is prepared **asynchronously right after creation**. You can control this with `initializeOnCreation` inside `VideoConfig`.
#### Deferred Initialization (Advanced)
If you pass a `VideoConfig` with `{ initializeOnCreation: false }`, the player will skip preparing the media item automatically. This is useful when:
- You need to batch‑create many players without incurring immediate decoding / network cost
- You want to attach event handlers before any network requests happen
- You want explicit control over when buffering begins (e.g. on user interaction)
To initialize later, call:
```ts
await player.initialize();
// or preload if you also want it prepared & ready
await player.preload();
```
#### Initialization Methods Comparison
| Method | When to use | What it does |
|--------|-------------|--------------|
| `initialize()` | You deferred initialization and now want to create the native player item / media source | Creates & attaches the underlying player item / media source without starting playback |
| `preload()` | You want the player item prepared (buffering kicked off) ahead of an upcoming `play()` call | Ensures the media source is set and prepared; resolves once preparation started (may already be initialized) |
| Implicit (default) | `initializeOnCreation` not set or `true` | Automatically schedules initialization after JS construction |
:::info
By default, the player initializes automatically after construction. If you need to defer initialization, set `initializeOnCreation: false` in the config. You can then call `player.initialize()` or `player.preload()` later to start the player.
:::
### Playing a Video
1. **Loading**: When the player (auto) initializes, `preload()` is called, or after `replaceSourceAsync()`, the player starts loading the video metadata and buffering content.
- `onLoadStart`: Fired when the video starts loading.
- `onLoad`: Fired when the video metadata is loaded and the player is ready to play (duration, dimensions, etc., are available).
- `onBuffer`: Fired when buffering starts or ends.
2. **Playback**: Once enough data is buffered, playback begins.
- `onPlaybackStateChange`: Fired when the playback state changes (e.g., from `buffering` to `playing`).
- `onProgress`: Fired periodically with the current playback time.
- `onReadyToDisplay`: Fired when the first frame is ready to be displayed.
### Controlling Playback
- `pause()`: Pauses playback. `status` changes to `paused`.
- `seekTo(time)`, `seekBy(time)`: Changes the current playback position. `onSeek` is fired when the seek operation completes.
- `set volume(value)`, `set muted(value)`, `set loop(value)`, `set rate(value)`: Modify player properties. Corresponding events like `onVolumeChange` or `onPlaybackRateChange` might be fired.
### Changing Source
- `replaceSourceAsync(newSource)`: This method allows you to change the video source dynamically.
1. The current native player resources associated with the old source are released (similar to `release()` but specifically for the source).
2. A new native player instance (or reconfigured existing one) is prepared for the `newSource`.
3. The loading lifecycle events (`onLoadStart`, `onLoad`, etc.) will fire for the new source.
- `replaceSourceAsync(null)`: This effectively unloads the current video and releases its associated resources without loading a new one. This is useful for freeing up memory if the player is temporarily not needed but might be used again later.
### Releasing Resources
There are two main ways to release resources:
1. **`replaceSourceAsync(null)`**: This is a less destructive way to free resources related *only* to the current video source.
- The `VideoPlayer` instance itself remains usable.
- You can later call `replaceSourceAsync(newSource)` to load and play a new video.
2. **`release()`**: This is a destructive operation.
:::danger
After calling `release()`, the player instance becomes unusable. Any subsequent calls to its methods or property access will result in errors.
:::
:::tip
It is recommended to use `replaceSourceAsync(null)` when you want to free resources related to the current video source. You should call `release()` only when you are 100% sure that you don't need the player instance anymore. Anyway garbage collector will release the player instance when it is no longer needed.
:::
### Error Handling
- The `onError` callback, if provided, will be called when a `VideoRuntimeError` occurs. This allows you to handle issues like network errors, invalid source, or platform-specific playback problems.
- If `onError` is not provided, errors might be thrown as exceptions.
### Using with Hooks (`useVideoPlayer`)
The `useVideoPlayer` hook simplifies managing the `VideoPlayer` lifecycle within React components.
```typescript
import { useVideoPlayer } from 'react-native-video';
const MyComponent = () => {
const player = useVideoPlayer('https://example.com/video.mp4', (playerInstance) => {
// Optional setup function: configure the player instance after creation
playerInstance.loop = true;
});
// ... use player ...
return ;
};
```
- **Automatic Creation**: `useVideoPlayer` creates a `VideoPlayer` instance when the component mounts or when the source dependency changes.
- **Automatic Cleanup**: It automatically cleanup resources when the component unmounts or before recreating the player due to a source change. This prevents resource leaks.
- **Dependency Management**: If the `source` prop passed to `useVideoPlayer` changes, the hook will clean up the old player instance and create a new one with the new source.
:::tip
Using `useVideoPlayer` is the recommended way to manage `VideoPlayer` instances in functional components to ensure proper lifecycle management and resource cleanup. It will also respect `initializeOnCreation` (defaults to `true`). If you need deferred initialization with the hook:
```tsx
const player = useVideoPlayer({
source: { uri: 'https://example.com/video.mp4' },
initializeOnCreation: false,
}, (instance) => {
// Attach listeners first
instance.onLoad = () => console.log('Loaded');
});
// Later (e.g. on user tap)
await player.initialize(); // or player.preload()
player.play();
```
:::
================================================
FILE: docs/docs/plugins/_category_.json
================================================
{
"label": "Plugins",
"position": 4,
"collapsed": false
}
================================================
FILE: docs/docs/plugins/ask-for-plugin.md
================================================
---
title: Ask for Plugin
description: Request a custom plugin for your React Native Video application
sidebar_position: 7
---
# Ask for Plugin
Need a custom plugin for your React Native Video application? We can build it for you.
## What We Build
Our team specializes in creating production-ready plugins for `react-native-video`. Whatever video functionality you need - we can build it.
**Examples of plugins we can build:**
- **Analytics integrations** - Connect with any analytics provider (Mux, Conviva, custom solutions)
- **Live streaming features** - Real-time chat overlays, live reactions, viewer counts
- **Interactive video** - Clickable hotspots, branching narratives, shoppable videos
- **Accessibility enhancements** - Audio descriptions, sign language overlays, custom captions styling
- **AI-powered features** - Auto-generated thumbnails, content moderation, speech-to-text
- **Social features** - Watch parties, synchronized playback, clip sharing
- **Custom UI components** - Branded controls, gesture handlers, thumbnail previews on seek
## Why Choose Us?
- **Deep expertise** - We maintain `react-native-video` and know the codebase inside out
- **Production-ready** - Battle-tested code with proper error handling and edge cases covered
- **Native performance** - Plugins implemented in Kotlin/Swift for optimal performance
- **Ongoing support** - We maintain and update plugins as the library evolves
## Our Existing Plugins
We've already built several production-ready plugins:
| Plugin | Description |
|--------|-------------|
| [Offline Video SDK](https://www.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=docs&utm_campaign=ask-for-plugin&utm_id=offline-video-sdk) | Download and play HLS, MPEG-DASH, MP4 streams offline with DRM support |
| [Background Uploader](https://sdk.thewidlarzgroup.com/background-uploader?utm_source=rnv&utm_medium=docs&utm_campaign=ask-for-plugin&utm_id=background-uploader) | Upload files in the background with retry support and progress tracking |
| [Chapters Plugin](https://sdk.thewidlarzgroup.com/chapters?utm_source=rnv&utm_medium=docs&utm_campaign=ask-for-plugin&utm_id=chapters-plugin) | Chapter markers and navigation on the seekbar |
## Get Started
Ready to discuss your plugin needs?
➡️ [**Request a Plugin**](https://sdk.thewidlarzgroup.com/ask-for-plugin?utm_source=rnv&utm_medium=docs&utm_campaign=ask-for-plugin&utm_id=request-plugin-button)
We'll review your requirements and get back to you with a proposal.
================================================
FILE: docs/docs/plugins/examples.md
================================================
---
title: Plugin Usage Examples
description: Simple examples of implementing common plugin scenarios for React Native Video
sidebar_position: 3
---
# Plugin Usage Examples
This document provides practical examples of implementing common plugin scenarios for React Native Video.
## Basic Plugin Template
```kotlin title="Android"
class MyPlugin : ReactNativeVideoPlugin("MyPlugin") {
override fun onPlayerCreated(player: WeakReference) {
Log.d("MyPlugin", "Player created with uri ${player.get()?.source.uri}")
}
override fun onPlayerDestroyed(player: WeakReference) {
Log.d("MyPlugin", "Player destroyed")
}
override fun overrideSource(source: NativeVideoPlayerSource): NativeVideoPlayerSource {
Log.d("MyPlugin", "Overriding source with uri ${source.uri}")
return source
}
}
// Usage
val plugin = MyPlugin() // Automatically registered
```
```swift title="iOS"
class MyPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "MyPlugin")
}
override func onPlayerCreated(player: Weak) {
// Custom logic when player is created
}
override func onPlayerDestroyed(player: Weak) {
// Custom cleanup when player is destroyed
}
override func overrideSource(source: NativeVideoPlayerSource) async -> NativeVideoPlayerSource {
// Modify source if needed
return source
}
}
// Usage
let plugin = MyPlugin() // Automatically registered
```
## DRM Plugin
Implement custom DRM handling for protected content.
:::warning
DRM plugins are not supported yet in React Native Video. `getDRMManager` is not implemented yet and will have no effect.
:::
```kotlin title="Android"
class CustomDRMPlugin : ReactNativeVideoPlugin("CustomDRM") {
override fun getDRMManager(source: NativeVideoPlayerSource): Any? {
if (source.isDRMProtected() && source.drmType == "custom") {
return CustomDRMManager(
licenseUrl = source.drmLicenseUrl,
certificateUrl = source.drmCertificateUrl,
keyId = source.drmKeyId
)
}
return null
}
}
```
```swift title="iOS"
class CustomDRMPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "CustomDRM")
}
override func getDRMManager(source: NativeVideoPlayerSource) async -> Any? {
guard source.isDRMProtected() && source.drmType == "custom" else {
return nil
}
return CustomDRMManager(
licenseUrl: source.drmLicenseUrl,
certificateUrl: source.drmCertificateUrl,
keyId: source.drmKeyId
)
}
}
```
================================================
FILE: docs/docs/plugins/interface.md
================================================
---
title: Plugin Interface
description: Reference for the React Native Video plugin interface
sidebar_position: 2
---
# Plugin Interface Reference
This document provides a complete reference for the `ReactNativeVideoPluginSpec` interface and the base `ReactNativeVideoPlugin` implementation.
## ReactNativeVideoPluginSpec Interface
### Required Properties
#### id: String
Unique identifier for the plugin. Must be unique across all registered plugins.
```kotlin
// Android
override val id: String = "my_unique_plugin_id"
```
```swift
// iOS
var id: String { "my_unique_plugin_id" }
```
#### name: String
Human-readable name for the plugin, used in debug logging.
```kotlin
// Android
override val name: String = "My Custom Plugin"
```
```swift
// iOS
var name: String { "My Custom Plugin" }
```
## Lifecycle Methods
### Player Lifecycle
#### onPlayerCreated
Called when a new player instance is created.
```kotlin
// Android
@UnstableApi
fun onPlayerCreated(player: WeakReference)
```
```swift
// iOS
func onPlayerCreated(player: Weak)
```
**Parameters:**
- `player`: Weak reference to the newly created player instance
**Use Cases:**
- Initialize player-specific resources
- Set up player event listeners
- Configure player settings
#### onPlayerDestroyed
Called when a player instance is being destroyed.
```kotlin
// Android
@UnstableApi
fun onPlayerDestroyed(player: WeakReference)
```
```swift
// iOS
func onPlayerDestroyed(player: Weak)
```
**Parameters:**
- `player`: Weak reference to the player instance being destroyed
**Use Cases:**
- Clean up player-specific resources
- Remove event listeners
- Save state or analytics data
### Video View Lifecycle
#### onVideoViewCreated
Called when a new video view is created.
```kotlin
// Android
@UnstableApi
fun onVideoViewCreated(view: WeakReference)
```
```swift
// iOS
func onVideoViewCreated(view: Weak)
```
**Parameters:**
- `view`: Weak reference to the newly created video view
**Use Cases:**
- Configure view-specific settings
- Set up UI event handlers
- Initialize view overlays
#### onVideoViewDestroyed
Called when a video view is being destroyed.
```kotlin
// Android
@UnstableApi
fun onVideoViewDestroyed(view: WeakReference)
```
```swift
// iOS
func onVideoViewDestroyed(view: Weak)
```
**Parameters:**
- `view`: Weak reference to the video view being destroyed
**Use Cases:**
- Clean up view-specific resources
- Remove UI event handlers
- Save view state
## Content Modification Methods
### overrideSource
Modify the video source before it's processed by the player.
```kotlin
// Android
fun overrideSource(source: NativeVideoPlayerSource): NativeVideoPlayerSource
```
```swift
// iOS
func overrideSource(source: NativeVideoPlayerSource) async -> NativeVideoPlayerSource
```
**Parameters:**
- `source`: The original video source
**Returns:**
- Modified video source (can be the same instance if no changes needed)
**Use Cases:**
- Add authentication headers
- Modify URLs (e.g., CDN switching)
- Add tracking parameters
- Transform source format
### getDRMManager
Provide a custom DRM manager for protected content.
```kotlin
// Android
fun getDRMManager(source: NativeVideoPlayerSource): Any?
```
```swift
// iOS
func getDRMManager(source: NativeVideoPlayerSource) async -> Any?
```
**Parameters:**
- `source`: The video source that may require DRM
**Returns:**
- DRM manager instance, or `null` if this plugin doesn't handle DRM for this source
**Use Cases:**
- Widevine DRM implementation
- FairPlay DRM implementation
- Custom DRM solutions
- License acquisition logic
## Android-Specific Methods
### getMediaDataSourceFactory
Override the data source factory used by ExoPlayer.
```kotlin
fun getMediaDataSourceFactory(
source: NativeVideoPlayerSource,
mediaDataSourceFactory: DataSource.Factory
): DataSource.Factory?
```
**Parameters:**
- `source`: The video source
- `mediaDataSourceFactory`: The default data source factory
**Returns:**
- Custom data source factory, or `null` to use the default
**Use Cases:**
- Custom caching strategies
- Network optimization
- Custom authentication
- Analytics data collection
### getMediaSourceFactory
Override the media source factory used by ExoPlayer.
```kotlin
fun getMediaSourceFactory(
source: NativeVideoPlayerSource,
mediaSourceFactory: MediaSource.Factory,
mediaDataSourceFactory: DataSource.Factory
): MediaSource.Factory?
```
**Parameters:**
- `source`: The video source
- `mediaSourceFactory`: The default media source factory
- `mediaDataSourceFactory`: The data source factory
**Returns:**
- Custom media source factory, or `null` to use the default
**Use Cases:**
- Custom media format support
- Advanced ExoPlayer configuration
- Source-specific optimizations
### getMediaItemBuilder
Override the media item builder used by ExoPlayer.
```kotlin
fun getMediaItemBuilder(
source: NativeVideoPlayerSource,
mediaItemBuilder: MediaItem.Builder
): MediaItem.Builder?
```
**Parameters:**
- `source`: The video source
- `mediaItemBuilder`: The default media item builder
**Returns:**
- Modified media item builder, or `null` to use the default
**Use Cases:**
- Add custom metadata
- Configure subtitles
- Set playback preferences
- Configure DRM settings
### shouldDisableCache
Control whether caching should be disabled for a source.
```kotlin
fun shouldDisableCache(source: NativeVideoPlayerSource): Boolean
```
**Parameters:**
- `source`: The video source
**Returns:**
- `true` to disable caching, `false` to allow caching
**Use Cases:**
- Disable caching for live streams
- Disable caching for DRM content
- Custom caching policies
## Base Implementation: ReactNativeVideoPlugin
The base class provides default implementations for all methods:
### Automatic Registration
```kotlin
// Android
init {
PluginsRegistry.shared.register(this)
}
```
```swift
// iOS
public init(name: String) {
self.name = name
self.id = "RNV_Plugin_\(name)"
PluginsRegistry.shared.register(plugin: self)
}
```
### Automatic Cleanup (iOS only)
```swift
deinit {
PluginsRegistry.shared.unregister(plugin: self)
}
```
### Default Implementations
All methods have sensible defaults:
- Lifecycle methods: No-op implementations
- `overrideSource`: Returns the original source unchanged
- `getDRMManager`: Returns `null`
- Factory methods (Android): Return `null` (use defaults)
- `shouldDisableCache`: Returns `false`
## Method Calling Order
### Source Processing Flow
1. `overrideSource` - Called for each registered plugin in order
2. `getDRMManager` - Called for each plugin until one returns non-null
3. Factory methods (Android) - Called for each plugin until one returns non-null
### Lifecycle Flow
1. View/Player creation methods called for all plugins
2. Source processing happens during playback
3. View/Player destruction methods called for all plugins
## Error Handling
### DRM Plugin Not Found
If no plugin provides a DRM manager when required:
```kotlin
// Android
throw LibraryError.DRMPluginNotFound
```
```swift
// iOS
throw LibraryError.DRMPluginNotFound.error()
```
### Best Practices
- Return `null` from optional methods when not providing custom behavior
- Handle weak reference nullability properly
- Use appropriate error handling in async methods (iOS)
- Log meaningful debug information
## Platform Differences Summary
| Feature | Android | iOS |
|---------|---------|-----|
| Async Support | No | Yes (async/await) |
| Media Factories | Full ExoPlayer support | Limited AVFoundation |
| Cache Control | Yes | No |
| Auto Cleanup | Manual | Automatic (deinit) |
| Weak References | `WeakReference` | `Weak` |
================================================
FILE: docs/docs/plugins/plugins.mdx
================================================
---
title: Plugins
description: Overview of the React Native Video plugin system
sidebar_position: 6
---
# Plugins Overview
The **React Native Video** library offers a robust plugin system to extend and customize video functionality on Android and iOS. With plugins, you can override source handling, implement custom DRM, modify media factories, and respond to player lifecycle events.
:::tip Use in Third Party Library Guide
If you are looking how to import React Native Video in your native code to use plugins, you can checkout [Use in Third Party Library](./use-in-third-party-library) page.
:::
## What Are Plugins?
Plugins are modular extensions that hook into the video player's lifecycle and processing pipeline. They allow you to:
- **Customize video sources** before playback
- **Implement custom DRM** for protected content
- **Override media factories** (Android only)
- **React to player lifecycle events** for analytics, logging, or cleanup
- **Control caching behavior** for performance
## Architecture
The plugin system consists of three main components:
| Component | Description |
|:-----------------------------|:-------------------------------------------------------------------------------------------|
| `PluginsRegistry` | Singleton that manages plugin registration and coordinates plugin interactions |
| `ReactNativeVideoPluginSpec` | Interface/protocol defining the contract all plugins must implement |
| `ReactNativeVideoPlugin` | Base implementation with convenient defaults; override only the methods you care about |
:::tip
Plugins are automatically registered when you instantiate a class that extends `ReactNativeVideoPlugin`.
:::
## Core Concepts
### Plugin Lifecycle
Plugins receive notifications for key events:
- **Player creation/destruction** - react to player instance lifecycle
- **Video view creation/destruction** - handle UI component lifecycle
- **Source processing** - modify video sources before playback
- **DRM requests** - provide custom DRM handling
- **Media factory overrides** - customize ExoPlayer components (Android)
:::caution
All player and view references passed to plugins are **weak references** to prevent memory leaks.
:::
## Platform Differences
| Feature | Android (Kotlin / ExoPlayer) | iOS (Swift / AVFoundation) |
|:-------------------------------|:-----------------------------------------------|:-----------------------------------|
| Underlying player | ExoPlayer | AVFoundation |
| Media factory customization | Extensive | Limited |
| Cache control | Supported | Limited |
| Plugin method style | Synchronous | Async/await |
| Memory management | Manual cleanup | Manual cleanup |
## Getting Started
### Example: Android Plugin
```kotlin
// Android
class MyPlugin : ReactNativeVideoPlugin("MyPluginName") {
override fun onPlayerCreated(player: WeakReference) {
// Custom logic here
}
}
```
### Example: iOS Plugin
```swift
// iOS
class MyPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "MyPluginName")
}
override func onPlayerCreated(player: Weak) {
// Custom logic here
}
}
```
## Next Steps
- [**Plugin Registry**](./registry) - Learn about plugin management and registration
- [**Plugin Interface**](./interface) - Complete API reference for plugin methods
- [**Usage Examples**](./examples) - Practical implementations and common patterns
- [**Ask for Plugin**](./ask-for-plugin) - Request a custom plugin built by our team
================================================
FILE: docs/docs/plugins/registry.md
================================================
---
title: Plugin Registry
description: Overview of the React Native Video plugin registry and how to use it
sidebar_position: 1
---
# Plugin Registry
The `PluginsRegistry` is a singleton that manages all plugin instances and coordinates their interactions with the video player system. It handles registration, unregistration, and notification distribution to all active plugins.
## Singleton Pattern
Both Android and iOS use a shared singleton:
```kotlin title="Android"
// Register
PluginsRegistry.shared.register(plugin)
// Unregister
PluginsRegistry.shared.unregister(plugin)
```
```swift title="iOS"
// Register
PluginsRegistry.shared.register(plugin: plugin)
// Unregister
PluginsRegistry.shared.unregister(plugin: plugin)
```
:::tip Plugin Ordering
Plugins are processed in registration order. Later plugins can override earlier ones.
:::
## Registration Methods
### Automatic Registration
You can use the base class `ReactNativeVideoPlugin` to automatically register your plugin. This will also mock all the methods that are not implemented in your plugin.
:::danger
You still need to unregister your plugin manually when you are done with it. Otherwise, you will have a memory leak.
:::
```kotlin title="Android"
class MyPlugin : ReactNativeVideoPlugin("MyPlugin") {
// ...
}
val plugin = MyPlugin() // Auto-registered
```
```swift title="iOS"
class MyPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "MyPlugin")
}
// Auto-unregistered in deinit
}
let plugin = MyPlugin() // Auto-registered
```
### Manual Registration
You can also manually register your plugin. This is useful if you want to implement a plugin that is not a subclass of `ReactNativeVideoPlugin`.
You will need to implement the `ReactNativeVideoPluginSpec` interface. This is a protocol that defines the methods and properties that a plugin must implement.
```kotlin title="Android"
class MyCustomPlugin : ReactNativeVideoPluginSpec {
override val id = "my_custom_id"
override val name = "MyCustomPlugin"
// ...
}
val plugin = MyCustomPlugin()
PluginsRegistry.shared.register(plugin)
```
```swift title="iOS"
class MyCustomPlugin: ReactNativeVideoPluginSpec {
let id = "my_custom_id"
let name = "MyCustomPlugin"
// ...
}
let plugin = MyCustomPlugin()
PluginsRegistry.shared.register(plugin: plugin)
```
## Plugin ID Generation
When using the base class, IDs are auto-generated:
```kotlin title="Android"
ID Format: "RNV_Plugin_{name}"
Example: "RNV_Plugin_MyCustomDRM"
```
```swift title="iOS"
ID Format: "RNV_Plugin_{name}"
Example: "RNV_Plugin_MyCustomDRM"
```
## Plugin Internals
Bellow are the internals of the plugin registry, that shows logic for certain methods.
### Source Processing
The registry coordinates source modifications:
```kotlin title="Android"
internal fun overrideSource(source: NativeVideoPlayerSource): NativeVideoPlayerSource {
var overriddenSource = source
for (plugin in plugins.values) {
overriddenSource = plugin.overrideSource(overriddenSource)
}
return overriddenSource
}
```
```swift title="iOS"
internal func overrideSource(source: NativeVideoPlayerSource) async -> NativeVideoPlayerSource {
var overriddenSource = source
for plugin in plugins.values {
overriddenSource = await plugin.overrideSource(source: source)
}
return overriddenSource
}
```
### DRM Manager Resolution
Finds the first plugin that can provide a DRM manager:
```kotlin title="Android"
internal fun getDRMManager(source: NativeVideoPlayerSource): Any {
for (plugin in plugins.values) {
val manager = plugin.getDRMManager(source)
if (manager != null) return manager
}
throw LibraryError.DRMPluginNotFound
}
```
```swift title="iOS"
internal func getDrmManager(source: NativeVideoPlayerSource) async throws -> Any? {
for plugin in plugins.values {
if let drmManager = await plugin.getDRMManager(source: source) {
return drmManager
}
}
throw LibraryError.DRMPluginNotFound.error()
}
```
## Android-Specific Registry Methods
| Method Name | Purpose |
|:----------------------------------|:----------------------------------------------------------|
| `overrideMediaDataSourceFactory` | Override data source factory for custom ExoPlayer sources |
| `overrideMediaSourceFactory` | Override media source factory |
| `overrideMediaItemBuilder` | Customize the media item builder |
| `shouldDisableCache` | Control caching behavior |
Example signatures:
```kotlin
internal fun overrideMediaDataSourceFactory(
source: NativeVideoPlayerSource,
mediaDataSourceFactory: DataSource.Factory
): DataSource.Factory
internal fun overrideMediaSourceFactory(
source: NativeVideoPlayerSource,
mediaSourceFactory: MediaSource.Factory,
mediaDataSourceFactory: DataSource.Factory
): MediaSource.Factory
internal fun overrideMediaItemBuilder(
source: NativeVideoPlayerSource,
mediaItemBuilder: MediaItem.Builder
): MediaItem.Builder
internal fun shouldDisableCache(source: NativeVideoPlayerSource): Boolean
```
## Best Practices
- **Memory management**: Registry holds strong references to plugins; plugins get weak references to players/views.
- **Unregister plugins**: Use unregistration to prevent memory leaks.
- **Performance**: Minimize work in notification handlers. Cache expensive operations. Be mindful of plugin order.
:::tip Need a Custom Plugin?
If you need functionality that the library doesn't currently offer, we can build it for you. Check out [Ask for Plugin](./ask-for-plugin.md) to request a custom plugin built by our team.
:::
================================================
FILE: docs/docs/plugins/use-in-third-party-library.md
================================================
---
title: Use in Third Party Library
description: How to use React Native Video in a third party library
sidebar_position: 4
---
# Use in Third Party Library
You can use React Native Video in your third party library either as a dependency if you want to have specific version of the library or as a peer dependency if you want to version selection to be handled by the consumer of the library.
## In JS
Add `react-native-video` as a dependency or peer dependency.
```json title="package.json"
{
"dependencies": {
"react-native-video": "latest"
}
// OR
"peerDependencies": {
"react-native-video": "*"
}
}
```
And then you can import it in your code.
```ts
import { VideoPlayer } from 'react-native-video';
const player = new VideoPlayer({ uri: 'https://www.example.com/video.mp4' });
player.play();
```
## In Native
### iOS
Add `ReactNativeVideo` as a dependency in your `*.podspec` file.
```ruby title="*.podspec"
Pod::Spec.new do |s|
// ...
s.dependency 'ReactNativeVideo'
end
```
### Android
Add `:react-native-video` and `:react-native-nitro-modules` as a dependency in your `build.gradle` file. Also you will need to add `androidx.media3` dependencies. to use player and source in your library.
```groovy title="build.gradle"
// ...
dependencies {
// ...
implementation project(':react-native-video')
implementation project(':react-native-nitro-modules')
implementation "androidx.media3:media3-common:1.4.1"
implementation "androidx.media3:media3-exoplayer:1.4.1"
}
```
================================================
FILE: docs/docs/projects.md
================================================
---
title: Useful Projects
description: React Native Video Useful Projects
sidebar_class_name: hidden
---
# Useful Projects
This page lists open-source projects that can be helpful for your player implementation.
If you have a project that could benefit other users, feel free to open a PR to add it here.
## Our (TheWidlarzGroup) Libraries
- [react-native-video-player](https://github.com/TheWidlarzGroup/react-native-video-player): Our video player UI library.
- [Offline Video SDK](https://www.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=docs&utm_campaign=projects&utm_id=offline-video-sdk): If you're building an app that needs **offline playback** (e.g., downloading HLS videos, subtitles, audio tracks, or DRM-protected content), check out our commercial Offline Video SDK. It integrates with `react-native-video` and is available with a [free trial](https://sdk.thewidlarzgroup.com/signup?utm_source=rnv&utm_medium=docs&utm_campaign=projects&utm_id=start-trial-offline-video-sdk). To get started quickly, you can clone our [Offline Video Starter Project](https://github.com/TheWidlarzGroup/react-native-offline-video-starter?utm_source=rnv&utm_medium=docs&utm_campaign=projects&utm_id=offline-video-starter), which includes a ready-to-run example app demonstrating offline playback, multi-audio, subtitles, and DRM setup.
## Community Libraries
- [react-native-corner-video](https://github.com/Lg0gs/react-native-corner-video): A floating video player.
- [react-native-track-player](https://github.com/doublesymmetry/react-native-track-player): A toolbox for audio playback.
- [react-native-video-controls](https://github.com/itsnubix/react-native-video-controls): A video player UI.
- [react-native-media-console](https://github.com/criszz77/react-native-media-console): An updated version of react-native-video-controls, rewritten in TypeScript.
================================================
FILE: docs/docs/updating.md
================================================
---
title: Updating
description: React Native Video Updating Guide
sidebar_class_name: hidden
---
## Upgrading from react-native-video v6 to v7
Version 7 of `react-native-video` introduces a significant architectural shift, separating the video player logic from the UI rendering. This change unlocks new capabilities like video preloading and a more intuitive, hook-based API. This guide will walk you through the necessary steps to migrate your application from v6 to v7.
### Key Changes in v7
The most substantial change in v7 is the move from a monolithic `