Skip to main content

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.

MethodDescription
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

IDContext
playerMain page inline player
detailPageDetail page player
overviewPageOverview/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 },
});