LStream Reference Manual & Live Streaming Guide
The LStream class is the live network stream transmission and broadcasting engine of the now2sdk framework, derived from the LSink base class. It is designed to encode, multiplex, and publish live program feeds over network protocols such as RTMP (Real-Time Messaging Protocol) and SRT (Secure Reliable Transport) to media servers (e.g. YouTube Live, Twitch, Wowza).
To maintain ultra-low latency, LStream uses a tight frame queue and incorporates automatic black-frame underflow protection to keep network connections alive even during ingest interruptions.
1. Low-Latency Streaming & Cloning Architecture
graph TD
A[Program Stream Source - LObject] -->|pushVideoFrame / pushAudioFrame| B(LStream Sink)
subgraph Video Ingest & Starvation Check
B -->|AVFrame*| C{Queue Size > 10?}
C -->|Yes| D[Drop Frame / Low Latency Bypass]
C -->|No| E[Push to Frame Queue]
E -->|Video Frame| F[streamLoop Thread]
F -->|Queue Empty & Stream Active| G[Generate mathematically black YUV420P frame]
F -->|Normal Frame| H[SwsContext Scale & Normalization]
G --> I[Video Encoder - e.g. libx264]
H --> I
end
subgraph Dual Playout Multiplexing
I -->|Encoded AVPacket| J{Stream Cloning Enabled?}
J -->|Yes| K[Muxer 2 - Write to local MP4 Clone]
I -->|Encoded AVPacket| L[Muxer 1 - Write to Network RTMP/SRT URL]
end
subgraph Audio Processing
B -->|Audio AVFrame*| M[Audio resampler & buffer]
M --> N[Resample to FLTP 48kHz Stereo]
N --> O[Audio Encoder - e.g. AAC]
O -->|Encoded AVPacket| L
O -->|Encoded AVPacket| K
end
Key Technical Details:
- Tight Queue Low-Latency Enforcement: The ingestion queue size is limited to
10frames (compared to60inLRecorder). If network bottlenecks occur, older frames are dropped immediately to prioritize the real-time live head. - Black-Frame Starvation Protection: If the source feed stops distributing frames but the stream is active,
LStreamautomatically generates dummy black video frames mathematically (Y = 16,U = 128,V = 128YUV420p) and passes them to the encoder. This maintains packet flow, preventing connection drops. - Dual Output Stream Cloning (Local Backup): Allows recording a local backup copy of the stream directly to an MP4 file on the fly. The clone muxer context
m_cloneFmtCtxwrites the encoded video/audio packets directly to disk, avoiding double-compression CPU overhead. - Strict CBR Rate Control: Supports H.264 constant bitrate (CBR) profiles for broadcasting. When activated, it restricts encoder bitrates by setting
rc_max_rate = rc_min_rate = bit_ratewith equal buffer sizes and H.264nal-hrdoptions. - Bandwidth Metrics Accumulator: Aggregates total bytes sent and computes current upload bandwidth speeds in Mbps (
bitrateMbps) at 1-second intervals.
2. API Reference & Public Methods
📌 Stream Publishing Controls
void setStreamUrl(const std::string& url);
Sets the destination server endpoint URL (e.g. "rtmp://live.twitch.tv/app/" or "srt://12.34.56.78:9000").
void setStreamKey(const std::string& key);
Sets the stream authentication key. If not already present, it is appended automatically to the RTMP URL.
void setStreamClone(bool enable);
Toggles simultaneous local backup recording.
void setStreamClonePath(const std::string& path);
Sets the folder directory where the backup local .mp4 stream clone will be saved.
📌 Target Encoding Controls
void setVideoCodec(const std::string& codec);
Sets the target video encoder (e.g. "libx264", "h264_nvenc").
void setAudioCodec(const std::string& codec);
Sets the target audio encoder (e.g. "aac").
void setVideoBitrate(int bitrateKbps);
Sets the target video encoding bitrate (e.g. 4000 for 4 Mbps).
void setAudioBitrate(int bitrateKbps);
Sets the target audio encoding bitrate (e.g. 128 for 128 kbps).
void setVideoFormat(const videoFormatProps& props);
void getVideoFormat(videoFormatProps& props);
Configures target video resolution and frame rate.
void setAudioFormat(const audioFormatProps& props);
void getAudioFormat(audioFormatProps& props);
Configures target audio sample rate and channel layout.
📌 Custom Configuration & Properties
void customSet(const std::string& params);
Parses a space-separated string of key-value parameters (e.g. "preset=ultrafast tune=zerolatency profile=main") to inject directly into the FFmpeg encoder context.
void setProps(const std::string& key, const std::string& value);
Sets dynamic configuration properties at runtime. Key options are detailed in Section 3.
📌 Process Control
void streamObject(LObject* source);
Binds LStream to an active streaming source (e.g. LMixer, LLive, LFile).
bool Start();
Initializes demuxers, opens server sockets, writes headers, and starts the background publishing thread loop. Returns true if successful.
void Stop();
Stops publishing, writes stream trailers, closes sockets, finalizes local clone files, and joins the background thread.
void statusGet(int& status);
Retrieves the publishing status: 1 if actively streaming, 0 if idle.
📌 Streaming Performance Metrics
struct Stats {
double bitrateMbps = 0.0; // Current upload network bandwidth speed in Mbps
int64_t totalBytesSent = 0; // Accumulated bytes sent during this session
int width = 0; // Stream video width
int height = 0; // Stream video height
double fps = 0.0; // Stream video framerate standard
int droppedFrames = 0; // Total dropped video frames during buffer congestion
int bufferedFrames = 0; // Currently buffered video frames in queue
std::string streamUrl = ""; // Destination URL
bool isConnected = false; // True if socket connection is alive
};
void getStats(Stats& stats);
Fills a Stats structure with upload speeds, network packet dropouts, and socket diagnostics.
3. Properties & Configuration Options
All options listed below are configured at runtime via the setProps(const std::string& key, const std::string& value) interface:
| Key | Value Type | Default Value | Description |
|---|---|---|---|
preset |
string |
"ultrafast" |
x264/nvenc encoding compression complexity preset (e.g. "ultrafast", "veryfast", "fast", "medium"). |
tune |
string |
"zerolatency" |
x264/nvenc latency optimizer profile (e.g. "zerolatency", "film"). |
gop |
int |
"2" |
GOP size interval in seconds (default is 2 seconds, which resolves keyframe intervals based on framerate). |
profile |
string |
"main" |
H.264 profile restriction (e.g. "baseline", "main", "high"). |
bitrate_mode |
string |
"cbr" |
Encoding rate control scheme:"cbr": Strict Constant Bitrate (enables HRD buffers)."vbr": Variable Bitrate. |
4. Live Stream Publisher Dashboard Example
This Qt C++ dashboard implementation (adapted from the Recorder/Playout Example structures) shows how to configure LStream to publish live RTMP feeds, manage simultaneous local MP4 clones, and monitor bandwidth metrics in real time.
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "LLive.h"
#include "LStream.h"
#include <QTimer>
#include <QMessageBox>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
ui(new Ui::MainWindow),
m_live(new LLive()),
m_stream(new LStream()),
m_statsTimer(new QTimer(this)),
m_isStreaming(false)
{
ui->setupUi(this);
// 1. Initialize camera ingest (1080p50)
videoFormatProps format;
format.setVideoFormat = vF::HD1080_50p;
m_live->setVideoFormat(format);
m_live->Start();
// 2. Setup dynamic stream updates at 500ms intervals
connect(m_statsTimer, &QTimer::timeout, this, &MainWindow::updateStreamStats);
// 3. Connect UI Control Triggers
connect(ui->startStreamBtn, &QPushButton::clicked, this, &MainWindow::onStartStreamingClicked);
connect(ui->stopStreamBtn, &QPushButton::clicked, this, &MainWindow::onStopStreamingClicked);
connect(ui->selectClonePathBtn, &QPushButton::clicked, this, &MainWindow::onSelectClonePathClicked);
}
MainWindow::~MainWindow() {
if (m_isStreaming) m_stream->Stop();
m_live->Stop();
delete m_statsTimer;
delete m_stream;
delete m_live;
delete ui;
}
// ─── Step 1: Select Local Backup Path ───
void MainWindow::onSelectClonePathClicked() {
QString folder = QFileDialog::getExistingDirectory(this, "Select Backup Directory", "/home/user");
if (!folder.isEmpty()) {
ui->cloneFolderEdit->setText(folder);
}
}
// ─── Step 2: Bind Specifications & Start Stream ───
void MainWindow::onStartStreamingClicked() {
if (m_isStreaming) return;
// 1. Server Targets
QString endpoint = ui->serverUrlEdit->text(); // e.g. "rtmp://live.twitch.tv/app/"
QString streamKey = ui->streamKeyEdit->text();
if (endpoint.isEmpty()) {
QMessageBox::warning(this, "Streaming", "Please enter a valid server URL.");
return;
}
m_stream->setStreamUrl(endpoint.toStdString());
m_stream->setStreamKey(streamKey.toStdString());
// 2. Local Backup Recording (Clone)
bool enableClone = ui->enableCloneCheckbox->isChecked();
QString cloneFolder = ui->cloneFolderEdit->text();
if (enableClone && !cloneFolder.isEmpty()) {
m_stream->setStreamClone(true);
m_stream->setStreamClonePath(cloneFolder.toStdString());
} else {
m_stream->setStreamClone(false);
}
// 3. Normalization Formats
videoFormatProps vFormat;
vFormat.setVideoFormat = vF::HD1080_50p;
m_stream->setVideoFormat(vFormat);
audioFormatProps aFormat;
aFormat.setAudioFormat = aF::_16B_48K_2CH;
m_stream->setAudioFormat(aFormat);
// 4. Codecs and Bitrate (4 Mbps CBR Video + 128 kbps AAC Audio)
m_stream->setVideoCodec("libx264");
m_stream->setAudioCodec("aac");
m_stream->setVideoBitrate(4000);
m_stream->setAudioBitrate(128);
// 5. Dynamic latency props
m_stream->setProps("preset", "ultrafast");
m_stream->setProps("tune", "zerolatency");
m_stream->setProps("profile", "main");
m_stream->setProps("bitrate_mode", "cbr"); // Use strict Constant Bitrate
m_stream->setProps("gop", "2"); // GOP size is 2 seconds (100 frames)
// 6. Connect source live camera
m_stream->streamObject(m_live);
// 7. Publish
if (m_stream->Start()) {
m_isStreaming = true;
ui->startStreamBtn->setEnabled(false);
ui->stopStreamBtn->setEnabled(true);
ui->destGroup->setEnabled(false); // lock form
m_statsTimer->start(500);
} else {
QMessageBox::critical(this, "Network Error", "Failed to resolve host or connect to RTMP socket.");
}
}
// ─── Step 3: Cease Stream Playout ───
void MainWindow::onStopStreamingClicked() {
if (!m_isStreaming) return;
m_statsTimer->stop();
m_stream->Stop(); // Safely flushes encoder queues and closes sockets/files
m_isStreaming = false;
ui->startStreamBtn->setEnabled(true);
ui->stopStreamBtn->setEnabled(false);
ui->destGroup->setEnabled(true);
ui->statsBitrateVal->setText("0.00 Mbps");
ui->statsStatusVal->setText("DISCONNECTED");
}
// ─── Step 4: Monitor Upload Bandwidth ───
void MainWindow::updateStreamStats() {
if (!m_isStreaming) return;
LStream::Stats stats;
m_stream->getStats(stats);
// Display connection stats
ui->statsBitrateVal->setText(QString("%1 Mbps").arg(stats.bitrateMbps, 0, 'f', 2));
double totalMB = static_cast<double>(stats.totalBytesSent) / (1024.0 * 1024.0);
ui->statsDataSentVal->setText(QString("%1 MB").arg(totalMB, 0, 'f', 1));
ui->statsDropsVal->setText(QString::number(stats.droppedFrames));
ui->statsBufferVal->setText(QString("%1 frames").arg(stats.bufferedFrames));
if (stats.isConnected) {
ui->statsStatusVal->setText("LIVE STREAMING ACTIVE");
ui->statsStatusVal->setStyleSheet("color: #16a34a; font-weight: bold;");
} else {
ui->statsStatusVal->setText("SOCKET RE-CONNECTING...");
ui->statsStatusVal->setStyleSheet("color: #ea580c; font-weight: bold;");
}
}