Skip to content

ImRoodyDev/react-native-cross-player

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Grabit Engine

React Native Cross Player

npm license


React Native / Web video player wrapper with HLS, subtitles and proxy support.

Installation

Install the package:

npm install react-native-cross-player
# or
yarn add react-native-cross-player

Install the required peer dependencies (required by the library at runtime):

npm install --save nativewind react react-native react-native-awesome-slider react-native-blob-util react-native-gesture-handler react-native-orientation-locker react-native-reanimated react-native-safe-area-context react-native-svg react-native-system-navigation-bar react-native-video react-native-worklets tailwindcss
# or with yarn
yarn add nativewind react react-native react-native-awesome-slider react-native-blob-util react-native-gesture-handler react-native-orientation-locker react-native-reanimated react-native-safe-area-context react-native-svg react-native-system-navigation-bar react-native-video react-native-worklets tailwindcss

Notes:

  • You do not need to install these in a library project if your app already provides them; they are peers and must be available in the consuming app.

NativeWind Setup (Required)

This library uses NativeWind v4 for styling. You must set up NativeWind in your consuming application.

1. Import Component Styles

For Web: Import the CSS file in your global CSS or directly in your app:

/* Option 1: In your global.css or app.css */
@import "react-native-cross-player/styles.css";

Or import directly in your entry file (e.g., with Metro Web / Expo Web):

// In your App.tsx or _layout.tsx
import "react-native-cross-player/styles.css";

For Native (iOS/Android): The styles are applied via NativeWind's className processing. Make sure your babel.config.js includes:

module.exports = function (api) {
	api.cache(true);
	return {
		presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'],
		plugins: ['@babel/plugin-proposal-export-namespace-from', 'react-native-reanimated/plugin'],
	};
};

Your Expo Metro config should pass the same global CSS entry to NativeWind:

const { getDefaultConfig } = require('expo/metro-config');
const { withNativeWind } = require('nativewind/metro');

const config = getDefaultConfig(__dirname);

module.exports = withNativeWind(config, { input: './global.css' });

Usage

Basic usage in a React Native app:

import React from "react";
import { VideoPlayer } from "react-native-cross-player";

export default function App() {
	const playerId = "demo-player";
	const playerConfig = {
		playerId,
		videoSources: [
			{
				id: "main",
				playerId,
				label: "Main stream",
				source: "https://example.com/video.m3u8",
				format: "m3u8"
			}
		],
		subtitleSources: [],
		initialVideoSource: 0
	};

	return (
		<VideoPlayer
			videoTitle="Demo"
			playerConfig={playerConfig}
			viewStyle={{ flex: 1 }}
			theme={{
				minimumTrackTintColor: "#0ea5e9",
				maximumTrackTintColor: "#3f3f46",
				cacheTrackTintColor: "#71717a",
				bubbleBackgroundColor: "#0ea5e9"
			}}
		/>
	);
}

You can also import the controls separately:

import { VideoPlayer, PlayerControls } from "react-native-cross-player";

Build your own video player

VideoPlayer is the ready-made player shell, but the package also exports the lower-level pieces used to build it. Use usePlayerController when you want your own layout, your own buttons, or a custom TV/mobile/web player surface while still reusing the package playback logic for HLS, subtitles, source switching, proxy handling, fullscreen, audio tracks, and progress state.

The custom-player flow is:

  • Render your own react-native-video element.
  • Pass videoRef, controlsRef, and playerViewRef into usePlayerController.
  • Spread controller.nativeVideoProps onto your video element.
  • Keep native video controls disabled and drive playback through controller.controls.
  • Render your own UI, or reuse the exported PlayerControls overlay.
import React from "react";
import { Pressable, Text, View } from "react-native";
import Video from "react-native-video";
import { PlayerControls, usePlayerController } from "react-native-cross-player";

const playerId = "custom-player";

export function CustomPlayer() {
	const videoRef = React.useRef(null);
	const controlsRef = React.useRef(null);
	const playerViewRef = React.useRef(null);

	const controller = usePlayerController({
		playerId,
		videoRef,
		controlsRef,
		playerViewRef,
		videoSources: [
			{
				id: "main",
				playerId,
				label: "Main stream",
				source: "https://tears-of-steel-subtitles.s3.amazonaws.com/tos.mp4",
				format: "mp4"
			}
		],
		subtitleSources: [],
		initialVideoSource: 0
	});

	return (
		<View ref={playerViewRef} style={{ flex: 1, backgroundColor: "black" }}>
			<Video
				ref={videoRef}
				{...controller.nativeVideoProps}
				controls={false}
				paused={controller.playerState.paused}
				resizeMode="contain"
				style={{ flex: 1 }}
			/>

			<Pressable onPress={() => controller.controls.setPause(!controller.playerState.paused)}>
				<Text>{controller.playerState.paused ? "Play" : "Pause"}</Text>
			</Pressable>

			<PlayerControls
				ref={controlsRef}
				videoTitle="Custom player"
				controls={controller.controls}
				resources={controller.playbackResources}
				playerState={controller.playerState}
			/>
		</View>
	);
}

Public exports

The package entry exports more than the ready-made components:

  • UI: VideoPlayer, PlayerControls, VideoPlayerRef, PlayerControlsRef, ControlsProps.
  • Controller hooks: usePlayerController, PlayerControllerProps, useWebKeyboard.
  • Media helpers: createM3U8Source, createMasterM3U8Raw, createVTTSource, convertSRTtoVTT, createM3U8File, createVTTFile, clearBlobFiles, clearBlobGroup, revokeAllBlobURLs.
  • HLS/proxy helpers: HlsProxy, HlsProxyManager, ProxyLoader, ProxyPlaylistLoader, ProxyFragmentLoader, HlsProxyConfig, ProxyURLResolverCallback.
  • Types: VideoSource, SubtitleSource, SourceRequestOptions, M3U8BlobOptions, M3U8PlaylistTrack, M38USubtitleTrack, M3U8AudioTrack, SubtitleBlobOptions, SourceTypes, SubtitleTypes, TextEncoding, VideoFormats.
  • Utilities: CustomPlayerError, isCustomPlayerError, detectSourceType, detectSubtitleType, detectSubtitleEncoding, CNPLogger, ProxyLogger, CSS_PATH.

API & exports

For the complete, structured API reference (component props, hook options, controllers and types) see API.md.

Why the API is exposed

This library exports both higher-level UI components (VideoPlayer, PlayerControls) and lower-level hooks/controllers (for example usePlayerController) so you can either use the ready-made player UI or build your own custom player interface. Exposing the controller makes it straightforward to wire the playback logic into a custom layout or use different control components while reusing the underlying HLS/subtitle/proxy logic.

usePlayerController is exported from the package entry so consumers can import it directly:

import { usePlayerController } from "react-native-cross-player";

VideoPlayer props (detailed)

VideoPlayer is a small wrapper that wires usePlayerController and PlayerControls together. Important props:

Prop Type Default Required Description
videoTitle string Yes Title displayed in the controls header
language Languages en No Localization setting
playerConfig Omit<PlayerControllerProps, "playerViewRef" | "videoRef" | "controlsRef"> {} Yes Full runtime configuration for usePlayerController. See key fields below.
viewStyle StyleProp<ViewStyle> No Style for the outer container view
videoStyle StyleProp<ViewStyle> No Style applied to the native video element
theme SliderThemeType No Optional slider theme forwarded to the built-in progress bar from react-native-awesome-slider
onControlVisibilityChange (visible: boolean) => void No Called whenever the built-in controls become visible or hidden
onSourceChange (index: number, source: VideoSource) => void No Called when the active video source changes, including initial source selection and imperative/UI-driven switches
onSubtitleChange (index: number, subtitle: SubtitleSource) => void No Called when a subtitle track becomes active. It is not fired when subtitles are turned off.
onPlaybackChange (isPlaying: boolean) => void No Called when playback toggles between playing and paused after the player has mounted.
onProgress (currentTime: number) => void No Called on each native progress update with the current playback time in seconds.
onEnd () => void No Called when playback reaches the end of the active media.

theme lets you override the colors used by the built-in seek slider without replacing the controls UI.

import { VideoPlayer } from "react-native-cross-player";
import type { SliderThemeType } from "react-native-awesome-slider";

const sliderTheme: SliderThemeType = {
	minimumTrackTintColor: "#0ea5e9",
	maximumTrackTintColor: "#3f3f46",
	cacheTrackTintColor: "#71717a",
	bubbleBackgroundColor: "#0ea5e9"
};

<VideoPlayer videoTitle="Demo" playerConfig={playerConfig} theme={sliderTheme} />;

You can observe source and subtitle switches from the built-in UI or imperative ref calls:

<VideoPlayer
	videoTitle="Demo"
	playerConfig={playerConfig}
	onSourceChange={(index, source) => {
		console.log("Active source", index, source.label);
	}}
	onSubtitleChange={(index, subtitle) => {
		console.log("Active subtitle", index, subtitle.label ?? subtitle.langISO);
	}}
	onPlaybackChange={(isPlaying) => {
		console.log("Playback changed", isPlaying ? "playing" : "paused");
	}}
	onProgress={(currentTime) => {
		console.log("Current time", currentTime);
	}}
	onEnd={() => {
		console.log("Playback finished");
	}}
/>

VideoPlayer ref methods

VideoPlayer also exposes an imperative ref API for playback and track/source switching.

type VideoPlayerRef = {
	setState: (state: State) => void;
	setSubtitle: (index: number) => Promise<void>;
	setVideoSource: (index: number) => Promise<void>;
	seek: (time: number) => void;
	play: () => void;
	pause: () => void;
	getCurrentTime: () => Promise<number>;
	getCurrentVideoIndex: () => number;
	getCurrentSubtitleIndex: () => number;
};

setSubtitle(index) selects a subtitle track by index, setVideoSource(index) switches the active video source by index, and the getter methods return the currently selected source and subtitle indexes.

Example usage (concise playerConfig):

const playerConfig = {
	playerId: 'demo-player',
	videoSources: [{
		id: 'main',
		playerId: 'demo-player',
		label: 'Main stream',
		source: 'https://example.com/stream.m3u8',
		format: 'm3u8'
	}],
	subtitleSources: [],
	initialVideoSource: -1,
	autoStart: false,
	proxyURL: 'https://proxy.example.com'
};

<VideoPlayer playerConfig={playerConfig} />

Key playerConfig fields (examples):

Field Type Default Description
videoSources VideoSource[] [] List of sources to present in the sources menu
subtitleSources SubtitleSource[] [] List of subtitle tracks to offer
initialVideoSource number -1 Index to auto-select a video source on mount
initialSubtitleSource number -1 Index to auto-select a subtitle
initialAudioTrack number -1 Index to auto-select an audio track (applied after load)
autoStart boolean false Start playback automatically after load
startPosition number 0 Seek position (seconds) applied on initial load
proxyURL string Proxy tunnel URL used for playlist and fragment requests
lazyLoadSources boolean true Defer creation of blob/playlist URLs until first use
preservePlaybackOnSourceChange boolean true Preserve current time when switching sources
maxResolutionHeight number Prefer/filter quality levels with height <= this value. Useful to cap resolution for bandwidth or device constraints.

For the full API (controller methods, control props, types and helper exports) see API.md.

Contributing

Contributions are welcome. Open an issue or submit a PR. Follow the code style in the repo and add tests if you introduce behavior changes.

License

This project is licensed under the MIT License. See the full license text in LICENSE.


About

React Native Video Player Wrapper with support for HLS, Subtitles and Proxy across Web, Android, iOS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors