Troubleshooting
Solutions to common issues when implementing NativeFrame
This guide covers common issues you might encounter when implementing NativeFrame and provides solutions to resolve them quickly.
Camera and Microphone Issues
Problem: The preview player shows a black screen or no audio is captured.
Common causes and solutions:
-
Permissions not granted
- Check browser permissions in the address bar or browser settings
- The browser should prompt for camera/microphone access on first use
- On mobile, check device Settings → Safari/Chrome → Camera/Microphone permissions
-
Camera/mic in use by another application
- Close other applications that might be using the camera (Zoom, Skype, etc.)
- Refresh the browser page to release any stale media streams
-
Media stream not initialized
// React: Ensure usePreviewPlayer has completed initialization
const { mediaStreamController, previewPlayer, isLoading } = usePreviewPlayer();
if (isLoading) {
return <div>Loading camera...</div>;
}
// Vanilla JS: Wait for requestEncoder to complete
const { mediaStreamController, previewPlayer } = await requestEncoder(); -
Video element not properly attached
// React: Wrap components with PlayerAPIProvider
<PlayerAPIProvider player={previewPlayer}>
<Video />
</PlayerAPIProvider>
// Vanilla JS: Ensure player.attach() is called
previewPlayer.attach(videoElement); -
Device not available
- Check if the device actually has a camera/microphone
- Try unplugging and replugging external USB cameras
- Check for hardware switches (some laptops have physical camera disable switches)
Problem: Browser throws permission error when requesting camera/microphone.
Solutions:
-
Reset browser permissions
- Chrome: Click the lock icon in address bar → Site settings → Reset permissions
- Safari: Safari menu → Settings → Websites → Camera/Microphone → Remove site
- Firefox: Click the shield icon → Clear permissions
-
HTTPS requirement
- Camera/microphone access requires HTTPS (except localhost)
- Ensure your site is served over HTTPS in production
- For local development, use
https://localhostorhttp://localhost
-
Check browser compatibility
- Ensure you're using a supported browser (Chrome, Safari, Firefox, Edge)
- Update to the latest browser version
- Some older browsers don't support
getUserMediaAPI
-
Handle permission denial gracefully
try {
const { mediaStreamController } = usePreviewPlayer();
} catch (error) {
if (error.name === 'NotAllowedError') {
// Show UI prompting user to grant permissions
showPermissionDeniedMessage();
}
}
Problem: Device selection dropdown doesn't work or switching devices fails.
Solutions:
-
Enumerate devices before selecting
// Get available devices
const { audioDevices, videoDevices } = await mediaController.enumerateDevices();
// Select specific device
await mediaStreamController.setVideoDevice(videoDevices[0].deviceId);
await mediaStreamController.setAudioDevice(audioDevices[0].deviceId); -
Wait for device list to populate
- Device labels may be empty until permissions are granted
- Request permissions first, then enumerate devices
-
Handle device errors
try {
await mediaStreamController.setVideoDevice(deviceId);
} catch (error) {
console.error('Failed to switch camera:', error);
// Fall back to default device or show error to user
} -
Check device constraints
- Some devices may not support requested resolutions
- Try setting device without additional constraints first
Problem: Preview video appears flipped or inverted.
Solutions:
-
Mirror the preview for better UX
/* Mirror the video element for preview (like a mirror) */
video.preview {
transform: scaleX(-1);
}Note: This only affects the local preview. Viewers will see the correct orientation.
-
Handle device orientation
- Mobile devices may need orientation handling
- Use CSS transforms to adjust based on device orientation
- Consider using the Screen Orientation API
-
Rotation issues
- Some cameras (especially external USB cameras) may have incorrect rotation metadata
- This is typically a hardware/driver issue
- Can be corrected with CSS transforms on the video element
Connection and Network Issues
Problem: Call creation fails with connection error.
Common causes and solutions:
-
Invalid authentication
// Verify token is valid and not expired
const authClient = new BaseAuthClient({
authUrl: 'https://your-subdomain.nativeframe.com/api/auth',
token: validBroadcasterToken, // Must be valid JWT
}); -
Incorrect backend endpoint
// Ensure backend endpoint is correct (include https://)
const backendEndpoints = [
'https://your-subdomain.nativeframe.com' // Correct format
];
// Common mistakes:
// ❌ 'your-subdomain.nativeframe.com' (missing https://)
// ❌ 'https://nativeframe.com' (missing subdomain) -
Invalid stream key
- Verify stream key is correct and active
- Stream keys are case-sensitive
- Contact NativeFrame if you need to regenerate your stream key
-
Network/firewall blocking WebRTC
- Check if corporate firewall blocks WebRTC traffic
- Verify ports are open: UDP 16384-32767, TCP 443
- Try from different network to isolate issue
-
CORS issues
- Ensure your domain is whitelisted with NativeFrame
- Check browser console for CORS errors
- Verify
clientReferrermatches your domain
Problem: Call connects but disconnects unexpectedly.
Solutions:
-
Handle connection state changes
call.on('connectionStateChange', (state) => {
console.log('Connection state:', state);
if (state === 'disconnected') {
// Attempt reconnection
attemptReconnect();
}
}); -
Network instability
- Poor WiFi or cellular connection
- Try wired connection if possible
- Check network speed (recommend 5+ Mbps upload for HD broadcasting)
-
Token expiration
- Implement token refresh before expiration
const authClient = new BaseAuthClient({
token: currentToken,
refreshToken: async () => {
// Fetch new token from your backend
const newToken = await fetchNewToken();
return newToken;
}
}); -
Browser tab backgrounded (mobile)
- Mobile browsers may suspend WebRTC when tab is backgrounded
- Warn users to keep app in foreground
- Consider implementing reconnection logic
-
Server maintenance
- Check NativeFrame status page for service interruptions
- Implement retry logic with exponential backoff
Problem: Video is low quality, pixelated, or buffers frequently.
Solutions:
-
Network bandwidth insufficient
- Check upload speed for broadcaster:
speedtest.net - Check download speed for viewer:
speedtest.net - Recommended: 5+ Mbps upload (broadcast), 3+ Mbps download (view)
- Check upload speed for broadcaster:
-
Adjust video constraints
// Lower resolution for poor connections
const constraints = {
video: {
width: { ideal: 640 }, // Lower from 1280
height: { ideal: 480 }, // Lower from 720
frameRate: { ideal: 24 } // Lower from 30
}
}; -
Enable adaptive bitrate
- NativeFrame automatically provides adaptive bitrate
- Ensure viewer player is configured to use ABR
- Multiple quality levels will be available based on network
-
CPU/GPU limitations
- Encoding HD video requires significant CPU
- Close other applications
- Lower resolution/framerate if device struggles
- Check CPU usage during broadcast
-
Server selection
- Ensure using the closest backend endpoint
- Geographic distance affects latency
- Configure multiple endpoints for failover
Problem: Significant delay (5+ seconds) between broadcasting and viewing.
Solutions:
-
Use WebRTC for lowest latency
- Ensure using Video Client SDK (not HLS/manifest)
- Direct call viewing has lowest latency (< 1 second)
- Manifest/HLS playback has higher latency (3-30 seconds)
-
Check network conditions
- Latency increases with poor network conditions
- Use wired connections when possible
- Minimize network hops
-
Server geographic distance
- Choose backend endpoint closest to broadcaster
- Viewers should connect to same region
- Cross-region streaming adds latency
-
Browser/device performance
- Older devices may have processing delays
- Close unnecessary browser tabs
- Disable browser extensions that might interfere
-
Expected latency ranges
- WebRTC direct: 0.5-2 seconds (glass-to-glass)
- HLS: 6-30 seconds
- Some latency is normal and expected
Authentication and Authorization Issues
Problem: Requests fail with authentication errors.
Solutions:
-
Verify token format and validity
// Token should be a valid JWT string
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Check token hasn't expired
const payload = JSON.parse(atob(token.split('.')[1]));
const isExpired = payload.exp * 1000 < Date.now();
console.log('Token expired:', isExpired); -
Correct token scope
- Broadcaster tokens: Required for creating calls and broadcasting
- Viewer tokens: Required for viewing streams
- Private viewer tokens: Required for private/direct streams
- Ensure token has appropriate scope for the operation
-
Token refresh implementation
const authClient = new BaseAuthClient({
authUrl: 'https://your-subdomain.nativeframe.com/api/auth',
token: initialToken,
refreshToken: async () => {
// Implement token refresh logic
const response = await fetch('/api/refresh-token');
const { token } = await response.json();
return token;
}
}); -
Authorization URL mismatch
- Ensure
authUrlmatches your backend endpoint - URL should include
/api/authpath - Verify HTTPS is used
- Ensure
-
Contact NativeFrame
- If issues persist, verify your account is active
- Check if your domain is whitelisted
- Confirm your credentials are correct
Problem: Operation fails with permission error.
Solutions:
-
Verify token scope matches operation
// Creating call requires broadcaster token
const call = await createCall({
auth: broadcasterAuthClient, // ✓ Correct
// auth: viewerAuthClient, // ✗ Will fail
});
// Viewing stream requires viewer token
const player = await createPlayer({
auth: viewerAuthClient, // ✓ Correct
}); -
Check stream key permissions
- Ensure stream key is associated with your account
- Verify stream key hasn't been revoked
- Confirm stream key allows the requested operation
-
Private streaming permissions
- Private streams require special viewer tokens
- Ensure private viewer token includes correct permissions
- Verify viewer is authorized for specific stream
-
Account limitations
- Check if your account tier supports the feature
- Verify concurrent stream limits
- Confirm your account is active and in good standing
Broadcasting Issues
Problem: call.broadcast() fails or throws error.
Solutions:
-
Ensure call is connected first
// ✓ Correct: Wait for call to be created
const call = await createCall(options);
const broadcast = await call.broadcast(mediaStreamController, broadcastOptions);
// ✗ Wrong: Trying to broadcast without call
const broadcast = await call.broadcast(...); // call is undefined -
Verify media stream controller
// Ensure mediaStreamController is initialized
const { mediaStreamController } = usePreviewPlayer();
if (!mediaStreamController) {
console.error('Media stream controller not ready');
return;
}
// Check that media tracks exist
const tracks = mediaStreamController.getTracks();
console.log('Available tracks:', tracks); -
Check stream name
// Stream name is required and must be non-empty
const broadcastOptions = {
streamName: 'default', // ✓ Valid
// streamName: '', // ✗ Invalid
}; -
Network/firewall blocking
- Verify WebRTC traffic isn't blocked
- Check browser console for ICE connection errors
- Try from different network
-
Call state issues
// Check call state before broadcasting
if (call.state === 'connected') {
const broadcast = await call.broadcast(...);
}
Problem: Broadcast starts successfully but stops without user action.
Solutions:
-
Handle broadcast lifecycle events
broadcast.on('stateChange', (state) => {
console.log('Broadcast state:', state);
if (state === 'stopped') {
console.log('Broadcast stopped reason:', broadcast.stopReason);
// Handle reconnection or show error to user
}
}); -
Camera/microphone disconnected
- Check if device was unplugged
- Handle device change events
navigator.mediaDevices.addEventListener('devicechange', async () => {
console.log('Device list changed');
// Re-enumerate devices and update UI
}); -
Memory/resource exhaustion
- Broadcasting for extended periods may cause issues
- Ensure proper disposal of old streams
- Monitor browser memory usage
-
Network interruption
- Poor network caused connection drop
- Implement automatic reconnection logic
- Show connection quality indicators to broadcaster
-
Token expiration during broadcast
- Ensure token refresh is implemented
- Refresh tokens proactively before expiration
- Handle auth refresh during active broadcast
Problem: Broadcast appears successful but viewers can't see it.
Solutions:
-
Verify call ID sharing
// Broadcaster: Get and share call ID
const call = await createCall(options);
console.log('Share this call ID with viewers:', call.id);
// Viewer: Use correct call ID
const player = await createPlayer({
callId: 'call-id-from-broadcaster', // Must match exactly
}); -
Check viewer authentication
- Viewers need valid viewer tokens
- For private streams, viewers need private viewer tokens
- Verify viewer token hasn't expired
-
Broadcast state verification
// Check broadcast is actually active
console.log('Broadcast state:', broadcast.state);
console.log('Broadcast stream name:', broadcast.streamName);
// Viewer must request same stream name
const player = await createPlayer({
callId: callId,
streamName: 'default' // Must match broadcaster's streamName
}); -
Network/firewall on viewer side
- Viewer's network may block WebRTC
- Test from different network/device
- Check viewer's browser console for errors
-
Timing issues
- Viewer connected before broadcast started
- Have viewer refresh and reconnect
- Implement proper player state handling
Problem: Audio doesn't match video timing.
Solutions:
-
Check device/browser compatibility
- Some devices have audio/video sync issues
- Try different browser
- Update browser to latest version
-
Network jitter
- Unstable network can cause sync issues
- Check for packet loss
- Use wired connection if possible
-
Processing delays
- CPU overload can cause sync issues
- Close other applications
- Lower video quality/resolution
-
Browser bugs
- Some browsers have known sync issues
- Clear browser cache and restart
- Try incognito/private mode
-
Hardware acceleration
- Enable hardware acceleration in browser settings
- Chrome: Settings → Advanced → System → Hardware acceleration
- This can improve sync by offloading video processing
Viewing and Playback Issues
Problem: Player shows black screen instead of video.
Solutions:
-
Verify player initialization
// React: Ensure PlayerAPIProvider wraps Video component
<PlayerAPIProvider player={player}>
<Video />
</PlayerAPIProvider>
// Vanilla JS: Ensure attach is called
player.attach(videoElement); -
Check video element dimensions
/* Video element needs width and height */
video {
width: 640px;
height: 480px;
}
/* Or use aspect ratio */
video {
width: 100%;
aspect-ratio: 16/9;
} -
Verify broadcast is active
- Ensure broadcaster is actually streaming
- Check call ID is correct
- Verify stream name matches
-
Player not loaded
// Wait for player ready event
player.on('ready', () => {
console.log('Player ready to display video');
});
// Check player state
console.log('Player state:', player.state); -
Browser codec support
- Some browsers don't support all video codecs
- Try different browser
- Check for hardware acceleration settings
Problem: Viewer cannot find the stream.
Solutions:
-
Verify call ID
- Double-check call ID is copied correctly
- Call IDs are case-sensitive
- Ensure no extra spaces or characters
-
Broadcast timing
- Ensure broadcaster has created the call
- Verify broadcast is active
- Call must exist before viewer can join
-
Stream name mismatch
// Broadcaster
const broadcast = await call.broadcast(msc, { streamName: 'default' });
// Viewer must use same stream name
const player = await createPlayer({
callId: callId,
streamName: 'default' // Must match
}); -
Call already ended
- Broadcaster may have ended the call
- Call IDs are typically session-specific
- Need new call ID for new session
-
Authorization issues
- Viewer may not have permission to view stream
- Check viewer token is valid
- For private streams, ensure proper authorization
Problem: Playback is choppy with frequent pauses.
Solutions:
-
Network bandwidth
- Check viewer's internet speed
- Close bandwidth-heavy applications
- Reduce video quality if available
-
CPU/GPU overload
- Close other applications and browser tabs
- Enable hardware acceleration
- Try lower quality stream
-
Browser performance
// Monitor buffer health
player.on('buffering', (buffering) => {
if (buffering) {
console.log('Player is buffering...');
}
});
// Check frame drops
player.getStats((stats) => {
console.log('Dropped frames:', stats.droppedFrames);
}); -
Multiple streams
- Viewing multiple streams simultaneously causes strain
- Limit number of concurrent streams
- Prioritize important streams
-
Browser extensions
- Ad blockers or privacy extensions may interfere
- Try in incognito/private mode
- Disable extensions temporarily
Problem: Can hear stream audio but video doesn't show.
Solutions:
-
Video element rendering
- Ensure video element is visible in DOM
- Check CSS doesn't hide video element
/* Check for these issues */
video {
display: block; /* Not 'none' */
visibility: visible; /* Not 'hidden' */
opacity: 1; /* Not 0 */
} -
Video track availability
// Check if video track exists
const tracks = player.getTracks();
const hasVideo = tracks.some(track => track.kind === 'video');
console.log('Has video track:', hasVideo); -
Broadcaster camera disabled
- Broadcaster may have camera turned off
- Only audio is being broadcast
- Have broadcaster check camera state
-
Player configuration
// Ensure player accepts video
const player = await createPlayer({
video: true, // Enable video
audio: true,
}); -
Codec compatibility
- Browser may not support video codec
- Check console for codec errors
- Try different browser
Group Call Issues
Problem: Participant unable to join group call.
Solutions:
-
Verify call ID from owner
- Ensure using correct call ID
- Call must be created by owner first
- Call ID must be shared exactly as provided
-
Check participant token
// Participant needs valid token
const authClient = new BaseAuthClient({
token: participantToken, // Must be valid
}); -
Waiting room enabled
- Owner may need to approve join request
- Check if waiting in lobby
- Owner must admit participant
-
Call capacity reached
- Group calls may have participant limits
- Check if call is at max capacity
- Wait for spot to open or contact owner
-
Network/permission issues
- Same troubleshooting as regular connection issues
- Verify camera/mic permissions
- Check network connectivity
Problem: Joined call but don't see other participants' video.
Solutions:
-
Check remote stream rendering
// Listen for remote streams
call.on('remoteStreamAdded', (stream) => {
console.log('Remote stream added:', stream);
// Render remote stream in video element
renderRemoteStream(stream);
}); -
Other participants not broadcasting
- Verify other participants have cameras enabled
- Check if participants are actually broadcasting
- Only broadcasters will be visible
-
Subscription issues
// Ensure subscribed to remote streams
await call.subscribe(remoteStream); -
Network bandwidth
- Multiple streams require significant bandwidth
- May need to reduce quality
- Close other applications
-
Render all remote participants
// Maintain list of remote streams
const remoteStreams = new Map();
call.on('remoteStreamAdded', (stream) => {
remoteStreams.set(stream.id, stream);
updateParticipantGrid();
});
call.on('remoteStreamRemoved', (streamId) => {
remoteStreams.delete(streamId);
updateParticipantGrid();
});
Problem: Participants bypass waiting room or approval doesn't work.
Solutions:
-
Verify waiting room configuration
// Enable waiting room when creating call
const call = await createCall({
waitingRoom: {
enabled: true,
},
// ... other options
}); -
Handle admission events
// Owner: Listen for join requests
call.on('participantJoinRequest', (participant) => {
console.log('Join request from:', participant);
// Admit participant
await call.admitParticipant(participant.id);
// Or deny
await call.denyParticipant(participant.id);
}); -
Participant not waiting
// Participant: Show waiting UI
call.on('waitingRoomEntered', () => {
showWaitingUI();
});
call.on('waitingRoomExited', () => {
hideWaitingUI();
// Admitted to call
}); -
Permission issues
- Owner must have permission to admit/deny
- Verify owner role/permissions
- Check token scopes
Canvas and Screen Sharing Issues
Problem: Canvas element not broadcasting or shows black.
Solutions:
-
Verify canvas stream capture
// Ensure canvas is capturing correctly
const canvas = document.getElementById('myCanvas');
const stream = canvas.captureStream(30); // 30 FPS
// Check stream has tracks
console.log('Canvas stream tracks:', stream.getTracks()); -
Canvas dimensions
// Canvas must have valid dimensions
canvas.width = 1280;
canvas.height = 720;
// Or use CSS sizing
canvas.style.width = '100%'; -
Canvas content rendering
- Ensure canvas is actually drawn to
- Canvas must have visible content
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Now capture will show blue canvas -
Frame rate
// Specify frame rate when capturing
const stream = canvas.captureStream(30); // 30 FPS
// Don't use 0 or omit (causes variable frame rate) -
Canvas context
- Use '2d' context for standard canvas
- WebGL contexts may have different capture behavior
- Ensure preserveDrawingBuffer for WebGL
const gl = canvas.getContext('webgl', {
preserveDrawingBuffer: true
});
Problem: getDisplayMedia fails or screen share doesn't start.
Solutions:
-
HTTPS requirement
- Screen sharing requires HTTPS (except localhost)
- Ensure using https:// in production
- For development, use https://localhost
-
User cancelled selection
// Handle user cancellation
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true
});
} catch (error) {
if (error.name === 'NotAllowedError') {
console.log('User cancelled screen share');
// Show appropriate message
}
} -
Browser support
- Ensure browser supports
getDisplayMedia - Chrome, Firefox, Edge support it
- Safari requires recent version (13+)
- Ensure browser supports
-
Check screen share constraints
const stream = await navigator.mediaDevices.getDisplayMedia({
video: {
cursor: 'always', // Include cursor
displaySurface: 'monitor' // Or 'window', 'browser'
},
audio: true // Include system audio if needed
}); -
Handle screen share stop
// Detect when user stops screen share via browser UI
stream.getVideoTracks()[0].addEventListener('ended', () => {
console.log('Screen share stopped');
handleScreenShareStop();
});
Integration and Development Issues
Problem: Cannot find NativeFrame packages in the npm registry.
Solution:
- Ensure proper
.npmrcconfiguration
Make sure your .npmrc file includes the following:
@video:registry=https://npm-packages.nativeframe.com/
Problem: Cannot import NativeFrame packages.
Solutions:
-
Verify package installation
# For React
npm install @video/video-client-react
# For Vanilla JS (core)
npm install @video/video-client-core -
Check import statements
// React - Correct imports
import { usePreviewPlayer } from '@video/video-client-react/hooks';
import { MediaStreamControllerAPIProvider } from '@video/video-client-react/context';
// Vanilla JS - CDN import
import { createCall, requestPlayer } from 'vdc-cdn'; -
Import map configuration (Vanilla JS)
<script type="importmap">
{
"imports": {
"vdc-cdn": "https://cdn.nativeframe.com/video-client-core/14.0.0/index.js"
}
}
</script> -
TypeScript configuration
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
} -
Package version compatibility
- Ensure using compatible versions
- Check package.json for version conflicts
- Update to latest stable version
Problem: React hooks throwing errors.
Solutions:
-
Call hooks at top level
// ✓ Correct
function MyComponent() {
const { mediaStreamController } = usePreviewPlayer();
return <div>...</div>;
}
// ✗ Wrong - inside callback
function MyComponent() {
const handleClick = () => {
const { mediaStreamController } = usePreviewPlayer(); // Error!
};
} -
Ensure functional component
// ✓ Correct - functional component
function MyComponent() {
const { player } = usePreviewPlayer();
}
// ✗ Wrong - class component
class MyComponent extends React.Component {
// Can't use hooks here
} -
React version
- Hooks require React 16.8+
- Update React if using older version
npm install react@latest react-dom@latest -
Multiple React copies
# Check for duplicate React installations
npm ls react
# If duplicates found, dedupe
npm dedupe -
Context provider wrapping
// Ensure components are wrapped in providers
<MediaStreamControllerAPIProvider mediaStreamController={msc}>
<MyComponent /> {/* Can now use context hooks */}
</MediaStreamControllerAPIProvider>
Problem: Application bundle is larger than expected.
Solutions:
-
Exclude optional libraries
// In webpack config or bundler
{
plugins: [
new webpack.DefinePlugin({
'process.env.HLSJS_BUNDLED': JSON.stringify('false'),
'process.env.MPEGTS_BUNDLED': JSON.stringify('false')
})
]
} -
Tree shaking
// Use named imports for better tree shaking
import { usePreviewPlayer, useAuthClient } from '@video/video-client-react/hooks';
// Instead of
import * as hooks from '@video/video-client-react/hooks'; -
Code splitting
// Lazy load video components
const VideoPlayer = React.lazy(() => import('./VideoPlayer'));
function App() {
return (
<Suspense fallback={<Loading />}>
<VideoPlayer />
</Suspense>
);
} -
Analyze bundle
# Use bundle analyzer to see what's large
npm install --save-dev webpack-bundle-analyzer
# Run analysis
npx webpack-bundle-analyzer build/stats.json -
Use CDN for Vanilla JS
- Core library from CDN doesn't count toward bundle
- Loads on demand
- Better for applications with optional video
Problem: TypeScript compilation errors with NativeFrame types.
Solutions:
-
Import types correctly
import { types } from '@video/video-client-react';
// Use types
const options: types.CreateCallOptions = {
streamKey: 'my-key',
// ...
}; -
Type declarations
// Create types.d.ts if types are missing
declare module '@video/video-client-react' {
export interface MediaStreamController {
// Add missing type definitions
}
} -
Strict mode issues
// Handle nullable types
const { mediaStreamController } = usePreviewPlayer();
if (mediaStreamController) {
// TypeScript knows it's not null here
mediaStreamController.setVideoDevice(deviceId);
} -
Update type definitions
# Ensure latest types are installed
npm update @video/video-client-react -
Type assertions (use sparingly)
// Last resort if types are incorrect
const player = createPlayer(options) as types.Player;
Browser and Device Compatibility
Problem: Features work in Chrome but not Safari.
Solutions:
-
WebRTC vendor prefixes
// Use adapter.js for cross-browser compatibility
// It normalizes WebRTC APIs across browsers
import 'webrtc-adapter'; -
Autoplay policy
// Safari requires user gesture for autoplay
video.muted = true; // Muted videos can autoplay
// Or wait for user interaction
button.onclick = () => {
video.play();
}; -
getUserMedia in iframes
<!-- Safari requires allow attribute -->
<iframe allow="camera; microphone"></iframe> -
VP8/VP9 codec support
- Safari prefers H.264
- Ensure backend supports H.264
- Check codec negotiation in logs
-
iOS-specific
- iOS Safari requires
playsinlineattribute
<video playsinline></video>- iOS doesn't support background playback
- Tab must be active for WebRTC
- iOS Safari requires
Problem: Features work on desktop but not mobile.
Solutions:
-
Camera orientation
// Handle device orientation changes
window.addEventListener('orientationchange', () => {
// Adjust video layout
updateVideoLayout();
}); -
Mobile bandwidth constraints
- Reduce default video quality on mobile
const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent);
const constraints = {
video: isMobile ? {
width: { ideal: 640 },
height: { ideal: 480 }
} : {
width: { ideal: 1280 },
height: { ideal: 720 }
}
}; -
Touch vs click events
// Handle both touch and click
button.addEventListener('touchstart', handleAction);
button.addEventListener('click', handleAction); -
Fullscreen API
// Use webkit prefix for iOS
if (video.webkitEnterFullscreen) {
video.webkitEnterFullscreen();
} else {
video.requestFullscreen();
} -
Background tab handling
- Mobile browsers aggressively suspend background tabs
- Warn users to keep app in foreground
- Handle visibility change events
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
console.log('App backgrounded');
// Pause non-critical operations
}
});
Problem: Users on older browsers experience issues.
Solutions:
-
Check browser support
function checkBrowserSupport() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
alert('Your browser does not support video streaming. Please update to a modern browser.');
return false;
}
return true;
} -
Polyfills
// Include polyfills for older browsers
import 'webrtc-adapter'; // WebRTC polyfill
import 'whatwg-fetch'; // Fetch polyfill -
Feature detection
// Detect features before using
if ('getDisplayMedia' in navigator.mediaDevices) {
// Screen sharing supported
} else {
// Show fallback or disable feature
} -
Graceful degradation
- Provide fallback UI for unsupported features
- Show clear error messages
- Link to browser update instructions
-
Minimum browser versions
- Chrome 74+
- Firefox 68+
- Safari 12.1+
- Edge 79+ (Chromium-based)
Echo Pillarbox Video
Solution:
- Ensure
previewPlayeris initialized before rendering - Verify PlayerAPIProvider wraps the EchoPillarboxWrapper
- Check that the Video component has a ref if needed
Solution:
- Make sure
EchoPillarboxVideois placed insideEchoPillarboxWrapper - Verify the Video component is also inside the same wrapper
- The components must be siblings within the wrapper
Solution:
- Add explicit sizing to EchoPillarboxWrapper via style or className
- Use
minHeight: '100%'or explicit dimensions - Ensure parent containers have proper height constraints
Solution:
- Dispose the player in useEffect cleanup
- Clean up mediaStreamController
- EchoPillarboxVideo handles its own cleanup automatically
Still Having Issues?
If you've tried the solutions above and are still experiencing problems:
- Check browser console for error messages and stack traces
- Enable debug logging in Video Client SDK for detailed diagnostics
- Test in incognito mode to rule out extensions/cache issues
- Try different network to isolate network-specific problems
- Contact NativeFrame support with:
- Detailed problem description
- Browser and OS version
- Console error messages
- Steps to reproduce
- Network conditions