Free now2sdk Core is completely free — no license key required   See what's included →
SDK Modules Docs Release Notes Pricing GitHub
Get the SDK →

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:


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;");
    }
}