Video Playback
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
MediaPlayerAPI 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():
- Call
player.GetTexture()to obtain the current frame as aTexture2D*. - 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 descriptorslibswscale— 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;
}