嵌入式Qt 开发一个视频播放器
上篇文章:嵌入式 Qt开发一个音乐播放器,使用Qt制作了一个音乐播放器,并在OK3568开发板上进行了运行测试,实际测试效果还不错。
本篇继续来实现一个Qt视频播放器软件,可以实现视频列表的显示与选择播放等,先来看下最终的效果:
本篇的Qt代码从野火开发板的例程中移植修改而来,下面分析下程序的代码结构。
1 视频播放器开发总体结构
整个Qt视频播放器项目的代码结构如下:
- 主代码中是视频播放器相关的代码,包括:
- 视频播放器主界面
- 视频名列表界面:在视频播放时可以查看视频列表并切换视频
- Qt视频播放界面:实现单纯的视频播放
- 操作按钮界面:实现播放、暂停、继续、上一个、下一个、进度调节,音量调节
- 播放预览列表界面:在进入视频播放之前的视频预览列表界面
- Ui代码中使用一些Qt的基本功能,包括:
- 一个Qt界面基类
- 滑条功能类
- 图标按钮显示类
- 列表功能类
- 工具类
- 视频帧解析
- 页面列表类
- Skin中是一些图片资源和字体/皮肤定义
- 最后是编译的中间文件和编译结果存储的目录
下面分类介绍了程序的主要代码实现。
2 通用界面代码
上篇介绍音乐播放器时,介绍过一些自定义控件的代码,本篇的视频播放器,可以复用这些代码:
- qtwidgetbase:窗口基类,该类的一个主要功能是可以向窗口内添加图片形式的按钮
- qtsliderbar:用于实现自定义滑条控件,分为水平滑条与竖直滑条
- qtpixmapbutton:图片按钮,支持未按下和按下后的两种样式的图片显示
- qtlistwidget :基础的列表显示
相比于音乐播放器,视频播放器还需要其它额外的功能,如下:
2.1 qttoolbar
工具条的基类,通过创建一个QPropertyAnimation属性动画对象,主要实现工具条的显示与隐藏的动画效果。
例如,在用户无操作一段时间后,将上方的标题栏和下方的播放工具栏隐藏。
主要代码如下:
QtToolBar::QtToolBar(QWidget *parent) : QtWidgetBase(parent)
{m_animation = new QPropertyAnimation(this, "pos");m_animation->setDuration(200);m_animation->setEasingCurve(QEasingCurve::Linear);connect(m_animation, SIGNAL(finished()), this, SIGNAL(signalFinihed()));
}void QtToolBar::SetAnimation(const QPoint &startPos, const QPoint &endPos)
{m_animation->setStartValue(startPos);m_animation->setEndValue(endPos);m_animation->start();
}
2.2 qtvideowidgetsurface
视频帧解析类,继承自QAbstractVideoSurface类,这是一个抽象基类,通过实现它的派生类可以获取来自QMediaPlayer或QCamera视频的帧。
- supportedPixelFormats 用于向Qt返回帧流的图像类型
- present 用于获取到每一帧
class QtVideoWidgetSurface : public QAbstractVideoSurface
{Q_OBJECT
public:QtVideoWidgetSurface(QWidget *widget, QObject *parent = 0);QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;bool isFormatSupported(const QVideoSurfaceFormat &format) const;bool start(const QVideoSurfaceFormat &format);void stop();bool present(const QVideoFrame &frame);QRect videoRect() const { return targetRect; }void updateVideoRect();void paint(QPainter *painter);private:QWidget *widget;QImage::Format imageFormat;QRect targetRect;QSize imageSize;QRect sourceRect;QVideoFrame currentFrame;
};
2.3 qtpagelistwidget
页面形式的预览排布以及左右页面的滑动切换
2.3.1 QtPageListWidgetItem
该类用于表示列表中的每一个项,可以是字符,并且可以带有图片(图标):
class QtPageListWidgetItem
{
public:QtPageListWidgetItem(int id, const QStringList &args);QtPageListWidgetItem(int id, const QString &name);QtPageListWidgetItem(int id, const QString &name, const QPixmap &icon);QtPageListWidgetItem(int id, const QString &path, const QString &name, const QPixmap &icon);int m_nId;QString m_strText;QPixmap m_pixmapIcon;QString m_strBaseName;QString m_strPath;QRect m_rect;QStringList m_strMultiParameters;
};
2…3.2 QtPageListWidget
class QtPageListWidget : public QtWidgetBase
{Q_OBJECT
public:typedef enum{None,LeftDirection,RightDirection} MoveDirection;Q_PROPERTY(int xPos READ Xpos WRITE setXPos)explicit QtPageListWidget(QWidget *parent = 0);~QtPageListWidget();void AddItem(int id, QtPageListWidgetItem *item);void SetItems(const QMap<int, QtPageListWidgetItem *> &items);void SetBackground(const QPixmap &pixmap);void SetBackground(const QColor &color);void SetPageCount(int count);void SetItemLayut(int rows, int columns);void SetItemLayoutSpace(int row, int col);void SetLoopbackChange(bool bOk);signals:void currentItemClicked(QtPageListWidgetItem *item);void currentPageChanged(int index);protected:QMap<int, QtPageListWidgetItem *> m_listItems;// skinQColor m_backgroundColor;QPixmap m_pixmapWallpaper;int m_nPageCnt;int m_nCurrentPage;int m_nPrevPage;int m_nLayoutRows;int m_nLayoutColumns;qreal m_nItemWidth;qreal m_nItemHeight;int m_nDirection;QTimer *m_timerMove;bool m_bLoopbackChange;QPropertyAnimation *m_animationMove;private:bool m_bPressed;QPoint m_startPos;int m_nStartPos;int m_nMoveEndValue;bool m_bRecovery;private:void setXPos(int nValue);int Xpos() { return m_nStartPos; }void resizeRect();
private slots:protected:int m_nCurrentIndex;int m_nHorSpace;int m_nVSpace;protected:void mousePressEvent(QMouseEvent *e);void mouseReleaseEvent(QMouseEvent *);void mouseMoveEvent(QMouseEvent *e);void paintEvent(QPaintEvent *);void drawListItem(QPainter *painter, int page, int xOffset = 0);virtual void drawItemInfo(QPainter *painter, QtPageListWidgetItem *item);
};
注意,该类和qtlistwidget类的功能比较相似
3 视频播放器代码
首先是整体的主界面部分,进行各项功能的初始化显示,主要包括:
- 播放列表初始化(视频预览列表界面)
- 视频播放界面初始化(先隐藏播放界面)
界面效果如下:
主要代码逻辑:
{QtWidgetTitleBar *m_widgetTitle = new QtWidgetTitleBar(this);m_widgetTitle->SetScalSize(Skin::m_nScreenWidth, 80);m_widgetTitle->SetBackground(Qt::transparent);m_widgetTitle->setFont(QFont(Skin::m_strAppFontNormal));m_widgetTitle->SetTitle(tr("视频播放器"), "#ffffff", 32);QVBoxLayout *verLayoutCentor = new QVBoxLayout(this);verLayoutCentor->setContentsMargins(0, 0, 0, 0);verLayoutCentor->setSpacing(0);verLayoutCentor->addWidget(m_widgetTitle, 1);// 播放列表(视频预览列表界面)m_videosList = new VideoListViewer(this);m_videosList->SetBackground(Qt::transparent);connect(m_videosList, SIGNAL(currentItemClicked(QtPageListWidgetItem *)), this, SLOT(SltItemClicked(QtPageListWidgetItem *)));verLayoutCentor->addWidget(m_videosList, 5);// 视频播放界面(先隐藏播放界面)m_videoWidget = new QtVideoWidget(this);m_videoWidget->hide();
}
初始化各个界面后,还要连接对应的槽函数,如切换视频播放列表中的视频时的处理等。
3.1 视频播放
视频播放部分,主要有两部分功能:
- 视频解码播放:使用Qt自带的媒体播放器组件进行音频播放
- 播放时的按钮操作:实现播放、暂停、继续、上一个、下一个、进度调节,音量调节
3.1.1 QMediaPlayer播放视频
这里使用Qt自带的QMediaPlayer组件进行视频的播放,QMediaPlayer播放视频于播放音频的步骤类似。
本项目的视频播放初始化部分:
QtVideoWidget::QtVideoWidget(QWidget *parent) : QtWidgetBase(parent)
{m_urlMedia = QUrl();m_bToolBarShow = false;m_nDuration = 0;m_nPostion = 0;m_player = new QMediaPlayer(this);surface = new QtVideoWidgetSurface(this);m_player->setVideoOutput(surface);m_playList = new MediaPlayListWidget(this);m_playList->setVisible(false);m_player->setPlaylist(m_playList->palyList());connect(m_player, SIGNAL(durationChanged(qint64)), this, SLOT(SltDurationChanged(qint64)));connect(m_player, SIGNAL(positionChanged(qint64)), this, SLOT(SltPostionChanged(qint64)));m_titleBar = new PlayTitleBarWidget(this);connect(m_playList, SIGNAL(signalMediaChanged(QString)), m_titleBar, SLOT(SetText(QString)));connect(m_titleBar, SIGNAL(signalBack()), this, SLOT(SltBackClicked()));m_playBar = new PlayerBarWidget(this);connect(m_playBar, SIGNAL(signalPlay(bool)), this, SLOT(SltBtnPlayClicked(bool)));connect(m_playBar, SIGNAL(currentPostionChanged(int)), this, SLOT(SltChangePostion(int)));connect(m_playBar, SIGNAL(signalPrev()), m_playList->palyList(), SLOT(previous()));connect(m_playBar, SIGNAL(signalNext()), m_playList->palyList(), SLOT(next()));connect(m_playBar, SIGNAL(signalMuenList()), this, SLOT(SltShowMenuList()));connect(m_playBar, SIGNAL(signalVolume()), this, SLOT(SltChangeVolume()));m_timerShow = new QTimer(this);m_timerShow->setSingleShot(true);m_timerShow->setInterval(5000);connect(m_timerShow, SIGNAL(timeout()), this, SLOT(SltAutoCloseToolBar()));m_volumeSlider = new QtSliderBar(this);m_volumeSlider->SetHorizontal(false);m_volumeSlider->SetValue(100);m_volumeSlider->hide();connect(m_volumeSlider, SIGNAL(currentValueChanged(int)), m_player, SLOT(setVolume(int)));
}
3.1.2 播放操作按钮
视频播放时,需要用到播放、暂停、继续、上一个、下一个、进度调节、音量调节等操作按钮,这里的对应按钮使用图标显示为对应的按钮,通过加载QPixmap来实现:
void PlayerBarWidget::InitWidget()
{m_btns.insert(0, new QtPixmapButton(0, QRect(295, 40, 60, 60), QPixmap(":/images/video/ic_prev.png"), QPixmap(":/images/video/ic_prev_pre.png")));m_btns.insert(1, new QtPixmapButton(1, QRect(375, 40, 60, 60), QPixmap(":/images/video/ic_play.png"), QPixmap(":/images/video/ic_pause.png")));m_btns.insert(2, new QtPixmapButton(2, QRect(465, 40, 60, 60), QPixmap(":/images/video/ic_next.png"), QPixmap(":/images/video/ic_next_pre.png")));m_btns.insert(3, new QtPixmapButton(3, QRect(655, 40, 60, 60), QPixmap(":/images/video/ic_volume.png"), QPixmap(":/images/video/ic_volume_pre.png")));m_btns.insert(4, new QtPixmapButton(4, QRect(728, 40, 60, 60), QPixmap(":/images/video/ic_list.png"), QPixmap(":/images/video/ic_list_pre.png")));m_btns.value(1)->setCheckAble(true);// 进度条m_progressBar = new QtSliderBar(this);m_progressBar->SetHorizontal(true);m_progressBar->SetSliderSize(2, 40);connect(m_progressBar, SIGNAL(currentValueChanged(int)), this, SIGNAL(currentPostionChanged(int)));
}
3.2 播放列表
播放列表功能用来实现视频文件的显示,以及手动指定切换要播放的视频。
3.2.1 读取视频文件
通过读取指定目录下的文件,查找类型为mp4的视频文件,构造视频播放列表。
void MediaPlayListWidget::ScanDirMedias(const QString &path)
{QDir dir(path);if (!dir.exists())return;dir.setFilter(QDir::Dirs | QDir::Files);dir.setSorting(QDir::DirsFirst);QFileInfoList list = dir.entryInfoList();int index = m_mapItems.size();for (int i = 0; i < list.size(); i++){QFileInfo fileInfo = list.at(i);if (fileInfo.fileName() == "." || fileInfo.fileName() == ".."){continue;}if (fileInfo.isDir()){ScanDirMedias(fileInfo.filePath());}else if (fileInfo.suffix() == "mp4"){QString strName = fileInfo.baseName().toLocal8Bit().constData();QString strPath = fileInfo.absoluteFilePath().toLocal8Bit().constData();m_playList->addMedia(QUrl::fromLocalFile(strPath));m_mapItems.insert(index, new QtListWidgetItem(index, strPath, strName, QPixmap(":/images/video/ic_video_preview.png")));index++;}}
}
3.2.2 视频列表界面
视频列表的具体界面实现,主要是在对应的矩形位置,显示各个视频文件的名称:
void VideoListViewer::drawItemInfo(QPainter *painter, QtPageListWidgetItem *item)
{painter->save();QFont font(Skin::m_strAppFontBold);font.setPixelSize(18);painter->setFont(font);QPixmap pixmap = item->m_pixmapIcon;int nXoffset = (item->m_rect.width() - pixmap.width()) / 2;int nYoffset = (item->m_rect.height() - pixmap.height() - painter->fontMetrics().height() - 10) / 2;QRect rectPixmap(nXoffset + item->m_rect.left(), item->m_rect.top() + nYoffset, pixmap.width(), pixmap.height());if (pixmap.isNull()){painter->setPen(QColor("#7effffff"));painter->drawRect(item->m_rect);}else{painter->drawPixmap(rectPixmap.topLeft(), pixmap);}painter->setPen(Qt::white);painter->drawText(item->m_rect.left(), rectPixmap.bottom() + 10, item->m_rect.width(), 30, Qt::AlignCenter, item->m_strBaseName);painter->restore();
}
3.3 视频预览列表
在进入正式的视频播放前,会先有一个视频预览列表界面,在这个界面会列出指定搜索目录下的所有视频,点击任意视频图标即可进入播放界面。
此部分的列表绘制代码逻辑如下:
void QtPageListWidget::drawItemInfo(QPainter *painter, QtPageListWidgetItem *item)
{painter->save();QRect rect = item->m_rect;int nXoffset = (rect.width() > ICON_SIZE) ? (rect.width() - ICON_SIZE) / 2 : 0;QRect rectPixmap(rect.left() + nXoffset + ICON_SIZE / 2, rect.top() + ICON_SIZE / 2, ICON_SIZE, ICON_SIZE);if (item->m_pixmapIcon.isNull()){painter->setPen(Qt::NoPen);painter->setBrush(QColor("#02a7f0"));painter->drawEllipse(rectPixmap.topLeft(), ICON_SIZE / 2, ICON_SIZE / 2);}else{painter->drawPixmap(rect.left() + nXoffset, rect.top(), item->m_pixmapIcon);}if (m_nCurrentIndex == item->m_nId){painter->setPen(Qt::NoPen);painter->setBrush(QColor("#32000000"));painter->drawEllipse(rectPixmap.topLeft(), ICON_SIZE / 2, ICON_SIZE / 2);}QFont font = painter->font();font.setPixelSize(16);painter->setFont(font);painter->setPen("#ffffff");int nTextHeight = painter->fontMetrics().height();QRect rectText(rect.left(), rect.bottom() - nTextHeight, rect.width(), nTextHeight);painter->drawText(rectText, Qt::AlignCenter, item->m_strText);painter->restore();
}
4 编译与测试
4.1 在Windows平台编译
Qt程序编写好后,可以先在Windows电脑上编译查看效果,在运行时,可能会遇到如下问题:
DirectShowPlayerService::doRender: Unresolved error code 0x80040266 (IDispatch error #102)
编译的时候没问题,但运行的时候报错:
这是因为Qt 中的多媒体播放,底层是使用DirectShowPlayerService,所以Windows电脑上需要先安装一个DirectShow解码器,例如LAV Filters。可以到这里(https://github.com/Nevcairiel/LAVFilters/releases)下载并安装 LAVFilters:
下载exe安装包安装即可,安装后,再运行就不会报错了,在Windows系统上运行视频播放器的效果如下:
4.2 交叉编译并在板子上运行
通过交叉编译,来测试视频播放器在OK3568-C板子上的播放效果。
使用编译音乐播放器时编写my3568build.sh的编译脚本编译即可,无需修改:
#! /bin/bashmkdir -p build
cd buildexport PATH=/home/xxpcb/myTest/OK3568/sourcecode/OK3568-linux-source/buildroot/output/OK3568/host/bin:$PATHqmake .. && make
执行该脚本即可编译:
./my3568build.sh
将编译成功的可执行文件VIdeoPlayer复制到OK3568-C板子中,然后在其同目录下创建一个video文件夹,里面放入若干个mp4视频文件,然后就可以测试了,此外,代码中,还指定了/userdata/media这个搜索目录,这里有板子自带的一些视频:
文件复制到板子,我这里仍然使用的ADB无线传输的方式,比较方便,具体ADB操作演示,可参考之前这篇文章: RK3568源码编译与交叉编译环境搭建最后的Qt交叉编译与测试部分
需注意的是,板子里的这个Linux系统,不支持中文的显示,视频名不要有中文。
实测效果见文末视频,整体体验播放流畅,视频切换流程,进度条调整播放进度没有音乐播放器调整的顺畅,另外不知道怎么原因,视频播放的声音外放声音特别小,用耳机插孔听是正常的。
5 总结
本篇介绍了使用Qt开发一个视频播放器,一些功能代码是复用上篇的音乐播放器的代码,使用Qt Creator编写视频播放器的代码,首先在Windows电脑上编译运行测试,然后交叉编译,在OK3568-C开发板上进行实际测试。
该视频播放器实现的功能包括基础的播放功能、暂停与继续,音量调节,视频列表显示,下一个、下一个切换,进度条调节播放进度等。
相关文章:

嵌入式Qt 开发一个视频播放器
上篇文章:嵌入式 Qt开发一个音乐播放器,使用Qt制作了一个音乐播放器,并在OK3568开发板上进行了运行测试,实际测试效果还不错。 本篇继续来实现一个Qt视频播放器软件,可以实现视频列表的显示与选择播放等,先…...

阿里巴巴内网 Spring Cloud Alibaba 强势来袭,开创微服务的新时代
Spring Cloud 发展史 Spring Cloud 从 15 年的 3 月份推出之后,迅速在 Java 微服务生态中,成为开发人员的首选技术栈。 Spring Cloud 在 Spring Boot 的基础上,保留 Java 开发习惯,加入分布式特性,提供了一系列通用工…...

边界检测方法总结
1:经典的边界检测方法有sobel,拉普拉斯,canny等。 sobel: def get_sobel(in_chan, out_chan):filter_x np.array([[1, 0, -1],[2, 0, -2],[1, 0, -1],]).astype(np.float32)filter_y np.array([[1, 2, 1],[0, 0, 0],[-1, -2, -…...

Softing dataFEED OPC Suite Extended新版本支持从XML文件中读取生产数据
Softing dataFEED OPC Suite Extended V5.25的新功能——“文件读取(File Read)”,支持访问XML文件中可用的过程数据。 (文件读取功能支持获取由XML文件提供的过程数据)dataFEED OPC Suite Extended是用于OPC通信和云连…...

央行罚单!金融机构被罚原因揭秘
近日,人民银行公布了2023年首批行政处罚罚单,引发业内广泛关注。 顶象防御云业务安全情报中心统计了人民银行官网,2020年1月至2023年2月10日期间,公布的101份行政处罚。 统计显示,16家金融机构被罚27066.9万元&#…...
js中var、let、const详解
首先 var、let、const 在项目开发中都是用来声明变量的,在ES5中只有两种声明变量的方法:var和function,在ES6中新增了 let、const、class、import 四种声明变量的方法,本文主要讲解 var、let 与 const 的语法,其他的大…...
【数据库】MySQL概念知识语法-基础篇(DCL),真的很详细,一篇文章你就会了
目录通用语法及分类DCL(数据控制语言)管理用户查询用户权限控制函数字符串函数数值函数日期函数流程函数约束外键约束多表查询一对多多对多一对一通用语法及分类 ● DDL: 数据定义语言,用来定义数据库对象(数据库、表、字段&…...

Blender骨骼动画快速教程
有关创建模型的更多详细信息,请参阅在 Blender 中创建模型。 我们将为这个例子做一个非常简单的模型——蠕虫! 从我们的初始立方体开始,进入编辑模式,切换到面选择,然后选择任何面: 推荐:将 NSD…...

【C++算法】dfs深度优先搜索(下) ——【全面深度剖析+经典例题展示】
💃🏼 本人简介:男 👶🏼 年龄:18 🤞 作者:那就叫我亮亮叭 📕 专栏:关于C/C那点破事 文章目录0.0 写在前面1. 中国象棋1.1 题干信息① 背景说明概述② 问题描述…...

HIVE 基础(三)
目录 建表语句 表数据 Hive建表高阶语句 - CTAS and WITH CTAS – as select方式建表 CTE (CTAS with Common Table Expression) LIKE 创建临时表 清空表数据 修改表(Alter针对元数据) 改名 修正表文件格式 修改列名 添加列 替换列 动态分…...

redis-cluster集群搭建
安装redis所需环境 yum install -y gcc-c yum install -y wget 创建文件夹 cd / mkdir redis/redis-cluster/7001 cd redis/redis-cluster mkdir 7002 7003 7004 7005 7006 7007 7008下载redis压缩包并解压安装 wget https://download.redis.io/redis-stable.tar.gz tar -…...
【C语言】可变参数列表va_list
本篇博客让我们来认识一下C语言学习过程中往往被忽略的可变参数列表 所谓可变参数,就是一个不限定参数数量的函数,我们可以往里面传入任意个数的参数,以达成某些目的。 关联:C11可变模板参数 1.函数 #include <stdarg.h>…...

CentOS7.6 MySQL8安装
1 检查是否安装过 MySQL rpm -qa | grep -i mysqlmariadb rpm -qa | grep mariadb2 卸载之前的安装 MySQL rpm -e --nodeps 软件名 //强力删除,对相关依赖的文件也进行强力删除卸载 rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_643 官网下载 MySQL :: D…...
安装Tomcat的步骤?
首先先完成JDK配置,javac -version 检测 1.把tomcat下载到本地硬盘 2.创建tomcat8.0文件夹,完成解压(免安装)4.打开解压之后的目录,进入bin目录,双击startup.bat,启动tomcat5.可以看到弹出一个黑色的窗口,不要关闭,如果关闭意味着…...

Redis之分布式锁
随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的 Java API并不能提供分布式锁的能力。为了解决这个问题…...

2022年中国前10电商GMV总结
我是卢松松,点点上面的头像,欢迎关注我哦! 1,阿里8万亿;2,京东3万亿;3,拼多多3万亿;4,小程序私域电商3万亿;5,抖音电商1.4万亿。6,抖音本地生活服务电商600亿。7…...
ES6新增扩展:字符串-数值-数组-函数-对象
ES6新增扩展字符串的扩展判断字符串是否包含在另一个字符中字符串补全字符串重复消除字符串空格replaceAll()替换全部字符串at字符串匹配输出数值的扩展数值分隔符检测数值是否有限检测是否为NaNNumber.parseInt()、Number.parseFloat()isInteger()判断是否为整数Math.sign()判…...
python中import原理
0. 前言 在 python 中引入 Module 是再常见不过了,那么当我们 import 时它做了什么事情呢?它是如何加载 Module 使用的呢? 1. 什么是 module? 一般,Module 是一个后缀为 .py 的文件,其 module 名称一般…...

《Qt6开发及实例》6-4 显示SVG格式图片
目录 一、简介与设计 1.1 简介 1.2 设计 二、SvgWidget 2.1 鼠标滚轮事件 三、svgwindow 四、MainWindow 一、简介与设计 1.1 简介 1、SVG 的英文全称是 Scalable Vector Graphics,即可缩放的矢量图形。它是由万维网联盟(W3C)在 200…...

OpenGL ES 绘制一张图片
GLSL 语法与内建函数 GLSL 的修饰符与数据类型 GLSL 中变量的修饰符 const:修饰不可被外界改变的常量attribute:修饰经常更改的变量,只可以在顶点着色器中使用uniform:修饰不经常更改的变量,可用于顶点着色器和片段…...

引领AI安全新时代 Accelerate 2025北亚巡展·北京站成功举办
6月5日,网络安全行业年度盛会——"Accelerate 2025北亚巡展北京站"圆满落幕!来自智库、产业界、Fortinet管理层及技术团队的权威专家,与来自各行业的企业客户代表齐聚一堂,围绕"AI智御全球引领安全新时代"主题…...
撰写脚本,通过发布/joint_states话题改变机器人在Rviz中的关节角度
撰写脚本,通过发布/joint_states话题改变机器人在Rviz中的关节角度 提问 为我写一个改变关节base_spherical_center_high_joint角度的python脚本吧。适用于ROS2的humble 回答 下面是一个适用于 ROS 2 Humble 的 Python 脚本,它会以指定频率持续发布 …...

浅聊一下,大模型应用架构 | 工程研发的算法修养系列(二)
大模型应用架构基础 AI应用演进概述 人工智能应用的发展经历了多个关键阶段,每个阶段都代表着技术范式的重大转变。 大语言模型基础 大语言模型(LLM)作为现代AI应用的核心组件,具有独特的技术特性和能力边界,理解这些基础对架构设计至关重要。…...

SpringBoot整合RocketMQ与客户端注意事项
SpringBoot整合RocketMQ 引入依赖(5.3.0比较稳定) <dependencies><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.3.1</version&…...
uniapp 集成腾讯云 IM 消息搜索功能
UniApp 集成腾讯云 IM 消息搜索功能实战指南 一、功能实现原理 腾讯云 IM 通过 消息漫游 服务端搜索接口 实现消息检索,核心机制如下: 数据存储:消息默认存储7天(可扩展至30天)索引构建:基于消息内容自…...
java教程笔记(十一)-泛型
Java 泛型(Generics)是 Java 5 引入的重要特性之一,它允许在定义类、接口和方法时使用类型参数。泛型的核心思想是将类型由具体的数据类型推迟到使用时再确定,从而提升代码的复用性和类型安全性。 1.泛型的基本概念 1. 什么是泛…...
解决神经网络输出尺寸过小的实战方案
训练CIFAR10分类模型时出现报错:RuntimeError: Given input size: (256x1x1). Calculated output size: (256x0x0). Output size is too small。该问题由网络结构设计缺陷导致图像尺寸过度缩小引发。 核心原因分析 网络结构缺陷 原始模型采用六层卷积层,…...

《Linux运维总结:宝德服务器RAID开启(方式一)》
总结:整理不易,如果对你有帮助,可否点赞关注一下? 更多详细内容请参考:Linux运维实战总结 一、背景信息 说明:从客户那里退回来的一台宝德服务器,硬盘不见了,现在需要用两个2T的硬盘…...

Vue前端篇——项目目录结构介绍
📘 前言 在正式开始学习 Vue 3 开发之前,了解并熟悉其项目目录结构是非常关键的第一步。一个清晰、规范的目录结构不仅有助于开发者高效地组织代码,还能显著提升项目的可读性和可维护性。 Vue 3 作为现代前端开发中广泛使用的主流框架之一&…...

快速用 uv 模拟发布一个 Python 依赖包到 TestPyPI 上,以及常用命令
目录 1. uv 介绍2. uv 安装(Windows版)3. 快速模拟一个要发布到TestPyPI上的依赖包,scoful-test-lib3.1 初始化 uv init3.2 进入scoful-test-lib3.3 修改pyproject.toml3.4 使用命令 uv sync3.5. 使用命令 uv lock3.6 使用命令 uv build3.7 获…...