LStinger Reference Manual & Transition Clip Guide
The LStinger class is the transition clip management engine of the now2sdk framework, derived from the LObject stream source. It is designed to pre-load a video file or an image sequence containing Alpha Transparency channels (e.g. VP9, ProRes 4444, PNG sequences), caching scaled frames directly in system memory as YUVA420p textures to enable instant transparent overlay transitions (stinger transitions).
1. Frame Loading & Transparent Playout Architecture
graph TD
A[load path] --> B{Path contains % ?}
B -->|Yes| C[Configure FFmpeg image2 demuxer for sequence]
B -->|No| D[Configure standard video demuxer]
C --> E[avformat_open_input & find stream]
D --> E
E --> F[Alloc SwsContext for target dimensions]
F --> G[Scale & Convert raw frames to YUVA420P]
G --> H[Cache AVFrame* inside m_frames vector]
I[playOnce trigger] --> J[Spawn playbackLoop Thread]
J --> K[Step through cached frames sequentially]
K --> L{Licensed?}
L -->|No| M[Overlay Watermark on-the-fly]
L -->|Yes| N[No watermark bypass]
M --> O[Call distributeVideoFrame to downstream mixers/preview]
N --> O
Key Technical Details:
- Image Sequence & Video Parsing: Supports single video files or sequential image paths using format parameters (e.g.,
/stinger/frame_%04d.png). If a percentage tag%is detected, the demuxer switches toimage2mode and sets the target framerate context. - Alpha Channel Normalization (YUVA420p): Converts incoming pixel configurations (e.g.
RGBA,BGRA,YUVA) to standard YUVA420p format usinglibswscaleto resolve transparency layers. If the source file lacks an alpha channel, it allocates and sets the alpha plane to solid opacity (255). - RAM Playout Cache: Pre-loads and scales all frames before use. Playout is performed directly from memory, guaranteeing zero disk latency and zero timing drift during live broadcast switches.
- Licensing Constraint: Checks if licensed under modular check
isLicensed("LStinger"). If unlicensed, overlays the"now2sdk"watermark on all transition frames.
2. API Reference & Public Methods
All public methods available in the LStinger class:
📌 Licensing Controls
bool setLicense(const std::string& licenseKey);
Registers a license key for the stinger instance. Returns true if valid.
bool isLicensed() const;
Checks if the stinger module is licensed on the host system.
📌 Clip Loading & Lifecycle
bool load(const std::string& path, int targetWidth, int targetHeight, double fps = 25.0);
Decompresses, scales, and caches frames from the target path.
path: Absolute video file path or image sequence format tag (e.g.,"/broadcasting/stinger_%03d.png").targetWidth/targetHeight: Output dimensions.fps: playout framerate profile.
void clear();
Clears the frame memory cache and releases allocated AVFrame objects. Called automatically in the destructor.
📌 Playback Control
void playOnce();
Triggers playout once. Spawns a background thread to sequentially distribute frames at the target frame rate.
void stop();
Terminates the active playback loop and joins the background thread.
📌 Status & Configuration Getters/Setters
Since LStinger is a lightweight transition source, it does not implement a setProps(key, value) dictionary interface. Instead, settings are configured directly using the following type-safe C++ methods:
| Method | Return / Param Type | Description |
|---|---|---|
isLoaded() |
bool |
Returns true if frames are currently cached in memory. |
getFrameCount() |
int |
Returns the total number of frames in the loaded transition clip. |
setFPS(fps) |
double (param) |
Sets/overrides the playout frame rate target. |
getFPS() |
double |
Returns the current playout frame rate target. |
statusGet(status) |
int& (out param) |
Sets status value: 1 if playing, 0 if idle. |
3. Transition Overlay Integration Example
This example demonstrates how to set up LStinger to overlay a transition graphic dynamically on top of a program feed. It uses a custom mixer or switcher to overlay the stinger frames over a live camera during a scene change.
#include "LStinger.h"
#include "LLive.h"
#include "LPreview.h"
#include <iostream>
class TransitionManager {
public:
TransitionManager()
: m_stinger(new LStinger()),
m_liveFeed(new LLive()),
m_preview(new LPreview())
{
// 1. Initialize license
m_stinger->setLicense("YOUR_LICENSE_KEY_HERE");
// 2. Configure camera ingest source (1080p50)
videoFormatProps format;
format.setVideoFormat = vF::HD1080_50p;
m_liveFeed->setVideoFormat(format);
m_liveFeed->Start();
// 3. Pre-load transparent overlay clip (e.g. 150 frames, 3-second transition)
// Loading is synchronous, pre-loading into RAM cache
bool success = m_stinger->load("/var/broadcasting/transitions/swipe_%03d.png", 1920, 1080, 50.0);
if (success) {
std::cout << "[Stinger] Loaded successfully! Total Frames: "
<< m_stinger->getFrameCount() << std::endl;
} else {
std::cerr << "[Stinger] Failed to load image sequence!" << std::endl;
}
// 4. Set preview target
m_preview->previewEnable(nullptr, true, true);
m_preview->previewObject(m_liveFeed); // Initially preview live feed
}
~TransitionManager() {
m_stinger->stop();
m_stinger->clear();
m_liveFeed->Stop();
delete m_preview;
delete m_stinger;
delete m_liveFeed;
}
// Triggered when a transition is requested
void triggerStingerCut() {
if (!m_stinger->isLoaded()) return;
std::cout << "[Transition] Triggering stinger overlay..." << std::endl;
// Route the preview to show the transparent stinger overlay
m_preview->previewObject(m_stinger);
// Start playout of stinger frames (runs in background thread)
m_stinger->playOnce();
// Wait for the cut point (e.g., middle of transition, frame 75 of 150)
// In a real broadcast switcher, this triggers the background scene cut.
std::thread([this]() {
int cutPointMs = (75.0 / 50.0) * 1000; // 1.5 seconds offset
std::this_thread::sleep_for(std::chrono::milliseconds(cutPointMs));
std::cout << "[Transition] CUT POINT REACHED - Performing background source switch!" << std::endl;
// Switch cameras/sources here...
// Wait for transition to fully complete (150 frames = 3 seconds total)
int remainingMs = ((150 - 75) / 50.0) * 1000;
std::this_thread::sleep_for(std::chrono::milliseconds(remainingMs));
std::cout << "[Transition] Transition complete. Restoring camera preview." << std::endl;
// Route preview back to program camera
m_preview->previewObject(m_liveFeed);
}).detach();
}
private:
LStinger* m_stinger;
LLive* m_liveFeed;
LPreview* m_preview;
};