MP4 Fragment Writer Sample
The IsoFragmentWriterSample demonstrates how to use IsoFormatParser to generate fragmented MP4 (fMP4) segments from a remote media source. Unlike regular MP4 recording where the entire file is written as a single unit, fragmented MP4 produces an initialization segment followed by a sequence of independent media fragments — the same format used by HLS and MPEG-DASH streaming.
This approach is useful when you need to produce fMP4 segments for a custom streaming pipeline, CDN integration, or segment-based storage.
Overview
The IsoFragmentWriterSample class performs the following:
- Creates a media source from a URI using SourceFactory
- On the
Openedstate, initializes an IsoFormatParser in write mode with segmented output - Writes the initialization fragment containing codec configuration (ftyp + moov boxes)
- Queues incoming samples and processes them on a background task
- Produces a new media fragment (moof + mdat boxes) at each video key frame
Creating the Source
this.source = VAST.Media.SourceFactory.Create(uri);
this.source.Uri = uri;
this.source.NewSample += source_NewSample;
this.source.Error += source_Error;
this.source.StateChanged += source_StateChanged;
this.source.Open();
SourceFactory creates the appropriate source for the URI protocol (rtsp://, rtmp://, srt://, etc.).
Initializing the Fragment Writer
When the source reaches the Opened state, the fragment writer is created and the initialization segment is produced:
case VAST.Media.MediaState.Opened:
this.writer = new VAST.File.ISO.IsoFormatParser(false);
this.writer.Parameters = new VAST.File.ISO.ParsingParameters { UseSegments = true };
var outputStream = new VAST.Common.VersatileBufferStream { AutoGrowth = true };
this.writer.SetOutputStream(outputStream);
var sd = new List<VAST.Common.MediaType>();
for (int i = 0; i < this.source.StreamCount; i++)
{
VAST.Common.MediaType mt = this.source.GetMediaType(i);
this.streams.Add(new StreamContext { InputMediaType = mt });
sd.Add(mt);
}
this.writer.StreamDescriptor = sd;
this.writer.Flush();
this.initializationFragment = outputStream;
this.initializationFragment.SetLength(this.initializationFragment.Position);
this.initializationFragment.Seek(0, System.IO.SeekOrigin.Begin);
// TODO: process initializationFragment accordingly
this.source.Start();
break;
Setup Steps
- Create the parser —
new IsoFormatParser(false)creates a parser in write mode.UseSegments = trueenables fragmented MP4 output. - Set output stream — a VersatileBufferStream with
AutoGrowthis used as an in-memory output buffer. - Set stream descriptors — the source's media types are collected via
GetMediaTypeand assigned toStreamDescriptor. This tells the writer about the video and audio tracks. - Flush the initialization fragment — the first
Flush()writes the ftyp and moov boxes. The resulting stream contains the initialization segment that must be sent to clients before any media fragments.
Queuing Samples
private void source_NewSample(object sender, VAST.Media.NewSampleEventArgs e)
{
this.inputSampleQueue.Enqueue(e.Sample);
e.Sample.AddRef();
}
Incoming samples are queued with AddRef() to keep them alive until the background task processes them.
Producing Fragments
The background task dequeues samples, writes them to the fragment writer, and produces a new fragment at each video key frame:
if (originalInputSample.KeyFrame
&& streamContext.InputMediaType.ContentType == VAST.Common.ContentType.Video)
{
if (this.currentFragmentStream != null)
{
// flush the current fragment
this.writer.Flush();
this.currentFragmentStream.SetLength(this.currentFragmentStream.Position);
this.currentFragmentStream.Seek(0, System.IO.SeekOrigin.Begin);
// TODO: currentFragmentStream contains fragment, process it accordingly
this.currentFragmentStream.Dispose();
this.currentFragmentStream = null;
}
// create new stream for the next fragment
this.currentFragmentStream = new VAST.Common.VersatileBufferStream { AutoGrowth = true };
this.writer.SetOutputStream(this.currentFragmentStream);
}
this.writer.Encode(originalInputSample, originalInputSample.StreamIndex);
Fragment Lifecycle
- Wait for key frame — all samples before the first video key frame are dropped to ensure fragments start with a clean reference point.
- Write samples —
Encodewrites each media sample (video or audio) to the current output stream. - Key frame boundary — when a new video key frame arrives,
Flush()finalizes the current fragment (moof + mdat boxes). The completed fragment stream is available for processing. - New output stream — a fresh
VersatileBufferStreamis created for the next fragment, andSetOutputStreamredirects the writer to it.
Each fragment contains all video and audio samples between two consecutive video key frames. Fragment duration is therefore determined by the source's key frame interval (GOP size).
Output Structure
The writer produces the following fMP4 structure:
| Output | Content | Description |
|---|---|---|
| Initialization fragment | ftyp + moov | Codec configuration, track definitions. Sent once before any media fragments. |
| Media fragment 1 | moof + mdat | Media samples from 1st key frame to 2nd key frame |
| Media fragment 2 | moof + mdat | Media samples from 2nd key frame to 3rd key frame |
| ... | ... | One fragment per GOP |
See Also
- Sample Applications — overview of all demo projects
- .NET Server Demo — parent page with setup instructions, license key configuration, and access URL reference