Player Control
Control video playback programmatically — play, pause, seek, adjust volume, and manage multiple players.
Basic Control
Use the channel ref to control the active player:
const channelRef = useRef<BBChannelRef>(null);
// Play / Pause
channelRef.current?.play();
channelRef.current?.pause();
// Seek to 30 seconds
channelRef.current?.seek(30);
// Volume (0.0 to 1.0)
channelRef.current?.setVolume(0.5);
// Mute / Unmute
channelRef.current?.setMuted(true);
channelRef.current?.setMuted(false);
Available Methods
All methods accept an optional playerId parameter. Without it, they target the active player.
| Method | Description |
|---|---|
play(playerId?) | Start or resume playback |
pause(playerId?) | Pause playback |
seek(time, playerId?) | Seek to position in seconds |
setVolume(level, playerId?) | Set volume (0.0 – 1.0) |
setMuted(muted, playerId?) | Mute or unmute |
getPlayerState(playerId?) | Get current player state (async) |
getPlayers() | List all registered player IDs (async) |
setActivePlayer(playerId) | Change the default target player |
Multi-Player
Channels can have multiple players active simultaneously — for example, an inline player on the main page and another on a detail page.
Discovering Players
// Get all registered player IDs
const players = await channelRef.current?.getPlayers();
// e.g., ['player', 'detailPage']
Common Player IDs
| ID | Context |
|---|---|
player | Main page inline player |
detailPage | Detail page player |
overviewPage | Overview/playlist page player |
Targeting Specific Players
// Pause the main player while detail page is open
channelRef.current?.pause('player');
// Play only the detail page player
channelRef.current?.play('detailPage');
// Get state of a specific player
const state = await channelRef.current?.getPlayerState('detailPage');
Changing the Active Player
The active player is the default target for all control methods. It changes automatically as the user navigates, but you can set it manually:
channelRef.current?.setActivePlayer('detailPage');
// Now play() targets 'detailPage' by default
channelRef.current?.play();
Player Events
Listen for player lifecycle and state changes:
<BBChannel
ref={channelRef}
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
onPlayersChange={(playerIds) => {
// Fired when players are added or removed
console.log('Active players:', playerIds);
}}
onActivePlayerChange={(playerId) => {
// Fired when the active player changes
console.log('Active player:', playerId);
}}
onPlayerStateChange={(state) => {
// Fired on playback state updates
console.log('Player state:', state);
}}
/>
Player State
The BBPlayerState interface:
interface BBPlayerState {
playerId: string;
isPlaying: boolean;
currentTime: number; // Seconds
duration: number; // Seconds
volume: number; // 0.0 – 1.0
isMuted: boolean;
isFullscreen: boolean;
}
Query it on demand:
const state = await channelRef.current?.getPlayerState();
if (state) {
console.log(`${state.currentTime}s / ${state.duration}s`);
console.log(`Playing: ${state.isPlaying}, Volume: ${state.volume}`);
}
Example: Multi-Player Dashboard
A complete example showing how to build a control overlay for all active players:
import React, { useRef, useState, useCallback } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, FlatList } from 'react-native';
import {
BBChannel,
BBChannelRef,
BBPlayerState,
} from '@bluebillywig/react-native-channel';
function PlayerDashboard() {
const channelRef = useRef<BBChannelRef>(null);
const [players, setPlayers] = useState<string[]>([]);
const [activePlayer, setActivePlayer] = useState<string | null>(null);
const [playerStates, setPlayerStates] = useState<Record<string, BBPlayerState>>({});
const refreshState = useCallback(async (playerId: string) => {
const state = await channelRef.current?.getPlayerState(playerId);
if (state) {
setPlayerStates((prev) => ({ ...prev, [playerId]: state }));
}
}, []);
const refreshAll = useCallback(async () => {
const ids = await channelRef.current?.getPlayers();
if (ids) {
for (const id of ids) {
await refreshState(id);
}
}
}, [refreshState]);
const formatTime = (seconds: number) => {
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m}:${s.toString().padStart(2, '0')}`;
};
return (
<View style={styles.container}>
<BBChannel
ref={channelRef}
channelUrl="https://demo.bbvms.com/ch/channel_name.json"
onPlayersChange={(ids) => {
setPlayers(ids);
ids.forEach(refreshState);
}}
onActivePlayerChange={(id) => setActivePlayer(id)}
onPlayerStateChange={(state) => {
setPlayerStates((prev) => ({ ...prev, [state.playerId]: state }));
}}
style={styles.channel}
/>
{/* Control overlay */}
<View style={styles.controls}>
<Text style={styles.title}>Players ({players.length})</Text>
{players.map((id) => {
const state = playerStates[id];
const isActive = id === activePlayer;
return (
<View
key={id}
style={[styles.playerRow, isActive && styles.activeRow]}
>
<Text style={styles.playerId}>
{id} {isActive ? '(active)' : ''}
</Text>
{state && (
<Text style={styles.time}>
{formatTime(state.currentTime)} / {formatTime(state.duration)}
</Text>
)}
<View style={styles.buttons}>
<TouchableOpacity
onPress={() => channelRef.current?.play(id)}
style={styles.btn}
>
<Text style={styles.btnText}>Play</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => channelRef.current?.pause(id)}
style={styles.btn}
>
<Text style={styles.btnText}>Pause</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => channelRef.current?.setMuted(
!state?.isMuted, id
)}
style={styles.btn}
>
<Text style={styles.btnText}>
{state?.isMuted ? 'Unmute' : 'Mute'}
</Text>
</TouchableOpacity>
</View>
</View>
);
})}
<TouchableOpacity onPress={refreshAll} style={styles.refreshBtn}>
<Text style={styles.btnText}>Refresh All</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
channel: { flex: 1 },
controls: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0,0,0,0.85)',
padding: 16,
},
title: { color: '#fff', fontSize: 16, fontWeight: 'bold', marginBottom: 8 },
playerRow: {
flexDirection: 'column',
paddingVertical: 8,
borderBottomWidth: 1,
borderBottomColor: '#333',
},
activeRow: { borderLeftWidth: 3, borderLeftColor: '#0af', paddingLeft: 8 },
playerId: { color: '#fff', fontWeight: 'bold' },
time: { color: '#aaa', fontSize: 12, marginVertical: 4 },
buttons: { flexDirection: 'row', gap: 8, marginTop: 4 },
btn: {
backgroundColor: '#333',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 4,
},
refreshBtn: {
backgroundColor: '#0af',
paddingHorizontal: 12,
paddingVertical: 8,
borderRadius: 4,
marginTop: 12,
alignItems: 'center',
},
btnText: { color: '#fff', fontSize: 13 },
});