Skip to content

Mixer Demo

This demo shows how to stream two video sources combined into a single preview sink using the AVT SDK’s mixer feature. It demonstrates camera device selection, overlay configuration from a mixed video output.

Code Example

#include "AvtCore.h"
#include "AvtString.h"
#include "Graph/AvtGraph.h"
#include "Source/AvtVideoSourceProperty.h"
#include "DeviceHelper.h"
#include "MixerHelper.h"
#include "Utility/ToString.h"

#include <iostream>
#include <csignal>
#include <unistd.h>
#include <vector>

using namespace AVTSDK;
using namespace AVTSDK::Utility;
using namespace AVTSDK::Log;
using namespace AVTSDK::Graph;
using namespace AVTSDK::Graph::Mixer;
using namespace AVTSDK::Source;

using namespace std;

bool running = true;

void signalHandler(int signum)
{
    running = false;
}

int main()
{
    AvtCore::init(AvtLogLevel::LOG_LEVEL_WARNING, AvtLogType::LOG_TYPE_DELAYED);

    AvtGraph *graph = nullptr;
    vector<int> sourceIDList;
    do {

        DeviceHelper devHelper;
        auto vDevCount = devHelper.getVideoDeviceCount();
        if (vDevCount == 0) {
            cout << "No Device" << endl;
            break;
        }

        AvtGraphFeature ft;
        ft.mVideo.mEnable = true;
        auto &vFt = ft.mVideo.mProperty;
#if defined(__QCOM__)
        vFt.mGraphicsAPI = AvtGraphicsAPI::GRAPHICS_API_QCOM;
#else
        vFt.mGraphicsAPI = AvtGraphicsAPI::GRAPHICS_API_NV;
#endif

        AvtVideoSourceProperty firstProp;
        firstProp.mLockDevice = true;

        if (!devHelper.selectVideoDevice(firstProp)) {
            cout << "Failed to select the correct device or device firstProperty" << endl;
            break;
        }

        AvtVideoSourceProperty secondProp;
        secondProp.mLockDevice = true;
        if (!devHelper.selectVideoDevice(secondProp)) {
            cout << "Failed to select the correct device or device secondProperty" << endl;
            break;
        }

        MixerHelper mixerHelper;
        vFt.mPreview.mEnable = true;
        vFt.mMixer.mEnable = true;
        auto &vFtMixerProp = vFt.mMixer.mProperty;
        mixerHelper.setMixerProperty(vFtMixerProp);

        graph = new AvtGraph(ft);

        AvtResult result;
        graph->createGraph();

        int sourceID;
        result = graph->addSource(firstProp, sourceID);
        if (result != AvtResult::AVT_RESULT_OK) {
            cout << "Failed to add first video source" << endl;
            break;
        }
        sourceIDList.push_back(sourceID);

        result = graph->addSource(secondProp, sourceID);
        if (result != AvtResult::AVT_RESULT_OK) {
            cout << "Failed to add second video source" << endl;
            break;
        }
        sourceIDList.push_back(sourceID);

        cout << endl << "start to set overlay for first camera: "
             << firstProp.mDevice.mName.getString() << " ===="<< endl;
        sourceID = sourceIDList[0];
        result = mixerHelper.setOverlay(graph, sourceID, vFtMixerProp);
        if (result != AvtResult::AVT_RESULT_OK) {
            cout << "Failed to set overlay for Source_" << sourceID  << endl;
            break;
        }

        cout << endl << "==== start to set overlay for secnod camera: "
             << secondProp.mDevice.mName.getString() << " ===="<< endl;
        sourceID = sourceIDList[1];
        result = mixerHelper.setOverlay(graph, sourceID, vFtMixerProp);
        if (result != AvtResult::AVT_RESULT_OK) {
            cout << "Failed to set overlay for Source_" << sourceID  << endl;
            break;
        }

        result = graph->runGraph();
        if (result != AvtResult::AVT_RESULT_OK) {
            cout << "Failed to run graph";
            break;
        }

        signal(SIGINT, signalHandler);

        // Pause briefly so the set menu shows up clearly after the run graph logs flood in.
        sleep (5);

        while (running) {
            mixerHelper.setOpacityaValue(graph, sourceIDList);
        }
    } while (false);


    if (graph)
        delete graph;

    AvtCore::uninit();
    return 0;
}

Explanation

Helper Classes

DeviceHelper and MixerHelper are helper classes written for the demos and are not part of the library. For details about how to handle devices and mixers, please check the source code of these classes.

  1. Initialization

    The program begins by initializing the AVT core with a warning-level log configuration.

  2. Device Availability Check

    It checks whether any video input devices are available. If none are found, the program exits early.

  3. Graph and Feature Setup

    AvtGraphFeature is the configuration structure for AvtGraph. It is configured to enable video preview, and the appropriate graphics API (QCOM or NV) is selected based on the platform.

    The video mixer is also enabled via vFt.mMixer.mEnable = true, and its properties are configured using MixerHelper.

    Mixer mode in AvtGraph

    To add multiple video sources to the graph, you need to enable the video mixer feature before creating the graph, or you may encounter errors when adding more than one video source. If you have no idea what AvtGraph is, please refer to AVT SDK Multimedia Framework.

  4. Device and Mixer Selection

    The user is prompted twice to select two video input devices via DeviceHelper::selectVideoDevice(). Then, the mixer resolution and frame rate are determined using MixerHelper.setMixerProperty().

  5. Graph Creation

    A new AvtGraph instance is created using the configured AvtGraphFeature.

  6. Source Node Addition

    Both selected video sources are added to the graph using addSource().

  7. Mixer Overlay Setup

    The two input video sources are individually mapped into the mixer layout using MixerHelper::setOverlay(), allowing them to be rendered simultaneously in a single output frame.

    Note

    MixerHelper::setOverlay() is just a helper function to interact with the user and configure the AvtMixerProperty.

  8. Graph Execution

    The graph is executed using runGraph(), which starts the video processing pipeline.

  9. Runtime Loop and Termination

    A signal handler is registered to listen for SIGINT (Ctrl+C). During runtime, the opacity values of the mixed sources are updated continuously in a loop using setOpacityaValue(). Upon termination, the graph is deleted and the AVT core is uninitialized.