部分代码github
1 目标
怎样在qml窗口中直接播放RTP视频流
2 终端机直接通过Gstreamer播放RTP流视频
首先是尝试直接在终端机中拉取视频流并播放,确保我的视频流达到了终端机,能够举行后续的操纵.
2.1 本地视频文件推拉流
windows虚拟机推流本地视频文件,终端机继承推流
windows虚拟机推流 gst-launch-1.0 filesrc location=/home/firefly/Desktop/test.mp4 ! qtdemux ! rtph264pay config-interval=-1 ! udpsink host=224.224.224.224 port=5000
终端机拉流 gst-launch-1.0 -ve udpsrc uri=udp://224.224.224.224:5000 ! application/x-rtp,media=video,encoding-name=H264 ! rtph264depay ! h264parse ! mppvideodec ! videoconvert ! autovideosink sync=false
2.2 终端机摄像头推拉流
在终端机上插上摄像头后推拉流,在linux虚拟机上也插上了摄像头举行尝试,无法获取到
终端机推流 gst-launch-1.0 -ve v4l2src device=/dev/video0 ! image/jpeg,width=800,height=600,framerate=15/1 ! jpegdec ! mpph264enc ! h264parse ! rtph264pay ! udpsink host=224.224.224.224 port=5000
终端机拉流 gst-launch-1.0 -ve udpsrc uri=udp://224.224.224.224:5000 caps=\"application/x-rtp,media=video,encoding-name=H264\" ! rtph264depay ! h264parse ! mppvideodec fast-mode=true ! videoconvert ! autovideosink"
直接拉取摄像头的话只能够拉取一起,摄像头就会占用,所以只能通过推流的方式来获得多路摄像头流
gst-launch-1.0 -ve v4l2src device=/dev/video0 ! videoconvert ! autovideosink
2.3 testvideosrc推拉流
testvideosrc是gstreamer自带的视频流,虚拟机和终端机都能够举行推流
推流 gst-launch-1.0 videotestsrc ! video/x-raw ! vp8enc deadline=1 ! rtpvp8pay ! udpsink host=224.224.224.224 port=5000
终端机拉流 gst-launch-1.0 -ve udpsrc uri=udp://224.224.224.224:5000 ! application/x-rtp,media=video,encoding-name=VP8 ! rtpvp8depay ! mppvideodec ! videoscale ! video/x-raw,width=1200,height=800 ! videoconvert ! autovideosink sync=false
3 终端机在qml中播放RTP流视频
确定RTP流能够到达终端机后,就可以开始研究怎么在qml里播放RTP流视频
3.1 思路 1 使用MediaPlayer调用gstreamer
使用MediaPlayer+Videooutput组件来播放视频,可以直接拉取rtsp流,对于rtp流不支持(必要混用gstreamer)
代码就是将QT自带的MediaPlayer中的 source改为gstreamer的下令.在自己电脑的虚拟机上有 qtvideosink可以让视频表现在qml上,但是对于终端机没有这个插件,尝试安装后也照旧没有,因此该思路无法举行下去
- import QtQuick 2.15
- import QtQuick.Window 2.15
- import QtMultimedia 5.13
- Window {
- width: 640
- height: 480
- visible: true
- title: qsTr("Hello World")
- Rectangle{
- anchors.fill: parent
- color: "gray"
- MediaPlayer{
- id:mediaPlayer;
- source:"gst-pipeline:udpsrc uri=udp://224.224.224.224:5000 ! application/x-rtp,media=video,encoding-name=H264 ! rtph264depay ! h264parse ! mppvideodec ! videoconvert ! autovideosink"
- autoPlay: true
- onError: {
- console.log(errorString);
- }
- }
- VideoOutput{
- anchors.fill:parent;
- source: mediaPlayer;
- //flushMode: VideoOutput.LastFrame;
- }
- }
- }
复制代码 3.2 思路 2 使用vlc-qt库
VLC-Qt 是一个 Qt 库,它封装了 VLC(VideoLAN)播放器的功能,使得在 Qt 应用中可以轻松地集成视频播放功能。具体来说,VLC-Qt 提供了一个可以嵌入到 Qt 应用中的组件,允许你在 QML 中表现视频,处置处罚播放、停息、音量调节、播放控制等功能。查询资料,相当于在qt里装了vlc这个播放器,资源斲丧会很大.
安装编译流程如下.在linux上编译vlc-qtgithub
下载源码,下载cmake,新建一个build文件夹,cd进去,实行代码 cmake .. -DCMAKE_BUILD_TYPE=Debug
遇到报错,没有qt5Coreconfig,运行 sudo apt-get install qtdeclarative5-dev举行安装
遇到报错 Could not find libVLC,实行以下代码
- sudo apt-get update
- sudo apt-get install libvlc-dev libvlccore-dev
复制代码
实行完 cmake .. -DCMAKE_BUILD_TYPE=Debug,生成了一些文件,接着实行
- sudo make -j8
- sudo make install
复制代码 安装完成后就能在src文件夹里看到这几个文件夹,把这几个文件夹里的.so文件都移植到自己的项目
使用它的exmpale,克隆 https://github.com/vlc-qt/examples.git
把9个.so文件复制到lib文件夹,修改simpleplayer的src.pro文件为你编译的路径和lib文件夹,运行就能看到拉流视频
- LIBS += -lVLCQtCore -lVLCQtWidgets
- # Edit below for custom library location
- LIBS += -L/home/bft/vlc-qt/build/lib -lVLCQtCore -lVLCQtWidgets
- INCLUDEPATH += /home/bft/vlc-qt/build/include
复制代码
3.3 思路 3 qmlsink
qmlglsink 基于 OpenGL 来渲染视频帧。它利用 QML 的 OpenGL 支持,将接收到的视频数据直接传输给 GPU 举行渲染。视频数据通常是通过 GStreamer 管道解码和转换为 OpenGL 可以处置处罚的格式(如 NV12 或 RGBA)。它可以将 OpenGL 渲染的内容嵌入到 QML 中的 Item 上。
qmlsink官方demogithub,可以很容易的跑通,必要安装gstreamer-qt的相干库.
主要流程如下
主要用到的RTP拉流管道代码 "udpsrc uri=udp://224.224.224.224:5000 caps=\"application/x-rtp,media=video,encoding-name=H264\" ! rtph264depay ! h264parse ! mppvideodec ! glupload ! glcolorconvert ! qmlglsink name=sink0"
摄像头拉流代码 "v4l2src device=/dev/video0 ! image/jpeg,width=1920,height=1080,framerate=15/1 ! mppjpegdec fast-mode=true ! glupload ! glcolorconvert ! qmlglsink name=sink0"
由于摄像头直接来流只能够拉取一起,所以是把摄像头的流先推流在拉取9路,代码和上边RTP拉流的代码一样
3.3.1 表现多路RTP视频
多路表现RTP视频着实跟表现一个是一样的,就是把所有的步骤都在来一遍,同时在qml里创建多个VideoItem组件
3.3.1.1 方法一 使用一个函数createPipeline创建管道
main.cpp
- void createPipeline(GstElement **pipeline, GstElement **src, GstElement **rtp_h264_depay, GstElement **h264_parse, GstElement **decoder, GstElement **videorate, GstElement **capsfilter1, GstElement **capsfilter2, GstElement **glupload, GstElement **glcolorconvert, GstElement **sink)
- {
- // 创建一个新的 GStreamer 管道
- *pipeline = gst_pipeline_new(NULL);
-
- // 创建 UDP 源元素
- *src = gst_element_factory_make("udpsrc", NULL);
- g_assert(src);
- // 创建 RTP H.264 depayload 元素
- *rtp_h264_depay = gst_element_factory_make("rtph264depay", NULL);
- g_assert(rtp_h264_depay);
- // 创建 H.264 解析器
- *h264_parse = gst_element_factory_make("h264parse", NULL);
- g_assert(h264_parse);
- // 创建解码器(mpp 视频解码器)
- *decoder = gst_element_factory_make("mppvideodec", NULL);
- g_assert(decoder);
- // 创建两个 CapsFilter,用于设置过滤条件
- *capsfilter1 = gst_element_factory_make("capsfilter", NULL);
- *capsfilter2 = gst_element_factory_make("capsfilter", NULL);
- // 创建 OpenGL 上传和颜色转换元素
- *glupload = gst_element_factory_make("glupload", NULL);
- g_assert(glupload);
- *glcolorconvert = gst_element_factory_make("glcolorconvert", NULL);
- g_assert(glcolorconvert);
- // 创建 QML 的视频渲染元素
- *sink = gst_element_factory_make("qmlglsink", NULL);
- g_assert(sink);
- // 设置 sink 属性
- g_object_set(G_OBJECT(*sink), "sync", FALSE, NULL);
- // 设置 UDP 源的属性,包括 URI 和缓冲区大小
- g_object_set(G_OBJECT(*src), "uri", "udp://224.224.224.224:5000", NULL);
- g_object_set(G_OBJECT(*src), "buffer-size", "2097152", NULL);
- // 设置 RTP caps 的过滤条件
- GstCaps *rtpCaps = gst_caps_from_string("application/x-rtp,media=video,encoding-name=H264");
- g_object_set(*capsfilter1, "caps", rtpCaps, NULL);
- // 设置帧率过滤条件
- GstCaps *scaleCaps = gst_caps_from_string("video/x-raw,framerate=15/1");
- g_object_set(*capsfilter2, "caps", scaleCaps, NULL);
- // 创建 videorate 元素,用于调整视频帧率
- *videorate = gst_element_factory_make("videorate", NULL);
- }
- // 递归打印 QML 项目树
- void printChildren(QQuickItem *item, int depth = 0)
- {
- QString indent = QString(" ").repeated(depth * 2);
- qDebug() << indent + item->objectName();
- // 遍历所有子项并递归打印
- for (QQuickItem *child : item->childItems())
- {
- printChildren(child, depth + 1);
- }
- }
- int main(int argc, char *argv[])
- {
- int ret;
- // 设置 QML 使用的 CPU 线程数
- setenv("QML_CPU_THREAD_COUNT", "4", 1);
- // 初始化 GStreamer 库
- gst_init(&argc, &argv);
- // 使用 Qt Quick 创建 GUI 应用
- QGuiApplication app(argc, argv);
- // 定义多个管道和其相关元素
- GstElement *pipeline1, *src1, *rtp_h264_depay1, *h264_parse1, *decoder1, *videorate1, *capsfilter1_1, *capsfilter1_2, *glupload1, *glcolorconvert1, *sink1;
- GstElement *pipeline2, *src2, *rtp_h264_depay2, *h264_parse2, *decoder2, *videorate2, *capsfilter2_1, *capsfilter2_2, *glupload2, *glcolorconvert2, *sink2;
- // 初始化多个管道
- createPipeline(&pipeline1, &src1, &rtp_h264_depay1, &h264_parse1, &decoder1, &videorate1, &capsfilter1_1, &capsfilter1_2, &glupload1, &glcolorconvert1, &sink1);
- createPipeline(&pipeline2, &src2, &rtp_h264_depay2, &h264_parse2, &decoder2, &videorate2, &capsfilter2_1, &capsfilter2_2, &glupload2, &glcolorconvert2, &sink2);
- // 将元素添加到相应管道中
- gst_bin_add_many(GST_BIN(pipeline1), src1, capsfilter1_1, rtp_h264_depay1, h264_parse1, decoder1, videorate1, capsfilter1_2, glupload1, glcolorconvert1, sink1, NULL);
- gst_bin_add_many(GST_BIN(pipeline2), src2, capsfilter2_1, rtp_h264_depay2, h264_parse2, decoder2, videorate2, capsfilter2_2, glupload2, glcolorconvert2, sink2, NULL);
- // 链接管道内的所有元素
- if (!gst_element_link_many(src1, capsfilter1_1, rtp_h264_depay1, h264_parse1, decoder1, videorate1, capsfilter1_2, glupload1, glcolorconvert1, sink1, NULL))
- {
- g_print("Failed to link elements in pipeline 1\n");
- }
- gst_element_link_many(src2, capsfilter2_1, rtp_h264_depay2, h264_parse2, decoder2, videorate2, capsfilter2_2, glupload2, glcolorconvert2, sink2, NULL);
- // 加载 QML 界面
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
- QQuickWindow *rootObject = static_cast<QQuickWindow *>(engine.rootObjects().first());
- printChildren(rootObject->contentItem()); // 打印 QML 项目结构
- QTimer::singleShot(10000, [&engine, &sink1, &sink2,
- &pipeline1, &pipeline2,
- &videoItem1, &videoItem2, ]() {
- // 获取 QML 应用的根对象
- QQuickWindow *rootObject = static_cast<QQuickWindow *>(engine.rootObjects().first());
- // 打印 QML 的子项结构,方便调试
- printChildren(rootObject->contentItem());
- // 从 QML 对象中查找名为 "videoItem1" 至 "videoItem4" 的 QQuickItem
- videoItem1 = rootObject->findChild<QQuickItem *>("videoItem1");
- g_assert(videoItem1); // 确保找到 videoItem1,否则程序会中止
- videoItem2 = rootObject->findChild<QQuickItem *>("videoItem2");
- // 将找到的 QQuickItem 设置到对应的 GStreamer sink 上
- g_object_set(sink1, "widget", videoItem1, NULL);
- g_object_set(sink2, "widget", videoItem2, NULL);
- // 使用 QQuickWindow 的渲染任务机制设置管道状态为播放
- // 在渲染同步阶段之前执行 SetPlaying 操作
- rootObject->scheduleRenderJob(new SetPlaying(pipeline1), QQuickWindow::BeforeSynchronizingStage);
- rootObject->scheduleRenderJob(new SetPlaying(pipeline2), QQuickWindow::BeforeSynchronizingStage);
- });
- // 启动应用程序事件循环
- ret = app.exec();
- // 退出事件循环后,清理 GStreamer 管道资源
- // 将所有管道设置为 NULL 状态以释放资源
- gst_element_set_state(pipeline1, GST_STATE_NULL);
- gst_element_set_state(pipeline2, GST_STATE_NULL);
- // 释放管道对象的引用
- gst_object_unref(pipeline1);
- gst_object_unref(pipeline2);
- // 关闭 GStreamer
- gst_deinit();
- return ret;
- }
复制代码 3.3.1.2 方法二 使用管道字符串(更简单)
通过循环来创建管道和元素,淘汰代码量,修改方便
main.cpp
- #define MAX_CHANNEL (9) // 定义最大的视频流通道数(9路视频流)
- // 这些是GStreamer命令,用于从UDP流中拉取视频流并显示
- const char gstLaunchCmd[][600]=
- {
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! videoscale ! video/x-raw,width=640,height=480 ! glupload ! glcolorconvert ! qmlglsink name=sink0",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! videoscale ! video/x-raw,width=640,height=480 ! glupload ! glcolorconvert ! qmlglsink name=sink1",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! videoscale ! video/x-raw,width=640,height=480 ! glupload ! glcolorconvert ! qmlglsink name=sink2",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! videoscale ! video/x-raw,width=640,height=480 ! glupload ! glcolorconvert ! qmlglsink name=sink3",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! glupload ! glcolorconvert ! qmlglsink name=sink4",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! glupload ! glcolorconvert ! qmlglsink name=sink5",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! glupload ! glcolorconvert ! qmlglsink name=sink6",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! glupload ! glcolorconvert ! qmlglsink name=sink7",
- "udpsrc uri=udp://224.224.224.224:5000 buffer-size=2097152 caps="application/x-rtp,media=video,encoding-name=H264" ! rtph264depay ! queue ! h264parse ! mppvideodec fast-mode=true ! glupload ! glcolorconvert ! qmlglsink name=sink8"
- };
- // 定义每个视频通道的GStreamer元素名称
- const char sinkName[][20]=
- {
- "sink0", "sink1", "sink2", "sink3", "sink4", "sink5", "sink6", "sink7", "sink8"
- };
- // 每个视频Item的名称,用于在QML中查找并绑定
- const char videoItemName[][20]=
- {
- "videoItem0", "videoItem1", "videoItem2", "videoItem3", "videoItem4", "videoItem5", "videoItem6", "videoItem7", "videoItem8"
- };
- // 用于设置视频流播放状态的线程类
- class SetPlaying : public QRunnable
- {
- public:
- SetPlaying(GstElement *); // 构造函数,接收GStreamer元素
- ~SetPlaying(); // 析构函数
- void run (); // 执行设置播放状态的函数
- private:
- GstElement * pipeline_; // GStreamer管道
- };
- // 构造函数,初始化pipeline指针
- SetPlaying::SetPlaying (GstElement * pipeline)
- {
- this->pipeline_ = pipeline ? static_cast<GstElement *> (gst_object_ref (pipeline)) : NULL;
- }
- // 析构函数,释放资源
- SetPlaying::~SetPlaying ()
- {
- if (this->pipeline_)
- gst_object_unref (this->pipeline_);
- }
- // 运行方法,设置GStreamer管道的播放状态为`PLAYING`
- void
- SetPlaying::run ()
- {
- if (this->pipeline_)
- gst_element_set_state (this->pipeline_, GST_STATE_PLAYING);
- }
- // 递归打印QQuickItem及其子项(用于调试查看QML树结构)
- void printChildren(QQuickItem *item ,int depth = 0)
- {
- QString indent = QString(" ").repeated(depth*2); // 添加缩进
- qDebug() << indent + item->objectName(); // 打印当前项的名称
- // 递归打印所有子项
- for(QQuickItem *child : item->childItems())
- {
- printChildren(child, depth + 1);
- }
- }
- int main(int argc, char *argv[])
- {
- int ret;
- // 设置多线程环境变量
- setenv("QML_CPU_THREAD_COUNT","4",1);
- gst_init (&argc, &argv); // 初始化GStreamer
- {
- QGuiApplication app(argc, argv); // 创建Qt应用程序
- GstElement *pipeline[MAX_CHANNEL]; // 用于存储每路视频流的管道
- GstElement *sink[MAX_CHANNEL]; // 用于存储每路视频流的渲染sink
- // 遍历所有视频通道,初始化管道并启动播放
- for(int i = 0; i < MAX_CHANNEL; i++)
- {
- // 通过解析GStreamer命令字符串来创建每个视频流的管道
- pipeline[i] = gst_parse_launch(gstLaunchCmd[i], NULL);
-
- // 获取视频渲染sink元素(与GStreamer命令中的sink名称一致)
- sink[i] = gst_bin_get_by_name(GST_BIN(pipeline[i]), sinkName[i]);
-
- // 启动视频流的播放
- gst_element_set_state(pipeline[i], GST_STATE_PLAYING);
- }
- QQmlApplicationEngine engine; // 创建QML应用引擎
- engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); // 加载QML文件
- QQuickItem *videoItem[MAX_CHANNEL]; // 用于存储每路视频的QML项
- QQuickWindow *rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first()); // 获取QML的根对象
- printChildren(rootObject->contentItem()); // 打印QML树结构(调试)
- // 遍历所有视频通道,找到对应的QML项并绑定GStreamer的sink元素
- for(int i = 0; i < MAX_CHANNEL; i++)
- {
- // 查找QML中与视频通道相关的项
- videoItem[i] = rootObject->findChild<QQuickItem *>(videoItemName[i]);
- g_assert(videoItem[i]); // 确保QML项被正确找到
- // 设置GStreamer的sink元素,指向对应的视频QML项
- g_object_set(sink[i], "widget", videoItem[i], NULL);
- // 在渲染前将播放状态设置为`PLAYING`,确保视频能够播放
- rootObject->scheduleRenderJob(new SetPlaying(pipeline[i]), QQuickWindow::BeforeRenderingStage);
- }
- ret = app.exec(); // 启动Qt应用事件循环
- // 退出时停止每路视频流的播放并释放资源
- for(int i = 0; i < MAX_CHANNEL; i++)
- {
- gst_element_set_state(pipeline[i], GST_STATE_NULL); // 停止播放
- gst_object_unref(pipeline[i]); // 释放GStreamer管道资源
- }
- }
- gst_deinit (); // 清理GStreamer资源
- return ret; // 返回程序的退出状态
- }
复制代码 3.3.2 popup弹窗表现RTP视频流
一开始直接在popup里嵌入videoItem运行代码不表现.就开始思考是不是main.cpp里没有绑定上videoitem
在main.cpp打印了rootObject初始的item,可以看到只有一个,popup和它里边的videoItem组件根本就不表现
- void printChildren(QQuickItem *item, int depth = 0) {
- // 打印当前项的名称
- QString indent = QString(" ").repeated(depth * 2);
- qDebug() << indent + item->objectName();
- // 递归遍历所有子项
- for (QQuickItem *child : item->childItems()) {
- printChildren(child, depth + 1);
- }
- }
- QQuickWindow *rootObject = static_cast<QQuickWindow *>(engine.rootObjects().first());
- printChildren(rootObject->contentItem());
复制代码
手动打开弹窗,耽误10s打印就是找到了,也就是说弹窗界面不出现,rootobject就找不到在popup的videoitem2,所以也就播放不了视频
将探求,绑定和播放的代码放入延时里,我们打开弹窗,等待一会就可以望见popup表现了视频
- QTimer::singleShot(10000, [&engine,&sink2,&pipeline2](){
- QQuickWindow *rootObject = static_cast<QQuickWindow *>(engine.rootObjects().first());
- printChildren(rootObject->contentItem());
- QQuickItem* videoItem2 = rootObject->findChild<QQuickItem*>("videoItem2");
- g_object_set(sink2, "widget", videoItem2, nullptr);
- rootObject->scheduleRenderJob(new SetPlaying(pipeline2), QQuickWindow::BeforeSynchronizingStage);
- });
复制代码
通过Repeater重复生成的videItem,rootObject延时也拿不到,9路只能是一个个创建
main.qml
- Popup{
- id:videopopup
- width:parent.width*0.8
- height:parent.height*0.8
- focus:true;
- objectName:"popup"
- Column{
- spacing:10
- anchors.fill:parent
- Repeater{
- id:firstrepeater
- model:3
- delegate:Row{
- property int rowIndex :modelData
- spacing:10
- Repeater{
- model:3
- GstGLVideoItem {
- anchors.centerIn:parent
- objectName: "videoItem"+(index+1+rowIndex*3)
- width:parent.width/3
- height:parent.height/3
- }
- }
- }
- }
- }
复制代码 3.3.3 拉流视频优化
在拉流的过程中出现了视频卡顿,CPU占用率高的问题,一些优化会淘汰卡顿,但会增长cpu斲丧
1.在src设置 buffer-size 来调整缓冲区巨细 uri=udp://224.224.224.224:5000 buffer-size=1048576
2.rtpjitterbuffer 元素用于缓冲 RTP 数据包以应对网络抖动。latency 参数指定了缓冲区的巨细(以毫秒为单位),其作用是允许一定时间的耽误来确保平滑播放.设置后视频会丝滑一些,但会增长cpu斲丧
3.videoscale元素来调整视频分辨率和帧率.videoscale ! video/x-raw,width=640,height=480,framerate=15/1
4.使用 queue元素可以在不同的处置处罚阶段之间引入缓冲区,减缓数据流的速率,避免瓶颈。! rtph264depay! queue ! h264parse !
5.设置 sink的时钟同步属性. sync=true:当渲染速率跟不上播放时,qmlglsink 会丢弃一些过时的帧,以保持与时间的同步,避免耽误堆积,cpu斲丧小,看着流畅一些。sync=false:所有帧都尽可能渲染,可能导致视频卡顿或播放耽误渐渐累积,cpu斲丧大,对于大分辨率的视频会直接卡住。
4 最闭幕果
qmlglsink使用 OpenGL 渲染帧,帧作为纹理嵌入到QML 场景中,与其他 QML 图形叠加,资源斲丧大,得当1-4路视频。
waylandsink直接使用 Wayland 渲染帧,视频输出到独立的 Wayland 表面,直接调用系统播放器,资源斲丧小。
相比于qmlglsink,waylandsink直接调用系统播放器,cpu斲丧小,更流畅
分辨率,帧率%cpuRES视觉结果一推9解,摄像头,qmlglsink640*480,15160360M流畅800*600,15230430M流畅1280*720,15260600M一些卡顿1920*1080,153201.1g明显卡顿2路1080p120350M一些卡顿+rtpjitterbuffer latency=100250350M流畅1推9解摄像头,waylandsink1280*720,1570100M流畅1920*1080,15110170M流畅 额外
开发版检察gpu频率
最高频率800Mhz cat /sys/devices/platform/fde60000.gpu/devfreq/fde60000.gpu/load
检察cpu等参数,top按下大写P参数列表会从高到低排列
在终端表现帧率
- std::chrono::steady_clock::time_point last_frame_time;
- int frame_count = 0;
- static GstPadProbeReturn frame_probe_callback(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) {
- auto now = std::chrono::steady_clock::now();
- frame_count++;
- if (last_frame_time.time_since_epoch().count() > 0) {
- auto duration = std::chrono::duration_cast[std::chrono::milliseconds](std::chrono::milliseconds)(now - last_frame_time).count();
- if (duration > 1000) {
- float fps = frame_count * 1000.0 / duration;
- g_print("FPS: %.2f\n", fps);
- frame_count = 0;
- last_frame_time = now;
- }
- } else {
- last_frame_time = now;
- }
- return GST_PAD_PROBE_OK;
- }
- // 在链接 glcolorconvert 和 sink 后添加帧探针
- GstPad *pad = gst_element_get_static_pad(glcolorconvert, "src");
- gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, frame_probe_callback, NULL, NULL);
- gst_object_unref(pad);
复制代码 vlc推拉流
首先先能够获取到RTP流,VLC->媒体->串流->添加一个视屏文件->选择RTP点击添加一个
ip输入224.224.224.224,取消激活转码转码->选择video-h264
播放的话,打开一个新的vlc,打开网络串流,输入 rtp://224.224.224.224:5004
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |