Qt 中使用 ffmpeg 获取采集卡数据录制视频
作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
前言
之前做了一个功能,从采集卡获取数据然后录制成视频,结果发现录制的视频内存占用非常大,1分钟的视频大概有 800MB 内存。在帧率和分辨率已确定的情况下,只能通过调整比特率来减少内存占用,但是设置比特率在不同编码器和平台支持情况都有所不同,有些编码器甚至不支持直接设置比特率,所以博主想起了 ffmpeg 这个神器。
这里先介绍一下视频的相关参数:
在用户视角:
清晰度 = 比特率(码率) / 分辨率
流畅度 = 帧率在开发者视角:
影响内存的:主要是分辨率
影响 CPU 的:码率和编码格式
影响 GPU 的:分辨率和编码格式
影响体积大小和带宽:码率
ffmpeg 库功能测试
首先在网上找到了 ffmpeg 库的 windows 安装包来做下测试
百度网盘下载链接:ffmpeg(windows安装包)
提取码:fkmn
下载完成之后可以直接使用 bin\ffmpeg.exe 在命令行做测试,把 ffmpeg\bin 路径添加到环境变量中
-
确认 ffmpeg 版本和配置:
ffmpeg -version -
列举所有设备:
ffmpeg -list_devices true -f dshow -i dummy
对已录制的内存占用较大的视频进行压缩:
- 设置码率:
ffmpeg -i input.mp4 -b:v 1000k output.mp4 - 设置分辨率:
ffmpeg -i input.mp4 -s 640x360 output.mp4 - 设置帧率:
ffmpeg -i input.mp4 -r 30 output.mp4 - 综合调整:
ffmpeg -i input.mp4 -b:v 800k -s 640x360 -r 30 output.mp4
也可以用 python 跑脚本:
import subprocess
def compress_video(input_file, output_file, bitrate='1000k'):command = ['ffmpeg','-i', input_file,'-b:v', bitrate,output_file]try:subprocess.run(command, check=True)print(f"视频压缩成功,输出文件为: {output_file}")except subprocess.CalledProcessError as e:print(f"视频压缩失败: {e}")# 使用示例
input_file = 'input.mp4'
output_file = 'output.mp4'
compress_video(input_file, output_file, bitrate='800k')
再测试一下直接打开设备录制视频
ffmpeg -f dshow -i video="@device_pnp_\\?\usb#vid_2b89&pid_5647&mi_00#7&223c07ce&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" -c:v libx264 -max_delay 10000 -b:v 1000k -bufsize 200k -r 30 output.mp4 -y命令解释
-c:v libx264: 指定使用libx264编码器进行视频编码。
-max_delay 10000: 设置编码器的最大延迟为10000毫秒(10秒)。这有助于控制视频编码的缓冲延迟。
-b:v 1000k: 设置视频比特率为1000 kbps(千比特每秒)。
-bufsize 200k: 设置编码器的缓冲区大小为200 kbps。这个参数用于控制编码器在遇到高负载或低负载时的比特率变化。
-r 30: 设置帧率为30帧每秒。
output.mp4: 输出文件名。
-y: 覆盖输出文件,如果文件已存在。
测试结果为用 ffmpeg 压缩过的视频内存占用率非常小,码率越小内存就越小。但是对于客户来说他不会使用命令行去做压缩,所以最终方案还是直接使用 ffmpeg 库来录制视频。
Qt 中集成 ffmpeg 库来录制视频
首先在网上下载编译完成的 ffmpeg 库,博主用的是 Qt 5.15.2 和 vs2019
百度网盘下载链接:ffmpeg(库文件)
提取码: cqwx
把库集成到 Qt 中:
INCLUDEPATH += $$PWD/ffmpeg/includeLIBS += -L$$PWD/ffmpeg/lib/ -lavcodec
LIBS += -L$$PWD/ffmpeg/lib/ -lavdevice
LIBS += -L$$PWD/ffmpeg/lib/ -lavfilter
LIBS += -L$$PWD/ffmpeg/lib/ -lavformat
LIBS += -L$$PWD/ffmpeg/lib/ -lavutil
LIBS += -L$$PWD/ffmpeg/lib/ -lpostproc
LIBS += -L$$PWD/ffmpeg/lib/ -lswresample
LIBS += -L$$PWD/ffmpeg/lib/ -lswscale
实现拍照和录屏的功能:
extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
}void showErrorInfo(int ret)
{static char errbuf[AV_ERROR_MAX_STRING_SIZE];memset(errbuf, 0, AV_ERROR_MAX_STRING_SIZE);av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);qDebug() << "Error info:" << errbuf;
}void videoRecord()
{avformat_network_init();avdevice_register_all();// 遍历所有设备类型// const AVInputFormat *iformat = nullptr;// while ((iformat = av_input_audio_device_next(iformat))) {// qDebug() << "Audio input device: " << iformat->name << " - " << iformat->long_name;// }// iformat = nullptr;// while ((iformat = av_input_video_device_next(iformat))) {// qDebug() << "Video input device: " << iformat->name << " - " << iformat->long_name;// }const AVInputFormat *iformat_dshow = av_find_input_format("dshow");if (!iformat_dshow){qDebug() << "Could not find input format !";return;}// 用于存储设备列表的上下文AVDeviceInfoList *deviceList = nullptr;int ret = avdevice_list_input_sources(iformat_dshow, nullptr, nullptr, &deviceList);if (ret < 0) {qDebug() << "Could not list input sources !";showErrorInfo(ret);return;}// 设备名称std::string deviceName = "";// 遍历设备列表// qDebug() << "Available DirectShow devices:";for (int i = 0; i < deviceList->nb_devices; ++i){AVDeviceInfo *device = deviceList->devices[i];// qDebug() << "Device" << i << ":" << device->device_description << "(" << device->device_name << ")";// 获取绿联采集卡的设备名称QString description = QString(device->device_description);if ( description.contains("UGREEN") ) {deviceName = "video=" + description.toStdString();break;}}// 释放设备列表avdevice_free_list_devices(&deviceList);//------------------------------// 创建 AVFormatContextAVFormatContext *inputFormatContext = avformat_alloc_context();if (!inputFormatContext) {qDebug() << "Could not allocate AVFormatContext !";return;}// 设置附加参数AVDictionary *options = nullptr;int framerate = 30;av_dict_set(&options, "rtbufsize", "100M", 0); // 设置缓冲区大小av_dict_set(&options, "framerate", "30", 0); // 设置帧率// 打开输入设备ret = avformat_open_input(&inputFormatContext, deviceName.c_str(), iformat_dshow, &options);if (ret < 0) {qDebug() << "Could not open input device !";showErrorInfo(ret);return;}// 打印输入设备的信息// av_dump_format(inputFormatContext, 0, deviceName.c_str(), 0);// 查找输入流信息ret = avformat_find_stream_info(inputFormatContext, nullptr);if (ret < 0) {qDebug() << "Could not find stream information !";showErrorInfo(ret);return;}// 查找视频流int videoStreamIndex = -1;for (unsigned int i = 0; i < inputFormatContext->nb_streams; i++){if (inputFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {videoStreamIndex = i;break;}}if (videoStreamIndex == -1) {qDebug() << "Could not find video stream !";return;}// 获取视频流参数AVCodecParameters *inputCodecParameters = inputFormatContext->streams[videoStreamIndex]->codecpar;// 查找输入解码器const AVCodec *inputCodec = avcodec_find_decoder(inputCodecParameters->codec_id);if (!inputCodec) {qDebug() << "Could not find codec !";return ;}// 创建输入解码器上下文AVCodecContext *inputCodecContext = avcodec_alloc_context3(inputCodec);if (!inputCodecContext) {qDebug() << "Could not allocate codec context !";return;}// 将视频流参数复制到输入解码器上下文ret = avcodec_parameters_to_context(inputCodecContext, inputCodecParameters);if (ret < 0) {qDebug() << "Could not copy codec parameters to context !";showErrorInfo(ret);return;}// 打开输入解码器ret = avcodec_open2(inputCodecContext, inputCodec, nullptr);if (ret < 0) {qDebug() << "Could not open codec !";showErrorInfo(ret);return;}//------------------------------// 分配帧和数据包AVFrame *frame = av_frame_alloc();AVPacket *packet = av_packet_alloc();if (!frame || !packet) {qDebug() << "Could not alloc frame and packet !";return;}// 分配图像转换上下文SwsContext *swsContext = sws_getContext(inputCodecContext->width, inputCodecContext->height, inputCodecContext->pix_fmt,inputCodecContext->width, inputCodecContext->height, AV_PIX_FMT_RGB24,SWS_BILINEAR, nullptr, nullptr, nullptr);if (!swsContext) {qDebug() << "Could not initialize SwsContext !";return;}// 分配 RGB 帧AVFrame *rgbFrame = av_frame_alloc();int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, inputCodecContext->width, inputCodecContext->height, 1);uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24,inputCodecContext->width, inputCodecContext->height, 1);// 读取帧数据(保存10张图片)int frameCount = 0;while (av_read_frame(inputFormatContext, packet) >= 0 && frameCount < 11){// 确保帧的数据类型和解码器的数据类型一致if (packet->stream_index == videoStreamIndex){// 发送数据包到解码器ret = avcodec_send_packet(inputCodecContext, packet);if (ret < 0) {qDebug() << "Error sending packet to decoder !";showErrorInfo(ret);continue;}// 接收解码后的帧while (avcodec_receive_frame(inputCodecContext, frame) == 0){// 舍弃第一帧if ( frameCount == 0 ) {frameCount++;continue;}// 转换图像格式sws_scale(swsContext, frame->data, frame->linesize, 0, inputCodecContext->height,rgbFrame->data, rgbFrame->linesize);// 创建 QImageQImage image(rgbFrame->data[0], inputCodecContext->width, inputCodecContext->height, QImage::Format_RGB888);// 保存图像QString fileName = QString("frame_%1.jpg").arg(frameCount++);if (!image.save(fileName)) {qDebug() << "Error saving image:" << fileName;} else {qDebug() << "Image saved:" << fileName;}}}av_packet_unref(packet);}//------------------------------// 输出格式上下文AVFormatContext *outputFormatContext = nullptr;const char *outputFileName = "output.mp4";// 创建输出格式上下文ret = avformat_alloc_output_context2(&outputFormatContext, nullptr, nullptr, outputFileName);if (!outputFormatContext) {qDebug() << "Could not create output context !";showErrorInfo(ret);return;}// 查找输出编码器const AVCodec *outputCodec = avcodec_find_encoder(AV_CODEC_ID_H264);if (!outputCodec) {qDebug() << "Could not find output codec !";return;}// 创建输出流AVStream *outputStream = avformat_new_stream(outputFormatContext, outputCodec);if (!outputStream) {qDebug() << "Could not create output stream !";return;}// 打开输出编码器上下文AVCodecContext *outputCodecContext = avcodec_alloc_context3(outputCodec);if (!outputCodecContext) {qDebug() << "Could not allocate output codec context !";return;}// 设置输出编码器参数outputCodecContext->codec_id = outputCodec->id;outputCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;outputCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;outputCodecContext->width = 1920; // 设置分辨率outputCodecContext->height = 1080;outputCodecContext->time_base = {1, framerate}; // 设置帧率outputCodecContext->framerate = {framerate, 1};outputCodecContext->bit_rate = 2000000; // 设置比特率outputCodecContext->rc_buffer_size = 2 * outputCodecContext->bit_rate; // 设置缓冲区大小为码率的两倍outputCodecContext->gop_size = 10; // 设置关键帧间隔outputCodecContext->max_b_frames = 1;if (outputFormatContext->oformat->flags & AVFMT_GLOBALHEADER) {outputCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;}// 打开输出编码器ret = avcodec_open2(outputCodecContext, outputCodec, nullptr);if (ret < 0) {qDebug() << "Could not open output codec !";showErrorInfo(ret);return;}// 复制输出编码器参数到输出流avcodec_parameters_from_context(outputStream->codecpar, outputCodecContext);outputStream->time_base = outputCodecContext->time_base;//------------------------------// 打开输出文件if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&outputFormatContext->pb, outputFileName, AVIO_FLAG_WRITE);if (ret < 0) {qDebug() << "Could not open output file !";showErrorInfo(ret);return;}}// 写入文件头ret = avformat_write_header(outputFormatContext, nullptr);if (ret < 0) {qDebug() << "Could not write header !";showErrorInfo(ret);return;}// 分配帧和数据包AVFrame *inputFrame = av_frame_alloc();AVPacket *inputPacket = av_packet_alloc();AVFrame *outputFrame = av_frame_alloc();AVPacket *outputPacket = av_packet_alloc();if (!inputFrame || !inputPacket || !outputFrame || !outputPacket) {qDebug() << "Could not alloc frame and packet !";return;}// 分配图像转换上下文SwsContext *swsContext2 = sws_getContext(inputCodecContext->width, inputCodecContext->height, inputCodecContext->pix_fmt,outputCodecContext->width, outputCodecContext->height, outputCodecContext->pix_fmt,SWS_BILINEAR, nullptr, nullptr, nullptr);if (!swsContext2) {qDebug() << "Could not initialize SwsContext !";return;}// 分配输出帧数据outputFrame->format = AV_PIX_FMT_YUV420P;outputFrame->width = inputCodecContext->width;outputFrame->height = inputCodecContext->height;ret = av_frame_get_buffer(outputFrame, 0);if (ret < 0) {qDebug() << "Could not get frame buffer !";showErrorInfo(ret);return;}// 录屏60秒,帧率30,保存1800帧数据int frameCount2 = 0;while (av_read_frame(inputFormatContext, inputPacket) >= 0 && frameCount2 < 1800){// 确保帧的数据类型和解码器的数据类型一致if (inputPacket->stream_index == videoStreamIndex){// 发送输入数据包到解码器ret = avcodec_send_packet(inputCodecContext, inputPacket);if (ret < 0) {qDebug() << "Error sending packet to decoder !";showErrorInfo(ret);continue;}// 接收解码后的帧while (avcodec_receive_frame(inputCodecContext, inputFrame) == 0){// 转换图像格式sws_scale(swsContext2, inputFrame->data, inputFrame->linesize, 0, inputCodecContext->height,outputFrame->data, outputFrame->linesize);outputFrame->pts = frameCount2++;qDebug() << "frameCount2: " << frameCount2;// 发送输出帧到输出编码器ret = avcodec_send_frame(outputCodecContext, outputFrame);if (ret < 0) {qDebug() << "Error sending frame to encoder !";showErrorInfo(ret);continue;}// 接收编码后的数据包while (avcodec_receive_packet(outputCodecContext, outputPacket) == 0){av_packet_rescale_ts(outputPacket, outputCodecContext->time_base, outputStream->time_base);outputPacket->stream_index = outputStream->index;// 写入数据包到输出文件ret = av_interleaved_write_frame(outputFormatContext, outputPacket);if (ret < 0) {qDebug() << "Error writing packet to output file";showErrorInfo(ret);continue;}av_packet_unref(outputPacket);}}}av_packet_unref(inputPacket);}// 刷新编码器avcodec_send_frame(outputCodecContext, nullptr);while (avcodec_receive_packet(outputCodecContext, outputPacket) == 0){av_packet_rescale_ts(outputPacket, outputCodecContext->time_base, outputStream->time_base);outputPacket->stream_index = outputStream->index;ret = av_interleaved_write_frame(outputFormatContext, outputPacket);if (ret < 0) {qDebug() << "Error writing packet to output file";showErrorInfo(ret);}av_packet_unref(outputPacket);}// 写入文件尾av_write_trailer(outputFormatContext);// 释放资源av_frame_free(&inputFrame);av_frame_free(&outputFrame);av_packet_free(&inputPacket);av_packet_free(&outputPacket);sws_freeContext(swsContext2);avcodec_free_context(&outputCodecContext);avformat_free_context(outputFormatContext);if (outputFormatContext && !(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {avio_closep(&outputFormatContext->pb);}av_free(buffer);av_frame_free(&rgbFrame);av_frame_free(&frame);av_packet_free(&packet);sws_freeContext(swsContext);avcodec_free_context(&inputCodecContext);avformat_close_input(&inputFormatContext);avformat_free_context(inputFormatContext);av_dict_free(&options);avformat_network_deinit();
}
相关文章:
Qt 中使用 ffmpeg 获取采集卡数据录制视频
作者:billy 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 前言 之前做了一个功能,从采集卡获取数据然后录制成视频,结果发现录制的视频内存占用非常大,1分钟的…...
用HTML5+CSS+JavaScript实现新奇挂钟动画
用HTML5+CSS+JavaScript实现新奇挂钟动画 引言 在技术博客中,如何吸引粉丝并保持他们的关注?除了干货内容,独特的视觉效果也是关键。今天,我们将通过HTML5、CSS和JavaScript实现一个新奇挂钟动画,并将其嵌入到你的网站中。这个动画不仅能让你的网站脱颖而出,还能展示你的…...
一周学会Flask3 Python Web开发-redirect重定向
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 前面我们学过渲染到模板页面,这个其实是一种内部的转发,浏览器地址栏地址没有变化。如果我们想重定向…...
如何在 ConstraintLayout 中将 ViewPager 始终置于 ll_title 的下方
关于如何在 ConstraintLayout 中将 ViewPager 始终置于 ll_title标题栏 的下方。。 如何将 ViewPager 始终置于 ll_title 下方 在 ConstraintLayout 中,可以通过约束来实现 ViewPager 始终位于 ll_title 标题栏的下方。以下是修改后的布局代码: <?…...
文心一言大模型的“三级跳”:从收费到免费再到开源,一场AI生态的重构实验
2025年2月,百度文心大模型接连抛出两枚“重磅炸弹”:4月1日起全面免费,6月30日正式开源文心大模型4.5系列。这一系列动作不仅颠覆了李彦宏此前坚持的“闭源优势论”13,更标志着中国AI大模型竞争进入了一个全新的阶段——从技术壁垒…...
IPv6报头40字节具体怎么分配的?
目录 IPv6报头结构 字段详解 示例代码:IPv6报头的Python实现 输出示例 IPv6协议是为了解决IPv4地址耗尽问题而设计的下一代互联网协议。与IPv4相比,IPv6不仅提供了更大的地址空间,还简化了报头结构,提高了网络设备的处理效率。…...
使用 Spark NLP 实现中文实体抽取与关系提取
在自然语言处理(NLP)领域,实体抽取和关系提取是两个重要的任务。实体抽取用于从文本中识别出具有特定意义的实体(如人名、地名、组织名等),而关系提取则用于识别实体之间的关系。本文将通过一个基于 Apache Spark 和 Spark NLP 的示例,展示如何实现中文文本的实体抽取和…...
大数据治理之solr的体现
大数据治理之solr的体现 一,大数据治理下Solr的作用 在大数据治理的背景下,Solr作为一个高性能的搜索平台,发挥这重要的作用,下面是Solr在大数据治理中的几个关键作用和体现: 数据索引与检索: 高效检索&a…...
[笔记.AI]如何判断模型是否通过剪枝、量化、蒸馏生成?
以下摘自与DeepSeek-R1在线联网版的对话 一、基础判断维度 技术类型核心特征验证方法剪枝模型参数减少、结构稀疏化1. 检查模型参数量是否显著小于同类标准模型1 2. 分析权重矩阵稀疏性(如非零参数占比<30%)4量化权重/激活值精度降低、推理速度提升1…...
Uniapp 从入门到精通:基础篇 - 搭建开发环境
Uniapp 从入门到精通:基础篇 - 搭建开发环境 前言一、Uniapp 简介1.1 什么是 Uniapp1.2 Uniapp 的优势二、搭建开发环境前的准备2.1 安装 Node.js2.2 安装 HBuilderX三、创建第一个 Uniapp 项目3.1 打开 HBuilderX 并创建项目3.2 项目结构介绍3.3 运行项目四、配置项目4.1 配置…...
CSDN文章质量分查询系统【赠python爬虫、提分攻略】
CSDN文章质量分查询系统 https://www.csdn.net/qc 点击链接-----> CSDN文章质量分查询系统 <------点击链接 点击链接-----> https://www.csdn.net/qc <------点击链接 点击链接-----> CSDN文章质量分查询系统 <------点击链接 点击链…...
GPT-SoVITS更新V3 win整合包
GPT-SoVITS 是由社区开发者联合打造的开源语音生成框架,其创新性地融合了GPT语言模型与SoVITS(Singing Voice Inference and Timbre Synthesis)语音合成技术,实现了仅需5秒语音样本即可生成高保真目标音色的突破。该项目凭借其开箱…...
nginx ngx_http_module(8) 指令详解
nginx ngx_http_module(8) 指令详解 nginx 模块目录 nginx 全指令目录 一、目录 1.1 模块简介 ngx_http_ssi_module:服务器端包含(SSI)模块,允许在HTML页面中插入其他内容或动态生成的内容。通过特殊的SSI指令(如 …...
CSS三大特性——继承、优先级与层叠
1. 层叠性 概念:如果发生了样式冲突,那就会根据一定的规则(选择器优先级),进行样式的层叠(覆 盖)。 什么是样式冲突? ——— 元素的同一个样式名,被设置了不同的值&…...
Java 中的方法参数传递与值传递
文章目录 Java 中的方法参数传递与值传递代码示例代码运行结果分析原因1. Java 中的参数传递机制2. 代码执行过程 值传递的图示如何实现真正的交换?1. 使用数组2. 使用对象 总结 Java 中的方法参数传递与值传递 在 Java 编程中,理解方法参数传递的机制是…...
敏捷开发06:用户故事估算方法介绍
估算介绍 在以前开发 IT 软件时,使用较多的衡量软件开发工作量的单位是:小时、人天 或 人月。它是预估开发时间。比如:这个功能张三一个人开发需要 3 天时间完成。 这种 “人天” 估算只是 “理想人天” 的估算,有时与实际开发完…...
在原有基础上的Python正则表达式终极指南,新增高级用法、复杂案例和底层原理分析
以下是Python正则表达式终极指南,新增高级用法、复杂案例和底层原理分析: Python正则表达式终极指南 一、正则表达式引擎原理 1. 回溯机制解析 NFA(非确定性有限自动机)工作原理回溯的产生场景及性能影响灾难性回溯案例:# 危险模式示例 re.match(r(a+)+b, aaaaaaaaac) …...
进制转换及C语言中进制转换方法
进制转换是计算机科学和数学中的基础操作,主要用于不同数制之间的数值表示转换。以下是常见进制(二进制、八进制、十进制、十六进制)的转换方法及示例: 一、其他进制 → 十进制 方法:按权展开,逐位相加。 …...
【我要成为配环境高手】node卸载与nvm安装
node卸载与nvm安装 1. node卸载 参考了这篇文章: https://blog.csdn.net/weixin_43801036/article/details/141487791 2. nvm安装 参考了这两篇文章: https://www.cnblogs.com/rnny/p/17839190.html#tid-z7A3nR https://blog.csdn.net/weixin_45811…...
环境变量与本地变量
目录 本地变量的创建 环境变量VS本地变量 认识完了环境变量我们来认识一下本地变量。 本地变量的创建 我们如果直接env是看不到本地变量的,因为本地变量和环境变量都具有独立性,环境变量是系统提供的具有全局属性的变量,都存在bash进程的…...
Docker安装Kafka(不依赖ZooKeeper)
创建docker-compose.yaml version: "3.9" #版本号 services:kafka:image: apache/kafka:3.9.0container_name: kafkahostname: kafkaports:- 9092:9092 # 容器内部之间使用的监听端口- 9094:9094 # 容器外部访问监听端口environment:KAFKA_NODE_ID: 1KAFKA_PROCES…...
Visual Studio中打开多个项目
1) 找到解决方案窗口 2) 右键添加→ 选择现有项目 3) 选择.vcxproj文件打开即可...
rust笔记7-生命周期显式标注
Rust 的生命周期(Lifetimes)是 Rust 内存安全模型的核心部分,用于确保引用始终有效,避免悬垂引用(Dangling References)。下面我们从生命周期的设计出发点、标注语法以及在不同上下文中的应用(函数、方法、结构体、trait 等)来详细介绍。 1. 生命周期设计的出发点 Rus…...
广西壮族自治区园区投促中心党委书记陶德文率团到访深兰科技
2月16日,广西壮族自治区园区投促中心党委书记、主任,自治区园区办党组成员陶德文率团来到深兰科技集团上海总部考察调研,并与深兰科技集团创始人、董事长陈海波等集团管理层座谈交流,双方围绕深兰科技人工智能项目落地广西的相关事…...
1005 K 次取反后最大化的数组和(贪心)
文章目录 题目[](https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations/)算法原理源码总结 题目 如上图,k是取反的次数,在数组【4,-1,3】中,当k 1,把-2取反为2,和为9;在数组…...
Softing线上研讨会 | 自研还是购买——用于自动化产品的工业以太网
| 线上研讨会时间:2025年1月27日 16:00~16:30 / 23:00~23:30 基于以太网的通信在工业自动化网络中的重要性日益增加。设备制造商正面临着一大挑战——如何快速、有效且经济地将工业以太网协议集成到其产品中。其中的关键问题包括:是否只需集成单一的工…...
SpringBoot整合Redis和Redision锁
参考文章 1.Redis 1.导入依赖 <!--Redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.c…...
【Pandas】pandas Series rename_axis
Pandas2.2 Series Computations descriptive stats 方法描述Series.align(other[, join, axis, level, …])用于将两个 Series 对齐,使其具有相同的索引Series.case_when(caselist)用于根据条件列表对 Series 中的元素进行条件判断并返回相应的值Series.drop([lab…...
数仓搭建(hive):DM搭建(数据集市层)
DM层 数据集市层 (Data Mart) 粒度上卷(Roll-up): 指的是沿着维度层次向上聚合汇总数据,从细粒度到粗粒度观察数据的操作。 示例 数仓的上一层DWS的是按日汇总 DM层基于DWS层主题日宽表上卷统计出按年,月,周的数…...
用LightRAG+智谱GLM-4提升政务对话精度:从知识图谱到精准问答的实战指南
在政务场景中,对话系统的精度至关重要,一个不准确的回答可能导致政策误解甚至行政失误。LightRAG结合智谱GLM-4,通过知识增强(如知识图谱、检索增强生成RAG和动态上下文)显著提升问答准确性。本文从代码实现到架构优化…...
