Table of Contents

Advanced Playback

The ExtendedPlaybackPage extends the Simple Playback page with recording to MP4 files, snapshot capture, decoder framework selection, rendering strategy configuration, audio output device selection, and ONVIF device discovery with PTZ camera control. Playback and recording can run simultaneously using a shared media source.

Overview

The ExtendedPlaybackPage performs the following:

  1. Plays media from a URI using MediaPlayerControl with configurable playback parameters
  2. Provides decoder framework and rendering strategy selection
  3. Configures video renderer type and audio output device on Windows
  4. Records the playing stream to an MP4 file while playback continues
  5. Captures JPEG snapshots of the current video frame
  6. Shares a single IMediaSource between the player and the recording session using reference counting
  7. Discovers ONVIF-compatible IP cameras on the network and plays their streams (Windows only)
  8. Controls PTZ (Pan-Tilt-Zoom) cameras via ONVIF with presets and snapshot capture

All features from the Simple Playback page — track selection, seeking, playback speed control, and live stream statistics — are also available.

Playback Parameters

Before starting playback, the page configures PlaybackParameters:

this.player.PlaybackParameters.RenderingStrategy = VAST.Media.RenderingStrategy.LowLatency;
this.player.PlaybackParameters.PreferredVideoFramework = VAST.Common.MediaFramework.Builtin;
this.player.PlaybackParameters.PreferredAudioFramework = VAST.Common.MediaFramework.Builtin;

Rendering Strategy

Strategy Description
Low Latency Minimal latency at the expense of smoothness
Smooth Smooth playback at the expense of latency (can be several seconds)

Decoder Framework

Option Framework Description
Media Foundation MediaFoundation Windows built-in framework (Windows only)
FFmpeg FFmpeg FFmpeg decoders (all platforms)
Nvidia CUDA Nvidia hardware decoding (Windows only, requires Nvidia GPU)
Builtin Builtin Built-in framework (non-Windows platforms)

Hardware Acceleration

this.player.PlaybackParameters.AllowHardwareAcceleration = true;

Enables GPU-accelerated decoding (forced on for Nvidia). This setting is effective on Windows and macOS only — on Android and iOS, hardware acceleration is always on for the built-in framework and always off for FFmpeg, regardless of this setting.

Video Renderer Type

this.player.PlaybackParameters.VideoRendererType = VAST.Media.VideoRendererType.Auto;

Video rendering backend with Auto, Best, and Compatible options. On platforms other than Windows, this setting has no effect because platform-specific rendering UI controls managed by the OS are used. On Windows, the Compatible option can be used to render on old computers, virtual machines, and other environments having issues with the Best renderer.

Output Audio Device

On Windows, the target audio output device can be set:

this.player.PlaybackParameters.AudioOutputDeviceId = deviceId;

Audio output devices are enumerated at startup using WASAPI:

await VAST.Capture.AudioDeviceEnumerator.Enumerate(
    new VAST.Capture.AudioDeviceEnumeratorParameters
    {
        Framework = VAST.Common.MediaFramework.WASAPI,
        Direction = VAST.Common.MediaFlowDirection.Output
    });

Shared Media Source

Unlike the Simple Playback page which sets the player's Source property directly, the Extended Playback page creates a shared IMediaSource that can be used by both the player and the recording session simultaneously:

this.mediaSource = VAST.Media.SourceFactory.Create(this.tboxUri.Text);
this.mediaSource.Uri = this.tboxUri.Text;
this.mediaSource.AddRef();

this.player.SourceMedia = this.mediaSource;
this.player.Play();

AddRef() is called because the source may be shared between the player and the recording session. The source is released only when both the player and the recording session have stopped.

Recording

The page can record the playing stream to an MP4 file using IsoSink and MediaSession:

VAST.File.ISO.IsoSink fileSink = new VAST.File.ISO.IsoSink(
    new VAST.File.ISO.ParsingParameters { WriteMediaDataLast = true });
fileSink.Uri = System.IO.Path.Combine(this.fileRecordingPath,
    $"rec-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.mp4");

this.createMediaSource();

VAST.Media.MediaSession recordingSession = new VAST.Media.MediaSession();
recordingSession.AddSource(this.mediaSource);
recordingSession.AddSink(fileSink);
recordingSession.Start();

WriteMediaDataLast optimizes the MP4 file structure by writing the media data (mdat) after the movie header (moov), which enables faster playback start for the recorded file.

Recording reuses the existing media source if playback is already active, or creates a new one if it is not. Recording and playback can start and stop independently — the shared source is released only when both have stopped.

The recording file path is obtained from a platform-specific helper that returns the appropriate documents directory for each platform.

Snapshots

The page captures JPEG snapshots of the currently displayed video frame:

using (var jpegStream = await this.player.TakeSnapshot())
{
    if (jpegStream != null)
    {
        using (var fs = new System.IO.FileStream(snapshotPath, System.IO.FileMode.OpenOrCreate))
        {
            jpegStream.Position = 0;
            await jpegStream.CopyToAsync(fs);
        }
    }
}

TakeSnapshot() returns an asynchronous Stream containing JPEG image data. The snapshot is saved to the same directory as recordings with a timestamped filename.

ONVIF Discovery and PTZ

The page includes an ONVIF panel for IP camera discovery and PTZ control. The ONVIF button appears when the VAST_FEATURE_ONVIF compilation symbol is defined. The panel is accessible via the ONVIF button overlaid on the video player. Currently enabled on Windows only. ONVIF discovery should also work on Android and iOS, but those platforms require a multicast networking entitlement which has not been tested yet.

Device Discovery

this.discoveredDevices = await VAST.ONVIF.OnvifDiscovery.SearchAsync(5000);

SearchAsync performs a WS-Discovery broadcast on the local network with a 5-second timeout. Discovered devices are displayed by IP address in a picker control.

Connecting to a Device

After selecting a discovered device, the user provides credentials and scans for available services:

this.onvifClient = new VAST.ONVIF.OnvifClient2(device.OnvifAddress, username, password);
await this.onvifClient.OpenAsync();

The page first attempts to connect using ONVIF Profile 2 (OnvifClient2). If that fails, it falls back to Profile 1 (OnvifClient1). After a successful connection, the available stream URIs are listed. Selecting a URI and pressing Play loads it into the player with credentials embedded in the URI for authentication.

PTZ Control

The PTZ panel provides continuous movement controls. On touch devices, pressing a button starts the movement and releasing it stops the movement:

this.cameraControl = this.onvifClient.CameraControl;

// Pan/Tilt — continuous movement while button is held
this.cameraControl.SetPanTiltSpeed(panSpeed, 0);   // left/right
this.cameraControl.SetPanTiltSpeed(0, panSpeed);    // up/down
this.cameraControl.StopPanTilt();                    // on release

// Zoom — continuous zoom while button is held
this.cameraControl.SetZoomSpeed(zoomSpeed);          // zoom in
this.cameraControl.SetZoomSpeed(-zoomSpeed);         // zoom out
this.cameraControl.StopSmoothZoom();                 // on release

PTZ features are enabled based on camera capabilities reported by the ICameraControl interface:

Feature Check Controls
Pan/Tilt IsPanTiltSupported Left, Right, Up, Down
Zoom IsZoomSpeedSupported Zoom In, Zoom Out
Presets IsPresetSupported Save Home, Recall Home, Save Preset, Recall Preset

ONVIF Snapshot

The page can capture a JPEG snapshot directly from the camera via ONVIF:

using (var jpegStream = await this.onvifClient.TakeSnapshot())
{
    // save jpegStream to file
}

This is separate from the player snapshot — it requests a still image directly from the camera hardware rather than capturing the currently rendered video frame.

Send Log

The page includes a Send Log button that uploads the application log file to VASTreaming support for diagnostics:

await VAST.Common.License.SendLog("MAUI extended playback issue");

SendLog sends the current log file to the support server. A valid license key must be configured for this feature to work.

See Also