Table of Contents

VAST.File Library

The VAST.File library provides file-based media sources and sinks for VASTreaming applications. It supports playlist playback, MPEG audio (MP1/MP2/MP3) and WAV file handling, metadata caching for VOD optimization, and media processing utilities.

Overview

Feature Description
Playlist Source Play M3U/M3U8/PLS playlists with seamless concatenation
MPEG Audio Read and write MP1, MP2, MP3 files
WAV Audio Read and write WAV files with multiple PCM formats
File Caching Cache file metadata for instant reopening
File Rotation Automatic file chunking for long recordings
Media Utilities Audio analysis, mixing, video composition

Requirements

File Sources

FileSource (Playlist)

The FileSource plays playlists with automatic file concatenation and seamless playback.

Supported Formats

Format Extension Description
M3U .m3u Standard playlist
M3U8 .m3u8 UTF-8 playlist (HLS compatible)
PLS .pls Winamp playlist format

Usage

var source = new VAST.File.FileSource();
source.Uri = "playlist.m3u";
source.Loop = true;

source.NewStream += (sender, e) =>
{
    Console.WriteLine($"Stream: {e.MediaType}");
};

source.NewSample += (sender, e) =>
{
    ProcessSample(e.Sample);
};

source.StateChanged += (sender, state) =>
{
    if (state == MediaState.Opened)
    {
        source.Start();
    }
};

source.Open();

Playlist Format

A playlist is a simple text file with a list of file paths, one per line:

C:\path\rec1.mp4
C:\path\rec2.mp4
C:\path\rec3.mp4

Playlist files must have .pls, .m3u, or .m3u8 extension.

Important

All files in a playlist must have exactly the same encoding settings, including low-level parameters. Files should be recorded from the same source or encoded using the same encoder with identical settings. You cannot combine files recorded from different sources in one playlist without first transcoding them to exactly the same output format using identical encoding settings.

Inline Playlists

Playlists can also be specified inline using the content: URI scheme:

source.Uri = @"content:
#EXT-X-VAST-INTERVAL:0:01:05.000-
T:\file1.mp4
T:\file2.mp4
#EXT-X-VAST-INTERVAL:0:00:00-0:00:33.133
T:\file3.mp4";

Playback Intervals

Use the #EXT-X-VAST-INTERVAL directive to play only a portion of a file:

#EXT-X-VAST-INTERVAL:<from>-<to>

Both <from> and <to> are optional:

  • If <from> is omitted, playback starts from the beginning
  • If <to> is omitted, playback continues to the end

Timestamps must be in a format parseable by the TimeSpan class.

Examples

Both start and end specified:

#EXT-X-VAST-INTERVAL:0:01:05.000-0:01:15.000
C:\path\rec1.mp4

Only start specified (play to end):

#EXT-X-VAST-INTERVAL:0:01:05.000-
C:\path\rec1.mp4

Only end specified (play from beginning):

#EXT-X-VAST-INTERVAL:-0:01:15.000
C:\path\rec1.mp4
Keyframe Alignment

Actual playback positions are aligned to the nearest keyframes, since splicing can only occur at keyframes:

  • For <from>: the keyframe equal to or preceding the specified position is used
  • For <to>: the keyframe equal to or following the specified position is used

This alignment behavior can be configured via StartPositionAlignment and EndPositionAlignment properties.

Note

Due to keyframe alignment, the actual duration of each playlist segment (and the total playlist duration) may differ slightly from the values specified in the interval directives.

NTP Timestamp Intervals

For MP4 files written by VASTreaming that contain NTP timestamps, use #EXT-X-VAST-NTP-INTERVAL:

#EXT-X-VAST-NTP-INTERVAL:<from>-<to>

Both <from> and <to> are optional and represent absolute NTP timestamps in hexadecimal format.

Example:

#EXT-X-VAST-NTP-INTERVAL:0xe5a5b9370e71f70e-0xe5a5b9c31b186799
C:\path\rec1.mp4

The same keyframe alignment rules apply as with #EXT-X-VAST-INTERVAL.

Error Handling

Control behavior when playlist items fail:

Mode Description
FailOnAny Stop on first error
FailOnAll Transition to error state only if all files in the playlist fail
source.ErrorHandling = FileSource.FileErrorHandling.FailOnAll;

MpaSource (MPEG Audio)

The MpaSource reads MPEG audio files with metadata extraction and HTTP streaming support.

Supported Formats

Format Description
MP1 MPEG Audio Layer I
MP2 MPEG Audio Layer II
MP3 MPEG Audio Layer III

Features

Feature Support
Local Files Yes
HTTP/HTTPS Streaming Yes
ID3v1/ID3v2 Tags Yes
APE Tags Yes
Seeking Frame-accurate
Looping Yes

Usage

var source = new VAST.File.MpaSource();
source.Uri = "audio.mp3";

source.StateChanged += (sender, state) =>
{
    if (state == MediaState.Opened)
    {
        Console.WriteLine($"Duration: {source.Duration}");
        source.Start();
    }
};

source.Open();

WavSource

The WavSource reads WAV audio files with multiple PCM format support.

Supported Formats

Format Description
PCM 8-bit Unsigned 8-bit samples
PCM 16-bit Signed 16-bit samples
PCM 32-bit Signed 32-bit integer or float samples
PCM 64-bit Signed 64-bit float samples

Usage

var source = new VAST.File.WavSource();
source.Uri = "audio.wav";

source.StateChanged += (sender, state) =>
{
    if (state == MediaState.Opened)
    {
        source.Start();
    }
};

source.Open();

Playback Control

All file sources support interactive playback:

// Seeking
source.Position = TimeSpan.FromSeconds(30);

// Playback rate
source.PlaybackRate = 2.0; // 2x speed

// Pause/Resume
source.Pause();
source.Start(); // Resume

Playback Features

Feature Support
Seeking Timestamp-based positioning
Pause/Resume Full support
Playback Rate Variable speed playback
Looping Continuous playback
Multi-track Video, audio, subtitles/metadata

Playback Rates

Rate Description
double.MinValue As fast as possible in backward direction (no pacing)
-32 to -1 Rewind (backward playback)
0 to 1 (exclusive) Slow motion
1 Normal speed
1 to 32 Fast forward
double.MaxValue As fast as possible (no pacing)

File Sinks

MpaSink (MPEG Audio)

The MpaSink writes MPEG audio to files with rotation support.

var sink = new VAST.File.MpaSink();
sink.Uri = "output.mp3";

sink.AddStream(0, audioMediaType);
sink.Open();
sink.Start();

// Write samples
sink.PushMedia(0, audioSample);

sink.Stop();

WavSink

The WavSink writes WAV files with automatic header management.

Supported Codecs

Codec Description
PCM (S16, S32, FLT, U8) Uncompressed PCM
GSM / GSM-MS GSM audio
G.711 A-law Telephony codec
G.711 ยต-law Telephony codec
G.722 Wideband telephony
G.723.1 Low bitrate telephony
G.729 Telephony codec
var sink = new VAST.File.WavSink();
sink.Uri = "output.wav";

sink.AddStream(0, audioMediaType);
sink.Open();
sink.Start();

sink.PushMedia(0, audioSample);

sink.Stop();

File Rotation

Both sinks support automatic file rotation for long recordings:

var sink = new VAST.File.MpaSink();
sink.RotationPeriod = 3600; // Rotate every hour (seconds)

int fileNumber = 0;
sink.NextUri = () => $"recording_{fileNumber++:D4}.mp3";

sink.UriRotated += (sender, e) =>
{
    Console.WriteLine($"Completed: {e.Uri}");
    Console.WriteLine($"Duration: {e.FinishTimestamp - e.StartTimestamp}");
};

sink.Open();
sink.Start();

Writing to Stream

Write audio data to a custom stream instead of a file:

var memoryStream = new MemoryStream();

var sink = new VAST.File.WavSink();
sink.Stream = memoryStream;

sink.AddStream(0, audioMediaType);

sink.StateChanged += (sender, state) =>
{
    if (state == MediaState.Opened)
    {
        sink.Start();
    }
};

sink.Open();

...

sink.PushMedia(0, audioSample);

sink.Stop();

// Access the WAV data
byte[] wavData = memoryStream.ToArray();

Delayed Start

Use delayed start when the first media sample may arrive after a significant delay:

sink.Start(delayedStart: true);

// File will be created/recreated when first sample arrives
// Empty file is automatically deleted if no samples received

File Caching

The FileCache class caches file metadata and indices for instant reopening, optimizing VOD scenarios where multiple clients access the same files.

Benefits

  • Instant Open: Cached files open without re-parsing metadata and indices
  • Reduced I/O: Avoid redundant disk reads
  • Memory Efficient: Sharing of references of big indice arrays instead of copying data, automatic expiration of unused entries
  • Thread-Safe: Safe for concurrent access

Usage

var cache = new VAST.File.FileCache();

// Add source to cache
cache.Add("video.mp4", source);

// Retrieve cached source
var cachedSource = cache.GetSource("video.mp4");

// Update access time to prevent expiration
cache.UpdateUseState("video.mp4");

// Cleanup expired entries
cache.Update();

Expiration

Cached entries expire after 600 seconds of inactivity. Call UpdateUseState() to refresh the expiration timer for actively used files.

Media Utilities

The following utilities require the VAST.File.ISO library.

AudioAnalyzer

Analyze audio files for signal metrics:

var analyzer = new VAST.File.Utility.AudioAnalyzer();
if (await analyzer.Execute("audio.wav"))
{
    Console.WriteLine($"RMS Level: {metrics.RmsSignal}");
    Console.WriteLine($"Peak Level: {metrics.PeakSignal}");
    Console.WriteLine($"Silence Duration: {metrics.TotalSilenceDuration}");
}

AudioMixer

Mix audio track of one video file with multiple others with individual volume control:

var mixer = new VAST.File.Utility.AudioMixer();

var inputFiles = new List<VAST.File.Utility.InputFileItem>();
inputFiles.Add(new VAST.File.Utility.InputFileItem
{
    FilePath = "video_and_audio_input.mp4",
	Volume = 1.0
});

inputFiles.Add(new VAST.File.Utility.InputFileItem
{
    FilePath = "audio_input.mp4",
	Volume = 0.3
});

await mixer.Execute(inputFiles, "output.mp4");

VideoMixer

Composite video from multiple sources with positioning:

var mixer = new VAST.File.Utility.VideoMixer();

mixer.InputFile.FilePath = "background.mp4";

mixer.Images.Add(new InputFileItem
{
    FilePath = "overlay.png",
    Waypoints = new List<Waypoint>
    {
        new Waypoint
        {
            Location = new VAST.Common.Rect(10, 10, 100, 100),
            StartTimestamp = 10 * 10_000_000, // 10 seconds in 100 ns units
            Duration = Timestamp.FromSeconds(30),
            Opacity = 0.8
        }
    }
});

mixer.OutputFilePath = "output.mp4";

await mixer.Execute();

ImageExtractor

Extract a single frame from a video file at a specified position and save it as an image:

var extractor = new VAST.File.Utility.ImageExtractor();
var image = await extractor.Execute("video.mp4", TimeSpan.FromSeconds(30));

if (image != null)
{
    // .NET Core: image is SkiaSharp.SKImage
    // .NET Framework: image is System.Drawing.Image

    // Save to file
    using (var stream = File.OpenWrite("thumbnail.jpg"))
    {
        image.Encode(SkiaSharp.SKEncodedImageFormat.Jpeg, 80).SaveTo(stream);
    }
}
else
{
    Console.WriteLine($"Extraction failed: {extractor.ErrorDescription}");
}

The extractor automatically seeks to the nearest keyframe before the specified position and decodes frames until reaching the target timestamp.

Concurrency Control

Limit concurrent Media Foundation decoder instances to manage system resources:

// Default is 5 concurrent instances
ImageExtractor.MaxMediaFoundationInstanceCount = 10;

Concatenator

Join multiple media files

var concatenator = new VAST.File.Utility.Concatenator();

var inputFiles = new List<VAST.File.Utility.InputFileItem>();

// take the first file from 0:00:10.000 to the end
inputFiles.Add(new VAST.File.Utility.InputFileItem
{
    FilePath = "part1.mp4",
    StartTimestamp = 10 * 10_000_000, // 10 seconds in 100 ns units
});

// take the whole second file
inputFiles.Add(new VAST.File.Utility.InputFileItem
{
    FilePath = "part2.mp4",
});

// take the third file from the beginning to the 0:00:45.000
inputFiles.Add(new VAST.File.Utility.InputFileItem
{
    FilePath = "part3.mp4",
    EndTimestamp = 45 * 10_000_000, // 45 seconds in 100 ns units
});

await concatenator.Execute(inputFiles, "output.mp4");

Cut single file

var concatenator = new VAST.File.Utility.Concatenator();

var inputFiles = new List<VAST.File.Utility.InputFileItem>();
inputFiles.Add(new VAST.File.Utility.InputFileItem
{
    FilePath = "input.mp4",
    StartTimestamp = 10 * 10_000_000, // 10 seconds in 100 ns units
    EndTimestamp = 45 * 10_000_000, // 45 seconds in 100 ns units
});

await concatenator.Execute(inputFiles, "output.mp4");

Troubleshooting

Common Issues

Issue Cause Solution
Playlist item fails File not found Use SkipOnError or ContinueOnError mode
MP3 seeking inaccurate VBR without seek table File plays correctly, seek may be approximate
WAV header invalid Incomplete write Ensure Stop() is called before closing
Cache not working Wrong file path Use consistent paths (absolute recommended)
Rotation not triggering RotationPeriod not set Set RotationPeriod > 0

Supported URI Schemes

Scheme Source Types
Local file path or file:// All sources
http:// MpaSource
https:// MpaSource
content: FileSource (playlist)

See Also