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 10

class 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用来保存音视频文件的metadata
    ret = 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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/60584.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

14-5_Qt 5.9 C++开发指南_基于HTTP 协议的网络应用程序

文章目录 1. 实现高层网络操作的类2. 基于HTTP协议的网络文件下载3.源码3.1 可是化UI设计3.2 mainwindow.h3.3 mainwindow.cpp 1. 实现高层网络操作的类 Qt 网络模块提供一些类实现 OSI 7 层网络模型中高层的网络协议&#xff0c;如 HTTP、FTP、SNMP等&#xff0c;这些类主要是…

生鲜蔬果小程序的完整教程

随着互联网的发展&#xff0c;线上商城成为了人们购物的重要渠道。其中&#xff0c;小程序商城在近年来的发展中&#xff0c;备受关注和青睐。本文将介绍如何使用乔拓云网后台搭建生鲜果蔬配送小程序&#xff0c;并快速上线。 首先&#xff0c;登录乔拓云网后台&#xff0c;进入…

Xilinx A7开发板LVDS IO无输出问题解决方法

使用A7-35T FGG484的FPGA开发板bank16上的IO作为差分LVDS的输入输出&#xff0c;搭建输入输出测试工程发现LVDS可以输入、无法输出。查阅UG471&#xff0c;找到如下信息&#xff1a; 手册中已经针对A7的LVDS做了明确的应用说明&#xff1a; &#xff08;1&#xff09;HP bank上…

客户流失分析预测案例 -- 机器学习项目基础篇(7)

客户流失 它是指现有的客户、用户、订阅者或任何类型的回头客停止与公司开展业务或结束与公司的关系。 客户流失的类型 合同客户流失&#xff1a;当客户签订了服务合同并决定取消服务时&#xff0c;例如有线电视&#xff0c;SaaS。自愿流失&#xff1a;当用户自愿取消服务时…

PHP正则绕过解析

正则绕过 正则表达式PHP正则回溯PHP中的NULL和false回溯案例案例1案例2 正则表达式 在正则中有许多特殊的字符&#xff0c;不能直接使用&#xff0c;需要使用转义符\。如&#xff1a;$,(,),*,,.,?,[,,^,{。 这里大家会有疑问&#xff1a;为啥小括号(),这个就需要两个来转义&a…

XSS漏洞原理及利用跨站请求伪造CSRF

XSS漏洞原理及利用&跨站请求伪造CSRF XSS一、案例二、什么是XSS三、XSS危害四、XSS的分类4.1、反射型XSS4.1.1、介绍4.1.2、利用过程 4.2、存储型XSS4.2.1、介绍4.2.2、利用过程4.2.3、案例 4.3、DOM型XSS4.3.1、介绍4.3.2、常用的DOM方法4.3.3、案例4.3.3.1、代码分析4.3.…

Linux上安装Keepalived,多台Nginx配置Keepalived(保姆级教程)

目录 一、yum安装 第一步&#xff1a;下载 第二步&#xff1a;编辑Keepalived配置文件&#xff08;第一台&#xff09; 第三步&#xff1a;编辑Keepalived配置文件&#xff08;第二台&#xff09; 第四步&#xff1a;我们在本机利用cmd ping一下 一、yum安装 第一步&…

IDEA基础使用

IDEA基础使用 1、IDEA中显示用法和用户截图展示有调用显示无调用显示 对应方法 2、如何找出项目中所有不被调用方法截图展示对应方法 3、常用代码(Code)说明及快捷键:4、未完待续待日后更新。。。总结&#xff1a;欢迎指导&#xff0c;也祝码友们代码越来越棒&#xff0c;技术越…

元素2D转3D 椭圆形旋转实现

椭圆旋转功能展示 transform-style: preserve-3d;&#xff08;主要css代码&#xff09; gif示例&#xff08;背景图可插入透明以此实现边框线的旋转&#xff09; 导致的无法点击遮挡问题可以参考我的另一个文章 穿透属性-----------------------css穿透属性 实时代码展示

【FAQ】在Linux中使用curl访问EasyCVR,返回报错Unauthorized的原因排查

EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&#xff0c;比如&#xff1a;视…

HCIE-Datacom真题和机构资料

通过认证验证的能力 具备坚实的企业网络跨场景融合解决方案理论知识&#xff0c;能够使用华为数通产品及解决方案进行企业园区网络、广域互联网络及广域承载网络的规划、建设、维护及优化&#xff0c;能够胜任企业网络全场景专家岗位&#xff08;包括客户经理、项目经理、售前…

CBCGPRibbon 添加背景图片

resource.h中声明资源的ID&#xff1a;ID_RIBBON_BACKIMAGE rc文件中添加png图片路径&#xff1a; ID_RIBBON_BACKIMAGE PNG DISCARDABLE "res\\bkribbon.png" 代码中添加下测&#xff1a; //添加背景图片 m_wndRibbonBar.SetBackgroundImage(ID_RIB…

使用公式与格式控制Excel快速实现计划甘特图

项目中都会遇到做任务计划的需求&#xff0c;有的客户要求需要有甘特图的形式本文介绍如何使用excel 单元格实现甘特图显示&#xff0c;调整任务时间自动填充单元格填色实现甘特图效果。废话不多说&#xff0c;先看效果。 准备工作先创建两列开始时间与完成时间&#xff0c;这…

EMD经验模态分解介绍

EMD概述 其实一种信号分解方法&#xff0c;是一种自适应的数据处理方法&#xff0c;适合非线性和非平稳时间序列的分析和研究&#xff0c;其本质是对数据序列或信号的平稳化处理。 将上面6个信号叠加如下&#xff1a; 就是6个简单信号叠加&#xff0c;形成一个复杂信号。 核心…

Java精品项目系统第151期大学生校园帮系统打包(编号S063)

Java精品项目系统第151期大学生校园帮系统打包&#xff08;编号S063&#xff09; 大家好&#xff0c;小辰今天给大家介绍一个基于Spring Springboot MyBatis实现的大学生校园帮系统&#xff0c;演示视频文章末尾公众号对号查询观看即可 文章目录 Java精品项目系统第151期大学…

flink1.17 eventWindow不要配置processTrigger

理论上可以eventtime processtime混用,但是下面代码测试发现bug,输入一条数据会一直输出. flink github无法提bug/问题. apache jira账户新建后竟然flink又需要一个账户,放弃 bug复现操作 idea运行代码后 往source kafka发送一条数据 a,1,1690304400000 可以看到无限输出…

etcd

文章目录 etcd单机安装设置键值对watch操作读取键过往版本的值压缩修订版本lease租约&#xff08;过期机制&#xff09;授予租约撤销租约keepAlive续约获取租约信息 事务基于etcd实现分布式锁原生实现官方 concurrency 包实现 服务注册与发现Go 操作 Etcd 参考 etcd etcd 是一…

计算机网络-三种交换方式

计算机网络-三种交换方式 电路交换(Circuit Switching) 电话交换机接通电话线的方式称为电路交换从通信资源分配的角度来看&#xff0c;交换(Switching)就是按照某种方式动态的分配传输线路的资源 电话交换机 为了解决电话之间通信两两之间连线过多&#xff0c;所以产生了电话…

原型模式(Prototype)

原型模式是一种创建型设计模式&#xff0c;使调用方能够复制已有对象&#xff0c;而又无需使代码依赖它们所属的类。当有一个类的实例&#xff08;原型&#xff09;&#xff0c;并且想通过复制原型来创建新对象时&#xff0c;通常会使用原型模式。 The Prototype pattern is g…

解决Pycharm下opencv代码提示问题

解决Pycharm下opencv代码提示问题 新安装了opencv-python4.7.0.72版本&#xff0c;在pycharm中导入cv2后没有代码提示&#xff0c;解决方法如下&#xff1a; 解决方案 我使用的是miniconda环境&#xff0c;opencv安装的路径为&#xff1a;C:\Env\miniconda3\envs\compute\Li…