当前位置: 首页 > news >正文

Qt中ffmpeg API存储和显示摄像头视频

Qt中ffmpeg API存储和显示摄像头视频的功能需要之前写的视频ffmpegAPI的视频播放的流程。

代码源码位置:https://download.csdn.net/download/qq_43812868/88157743?spm=1001.2014.3001.5503

一、存储和显示摄像头的视频的流程

在这里插入图片描述

这是读取打开视频文件的流程,视频文件在avformat_open_input参数中,最终将数据传递到av_frame_alloc创建的AVFrame。

在这里插入图片描述

这个是存储的流程,将第一部分的AVFrame数据传递进来,之后通过av_interleaved_write_frame写入到媒体文件。

二、相关结构体介绍

在了解使用api之前,还需要先了解一下ffmpeg中的相关结构体,在了解了这些结构体之后,可以更容易的理解代码。

AVFormatContext:此结构体存储音视频封装格式中包含的信息,并且这个结构体是贯穿整个播放流程的。在这个结构体中主要包含AVInputFormat,AVOutputFormat、AVStream等。

struct AVInputFormat *iformat; // 输入数据的封装格式
AVIOContext *pb; // 输入数据的缓存
unsigned int nb_streams; // 音视频流的个数
AVStream **streams; // 音视频流
char filename[1024]; // 文件名
int64_t duration; // 时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate; // 比特率(单位bps,转换为kbps需要除以1000)
AVDictionary *metadata; // 元数据

**AVCodecContext:**是一个描述编解码器上下文的结构体,包含了众多编解码器需要的参数信息。

enum AVMediaType codec_type; // 编解码器的类型(视频,音频...)
struct AVCodec  *codec; // 采用的解码器AVCodec(H.264,MPEG2...)
int bit_rate; // 平均比特率
uint8_t *extradata; int extradata_size; // 针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)
AVRational time_base; // 根据该参数,可以把PTS转化为实际的时间(单位为秒s)
int width, height; // 如果是视频的话,代表宽和高
int refs; // 运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)
int sample_rate; // 采样率(音频)
int channels; // 声道数(音频)
enum AVSampleFormat sample_fmt; // 采样格式
int profile; // 型(H.264里面就有,其他编码标准应该也有)
int level; // 级(和profile差不太多)

AVCodec:是存储编码器信息的结构体。

const char *name; // 编解码器的名字的简称
const char *long_name; // 编解码器名字的全称
enum AVMediaType type; // 指明了类型,是视频,音频,还是字幕
enum AVCodecID id; // ID,不重复
const AVRational *supported_framerates; // 支持的帧率(仅视频)
const enum AVPixelFormat *pix_fmts; // 支持的像素格式(仅视频),如RGB24、YUV420P等。
const int *supported_samplerates; // 支持的采样率(仅音频)
const enum AVSampleFormat *sample_fmts; // 支持的采样格式(仅音频)
const uint64_t *channel_layouts; // 支持的声道数(仅音频)
int priv_data_size; // 私有数据的大小

AVFrame:该结构描述解码的(原始的)音频或视频数据。AVFrame必须使用av_frame_alloc()进行分配。请注意,这只是分配AVFrame本身,必须管理数据的缓冲区通过其他方式。AVFrame必须使用av_frame_free()释放。


AVPacket:是存储压缩编码数据相关信息的结构体。

uint8_t *data; // 压缩编码的数据。
/* 例如对于H.264来说。1个AVPacket的data通常对应一个NAL。注意:在这里只是对应,而不是一模一样。他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流。因此在使用FFMPEG进行音视频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到音视频的码流文件。*/
int   size; // data的大小
int64_t pts; // 显示时间戳
int64_t dts; // 解码时间戳
int   stream_index; // 标识该AVPacket所属的视频/音频流。

三、ffmpeg函数介绍

int avformat_alloc_output_context2 (AVFormatContext **ctx, ff_const59 AVOutputFormat *oformat, const char *format_name, const char *filename);
功能:为输出格式分配AVFormatContext。
参数:ctx:设置为创建的格式上下文,或者在失败时设置为NULL;oformat:如果使用NULL format_name和filename,则用于分配上下文的格式;format_name:如果使用NULL文件名,则用于分配上下文的输出格式的名称;filename:用于分配上下文的文件名,可以是NULL;
返回值:
AVCodec* avcodec_find_encoder (enum AVCodecID id);
功能:根据AVCodecID寻找已经注册的编码器;
参数:已经注册的编码器ID;
返回值:返回寻找到的编码器;
AVStream* avformat_new_stream (AVFormatContext *s, const AVCodec *c);
功能:为AVFormatContext添加一个新的流;
参数:s:媒体文件c:编码器,如果为空,与新流相对应的AVCodecContext将被初始化以使用该编解码器。这是需要的,例如,要设置特定于编解码器的默认值,因此如果已知,则应提供编解码器
返回值:新创建的流或出现错误时为NULL;
AVCodecContext* avcodec_alloc_context3 (const AVCodec *codec);
功能:分配AVCodecContext并将其字段设置为默认值;
参数:如果非NULL,则分配专用数据并初始化给定编解码器的默认值。然后用不同的编解码器调用avcodec_open2()是非	   法的。如果为NULL,则编解码器特定的默认值将不会初始化,这可能会导致次优的默认设置(这主要对编码器很重	        要,例如libx264)。
返回值:AVCodecContext已填充默认值或失败时为NULL;
int avcodec_open2 (AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
功能:初始化AVCodecContext以使用给定的AVCodec。在使用此函数之前,必须使用avcodec_alloc_text3()分配上下文。
参数:avctx:要初始化的上下文;codec:要为其打开此上下文的编解码器。如果之前已将非NULL编解码器传递给avcodec_alloc_text3()或此上下文,则此参数必须为NULL或等于之前传递的编解码器;options:一个充满AVCodecContext和编解码器专用选项的字典。返回时,此对象将填充未找到的选项。可以为nullptr;
返回值:成功时为零,错误时为负值;
int avcodec_parameters_from_context (AVCodecParameters *par, const AVCodecContext *codec);
功能:根据提供的编解码器上下文中的值填充参数结构。par中任何分配的字段都将被释放,并替换为编解码器中相应字段的副本;
参数:par:AVCodecParamteres结构体;codec:编码器上下文;
返回值:>=成功时为0,失败时为负AVERROR代码;
int avio_open(AVIOContext **s, const char *url, int flags);
功能:创建并初始化AVIOContext以访问url指示的资源。
参数:s 用于返回指向创建的AVIOContext的指针。如果发生故障,则指向的值将设置为NULL;url 要访问url的资源;flags 标志用于控制url指示的资源的方式的标志将被打开;
返回值:return>=0如果成功,则为负值,对应于发生故障时的AVERROR代码
av_warn_unused_result int avformat_write_header (AVFormatContext *s, AVDictionary **options);
功能:分配流专用数据并将流头写入输出媒体文件。
参数:s 媒体文件句柄,必须使用avformat_alloc_context()进行分配。其oformat字段必须设置为所需的输出格         式;其pb字段必须设置为已打开的AVIOContext;options充满AVFormatContext和muxer私有选项的AVDictionary。返回时,此参数将被销毁,并替换为包含未	      找到的选项的dict。可能为NULL;
返回值:如果编解码器尚未在avformat_INIT中完全初始化,则AVSTREAM_INIT_IN_WRITE_HEADER成功;如果编解码	       器已在avformat_INIT中完全启动,则AVSTEAM_INIT_INIT_OUTPUT成功;如果失败,则为负AVERROR。
void av_packet_rescale_ts (AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
功能:将数据包中的有效时间字段(时间戳/持续时间)从一个时基转换为另一个时基。具有未知值(AV_NOPTS_VALUE)的时间戳将被忽略。
参数:pkt 将对其执行转换的pkt数据包;tb_src 源时基,其中表示pkt中的定时字段;tb_dst 目标时基,定时字段将转换为该时基;
返回值:
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
功能:将数据包写入输出媒体文件,确保正确交错;
参数:s 媒体文件句柄pkt 包含要写入的数据的数据包。
返回值:成功时为0,错误时为负AVERROR

四、代码

#ifndef FFMPEGAPISAVEVIDEO_H
#define FFMPEGAPISAVEVIDEO_H#include <QObject>
#include <QDebug>
#include <QThread>
extern "C"{
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
#include "libavcodec/avcodec.h"
}#define MaxFrameNum 10class ffmpegApiSaveVideo : public QObject
{Q_OBJECT
public:struct VideoInfo{//分辨率int wideth;int hight;//码率int kbps;//图像编码AVPixelFormat pixFormat;//帧数int fps;//编码格式AVCodecID codecId;//封装格式QString packageFormat;};explicit ffmpegApiSaveVideo(QObject *parent = nullptr);~ffmpegApiSaveVideo();QVector<AVFrame *> m_FrameVector;void insertFrame(AVFrame *frame);void init(QString filePath,AVCodecContext *pCodecCtx,VideoInfo videoInfo);void stopWrite();
private:void initOutput(QByteArray filepath);void addStreamToftc(AVFormatContext *oc);void open_video(AVCodec *codec, AVDictionary *opt_arg);int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt);
private:AVFormatContext *m_pOFormatCtx = nullptr;AVOutputFormat *m_pOutFmt = nullptr;      //定义一个输出的格式结构体AVCodecContext *m_pCodecCtx = nullptr;AVCodecContext *m_ICodecCtx = nullptr;AVCodec *m_pCodec = nullptr;AVStream *m_pStream = nullptr;AVFrame* m_pIFrameYUV = nullptr;SwsContext* m_imgYUV_convert_ctx = nullptr;int m_next_pts = 0;                     //视频时间戳bool issave = false;VideoInfo m_videoInfo;signals:public slots:void writeVideo();
};#endif // FFMPEGAPISAVEVIDEO_H
#include "ffmpegapisavevideo.h"ffmpegApiSaveVideo::ffmpegApiSaveVideo(QObject *parent) : QObject(parent)
{}ffmpegApiSaveVideo::~ffmpegApiSaveVideo()
{}void ffmpegApiSaveVideo::insertFrame(AVFrame *frame)
{if(m_FrameVector.length()>MaxFrameNum){m_FrameVector.pop_front();}m_FrameVector.append(frame);
}void ffmpegApiSaveVideo::init(QString filePath, AVCodecContext *pCodecCtx, ffmpegApiSaveVideo::VideoInfo videoInfo)
{m_videoInfo = videoInfo;m_ICodecCtx = pCodecCtx;initOutput(filePath.toUtf8());uint8_t *pIBuffer;  //开辟存储像素点的存储地址int pixSize = av_image_get_buffer_size(m_videoInfo.pixFormat,m_ICodecCtx->width,  m_ICodecCtx->height,16);//创建保存空间,底层使用malloc进行内存空间的开辟。pIBuffer = static_cast<uint8_t *>(av_malloc(static_cast<size_t>(pixSize)));//创建图像转换之后的帧m_pIFrameYUV = av_frame_alloc();av_image_fill_arrays(m_pIFrameYUV->data,m_pIFrameYUV->linesize,pIBuffer,m_videoInfo.pixFormat,1280,720,16);m_imgYUV_convert_ctx = sws_getContext(m_ICodecCtx->width,m_ICodecCtx->height,m_ICodecCtx->pix_fmt,1280,720,m_videoInfo.pixFormat,SWS_BICUBIC, nullptr, nullptr, nullptr);issave = true;
}void ffmpegApiSaveVideo::stopWrite()
{issave = false;
}void ffmpegApiSaveVideo::writeVideo()
{while(issave){int ret;int got_packet=0;AVPacket pkt;if(m_FrameVector.isEmpty()){continue;}AVFrame *pIFrame = m_FrameVector.front();int length = m_FrameVector.length();m_FrameVector.pop_front();if(pIFrame == nullptr){continue;}sws_scale(m_imgYUV_convert_ctx, static_cast<uint8_t const * const *>(pIFrame->data),pIFrame->linesize,0, m_ICodecCtx->height, m_pIFrameYUV->data,m_pIFrameYUV->linesize);m_pIFrameYUV->pts = m_next_pts++;av_init_packet(&pkt);/* 编码图像*/ret = avcodec_send_frame(m_pCodecCtx, m_pIFrameYUV);if (ret < 0) {qDebug()<<"Error sending the frame to the encoder";return;}ret = avcodec_receive_packet(m_pCodecCtx, &pkt);if (ret < 0) {qDebug()<<"Error encoding audio frame";return;}ret = write_frame(m_pOFormatCtx, &m_pCodecCtx->time_base, m_pStream, &pkt);QThread::msleep(10);av_packet_unref(&pkt);}av_write_trailer(m_pOFormatCtx);avcodec_free_context(&m_pCodecCtx);/*关闭输出文件*/if (!(m_pOutFmt->flags & AVFMT_NOFILE))avio_closep(&m_pOFormatCtx->pb);/*释放流*/avformat_free_context(m_pOFormatCtx);}void ffmpegApiSaveVideo::initOutput(QByteArray filepath)
{int ret;//根据文件路径判断获取编码格式,写入到oc中。向m_pOFormatCtx中写入数据avformat_alloc_output_context2(&m_pOFormatCtx, nullptr, nullptr, filepath.data());//为输出格式分配一个AVFormatContext。if(!m_pOFormatCtx){printf("无法从文件扩展名推断出输出格式:使用h264。\n");avformat_alloc_output_context2(&m_pOFormatCtx, nullptr, m_videoInfo.packageFormat.toUtf8(), filepath.data());}//添加流 向m_pOutFmt和m_pCodecCtx m_pCodec写入数据。addStreamToftc(m_pOFormatCtx);/*现在所有参数都设置好了,我们可以打开音频和视频编解码器并分配必要的编码缓冲器*/AVDictionary *opt = nullptr;//AVDictionary是一个健值对存储工具open_video(m_pCodec,opt);av_dump_format(m_pOFormatCtx,0,filepath.data(),1);// TODO:/* 打开输出文件(如果需要) */if(!(m_pOFormatCtx->flags & AVFMT_NOFILE)){//创建并初始化AVIOContext以访问url指示的资源。ret=avio_open(&m_pOFormatCtx->pb,filepath.data(),AVIO_FLAG_WRITE);if(ret<0){qDebug()<<QString("打不开'%1': %2").arg(filepath.data()).arg(av_err2str(ret));}}/* 编写流头(如果有)*/ret=avformat_write_header(m_pOFormatCtx, &opt);if(ret<0){fprintf(stderr, "打开输出文件时发生错误: %s\n",av_err2str(ret));}// 创建 缓存区}//*添加输出流。 */
void ffmpegApiSaveVideo::addStreamToftc(AVFormatContext *fct)
{int i;/* find the encoder */m_pOutFmt = m_pOFormatCtx->oformat;m_pOutFmt->video_codec = m_videoInfo.codecId;//设置编码格式m_pCodec = avcodec_find_encoder(m_pOutFmt->video_codec);if (!(m_pCodec)){qDebug()<<"Could not find encoder for :" << avcodec_get_name(m_pOutFmt->video_codec);return;}//给媒体文件添加一个流m_pStream = avformat_new_stream(fct, nullptr);if (!m_pStream) {qDebug()<<"Could not allocate stream";return;}m_pStream->id = static_cast<int>(fct->nb_streams-1);//创建 编码 上下文。m_pCodecCtx = avcodec_alloc_context3(m_pCodec);av_opt_set(m_pCodecCtx->priv_data, "tune", "zerolatency", 0);//解决avcodec_receive_packet返回为-11的问题if (!m_pCodecCtx) {qDebug()<<"Could not alloc an encoding context";return;}switch((m_pCodec)->type){case AVMEDIA_TYPE_AUDIO:{m_pCodecCtx->sample_fmt  = (m_pCodec)->sample_fmts ?(m_pCodec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;m_pCodecCtx->bit_rate    = 64000;m_pCodecCtx->sample_rate = 44100;if ((m_pCodec)->supported_samplerates) {m_pCodecCtx->sample_rate = (m_pCodec)->supported_samplerates[0];for (i = 0; (m_pCodec)->supported_samplerates[i]; i++) {if ((m_pCodec)->supported_samplerates[i] == 44100)m_pCodecCtx->sample_rate = 44100;}}m_pCodecCtx->channels = av_get_channel_layout_nb_channels(m_pCodecCtx->channel_layout);m_pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;if ((m_pCodec)->channel_layouts) {m_pCodecCtx->channel_layout = (m_pCodec)->channel_layouts[0];for (i = 0; (m_pCodec)->channel_layouts[i]; i++) {if ((m_pCodec)->channel_layouts[i] == AV_CH_LAYOUT_STEREO)m_pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;}}m_pCodecCtx->channels = av_get_channel_layout_nb_channels(m_pCodecCtx->channel_layout);m_pStream->time_base = (AVRational){ 1, m_pCodecCtx->sample_rate };break;}case AVMEDIA_TYPE_VIDEO:{m_pCodecCtx->codec_id =  m_pOutFmt->video_codec;m_pCodecCtx->bit_rate = m_videoInfo.kbps;  //平均比特率,例子代码默认值是400000/* 分辨率必须是2的倍数。*/m_pCodecCtx->width = m_videoInfo.wideth;m_pCodecCtx->height = m_videoInfo.hight;/*时基:这是基本的时间单位(以秒为单位)表示其中的帧时间戳。 对于固定fps内容,时基应为1 /framerate,时间戳增量应为等于1。*/m_pStream->time_base = (AVRational){1,m_videoInfo.fps};  //帧率设置   帧率为25;m_pCodecCtx->time_base = m_pStream->time_base;m_pCodecCtx->gop_size = 12; /* 最多每十二帧发射一帧内帧 */m_pCodecCtx->pix_fmt = m_videoInfo.pixFormat;if(m_pCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO){/* 只是为了测试,我们还添加了B帧 */m_pCodecCtx->max_b_frames = 2;}if(m_pCodecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO){/*需要避免使用其中一些系数溢出的宏块。*普通视频不会发生这种情况,因为*色度平面的运动与亮度平面不匹配。 */m_pCodecCtx->mb_decision = 2;}break;}default:break;}/* 某些格式希望流头分开。 */if (fct->oformat->flags & AVFMT_GLOBALHEADER)m_pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}void ffmpegApiSaveVideo::open_video(AVCodec *codec, AVDictionary *opt_arg)
{int ret;AVCodecContext *c = m_pCodecCtx;AVDictionary *opt = nullptr;//AVDictionary用来保存音视频文件的metadataret = av_dict_copy(&opt, opt_arg, 0);/* open the codec */ret = avcodec_open2(c, codec, &opt);av_dict_free(&opt);if (ret < 0){qDebug()<<"Could not open video codec:"<<av_err2str(ret);return;}/* 将流参数复制到多路复用器*/ret=avcodec_parameters_from_context(m_pStream->codecpar, c);if(ret<0){qDebug()<<"Could not copy the stream parameters";return;}
}int ffmpegApiSaveVideo::write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{/* 将输出数据包时间戳值从编解码器重新调整为流时基 */av_packet_rescale_ts(pkt, *time_base, st->time_base);pkt->stream_index = st->index;/*将压缩的帧写入媒体文件。*/
//  log_packet(fmt_ctx, pkt);return av_interleaved_write_frame(fmt_ctx, pkt);
}

相关文章:

Qt中ffmpeg API存储和显示摄像头视频

Qt中ffmpeg API存储和显示摄像头视频的功能需要之前写的视频ffmpegAPI的视频播放的流程。 代码源码位置&#xff1a;https://download.csdn.net/download/qq_43812868/88157743?spm1001.2014.3001.5503 一、存储和显示摄像头的视频的流程 这是读取打开视频文件的流程&#x…...

jfinal tomcat部署

首先明确一下 JFinal 项目是标准的 java web 项目&#xff0c;其部署方式与普通 java web 项目没有任何差别。Java Web 项目在 Tomcat 下部署有一些不必要的坑需要避免&#xff0c;所以撰写此文方便大家绕过一些坑&#xff0c;以下部署以 linux 为例&#xff0c;windows 与此类…...

Linux - MongoDB 数据库自动退出服务问题/闪退

问题&#xff1a;MongoDB 自动退出服务问题 原因&#xff1a; 由于 Mongodb 服务&#xff0c;使用过多系统内存&#xff0c;导致系统强制停止 Mongodb 服务。 解决方法&#xff1a; 在 mongodb.conf 配置文件内&#xff0c;添加新配置内容&#xff1a; wiredTigerCacheSi…...

B2B2C多语言电商系统平台搭建,多用户商城系统搭建(app、小程序、微商城)

搭建B2B2C多语言电商系统平台以及多用户商城系统&#xff08;包括app、小程序、微商城&#xff09;的步骤如下&#xff1a; 步骤一&#xff1a;需求分析和规划 1. 确定项目的目标和范围。明确平台所需的功能、支持的语言、用户权限等要求。 2. 分析竞争对手&#xff0c;并确定…...

【MySQL】创建高级联结

目录 一、使用表别名 二、使用不同类型的联结 1.自联结 2.自然联结 3.外部联结 3.使用带聚集函数的联结 4.使用联结和联结条件 一、使用表别名 别名除了用于列名和计算字段外&#xff0c;SQL还允许给表名起别名。 起别名有两个好处&#xff1a; 一个是缩短SQL语句&am…...

chatGPT应用于房地产行业

作为 2023 年的房地产专业人士&#xff0c;您无疑认识到技术对行业的重大影响。近年来&#xff0c;一项技术进步席卷了世界——人工智能。人工智能彻底改变了房地产业务的各个方面&#xff0c;从简化管理任务到增强客户互动。 在本文中&#xff0c;我们将探讨几种巧妙的人工智…...

java之jmh初识及使用

最近有场景需要数据支撑json的toJsonString方法和java原生的toString方法的运行速度&#xff0c;因此选用了JMH测试工具。 以下代码大致意思是&#xff1a;初始化一个list集合&#xff0c;放入100个对象&#xff0c;然后遍历这个集合&#xff0c;调用fastjson的toJsonString方…...

利用状态监测和机器学习提高冷却塔性能的具体方法

在现代工业生产中&#xff0c;冷却塔扮演着至关重要的角色&#xff0c;它们的性能直接影响着工艺流程的稳定性和效率。为了确保冷却塔的正常运行和减少系统故障&#xff0c;状态监测和机器学习成为了关键技术。 图.冷却塔&#xff08;PreMaint&#xff09; 在前文《基于人工智…...

LeetCode_02_1289. 下降路径最小和 II

1289. 下降路径最小和 II 给你一个 n x n 整数矩阵 grid &#xff0c;请你返回 非零偏移下降路径 数字和的最小值。 非零偏移下降路径 定义为&#xff1a;从 grid 数组中的每一行选择一个数字&#xff0c;且按顺序选出来的数字中&#xff0c;相邻数字不在原数组的同一列。 示…...

consul servicecheck 查看健康信息

在浏览器中地址栏输入如下信息&#xff1a;http://localhost:8500/v1/agent/checks 返回信息如下&#xff1a; { "service:springboot-security-oauth2-zuul-sso-server-1881": { "Node": "8DBQ2F05HUXZ2QO", "Check…...

什么是信息孤岛?如何打破信息孤岛?

一文让你看懂&#xff1a;什么是信息孤岛&#xff1f;信息孤岛形成的原因&#xff1f;以及如何打破信息孤岛&#xff1f; 本文重点结合了企业信息系统的需求&#xff0c;给出了整合企业现有信息系统的方法&#xff0c;能有效解决企业信息孤岛的问题&#xff0c;并帮助企业快速…...

Android开源 Skeleton 骨架屏

目录 一、简介 二、效果图 三、引用 Skeleton 添加jitpack 仓库 添加依赖: 四、使用 Skeleton 1、VIew 骨架屏使用 ViewSkeletonScreen 2、列表类View 骨架屏 RecyclerViewSkeletonScreen、GridViewSkeletonScreen、 ListViewSkeletonScreen 一、简介 骨架屏的作用是…...

都说IT就业难?到底难在哪?

现在网上关于IT行业&#xff0c;劝退的帖子真的很多&#xff0c;很多人看了后无比焦虑&#xff0c;没入行的&#xff0c;还没开始学&#xff0c;就担心找不到工作了&#xff1b;在行业内的&#xff0c;想跳槽的也纷纷开始摆烂&#xff0c;觉得市场根本没啥机会&#xff0c;简历…...

STM32芯片的内部架构介绍

STM32芯片由内核和片上外设两部分组成。STM32F103采用Cortex-M3内核&#xff0c;该内核由ARM公司设计。芯片生产厂商ST则负责在内核之外设计部件并生产整个芯片。这些内核之外的部件被称为核外外设或片上外设&#xff0c;如GPIO、USART&#xff08;串口&#xff09;、I2C、SPI等…...

Angular FormControl value属性的一些事

背景&#xff1a;一个输入校验&#xff0c;允许输入多行&#xff0c;每一行是ip或网段。写了个校验&#xff0c;将其按行拆分后单独校验。 1. FormControl无法深复制 使用JSON.parse(JSON.stringify(control))进行简单深复制报错&#xff0c;因为不是json类型&#xff1b;使用d…...

Nim游戏:取石头

&#xff08;一&#xff09;一堆取石头 背景&#xff1a; 在博弈论中&#xff0c;有一种称为Nim游戏的经典问题&#xff0c;它涉及到取石子的问题&#xff0c;其中有许多变种。Nim游戏是一种零和博弈&#xff0c;即两名玩家交替行动&#xff0c;每次只能从一堆物品中取走一定数…...

springboot国际化

springboot国际化 不需要引入额外的jar包 参考&#xff1a;https://zhuanlan.zhihu.com/p/551605839 1.rources要创建Resource Bundle 2.yml配置中引入Resource Bundle 引入Resource Bundle spring:messages:encoding: UTF-8basename: i18n/messages_common3.创建国际化工具…...

12种不宜使用的Javascript语法

1. Javascript有两组相等运算符&#xff0c;一组是和!&#xff0c;另一组是和!。前者只比较值的相等&#xff0c;后者除了值以外&#xff0c;还比较类型是否相同。 请尽量不要使用前一组&#xff0c;永远只使用和!。因为默认会进行类型转换&#xff0c;规则十分难记。如果你…...

vue3+element-plus点击列表中的图片预览时,图片被表格覆盖

文章目录 问题解决 问题 视觉 点击图片进行预览&#xff0c;但还能继续选中其他的图片进行预览&#xff0c;鼠标放在表格上&#xff0c;那一行表格也会选中&#xff0c;如图所示第一行的效果。 代码 <el-table-column prop"id" label"ID" width"…...

flutter:二维码生成与读取

前言 这csdn真的是服了&#xff0c;图片里有个二维码就直接变成违规图片了。至于效果的话&#xff0c;自己运行一下看看吧。 生成 flutter中生成二维码可以使用 qr_flutter。 官方文档 https://pub-web.flutter-io.cn/packages/qr_flutter 安装 flutter pub add qr_flutt…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...