Skip to content

AidStream 开发者文档

简介

AidStream 是用来构建流媒体应用的视频框架,其目标是要简化视频+AI应用程序的开发中需要插入算法的程序构建。 AidStream 基于pipeline的概念,aidstream中的pipeline元素也非常简单,只分为输入端和输出端,使用函数调用即可,且在pipeline开始后时,sdk可以在输入端和输出端之间通过回调函数获取RGB数据,在回调函数中可以将此RGB数据经算法处理后再返回给pipeline,经处理后的RGB数据会继续pipeline的后续输出过程。 V3版本是对之前AidStream初始版本的一次重大升级。

AidStream通用流程如下图所示:

AidStream通用流程图

通用示例参考

tip
**💡💡注意💡💡**
<自定义输入地址:视频源地址>:摄像头IP地址  

例如 "rtsp://admin:aidlux123@192.168.111.236:554/h264/ch1/main/av_stream"

<自定义输出地址:流媒体服务器地址>:可以将视频流推入的地址   

例如 "rtsp://admin:aidlux123@192.168.111.115:8554/test-111"

系统版本ubuntu2204-硬件型号8550

<示例:基于 Aidlite 工具链的使用示例>

bash
gst-launch-1.0 -e rtspsrc location=<自定义输入地址:视频源地> latency=200 protocols=tcp \
! rtph264depay ! h264parse config-interval=-1 ! qtic2vdec ! qtivtransform engine=2 \
! video/x-raw\(memory:GBM\),format=NV12,width=3840,height=2160,framerate=25/1 ! tee name=t \
t. ! queue ! qtimetamux name=mux \
t. ! queue max-size-buffers=4 max-size-bytes=0 max-size-time=0 \
! qtivtransform engine=2 ! video/x-raw\(memory:GBM\),format=BGR,width=640,height=640   \
! videorate ! video/x-raw\(memory:GBM\),format=BGR,width=640,height=640,framerate=25/1 \
! ast-aidlite model=./cutoff_yolov5s_sigmoid_qcs8550_w8a8.qnn236.ctx.bin roi-width=3840 roi-height=2160 ! text/x-raw,format=utf8 ! mux. \
mux.src ! qtioverlay  ! qtic2venc ! h264parse ! rtspclientsink protocols=tcp location=<自定义输出地址:流媒体服务器地>

编码插件:qtic2venc 解码插件:qtic2vdec

qtivtransform 插件

系统版本ubuntu2204-硬件型号6490-932

bash
gst-launch-1.0 rtspsrc location=<自定义输入地址:视频源地> \
! rtph264depay \
! h264parse \
! v4l2h264dec capture-io-mode=5 output-io-mode=2 \
! qtivtransform \
! video/x-raw,format=RGB \
! qtivtransform \
! video/x-raw\(memory:GBM\),format=NV12,colorimetry=bt601 \
! v4l2h264enc capture-io-mode=2 output-io-mode=5 \
! h264parse \
! rtspclientsink location=<自定义输出地址:流媒体服务器地>

该实例中用到了如下插件:

qtivtransform 插件

v4l2h264dec 插件

v4l2h264enc 插件

系统版本ubuntu2404-硬件型号6490-932

bash
gst-launch-1.0 rtspsrc location=<自定义输入地址:视频源地> latency=200 protocols=tcp \
! rtph264depay \
! h264parse \
! v4l2h264dec capture-io-mode=4 output-io-mode=4 \
! video/x-raw,format=NV12,colorimetry=bt709 \
! qtivtransform engine=3 \
! video/x-raw,format=RGB,colorimetry=bt709 \
! qtivtransform engine=3 \
! video/x-raw,format=NV12,colorimetry=bt709 \
! v4l2h264enc capture-io-mode=4 output-io-mode=4 \
! h264parse \
! rtspclientsink location=<自定义输出地址:流媒体服务器地>

该实例中用到了如下插件:

qtivtransform 插件

v4l2h264dec 插件

v4l2h264enc 插件

tips
**💡💡6490 aidlux2204融合系统 执行时请设置以下环境变量💡💡**

export GST_PLUGIN_PATH=/opt/ubun20/usr/lib/aarch64-linux-gnu/gstreamer-1.0/ && export  
LD_LIBRARY_PATH=/opt/ubun20/usr/lib/aarch64-linux-gnu/:/opt/ubun20/usr/lib:/usr/local/lib/aidlux_opencv/lib/:$LD_LIBRARY_PATH

系统版本aidlux2204融合系统-硬件型号6490-932

bash
gst-launch-1.0 rtspsrc location=<自定义输入地址:视频源地> \
! rtph264depay \
! h264parse \
! qtivdec \
! qtivtransform \
! video/x-raw,format=RGB \
! qtivtransform \
! qtic2venc \
! video/x-h264,profile=baseline \
! h264parse \
! rtspclientsink location=<自定义输出地址:流媒体服务器地>

该实例中用到了如下插件:

编码插件:qtic2venc 解码插件:qtivdec

qtivtransform 插件

系统版本aidlux2404融合系统-硬件型号6490-932

bash
gst-launch-1.0 rtspsrc location=<自定义输入地址:视频源地> \
! rtph264depay \
! h264parse \
! qtivdec \
! qtivtransform \
! video/x-raw,format=RGB \
! qtivtransform \
! qtic2venc \
! video/x-h264,profile=baseline \
! h264parse \
! rtspclientsink location=<自定义输出地址:流媒体服务器地>

编码插件:qtic2venc 解码插件:qtivdec

该实例中用到了如下插件:

qtivtransform 插件

v4l2h264dec 插件

v4l2h264enc 插件

系统版本ubuntu2404-硬件型号8550-972

H.264

bash
gst-launch-1.0 rtspsrc location=<自定义输入地址:视频源地> \
! rtph264depay \
! h264parse \
! qtic2vdec \
! qtivtransform engine=2 \
! video/x-raw,format=RGB \
! qtivtransform engine=2 \
! qtic2venc \
! queue \
! h264parse \
! rtspclientsink protocols=tcp location=<自定义输出地址:流媒体服务器地>

该实例中用到了如下插件:

编码插件:qtic2venc 解码插件:qtic2vdec

qtivtransform 插件

纯四合一

tips
<1920,1080> 可替换为其他可用分辨率如<3840,2160>
bash
export WAYLAND_DISPLAY=wayland-1 && export XDG_RUNTIME_DIR=/run/user/root && gst-launch-1.0 -e qtivcomposer name=mixer \
sink_0::position="<0, 0>" sink_0::dimensions="<1920, 1080>" \
sink_1::position="<1920, 0>" sink_1::dimensions="<1920, 1080>" \
sink_2::position="<0, 1080>" sink_2::dimensions="<1920, 1080>" \
sink_3::position="<1920, 1080>" sink_3::dimensions="<1920, 1080>" \
! video/x-raw\(memory:GBM\),format=NV12,width=3840,height=2160 ! qtic2venc ! h264parse config-interval=1 ! rtspclientsink protocols=tcp location=<自定义输出地址:流媒体服务器地> \
rtspsrc location=<自定义输入地址:视频源地> latency=100 ! rtph264depay ! h264parse config-interval=-1 ! qtic2vdec ! qtivtransform engine=2 ! mixer. \
rtspsrc location=<自定义输入地址:视频源地> latency=100 ! rtph264depay ! h264parse config-interval=-1 ! qtic2vdec ! qtivtransform engine=2 ! mixer. \
rtspsrc location=<自定义输入地址:视频源地> latency=100 ! rtph264depay ! h264parse config-interval=-1 ! qtic2vdec ! qtivtransform engine=2 ! mixer. \
rtspsrc location=<自定义输入地址:视频源地> latency=100 ! rtph264depay ! h264parse config-interval=-1 ! qtic2vdec ! qtivtransform engine=2 ! mixer.

该实例中用到了如下插件:

编码插件:qtic2venc 解码插件:qtic2vdec

qtivtransform 插件

qtivcomposer 插件

H.265

bash
gst-launch-1.0 rtspsrc location=<自定义输入地址:视频源地> \
! rtph265depay \
! h265parse \
! qtic2vdec \
! qtivtransform engine=2 \
! video/x-raw,format=RGB \
! qtivtransform engine=2 \
! qtic2venc \
! queue \
! h265parse \

该实例中用到了如下插件:

编码插件:qtic2venc 解码插件:qtic2vdec

qtivtransform 插件

C++示例代码

tip
使用AidStream SDK C++ API编译,需要引入AidStream SDK 头文件:

#include "aidstream.h"

链接时需要指定AidStream SDK so库,例如:

g++ example.cpp -o demo -L/usr/local/lib/ -laidstream-gst -lopencv_core -I/usr/local/include/aidlux/aidstream-gst/ -I/usr/local/lib/aidlux_opencv/include/opencv4/

AidStream SDK 头文件地址: /usr/local/include/aidlux/aidstream-gst/aidstream.h

AidStream SDK 库文件地址: /usr/local/lib/libaidstream-gst.so

在应用程序中,唯一建议使用的接口函数为:

int start_stream(string stream_id, GetImageCB &cb)

该函数配合配置文件/usr/local/share/aidstream-gst/conf/aidstream-gst.conf使用。(由于stream流配置繁多,强烈建议通过配置文件的方式起流)

安装程序自带example示例及相应的CMakeLists.txt,可到安装目录/usr/local/share/aidstream-gst/example/cxx/下查看。具体操作请参考以下介绍。

根据样例程序来实现推拉流(提前配置好公共配置文件)。关于配置文件,请参考Reference

配置文件为默认公共配置文件aidstream-gst.conf,位于目录:/usr/local/share/aidstream-gst/conf/下。

具体编译执行操作方法如下:

进入/usr/local/share/aidstream-gst/example/cxx/目录,创建build目录,然后进入build目录。

在build目录里执行cmake命令:

sh
需要注意的是在8550 / 6490 aibox平台执行:
#cmake .. 

而在6490 QL平台则执行(这是因为6490 QL平台有不同的数据处理方式及不同的推理模型):
#cmake -DV4L2=ON ..

然后执行make命令

sh
#make

编译链接成功后会得到4个可执行程序: demo / qnn_rtsp / rtsp / start

设置配置文件之后,通过start可执行程序可以快速启动流(关于配置文件,请参考Reference):

sh
比如执行以下命令(1为stream id):

#start 1

通过qnn_rtsp可执行程序可以快速启动自带模型推理的推拉流:

sh
与start类似启动:

#qnn_rtsp 1

模型目录:/usr/local/share/aidstream-gst/example/datas/

通过rtsp可执行程序可以快速启动多线程的推拉流,当前示例设置为2个线程的推拉流,可通过修改代码实现多个线程推拉流。

sh
可直接执行:
#rtsp

根据配置文件参数来选择输入输出流。关于配置文件,请参考Reference

cpp
#include <cstdlib>
#include <string>
#include <iostream>
#include "aidstream.h"

using namespace std;
using namespace Aidlux::AidStream;

int8_t my_img_cb(const Image &img)
{
    printf("width: %d, height: %d, fps: %f, id:%s  \n", img.width, img.height, img.fps, img.stream_id.c_str());
    return 0;
}

int main(int argc, char* argv[])
{
    string _idx;
    if (argc > 1)
    {
        _idx = argv[1];
    }
    else
    {
        _idx = "1";
    }

    Aidlux::AidStream::GetImageCB callback = my_img_cb;
    start_stream(_idx, callback);

    return 0;
}
tips
<视频输入源>     (例如rtsp://admin:aidlux123@192.168.110.234:554)
<视频输出推流>   (例如rtsp://192.168.111.115:8554)

读取rtsp流显示,不做任何处理,输出RTSP流到<视频输出推流>/aidstream-gst-rtsp-test

cpp
#include <cstdlib>
#include <string>
#include <iostream>
#include "aidstream.h"

using namespace std;
using namespace Aidlux::AidStream;

int8_t my_img_cb(const Image &img)
{
    printf("width: %d, height: %d, fps: %f\n", img.width, img.height, img.fps);
    return 0;
}

int main()
{
    string rtsp_path = "<视频输入源>/h264/ch1/main/av_stream";
    int res = start_stream_input_rtsp(rtsp_path, my_img_cb, StreamType::RTSPSINK, "<视频输出推流>/aidstream-gst-rtsp-test");
    if(res != 0)
    {
        return -1;
    }
    return 0;
}

<根据参数同时开启多路流显示,不做任何处理>

cpp
#include <cstdlib>
#include <thread>
#include <string>
#include <iostream>
#include "aidstream.h"

using namespace std;
using namespace Aidlux::AidStream;

int8_t my_img_cb(const Image &img)
{
    printf("width: %d, height: %d, fps: %f\n", img.width, img.height, img.fps);
    return 0;
}

int main(int argc, char* argv[])
{
    clogger("./aidclog_aidstream_c");
    set_log_level(GSTLogLevel::SINFO);
    Aidlux::AidStream::GetImageCB callback = my_img_cb;

    std::thread t1(start_stream, "1", ref(callback));
    std::thread t2(start_stream, "7", ref(callback));
    
    t1.join();
    t2.join();

    return 0;
}

模型执行异步处理,可用于模型推理时间较长的场景

tip
1. 由于该部分代码较多,下面仅贴出主函数部分,完整代码请参阅安装文件/usr/local/share/aidstream-gst/example/cxx/qnn_rtsp.cpp (对于6490 ql请参考qnn_rtsp_v4l2.cpp文件)

2. 对于追求实时性的推拉流而言,留给应用程序处理数据的时间窗口极短。比如,当帧率为25帧的时候,时间窗口仅20ms。所以异步处理属于一个折中方案。当模型推理时间太大的时候,异步处理虽能解决,但是在显示方面带来的体验感欠缺仍无法解决。总而言之,实时性与模型推理时间较长是一对不可调和的矛盾。
cpp
int main(int argc, char *argv[]) {
    // 日志开关, 一般情况可不设置
    // Aidlux::AidClog::clog_log_to_file("./snpe2_yolov5_multi_");
    // Aidlux::AidClog::clog_set_log_level(Aidlux::AidClog::LogLevel::INFO);

    init_share_mem_vec();

    if (argc < 1) {
        std::cerr << "Parameter missing, param1: streamId[reference /usr/local/share/aidstream-gst/conf/aidstream-gst.conf file]. eg: ./example 7" << std::endl;
        return -1;
    }
    int r = model_init();
    if (r != 0)
    {
        std::cerr << "model init failed" << std::endl;
        exit(1);
    }
    Aidlux::AidStream::GetImageCB callback = myCallback;
    std::set<std::string> validIDs = loadValidIDs();
    std::vector<std::thread> threads;

    bool allIDsFound = true;
    std::string arg = argv[1];
    // 配置文件有id才启线程
    if (validIDs.find(arg) == validIDs.end())
    {
        allIDsFound = false;
    }
    if (!allIDsFound)
    {
        std::cerr << "Parameter has an id that is not included in the configuration file, please check" << std::endl;
        return -1;
    }
    else
    {
        // gstreamer取数据主线程
        threads.emplace_back(start_stream, arg, ref(callback));
        // 启动三个模型推理处理线程 
        // 具体启动多少个线程需要依据模型推理时间长短,模型推理时间越长需要启动的线程数越多。比如推理时间在50 ~ 60ms,则启动3个线程已能应对。
        // 如果模型不支持并发,则只需要配置一个推理线程
        threads.emplace_back(update_data, 1);
        threads.emplace_back(update_data, 2);
        threads.emplace_back(update_data, 3);
    }
    for (auto &t : threads)
    {
        if (t.joinable())
        {
            t.join();
        }
    }

    return 0;
}