Skip to main content
react v1.0.0 core v14.0.0

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:

  1. 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
  2. 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
  3. 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();
  4. 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);
  5. 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:

  1. 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
  2. HTTPS requirement

    • Camera/microphone access requires HTTPS (except localhost)
    • Ensure your site is served over HTTPS in production
    • For local development, use https://localhost or http://localhost
  3. 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 getUserMedia API
  4. 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:

  1. 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);
  2. Wait for device list to populate

    • Device labels may be empty until permissions are granted
    • Request permissions first, then enumerate devices
  3. 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
    }
  4. 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:

  1. 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.

  2. Handle device orientation

    • Mobile devices may need orientation handling
    • Use CSS transforms to adjust based on device orientation
    • Consider using the Screen Orientation API
  3. 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:

  1. 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
    });
  2. 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)
  3. 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
  4. 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
  5. CORS issues

    • Ensure your domain is whitelisted with NativeFrame
    • Check browser console for CORS errors
    • Verify clientReferrer matches your domain

Problem: Call connects but disconnects unexpectedly.

Solutions:

  1. Handle connection state changes

    call.on('connectionStateChange', (state) => {
    console.log('Connection state:', state);

    if (state === 'disconnected') {
    // Attempt reconnection
    attemptReconnect();
    }
    });
  2. Network instability

    • Poor WiFi or cellular connection
    • Try wired connection if possible
    • Check network speed (recommend 5+ Mbps upload for HD broadcasting)
  3. 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;
    }
    });
  4. Browser tab backgrounded (mobile)

    • Mobile browsers may suspend WebRTC when tab is backgrounded
    • Warn users to keep app in foreground
    • Consider implementing reconnection logic
  5. 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:

  1. 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)
  2. 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
    }
    };
  3. 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
  4. CPU/GPU limitations

    • Encoding HD video requires significant CPU
    • Close other applications
    • Lower resolution/framerate if device struggles
    • Check CPU usage during broadcast
  5. 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:

  1. 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)
  2. Check network conditions

    • Latency increases with poor network conditions
    • Use wired connections when possible
    • Minimize network hops
  3. Server geographic distance

    • Choose backend endpoint closest to broadcaster
    • Viewers should connect to same region
    • Cross-region streaming adds latency
  4. Browser/device performance

    • Older devices may have processing delays
    • Close unnecessary browser tabs
    • Disable browser extensions that might interfere
  5. 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:

  1. 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);
  2. 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
  3. 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;
    }
    });
  4. Authorization URL mismatch

    • Ensure authUrl matches your backend endpoint
    • URL should include /api/auth path
    • Verify HTTPS is used
  5. 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:

  1. 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
    });
  2. 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
  3. Private streaming permissions

    • Private streams require special viewer tokens
    • Ensure private viewer token includes correct permissions
    • Verify viewer is authorized for specific stream
  4. 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:

  1. 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
  2. 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);
  3. Check stream name

    // Stream name is required and must be non-empty
    const broadcastOptions = {
    streamName: 'default', // ✓ Valid
    // streamName: '', // ✗ Invalid
    };
  4. Network/firewall blocking

    • Verify WebRTC traffic isn't blocked
    • Check browser console for ICE connection errors
    • Try from different network
  5. 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:

  1. 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
    }
    });
  2. 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
    });
  3. Memory/resource exhaustion

    • Broadcasting for extended periods may cause issues
    • Ensure proper disposal of old streams
    • Monitor browser memory usage
  4. Network interruption

    • Poor network caused connection drop
    • Implement automatic reconnection logic
    • Show connection quality indicators to broadcaster
  5. 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:

  1. 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
    });
  2. Check viewer authentication

    • Viewers need valid viewer tokens
    • For private streams, viewers need private viewer tokens
    • Verify viewer token hasn't expired
  3. 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
    });
  4. Network/firewall on viewer side

    • Viewer's network may block WebRTC
    • Test from different network/device
    • Check viewer's browser console for errors
  5. 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:

  1. Check device/browser compatibility

    • Some devices have audio/video sync issues
    • Try different browser
    • Update browser to latest version
  2. Network jitter

    • Unstable network can cause sync issues
    • Check for packet loss
    • Use wired connection if possible
  3. Processing delays

    • CPU overload can cause sync issues
    • Close other applications
    • Lower video quality/resolution
  4. Browser bugs

    • Some browsers have known sync issues
    • Clear browser cache and restart
    • Try incognito/private mode
  5. 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:

  1. Verify player initialization

    // React: Ensure PlayerAPIProvider wraps Video component
    <PlayerAPIProvider player={player}>
    <Video />
    </PlayerAPIProvider>

    // Vanilla JS: Ensure attach is called
    player.attach(videoElement);
  2. 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;
    }
  3. Verify broadcast is active

    • Ensure broadcaster is actually streaming
    • Check call ID is correct
    • Verify stream name matches
  4. 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);
  5. 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:

  1. Verify call ID

    • Double-check call ID is copied correctly
    • Call IDs are case-sensitive
    • Ensure no extra spaces or characters
  2. Broadcast timing

    • Ensure broadcaster has created the call
    • Verify broadcast is active
    • Call must exist before viewer can join
  3. 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
    });
  4. Call already ended

    • Broadcaster may have ended the call
    • Call IDs are typically session-specific
    • Need new call ID for new session
  5. 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:

  1. Network bandwidth

    • Check viewer's internet speed
    • Close bandwidth-heavy applications
    • Reduce video quality if available
  2. CPU/GPU overload

    • Close other applications and browser tabs
    • Enable hardware acceleration
    • Try lower quality stream
  3. 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);
    });
  4. Multiple streams

    • Viewing multiple streams simultaneously causes strain
    • Limit number of concurrent streams
    • Prioritize important streams
  5. 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:

  1. 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 */
    }
  2. 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);
  3. Broadcaster camera disabled

    • Broadcaster may have camera turned off
    • Only audio is being broadcast
    • Have broadcaster check camera state
  4. Player configuration

    // Ensure player accepts video
    const player = await createPlayer({
    video: true, // Enable video
    audio: true,
    });
  5. 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:

  1. 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
  2. Check participant token

    // Participant needs valid token
    const authClient = new BaseAuthClient({
    token: participantToken, // Must be valid
    });
  3. Waiting room enabled

    • Owner may need to approve join request
    • Check if waiting in lobby
    • Owner must admit participant
  4. Call capacity reached

    • Group calls may have participant limits
    • Check if call is at max capacity
    • Wait for spot to open or contact owner
  5. 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:

  1. 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);
    });
  2. Other participants not broadcasting

    • Verify other participants have cameras enabled
    • Check if participants are actually broadcasting
    • Only broadcasters will be visible
  3. Subscription issues

    // Ensure subscribed to remote streams
    await call.subscribe(remoteStream);
  4. Network bandwidth

    • Multiple streams require significant bandwidth
    • May need to reduce quality
    • Close other applications
  5. 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:

  1. Verify waiting room configuration

    // Enable waiting room when creating call
    const call = await createCall({
    waitingRoom: {
    enabled: true,
    },
    // ... other options
    });
  2. 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);
    });
  3. Participant not waiting

    // Participant: Show waiting UI
    call.on('waitingRoomEntered', () => {
    showWaitingUI();
    });

    call.on('waitingRoomExited', () => {
    hideWaitingUI();
    // Admitted to call
    });
  4. 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:

  1. 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());
  2. Canvas dimensions

    // Canvas must have valid dimensions
    canvas.width = 1280;
    canvas.height = 720;

    // Or use CSS sizing
    canvas.style.width = '100%';
  3. 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
  4. Frame rate

    // Specify frame rate when capturing
    const stream = canvas.captureStream(30); // 30 FPS
    // Don't use 0 or omit (causes variable frame rate)
  5. 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:

  1. HTTPS requirement

    • Screen sharing requires HTTPS (except localhost)
    • Ensure using https:// in production
    • For development, use https://localhost
  2. 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
    }
    }
  3. Browser support

    • Ensure browser supports getDisplayMedia
    • Chrome, Firefox, Edge support it
    • Safari requires recent version (13+)
  4. 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
    });
  5. 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:

  1. Ensure proper .npmrc configuration

Make sure your .npmrc file includes the following:

@video:registry=https://npm-packages.nativeframe.com/                                                                                                

Problem: Cannot import NativeFrame packages.

Solutions:

  1. Verify package installation

    # For React
    npm install @video/video-client-react

    # For Vanilla JS (core)
    npm install @video/video-client-core
  2. 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';
  3. Import map configuration (Vanilla JS)

    <script type="importmap">
    {
    "imports": {
    "vdc-cdn": "https://cdn.nativeframe.com/video-client-core/14.0.0/index.js"
    }
    }
    </script>
  4. TypeScript configuration

    // tsconfig.json
    {
    "compilerOptions": {
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
    }
    }
  5. Package version compatibility

    • Ensure using compatible versions
    • Check package.json for version conflicts
    • Update to latest stable version

Problem: React hooks throwing errors.

Solutions:

  1. 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!
    };
    }
  2. Ensure functional component

    // ✓ Correct - functional component
    function MyComponent() {
    const { player } = usePreviewPlayer();
    }

    // ✗ Wrong - class component
    class MyComponent extends React.Component {
    // Can't use hooks here
    }
  3. React version

    • Hooks require React 16.8+
    • Update React if using older version
    npm install react@latest react-dom@latest
  4. Multiple React copies

    # Check for duplicate React installations
    npm ls react

    # If duplicates found, dedupe
    npm dedupe
  5. 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:

  1. 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')
    })
    ]
    }
  2. 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';
  3. Code splitting

    // Lazy load video components
    const VideoPlayer = React.lazy(() => import('./VideoPlayer'));

    function App() {
    return (
    <Suspense fallback={<Loading />}>
    <VideoPlayer />
    </Suspense>
    );
    }
  4. 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
  5. 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:

  1. Import types correctly

    import { types } from '@video/video-client-react';

    // Use types
    const options: types.CreateCallOptions = {
    streamKey: 'my-key',
    // ...
    };
  2. Type declarations

    // Create types.d.ts if types are missing
    declare module '@video/video-client-react' {
    export interface MediaStreamController {
    // Add missing type definitions
    }
    }
  3. Strict mode issues

    // Handle nullable types
    const { mediaStreamController } = usePreviewPlayer();

    if (mediaStreamController) {
    // TypeScript knows it's not null here
    mediaStreamController.setVideoDevice(deviceId);
    }
  4. Update type definitions

    # Ensure latest types are installed
    npm update @video/video-client-react
  5. 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:

  1. WebRTC vendor prefixes

    // Use adapter.js for cross-browser compatibility
    // It normalizes WebRTC APIs across browsers
    import 'webrtc-adapter';
  2. Autoplay policy

    // Safari requires user gesture for autoplay
    video.muted = true; // Muted videos can autoplay

    // Or wait for user interaction
    button.onclick = () => {
    video.play();
    };
  3. getUserMedia in iframes

    <!-- Safari requires allow attribute -->
    <iframe allow="camera; microphone"></iframe>
  4. VP8/VP9 codec support

    • Safari prefers H.264
    • Ensure backend supports H.264
    • Check codec negotiation in logs
  5. iOS-specific

    • iOS Safari requires playsinline attribute
    <video playsinline></video>
    • iOS doesn't support background playback
    • Tab must be active for WebRTC

Problem: Features work on desktop but not mobile.

Solutions:

  1. Camera orientation

    // Handle device orientation changes
    window.addEventListener('orientationchange', () => {
    // Adjust video layout
    updateVideoLayout();
    });
  2. 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 }
    }
    };
  3. Touch vs click events

    // Handle both touch and click
    button.addEventListener('touchstart', handleAction);
    button.addEventListener('click', handleAction);
  4. Fullscreen API

    // Use webkit prefix for iOS
    if (video.webkitEnterFullscreen) {
    video.webkitEnterFullscreen();
    } else {
    video.requestFullscreen();
    }
  5. 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:

  1. 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;
    }
  2. Polyfills

    // Include polyfills for older browsers
    import 'webrtc-adapter'; // WebRTC polyfill
    import 'whatwg-fetch'; // Fetch polyfill
  3. Feature detection

    // Detect features before using
    if ('getDisplayMedia' in navigator.mediaDevices) {
    // Screen sharing supported
    } else {
    // Show fallback or disable feature
    }
  4. Graceful degradation

    • Provide fallback UI for unsupported features
    • Show clear error messages
    • Link to browser update instructions
  5. Minimum browser versions

    • Chrome 74+
    • Firefox 68+
    • Safari 12.1+
    • Edge 79+ (Chromium-based)

Echo Pillarbox Video

Solution:

  • Ensure previewPlayer is initialized before rendering
  • Verify PlayerAPIProvider wraps the EchoPillarboxWrapper
  • Check that the Video component has a ref if needed

Solution:

  • Make sure EchoPillarboxVideo is placed inside EchoPillarboxWrapper
  • 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:

  1. Check browser console for error messages and stack traces
  2. Enable debug logging in Video Client SDK for detailed diagnostics
  3. Test in incognito mode to rule out extensions/cache issues
  4. Try different network to isolate network-specific problems
  5. Contact NativeFrame support with:
    • Detailed problem description
    • Browser and OS version
    • Console error messages
    • Steps to reproduce
    • Network conditions