Video Playback

Microsoft::Xna::Framework::Media — VideoPlayer, Video, FFmpeg-backed frame decoding

Implementation status: Partial. Video playback is functional on Linux and Windows. FFmpeg is excluded from the Emscripten (WebAssembly) and Android builds; see the Platform Availability section for alternatives.

Overview

VideoPlayer and Video live in the Microsoft::Xna::Framework::Media namespace, matching the XNA 4.0 API. CNA uses FFmpeg as its video decoding back-end: the decoder reads compressed frames from the container, converts them from YUV colour space to RGBA, and uploads the result as a Texture2D. Your Draw() method retrieves that texture each frame via VideoPlayer::GetTexture() and renders it with SpriteBatch (or any other drawing path) exactly like any other Texture2D.

Supported container and codec combinations:

  • MP4 — H.264 / H.265
  • OGV — Ogg Theora
  • WEBM — VP8 / VP9
  • MKV — Matroska (any FFmpeg-supported codec)
  • AVI — legacy container
  • MOV — QuickTime container

Platform restriction — FFmpeg is NOT available on:

  • Emscripten / WebAssembly — FFmpeg is excluded from the WASM build. Use the HTML5 <video> element for in-browser video playback instead.
  • Android — FFmpeg integration is pending. Use the Android MediaPlayer API directly until support lands.

Video Class

A Video object represents a video asset loaded from disk. Load it through the standard ContentManager pipeline:

// LoadContent()
auto video = content.Load<Video>("videos/intro");

The ContentManager resolves the file extension automatically; the path should be relative to the content root and omit the extension. Once loaded, the following read-only properties are available:

Property Type Description
Width int Frame width in pixels
Height int Frame height in pixels
Duration TimeSpan Total playback duration of the video
FramesPerSecond float Native frame rate reported by the container
VideoSoundtrackType VideoSoundtrackType Indicates whether the video has a music track, dialog, or no audio (Music, Dialog, MusicAndDialog)

VideoPlayer Class

VideoPlayer controls playback of a Video asset. Create one instance per active video stream; it is not designed for concurrent playback of multiple videos from the same instance.

Methods

Method Description
Play(Video&) Starts playback of the given video from the beginning. If a video is already playing it is replaced.
Pause() Pauses playback at the current frame. The decoded texture remains valid.
Resume() Resumes playback from the paused position.
Stop() Stops playback and rewinds to the beginning.
GetTexture() Returns a Texture2D* containing the current decoded frame. Call this every frame inside Draw(). Returns nullptr when the player is stopped and no frame has been decoded.

Properties

Property Type Description
State MediaState Current playback state: MediaState::Playing, MediaState::Paused, or MediaState::Stopped
IsLooped bool Whether the video restarts automatically when it reaches the end. Default: false.
IsMuted bool Mutes the audio track without affecting the decoded video frames.
Volume float Audio volume in the range 0.0 (silent) to 1.0 (full volume).
PlayPosition TimeSpan Current playback position within the video. Read-only.

Rendering Pattern

VideoPlayer decodes frames on demand rather than pre-decoding the entire video. On each call to GetTexture(), CNA checks whether the elapsed playback time requires a new frame to be decoded, converts the YUV output to RGBA, and uploads it to the GPU as a Texture2D. This means the texture pointer returned by GetTexture() is stable for the lifetime of the player, but its contents are updated in-place each frame.

The recommended pattern inside Draw():

  1. Call player.GetTexture() to obtain the current frame as a Texture2D*.
  2. If the pointer is non-null, draw it with SpriteBatch::Draw() scaled to the desired screen rectangle.

Platform Availability

Platform FFmpeg available Recommended alternative
Linux Yes
Windows Yes
Emscripten / WebAssembly No HTML5 <video> element
Android Pending Android MediaPlayer API

FFmpeg Setup

FFmpeg must be installed on the host system before building CNA with video support. CNA's CMake configuration locates FFmpeg via find_package(FFmpeg) or pkg-config; no additional CMake flags are required once the libraries are present.

On Debian and Ubuntu:

sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev

The four required libraries are:

  • libavcodec — codec implementations (H.264, VP8/VP9, Theora, …)
  • libavformat — container demuxers (MP4, MKV, OGV, …)
  • libavutil — shared utilities and pixel format descriptors
  • libswscale — YUV-to-RGBA colour space conversion

On Windows, pre-built FFmpeg development packages are available from BtbN/FFmpeg-Builds. Extract the package and set the FFMPEG_ROOT CMake variable (or add the lib directory to CMAKE_PREFIX_PATH) so that find_package can locate the headers and import libraries.

Code Examples

Loading and playing a video

// Game member variables
Video         m_introVideo;
VideoPlayer   m_videoPlayer;

// LoadContent()
void MyGame::LoadContent() {
    m_introVideo = content.Load<Video>("videos/intro");
    m_videoPlayer.IsLooped = false;
    m_videoPlayer.Volume   = 1.0f;
    m_videoPlayer.Play(m_introVideo);
}

Getting the current frame and drawing with SpriteBatch

// Draw()
void MyGame::Draw(GameTime gameTime) {
    graphicsDevice->Clear(Color::Black);

    Texture2D* frame = m_videoPlayer.GetTexture();
    if (frame != nullptr) {
        spriteBatch->Begin();
        spriteBatch->Draw(
            frame,
            Rectangle(0, 0,
                graphicsDevice->GetViewport().Width,
                graphicsDevice->GetViewport().Height),
            Color::White);
        spriteBatch->End();
    }
}

Checking playback state and looping

// Update()
void MyGame::Update(GameTime gameTime) {
    if (m_videoPlayer.State == MediaState::Stopped) {
        // Intro finished — transition to the main menu
        LoadMainMenu();
        return;
    }

    // Display elapsed / total time
    TimeSpan pos   = m_videoPlayer.PlayPosition;
    TimeSpan total = m_introVideo.Duration;
    UpdateProgressBar(pos, total);
}

// Enable looping before playback begins
m_videoPlayer.IsLooped = true;
m_videoPlayer.Play(m_backgroundVideo);

Pausing and resuming on input

// Update()
void MyGame::Update(GameTime gameTime) {
    auto kb = Keyboard::GetState();

    if (kb.IsKeyDown(Keys::Space) && m_prevKeyboard.IsKeyUp(Keys::Space)) {
        if (m_videoPlayer.State == MediaState::Playing) {
            m_videoPlayer.Pause();
        } else if (m_videoPlayer.State == MediaState::Paused) {
            m_videoPlayer.Resume();
        }
    }

    if (kb.IsKeyDown(Keys::Escape) && m_prevKeyboard.IsKeyUp(Keys::Escape)) {
        m_videoPlayer.Stop();
    }

    m_prevKeyboard = kb;
}