diff --git a/example/App.tsx b/example/App.tsx index 7736a14..c0cd788 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import type React from "react"; import { Pressable, ScrollView, StyleSheet, Text, View } from "react-native"; import { @@ -51,6 +51,25 @@ function PlayerExample({ const muted = usePlayerState("muted"); const volume = usePlayerState("volume"); const [logs, setLogs] = useState([]); + const [tracks, setTracks] = useState(() => ({ + videos: [...player.videos], + audios: [...player.audios], + subtitles: [...player.subtitles], + renditions: [...player.rendition], + })); + + const refreshTracks = useCallback(() => { + setTracks({ + videos: [...player.videos], + audios: [...player.audios], + subtitles: [...player.subtitles], + renditions: [...player.rendition], + }); + }, [player]); + + useEffect(() => { + refreshTracks(); + }, [refreshTracks, trackLabel]); const pushLog = useCallback((message: string) => { setLogs((prev) => { @@ -96,6 +115,46 @@ function PlayerExample({ handleNext(); }, [handleNext, pushLog]), ); + useEvent( + "videoTrackChange", + useCallback( + (track) => { + pushLog(`Video track: ${track.label ?? track.id}`); + refreshTracks(); + }, + [pushLog, refreshTracks], + ), + ); + useEvent( + "audioTrackChange", + useCallback( + (track) => { + pushLog(`Audio track: ${track.label ?? track.id}`); + refreshTracks(); + }, + [pushLog, refreshTracks], + ), + ); + useEvent( + "subtitleChange", + useCallback( + (track) => { + pushLog(`Subtitle: ${track?.label ?? track?.id ?? "off"}`); + refreshTracks(); + }, + [pushLog, refreshTracks], + ), + ); + useEvent( + "renditionChange", + useCallback( + (rendition) => { + pushLog(`Rendition: ${rendition.width}x${rendition.height}`); + refreshTracks(); + }, + [pushLog, refreshTracks], + ), + ); const togglePlayback = () => { if (isPlaying) { @@ -121,8 +180,29 @@ function PlayerExample({ player.playbackRate = rates[nextIndex]; }; + const selectVideo = (video: (typeof tracks.videos)[number]) => { + player.selectVideo(video); + refreshTracks(); + }; + + const selectAudio = (audio: (typeof tracks.audios)[number]) => { + player.selectAudio(audio); + refreshTracks(); + }; + + const selectSubtitle = (subtitle?: (typeof tracks.subtitles)[number]) => { + player.selectSubtitle(subtitle); + refreshTracks(); + }; + + const selectRendition = (rendition?: (typeof tracks.renditions)[number]) => { + player.selectRendition(rendition); + refreshTracks(); + }; + + return ( - + react-native-omni {trackLabel} @@ -174,6 +254,153 @@ function PlayerExample({ + + Tracks & Renditions + Video + + {tracks.videos.length === 0 ? ( + No video tracks + ) : ( + tracks.videos.map((video) => ( + selectVideo(video)} + > + + {video.label ?? video.language ?? video.id} + + + )) + )} + + + Audio + + {tracks.audios.length === 0 ? ( + No audio tracks + ) : ( + tracks.audios.map((audio) => ( + selectAudio(audio)} + > + + {audio.label ?? audio.language ?? audio.id} + + + )) + )} + + + Subtitles + + subtitle.selected) && + styles.selectedTrackButton, + ]} + onPress={() => selectSubtitle(undefined)} + > + subtitle.selected) && + styles.selectedTrackButtonText, + ]} + > + Off + + + {tracks.subtitles.length === 0 ? ( + No subtitles + ) : ( + tracks.subtitles.map((subtitle) => ( + selectSubtitle(subtitle)} + > + + {subtitle.label ?? subtitle.language ?? subtitle.id} + + + )) + )} + + + Renditions + + rendition.selected) && + styles.selectedTrackButton, + ]} + onPress={() => selectRendition(undefined)} + > + rendition.selected) && + styles.selectedTrackButtonText, + ]} + > + Auto + + + {tracks.renditions.length === 0 ? ( + No renditions + ) : ( + tracks.renditions.map((rendition) => ( + selectRendition(rendition)} + > + + {rendition.width}x{rendition.height} ({Math.round(rendition.bitrate / 1000)} kbps) + + + )) + )} + + + - + ); } @@ -284,6 +511,51 @@ const styles = StyleSheet.create({ color: "#cfd9ff", fontSize: 13, }, + selectorCard: { + backgroundColor: "#101833", + borderRadius: 10, + padding: 12, + gap: 8, + }, + selectorHeading: { + color: "#e8ecff", + fontSize: 14, + fontWeight: "700", + }, + selectorTitle: { + color: "#cfd9ff", + fontSize: 12, + fontWeight: "600", + }, + selectorRow: { + flexDirection: "row", + flexWrap: "wrap", + gap: 8, + }, + trackButton: { + backgroundColor: "#1a2442", + paddingVertical: 8, + paddingHorizontal: 10, + borderRadius: 8, + borderWidth: 1, + borderColor: "#2d3f74", + }, + selectedTrackButton: { + backgroundColor: "#2f4fa0", + borderColor: "#6e93f9", + }, + trackButtonText: { + color: "#cfd9ff", + fontSize: 12, + }, + selectedTrackButtonText: { + color: "#f4f7ff", + fontWeight: "700", + }, + emptyTrackText: { + color: "#8fa1d8", + fontSize: 12, + }, logCard: { flex: 1, backgroundColor: "#101833",