How FileSequenceDriver Manages Frame Indexing for Image Sequences in ApraPipes
The FileSequenceDriver delegates all frame indexing logic to a pluggable FilenameStrategy class, which maintains an internal counter, formats filenames using Boost patterns, and handles looping, bounds checking, and play/pause semantics while exposing explicit seek capabilities through the jump() method.
The ApraPipes framework provides robust tools for media pipeline construction, and the FileSequenceDriver is a critical component for handling sequential image files. Understanding how FileSequenceDriver frame indexing works is essential for building reliable video processing workflows that require precise control over frame ordering, random access, or looping playback. The driver achieves this flexibility by decoupling the indexing algorithm from the I/O operations through a strategy pattern implemented in base/src/FileSequenceDriver.cpp.
Architecture: Delegation to FilenameStrategy
The FileSequenceDriver does not implement indexing logic directly. Instead, it maintains a pointer to a FilenameStrategy object that encapsulates all frame tracking and filename generation. When the driver needs to read or write a frame, it calls mStrategy->GetFileNameToUse(true, index), passing a reference to a uint64_t that receives the current frame index. This design allows the driver to focus on file I/O while the strategy handles the complexities of sequence navigation.
The strategy maintains an internal mCurrentIndex member initialized to mStartIndex at construction. This counter represents the logical position within the image sequence, independent of the actual filesystem operations.
Filename Generation and Pattern Matching
Boost Format String Processing
The concrete filename is constructed in FilenameStrategy::GetFileNameForCurrentIndex located in base/src/FilenameStrategy.cpp. This method uses Boost's format facility to substitute the current index into the user-supplied pattern:
// From base/src/FilenameStrategy.cpp lines 13-18
std::string FilenameStrategy::GetFileNameForCurrentIndex() {
boost::format formatter(mPattern);
formatter % mCurrentIndex;
return formatter.str();
}
The pattern string (e.g., "frame_%04d.png") determines the zero-padding and formatting of the frame number. This approach supports complex naming conventions including multiple wildcards and non-sequential file layouts.
Play Mode and Index Incrementation
The strategy only advances mCurrentIndex when the driver is in play mode. The FilenameStrategy::incrementIndex method (lines 44-50 in base/src/FilenameStrategy.cpp) checks the internal play flag before modifying the counter. When notifyPlay(true) is called on the driver, subsequent read operations automatically increment the index after each frame retrieval. When notifyPlay(false) is set, the index remains fixed, enabling repeated reads of the same frame for random-access workflows.
Looping and Bounds Management
Automatic Reset on Missing Files
For robust playback of incomplete sequences, the strategy implements a read loop mechanism. In base/src/FilenameStrategy.cpp (lines 70-82), if mReadLoop is enabled and the generated filename does not exist on disk, the strategy resets mCurrentIndex to mStartIndex and attempts to locate the file again. This handles cases where frames are missing from the middle of a sequence or when the end of the sequence is reached and continuous looping is desired.
Max Index Wrapping
The strategy enforces upper bounds through mMaxIndex. After each increment in incrementIndex (lines 90-96), the code validates whether mCurrentIndex exceeds the maximum allowed value. When the bounds are exceeded, the index wraps back to mStartIndex, creating a circular buffer behavior for bounded sequences.
Random Access and Explicit Seeking
For applications requiring non-linear access, the driver exposes a jump(uint64_t index) method implemented in base/src/FileSequenceDriver.cpp (lines 77-80). This forwards the request to FilenameStrategy::jump, which validates and clamps the target index against mStartIndex before setting mCurrentIndex directly (lines 52-60 in base/src/FilenameStrategy.cpp).
This capability allows precise positioning within the sequence without sequential iteration:
// Seek to frame 42 without reading intermediate frames
seqDriver.jump(42);
uint64_t frameIdx = 0;
BufferMaker bm;
if (seqDriver.ReadP(bm, frameIdx)) {
std::cout << "Positioned at frame " << frameIdx << "\n";
}
Special Modes: Append and Single-File Writing
When operating in append mode (typically used by writers), the indexing behavior changes significantly. As implemented in base/src/FilenameStrategy.cpp (lines 200-204), append mode forces mCurrentIndex to 0 on every call, ensuring that filename generation produces a consistent single-file output regardless of the logical frame count. This distinguishes streaming writes from sequence writes while reusing the same strategy interface.
Practical Implementation Examples
The following examples demonstrate typical FileSequenceDriver frame indexing patterns for different use cases:
// Example 1: Sequential playback with looping
FileSequenceDriver seqDriver(
"frames/frame_%04d.png", // Wildcard pattern
0, // mStartIndex
-1, // mMaxIndex (unlimited)
true); // Enable mReadLoop
seqDriver.Connect();
seqDriver.notifyPlay(true); // Enable auto-increment
// Read 10 frames sequentially
for (int i = 0; i < 10; ++i) {
uint64_t frameIdx = 0;
BufferMaker bm;
if (seqDriver.ReadP(bm, frameIdx)) {
std::cout << "Read frame index: " << frameIdx << "\n";
}
}
// Example 2: Random access without auto-increment
seqDriver.notifyPlay(false); // Disable auto-stepping
seqDriver.jump(5); // Set mCurrentIndex to 5
uint64_t idx = 0;
BufferMaker bm;
seqDriver.ReadP(bm, idx); // Reads frame 5
seqDriver.ReadP(bm, idx); // Reads frame 5 again (no increment)
// Example 3: Append mode for single-file output
// In append mode, index is forced to 0 regardless of calls
FileSequenceDriver writer("output.png", 0, 0, false);
writer.SetAppendMode(true); // Internal flag affecting strategy
// All writes target the same filename
Summary
- FileSequenceDriver delegates all frame indexing to FilenameStrategy, separating I/O logic from sequence navigation.
- The strategy maintains
mCurrentIndexand formats filenames using Boost format strings with configurable zero-padding. - Frame advancement only occurs when
notifyPlay(true)is active; otherwise the index remains fixed for random access. - Looping (
mReadLoop) resets tomStartIndexwhen files are missing, whilemMaxIndexenforces upper bounds with wrapping. - Explicit seeking is available through
jump(uint64_t index), which validates bounds before updating the internal counter. - Append mode overrides indexing to force index 0 for single-file write operations.
Frequently Asked Questions
How does FileSequenceDriver handle missing frames in a sequence?
When mReadLoop is enabled and the strategy cannot locate the file for the current index, it automatically resets mCurrentIndex to mStartIndex and searches again from the beginning. This logic in base/src/FilenameStrategy.cpp (lines 70-82) ensures continuous playback even when frames are missing from the middle of the sequence.
What is the difference between notifyPlay(true) and notifyPlay(false)?
Calling notifyPlay(true) enables automatic frame advancement after each read operation via incrementIndex, while notifyPlay(false) freezes the current index. This distinction allows the same driver to support both streaming playback and static random-access modes without reconstructing the strategy object.
Can I seek to an arbitrary frame without reading all previous frames?
Yes. The jump(uint64_t index) method in FileSequenceDriver forwards the request to FilenameStrategy::jump, which validates the target against mStartIndex and updates mCurrentIndex directly. This provides O(1) random access to any position in the sequence.
How does the driver format frame numbers with leading zeros?
The strategy uses Boost.Format in GetFileNameForCurrentIndex to process the pattern string (e.g., "%04d"). The formatter substitutes mCurrentIndex into the pattern and applies the specified padding, returning a string like "frame_0005.png" for index 5 with a 4-digit wildcard.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →