Vega Media Controls: Developer Guide

Vega Media Controls (VMC) is a unified system for handling media input across all interaction methods. Instead of each app implementing custom remote button handling, voice command processing, and system UI synchronization, VMC provides a centralized service.

The service is designed to handle the following:

Routes Input Automatically: Remote button presses, Alexa voice commands (“Alexa, pause”), Bluetooth headphones and system UI interactions are captured by Fire TV OS and routed to your app through standardized callback methods.

Manages State Centrally: VMC automatically synchronizes your app’s playback state with the seek bar, playback indicators, and Alexa integration - no manual state management required.

Handles Focus Logic: VMC automatically determines which app should receive media commands based on focus, so developers don’t need to specify which app to control.

How VMC Integration Works

The integration happens through two main patterns:

W3C Media Integration (Automatic): For apps using the W3C Media API, VMC integration is automatic for basic playback controls (play, pause, seek). The W3C Media API internally creates the VMC server, publishes server states, and handles basic commands.

To opt-in to W3C Media integration:

  1. Enable VMC - Add the necessary manifest entries to your manifest.toml file:
[package]
title = "<Your app title>"
id = "com.amazondeveloper.media.sample"

[components]
[[components.interactive]]
id = "com.amazondeveloper.media.sample.main"
launch-type = "singleton"
# The category "com.amazon.category.kepler.media" is only necessary for the primary component, which is identified in the extras
# section of the manifest using the "interface.provider" key.
categories = ["com.amazon.category.kepler.media"]
runtime-module = "/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"

[[extras]]
key = "interface.provider"
component-id = "com.amazondeveloper.media.sample.main"

[extras.value.application]
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IMediaPlaybackServer"
command_options = [
    "StartOver",
    "Previous",
    "Next",
    "SkipForward",
    "SkipBackward", 
]
attribute_options = ["AudioAdvanceMuted"]
features = ["AdvancedSeek", "VariableSpeed", "AudioTracks", "TextTracks"]
  1. Update dependencies - Add to your package.json:
{
  "@amzn/kepler-media-controls": "~1.0.0",
  "@amzn/react-native-w3cmedia": "~1.0.0"
}

  1. Get the component instance and set media control focus:
import { useComponentInstance, IComponentInstance } from '@amzn/react-native-kepler';
import { W3CMediaPlayer } from '@amzn/react-native-w3cmedia';

const componentInstance: IComponentInstance = useComponentInstance();
const player = new W3CMediaPlayer();

// This enables VMC for basic controls automatically
player.setMediaControlFocus(componentInstance);

Now your player instance will be enabled with VMC and will support basic functionality of pause/play and seek remote control and Alexa voice controls.

Override W3C Media Behavior: You can extend the default W3C Media handler for custom control logic:

import { KeplerMediaControlHandler } from "@amzn/react-native-w3cmedia";
import { IMediaSessionId } from '@amzn/kepler-media-controls';

class AppOverrideMediaControlHandler extends KeplerMediaControlHandler {
  async handlePlay(mediaSessionId?: IMediaSessionId) {
    if (shouldOverride) {
      // Custom handling (e.g., disable during live content)
    } else {
      super.handlePlay(mediaSessionId);
    }
  }
}

// Pass custom handler as second parameter
player.setMediaControlFocus(componentInstance, new AppOverrideMediaControlHandler());

When properly integrated, user input flows like this:

User Input                Fire TV OS               VMC                    Media Player
    |                         |                     |                          |
    |                         |                     |                          |
    |   Press FF Button       |                     |                          |
    |------------------------>|                     |                          |
    |                         |                     |                          |
    |                         | Forward FF Event    |                          |
    |                         |-------------------->|                          |
    |                         |                     |                          |
    |                         |                     | handleSkipForward()      |
    |                         |                     |------------------------->|
    |                         |                     |                          |
    |                         |                     |                          |
    |                         |                     |      Seek Operation      |
    |                         |                     |                          |
    |                         |                     |     (Media Advances)     |
    |                         |                     |                          |


*This integration is only applicable for use cases where the player is integrated with an interactive component. If the app handle VMCs itself, it need not use W3C Media integration. Apps shouldn’t call player.setMediaControlFocus(componentInstance) or implement override functionality.


Fire TV Vega Media Controls Troubleshooting Guide


Overview

Purpose

This guide provides practical solutions for Fire TV developers experiencing Vega Media Controls (VMC) integration issues. It focuses on rapid diagnosis and resolution of common problems without requiring deep platform knowledge.

Target Audience

  • Fire TV app developers
  • QA engineers testing media functionality

Common Symptoms

  • Unresponsive fast-forward/rewind buttons
  • Ignored Alexa voice commands
  • Out-of-sync seek bars
  • Intermittent media control functionality

How to Use This Guide

  1. Start with Quick Triage - Determine if you have a platform or app-specific issue
  2. Identify your symptoms in the Common Issues section
  3. Run diagnostic tools to confirm the root cause
  4. Apply the recommended solution or escalate as needed

Quick Triage

Platform vs. App Issue Assessment

Before investigating app-specific code, evaluate these indicators:

Question Platform Issue Indicator
Does the problem only occur on specific Fire TV device models? ✓ Yes
Do some input methods work while others don’t (e.g., D-pad works, media buttons don’t)? ✓ Yes
Did the issue appear after a Fire TV OS update? ✓ Yes

Result: If you answered “yes” to 2+ questions, you likely have a platform issue requiring escalation to Amazon support.


Common Issues & Solutions

Issue #1: Fast Forward/Rewind Buttons Not Responding

Symptoms:

  • FF/RW buttons on Fire TV remote don’t respond
  • D-pad left/right still work for seeking
  • Play/pause buttons work normally
  • Issue affects multiple streaming apps

Root Cause: W3C Media + Vega Media Controls integration failure

Diagnostic Test:

// Test if your app is properly registered with VMC
const testKMCRegistration = async () => {
  const locator = MediaControlComponentAsync. makeMediaControlEndpointLocator();
  const endpoints = await locator.getMediaControlEndpoints();

  console.log('Registered apps:', endpoints.length);
  endpoints.forEach((endpoint, index) => {
    console.log(`App ${index}:`, endpoint.displayName);
  });
};

Solution:

  1. Verify W3C Media Player Configuration:
const player = new W3CMediaPlayer();
const componentInstance = useComponentInstance();

// Critical: This must be called BEFORE playing content
player.setMediaControlFocus(componentInstance);
  1. Check Manifest Configuration:
[[components.interactive]]
categories = ["com.amazon.category.kepler.media"]

Issue #2: Intermittent Media Control Functionality

Symptoms:

  • Sometimes FF/RW works, sometimes doesn’t
  • Controls work after app restart
  • Issue appears random

Root Cause: VMC registration timing issues

Solution:

export const App = () => {
  const componentInstance = useComponentInstance();
  const [player, setPlayer] = useState(null);

  useEffect(() => {
    if (componentInstance) {
      const mediaPlayer = new W3CMediaPlayer();

      // Wait for component to be fully initialized
      setTimeout(() => {
        mediaPlayer.setMediaControlFocus(componentInstance);
        setPlayer(mediaPlayer);
      }, 100);
    }
  }, [componentInstance]);

  // Only start playback after VMC registration
  const startPlayback = () => {
    if (player) {
      player.play(videoUrl);
    }
  };
};

Issue #3: Seek Bar Synchronization Problems

Symptoms:

  • FF/RW buttons work but seek bar doesn’t move
  • Playback position gets out of sync
  • User can’t see current position

Root Cause: Not updating VMC server state

Solution:

class MediaControlHandler implements IMediaControlHandlerAsync {
  private mediaPlayerState: MediaPlayerState;

  async handleSkipForward(sessionId?: IMediaSessionId): Promise<void> {
    // Update your player position
    const newPosition = this.player.currentTime + 10;
    this.player.currentTime = newPosition;

    // CRITICAL: Update VMC state
    this.mediaPlayerState.currentPosition = {
      seconds: Math.floor(newPosition),
      nanoseconds: (newPosition % 1) * 1000000000
    };

    this.mediaPlayerState.playbackPosition = {
      updatedAtTime: { seconds: Date.now() / 1000, nanoseconds: 0 },
      position: this.mediaPlayerState.currentPosition
    };

    // Notify VMC of state change
    const sessionState = this.mediaPlayerState.getServerState();
    this.mediaControlServer.updateMediaSessionStates([sessionState]);
  }
}

Diagnostic Tools

Tool #1: VMC Health Check

const runKMCDiagnostics = async () => {
  console.log('=== KMC Diagnostics ===');

  try {
    // Test 1: Check if KMC is available
    const locator = MediaControlComponentAsync.m[REDACTED:AWS_ACCESS_KEY]tLocator();
    console.log('✅ KMC locator created');

    // Test 2: Get registered endpoints
    const endpoints = await locator.getMediaControlEndpoints();
    console.log(`✅ Found ${endpoints.length} registered apps`);

    // Test 3: Test command routing
    if (endpoints.length > 0) {
      const firstApp = endpoints[0];
      await firstApp.play();
      console.log('✅ Command routing works');
    }

  } catch (error) {
    console.error('❌ KMC Diagnostics failed:', error.message);
    console.log('Possible causes:');
    console.log('- KMC not properly initialized');
    console.log('- Missing manifest entries');
    console.log('- Component not registered');
  }
};

Tool #2: W3C Media Integration Test

const testW3CMediaIntegration = () => {
  const componentInstance = useComponentInstance();

  if (!componentInstance) {
    console.error('❌ Component instance not available');
    return;
  }

  const player = new W3CMediaPlayer();

  try {
    player.setMediaControlFocus(componentInstance);
    console.log('✅ W3C Media + KMC integration successful');

    // Verify registration after a delay
    setTimeout(async () => {
      const locator = MediaControlComponentAsync.m[REDACTED:AWS_ACCESS_KEY]tLocator();
      const endpoints = await locator.getMediaControlEndpoints();
      const ourApp = endpoints.find(e => e.componentId === componentInstance.id);

      if (ourApp) {
        console.log('✅ App successfully registered with KMC');
      } else {
        console.error('❌ App not found in KMC registry');
      }
    }, 1000);

  } catch (error) {
    console.error('❌ W3C Media integration failed:', error.message);
  }
};

Common Pitfalls

:cross_mark: Pitfall #1: Late Media Control Focus Registration

Wrong:

const startVideo = () => {
  player.play(videoUrl);
  player.setMediaControlFocus(componentInstance); // Too late!
};

Correct:

useEffect(() => {
  if (componentInstance && player) {
    player.setMediaControlFocus(componentInstance);
  }
}, [componentInstance, player]);

:cross_mark: Pitfall #2: Ignoring Session Management

Wrong:

async handlePlay(): Promise<void> {
  this.player.play(); // Assumes single session
}

Correct:

async handlePlay(sessionId?: IMediaSessionId): Promise<void> {
  const targetPlayer = sessionId ? 
    this.getPlayerForSession(sessionId.id) : 
    this.defaultPlayer;

  targetPlayer.play();
}

:cross_mark: Pitfall #3: Missing VMC State Updates

Wrong:

const seekTo = (position: number) => {
  player.currentTime = position; // Only updates local player
};

Correct:

const seekTo = (position: number) => {
  player.currentTime = position;

  // Update VMC state
  this.mediaPlayerState.currentPosition = {
    seconds: Math.floor(position),
    nanoseconds: (position % 1) * 1000000000
  };

  const sessionState = this.mediaPlayerState.getServerState();
  this.mediaControlServer.updateMediaSessionStates([sessionState]);
};

:cross_mark: Pitfall #4: Inadequate Error Handling

Wrong:

player.setMediaControlFocus(componentInstance); // No error handling

Correct:

try {
  player.setMediaControlFocus(componentInstance);
  console.log('KMC integration successful');
} catch (error) {
  console.error('KMC integration failed:', error);
  // Fallback: App still works, just no remote control
  this.fallbackToManualControls();
}

Step-by-Step Troubleshooting

Step 1: Verify Basic Setup

Check Dependencies:

{
  "@amzn/kepler-media-controls": "~1.0.0",
  "@amzn/kepler-media-types": "~1.0.0",
  "@amzn/react-native-w3cmedia": "~1.0.0"
}

Verify Manifest Configuration:

[components]
[[components.interactive]]
categories = ["com.amazon.category.kepler.media"]

[[extras]]
key = "interface.provider"
component-id = "com.yourapp.main"

Important: When using W3C Media integration, avoid implementing custom VMC handlers or calling setMediaControlFocus() with custom handlers unless you need to override default behavior.

Step 2: Test VMC Registration

const verifyKMCSetup = async () => {
  console.log('Verifying KMC setup...');

  // Check component instance
  const componentInstance = useComponentInstance();
  if (!componentInstance) {
    console.error('❌ No component instance - check your React setup');
    return;
  }

  // Check W3C Media player
  const player = new W3CMediaPlayer();
  if (!player.setMediaControlFocus) {
    console.error('❌ setMediaControlFocus not available - check dependencies');
    return;
  }

  // Test registration
  try {
    player.setMediaControlFocus(componentInstance);
    console.log('✅ KMC registration successful');
  } catch (error) {
    console.error('❌ KMC registration failed:', error);
  }
};

Step 3: Test Media Controls

const testMediaControls = async () => {
  console.log('Testing media controls...');

  const locator = MediaControlComponentAsync.m[REDACTED:AWS_ACCESS_KEY]tLocator();
  const endpoints = await locator.getMediaControlEndpoints();

  if (endpoints.length === 0) {
    console.error('❌ No apps registered with KMC');
    return;
  }

  const ourApp = endpoints[0]; // Assuming your app is focused

  // Test each control
  const tests = [
    { name: 'Play', action: () => ourApp.play() },
    { name: 'Pause', action: () => ourApp.pause() },
    { name: 'Skip Forward', action: () => ourApp.skipForward() },
    { name: 'Skip Backward', action: () => ourApp.skipBackward() }
  ];

  for (const test of tests) {
    try {
      await test.action();
      console.log(`✅ ${test.name} works`);
    } catch (error) {
      console.error(`❌ ${test.name} failed:`, error.message);
    }
  }
};

Reference Implementation

Minimal Working Implementation

import { useComponentInstance } from '@amzn/react-native-kepler';
import { W3CMediaPlayer } from '@amzn/react-native-w3cmedia';
import { useEffect } from 'react';

export const App = () => {
  const componentInstance = useComponentInstance();

  useEffect(() => {
    if (componentInstance) {
      const player = new W3CMediaPlayer();
      player.setMediaControlFocus(componentInstance);
    }
  }, [componentInstance]);

  return <YourVideoPlayerUI />;
};

Additional Resources