A comprehensive React Native video player component with streaming, DRM, and Picture-in-Picture support
—
Comprehensive event system covering playback events, state changes, track information, buffering, error handling, and platform-specific events for complete video playback monitoring and control.
Main interface defining all available event handlers for the Video component.
/**
* Complete event handling interface for video playback monitoring
* All events are optional and provide detailed information about video state
*/
interface ReactVideoEvents {
// Core Playback Events
onLoad?: (e: OnLoadData) => void;
onLoadStart?: (e: OnLoadStartData) => void;
onProgress?: (e: OnProgressData) => void;
onEnd?: () => void;
onError?: (e: OnVideoErrorData) => void;
onSeek?: (e: OnSeekData) => void;
// State Change Events
onPlaybackStateChanged?: (e: OnPlaybackStateChangedData) => void;
onPlaybackRateChange?: (e: OnPlaybackRateChangeData) => void;
onVolumeChange?: (e: OnVolumeChangeData) => void;
onBuffer?: (e: OnBufferData) => void;
onReadyForDisplay?: () => void;
// Track Events
onAudioTracks?: (e: OnAudioTracksData) => void;
onTextTracks?: (e: OnTextTracksData) => void;
onVideoTracks?: (e: OnVideoTracksData) => void;
onTextTrackDataChanged?: (e: OnTextTrackDataChangedData) => void;
onAspectRatio?: (e: OnVideoAspectRatioData) => void;
// UI & Interaction Events
onFullscreenPlayerWillPresent?: () => void;
onFullscreenPlayerDidPresent?: () => void;
onFullscreenPlayerWillDismiss?: () => void;
onFullscreenPlayerDidDismiss?: () => void;
onControlsVisibilityChange?: (e: OnControlsVisibilityChange) => void;
// Picture-in-Picture Events
onPictureInPictureStatusChanged?: (e: OnPictureInPictureStatusChangedData) => void;
onRestoreUserInterfaceForPictureInPictureStop?: () => void;
// Platform-Specific Events
onAudioBecomingNoisy?: () => void;
onAudioFocusChanged?: (e: OnAudioFocusChangedData) => void;
onIdle?: () => void;
onExternalPlaybackChange?: (e: OnExternalPlaybackChangeData) => void;
onBandwidthUpdate?: (e: OnBandwidthUpdateData) => void;
onTimedMetadata?: (e: OnTimedMetadataData) => void;
onReceiveAdEvent?: (e: OnReceiveAdEventData) => void;
}Usage Examples:
<Video
source={{ uri: "https://example.com/video.mp4" }}
onLoad={(data) => {
console.log("Video loaded:", {
duration: data.duration,
naturalSize: data.naturalSize,
audioTracks: data.audioTracks.length,
textTracks: data.textTracks.length
});
}}
onProgress={(data) => {
console.log(`Progress: ${data.currentTime}/${data.seekableDuration}`);
}}
onError={(error) => {
console.error("Video error:", error.error.errorString);
}}
onEnd={() => {
console.log("Video playback completed");
}}
/>Events related to video loading, progress, and basic playback state.
/**
* Fired when video metadata and tracks are loaded
*/
interface OnLoadData {
currentTime: number;
duration: number;
naturalSize: {
width: number;
height: number;
orientation: "landscape" | "portrait";
};
audioTracks: AudioTrack[];
textTracks: TextTrack[];
videoTracks: VideoTrack[];
}
/**
* Fired when video loading begins
*/
interface OnLoadStartData {
isNetwork: boolean;
type: string;
uri: string;
}
/**
* Fired periodically during playback with current progress
*/
interface OnProgressData {
currentTime: number;
playableDuration: number;
seekableDuration: number;
}
/**
* Fired when seek operation completes
*/
interface OnSeekData {
currentTime: number;
seekTime: number;
}
/**
* Fired when video playback error occurs
*/
interface OnVideoErrorData {
error: {
errorString: string;
errorException: string;
errorStackTrace: string;
errorCode: string;
domain?: string;
};
}Usage Examples:
<Video
source={{ uri: "https://example.com/video.mp4" }}
onLoadStart={(data) => {
console.log("Loading started:", data.uri, "Network:", data.isNetwork);
}}
onLoad={(data) => {
console.log("Video loaded:", {
duration: Math.round(data.duration),
resolution: `${data.naturalSize.width}x${data.naturalSize.height}`,
orientation: data.naturalSize.orientation,
tracks: {
audio: data.audioTracks.length,
text: data.textTracks.length,
video: data.videoTracks.length
}
});
}}
onProgress={(data) => {
const progress = (data.currentTime / data.seekableDuration) * 100;
console.log(`Progress: ${progress.toFixed(1)}%`);
}}
onSeek={(data) => {
console.log(`Seek completed: ${data.currentTime}s (target: ${data.seekTime}s)`);
}}
onError={(error) => {
console.error("Playback error:", {
code: error.error.errorCode,
message: error.error.errorString,
domain: error.error.domain
});
}}
/>Events for monitoring playback state, rate, and volume changes.
/**
* Fired when playback state changes (play/pause/buffering/idle)
*/
interface OnPlaybackStateChangedData {
isPlaying: boolean;
}
/**
* Fired when playback rate changes
*/
interface OnPlaybackRateChangeData {
playbackRate: number;
}
/**
* Fired when volume changes
*/
interface OnVolumeChangeData {
volume: number;
}
/**
* Fired during buffering events
*/
interface OnBufferData {
isBuffering: boolean;
}Usage Examples:
<Video
source={{ uri: "https://example.com/video.mp4" }}
onPlaybackStateChanged={(data) => {
console.log("Playback state:", data.isPlaying ? "Playing" : "Paused");
}}
onPlaybackRateChange={(data) => {
console.log("Playback rate changed to:", data.playbackRate);
}}
onVolumeChange={(data) => {
console.log("Volume changed to:", Math.round(data.volume * 100) + "%");
}}
onBuffer={(data) => {
console.log("Buffering:", data.isBuffering ? "Started" : "Finished");
}}
onReadyForDisplay={() => {
console.log("Video is ready for display");
}}
/>Events providing information about available and selected tracks.
/**
* Audio track information
*/
interface AudioTrack {
index: number;
title?: string;
language?: string;
bitrate?: number;
type?: string;
selected?: boolean;
}
/**
* Text track information
*/
interface TextTrack {
index: number;
title?: string;
language?: string;
type?: "srt" | "ttml" | "vtt";
selected?: boolean;
}
/**
* Video track information
*/
interface VideoTrack {
index: number;
tracksID?: string;
codecs?: string;
width?: number;
height?: number;
bitrate?: number;
selected?: boolean;
}
/**
* Fired when audio tracks are available
*/
interface OnAudioTracksData {
audioTracks: AudioTrack[];
}
/**
* Fired when text tracks are available
*/
interface OnTextTracksData {
textTracks: TextTrack[];
}
/**
* Fired when video tracks are available
*/
interface OnVideoTracksData {
videoTracks: VideoTrack[];
}
/**
* Fired when text track data changes (iOS)
*/
interface OnTextTrackDataChangedData {
subtitleTracks: TextTrack[];
}
/**
* Fired when video aspect ratio changes
*/
interface OnVideoAspectRatioData {
aspectRatio: number;
}Usage Examples:
<Video
source={{ uri: "https://example.com/video.mp4" }}
onAudioTracks={(data) => {
console.log("Audio tracks available:");
data.audioTracks.forEach(track => {
console.log(`- ${track.title || 'Track ' + track.index}: ${track.language} (${track.bitrate}bps)`);
});
}}
onTextTracks={(data) => {
console.log("Text tracks available:");
data.textTracks.forEach(track => {
console.log(`- ${track.title || 'Track ' + track.index}: ${track.language} (${track.type})`);
});
}}
onVideoTracks={(data) => {
console.log("Video tracks available:");
data.videoTracks.forEach(track => {
console.log(`- Track ${track.index}: ${track.width}x${track.height} (${track.bitrate}bps)`);
});
}}
onAspectRatio={(data) => {
console.log("Aspect ratio changed to:", data.aspectRatio);
}}
/>Events related to user interface changes and fullscreen transitions.
/**
* Fired when controls visibility changes
*/
interface OnControlsVisibilityChange {
isVisible: boolean;
}Usage Examples:
<Video
source={{ uri: "https://example.com/video.mp4" }}
controls={true}
onFullscreenPlayerWillPresent={() => {
console.log("Fullscreen presentation will start");
}}
onFullscreenPlayerDidPresent={() => {
console.log("Fullscreen presentation completed");
}}
onFullscreenPlayerWillDismiss={() => {
console.log("Fullscreen dismissal will start");
}}
onFullscreenPlayerDidDismiss={() => {
console.log("Fullscreen dismissal completed");
}}
onControlsVisibilityChange={(data) => {
console.log("Controls are now:", data.isVisible ? "visible" : "hidden");
}}
/>Events for Picture-in-Picture mode changes and state restoration.
/**
* Fired when Picture-in-Picture status changes
*/
interface OnPictureInPictureStatusChangedData {
isActive: boolean;
}Usage Examples:
<Video
source={{ uri: "https://example.com/video.mp4" }}
onPictureInPictureStatusChanged={(data) => {
console.log("Picture-in-Picture:", data.isActive ? "Active" : "Inactive");
}}
onRestoreUserInterfaceForPictureInPictureStop={() => {
console.log("Restore UI for PiP stop requested");
// Restore your app's UI here
}}
/>Events that are specific to certain platforms or provide platform-specific information.
/**
* Audio focus change event (Android)
*/
interface OnAudioFocusChangedData {
hasAudioFocus: boolean;
}
/**
* External playback change event (iOS)
*/
interface OnExternalPlaybackChangeData {
isExternalPlaybackActive: boolean;
}
/**
* Bandwidth update event (Android)
*/
interface OnBandwidthUpdateData {
bitrate: number;
}
/**
* Timed metadata event (Android, iOS)
*/
interface OnTimedMetadataData {
metadata: {
identifier: string;
value: string;
}[];
}Usage Examples:
<Video
source={{ uri: "https://example.com/video.mp4" }}
// Android events
onAudioFocusChanged={(data) => {
console.log("Audio focus:", data.hasAudioFocus ? "gained" : "lost");
}}
onBandwidthUpdate={(data) => {
console.log("Current bitrate:", Math.round(data.bitrate / 1000) + " kbps");
}}
onIdle={() => {
console.log("Player entered idle state");
}}
// iOS events
onExternalPlaybackChange={(data) => {
console.log("AirPlay:", data.isExternalPlaybackActive ? "active" : "inactive");
}}
// Cross-platform events
onAudioBecomingNoisy={() => {
console.log("Audio becoming noisy (headphones unplugged?)");
// Pause playback
}}
onTimedMetadata={(data) => {
console.log("Timed metadata received:");
data.metadata.forEach(item => {
console.log(`- ${item.identifier}: ${item.value}`);
});
}}
/>Events related to advertisement playback and interaction.
/**
* Advertisement event data
*/
interface OnReceiveAdEventData {
event: AdEvent;
data?: object;
}Usage Examples:
<Video
source={{
uri: "https://example.com/video.mp4",
ad: {
adTagUrl: "https://pubads.g.doubleclick.net/gampad/ads?...",
adLanguage: "en"
}
}}
onReceiveAdEvent={(data) => {
console.log("Ad event:", data.event);
switch (data.event) {
case "LOADED":
console.log("Ad loaded and ready to play");
break;
case "STARTED":
console.log("Ad playback started");
break;
case "COMPLETED":
console.log("Ad playback completed");
break;
case "ERROR":
console.error("Ad error:", data.data);
break;
case "CLICK":
console.log("Ad was clicked");
break;
}
}}
/>import React, { useState } from "react";
import Video from "react-native-video";
function VideoPlayerWithProgress() {
const [progress, setProgress] = useState({ currentTime: 0, duration: 0 });
const [isBuffering, setIsBuffering] = useState(false);
return (
<Video
source={{ uri: "https://example.com/video.mp4" }}
onLoad={(data) => {
setProgress(prev => ({ ...prev, duration: data.duration }));
}}
onProgress={(data) => {
setProgress(prev => ({ ...prev, currentTime: data.currentTime }));
}}
onBuffer={(data) => {
setIsBuffering(data.isBuffering);
}}
/>
);
}function VideoPlayerWithErrorHandling() {
const [error, setError] = useState<string | null>(null);
const [retryCount, setRetryCount] = useState(0);
const handleError = (errorData: OnVideoErrorData) => {
console.error("Video error:", errorData.error);
setError(errorData.error.errorString);
// Implement retry logic
if (retryCount < 3) {
setTimeout(() => {
setRetryCount(prev => prev + 1);
setError(null);
}, 2000);
}
};
return (
<Video
source={{ uri: "https://example.com/video.mp4" }}
onError={handleError}
onLoad={() => {
setError(null);
setRetryCount(0);
}}
/>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-native-video