【FFmpeg】FFmpeg 函数简介 ③ ( 编解码相关函数 | FFmpeg 源码地址 | FFmpeg 解码器相关 结构体 和 函数 )

news2024/11/14 16:28:57

文章目录

  • 一、FFmpeg 解码器简介
    • 1、解码流程分析
    • 2、FFmpeg 编解码器 本质
    • 3、FFmpeg 编解码器 ID 和 名称
  • 二、FFmpeg 解码器相关 结构体 / 函数
    • 1、AVFormatContext 结构体
    • 2、avcodec_find_decoder 函数 - 根据 ID 查找 解码器
    • 3、avcodec_find_decoder_by_name 函数 - 根据 名称 查找 解码器
    • 4、avcodec_alloc_context3 函数 - 初始化编解码上下文结构体
    • 5、avcodec_parameters_to_context 函数 - 拷贝 编解码上下文结构体 数据
    • 6、avcodec_open2 函数 - 打开编解码器
    • 7、avcodec_send_packet 函数 和 avcodec_receive_frame 函数 - 解码组合函数
    • 8、avcodec_free_context 函数 和 avcodec_close 函数 - 释放编解码器上下文结构体


FFmpeg 4.0 版本源码地址 : https://github.com/FFmpeg/FFmpeg/tree/release/4.0





一、FFmpeg 解码器简介




1、解码流程分析


音视频 解码 相关函数 , 对应下图 红色矩形框区域 中的操作 :

在这里插入图片描述

音视频 解码流程 如下图所示 :

  • 首先 , 调用 avcodec_alloc_context3 函数 , 分配解码器上下文 AVCodecContext 结构体对象 ;
  • 然后 , 调用 avcodec_parameters_to_context 函数 , 将 编解码器 信息拷贝到 AVCodecContext 结构体对象 中 ;
  • 再后 , 调用 avcodec_find_decoder 函数 或者 avcodec_find_decoder_by_name 函数 , 查找相应的编解码器 ;
  • 之后 , 调用 avcodec_open2 函数 , 打开编解码器 , 并与 AVCodecContext 结构体对象 进行关联 ;
  • 最后 , 进行 逐包 循环解码 , 先调用 avcodec_send_packet 函数 , 向解码器发送数据包 , 然后调用 avcodec_receive_frame 函数 , 从解码器获取解码后的数据帧 ;

程序执行完毕后 , 调用 avcodec_free_context 函数 或者 avcodec_close 函数 , 释放编解码器占用的资源 , 上述函数中 avcodec_free_context 函数 包含 avcodec_close 函数 ;
在这里插入图片描述


2、FFmpeg 编解码器 本质


H.264 只是一个 编解码器 规范标准 , 不同的 厂家 会根据该 规范 开发 不同的编解码器 ,

下面的代码是 FFmpeg 中定义的 H.264 规范的 解码器 , 这是一个 AVCodec 类型的结构体对象 , 该结构体对象中 包含了与 H.264 编解码器 相关的所有必要数据和函数指针 , 用于初始化、解码、关闭等操作 ;

AVCodec ff_h264_decoder = {
    .name                  = "h264",  // 编解码器名称
    .long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),  // 编解码器的全名
    .type                  = AVMEDIA_TYPE_VIDEO,  // 媒体类型(视频)
    .id                    = AV_CODEC_ID_H264,  // 编解码器ID
    .priv_data_size        = sizeof(H264Context),  // 私有数据大小,H264上下文结构的大小
    .init                  = h264_decode_init,  // 初始化函数
    .close                 = h264_decode_end,  // 关闭函数
    .decode                = h264_decode_frame,  // 解码函数
    .capabilities          = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |  // 编解码器能力
                             AV_CODEC_CAP_DELAY | 
                             AV_CODEC_CAP_SLICE_THREADS | 
                             AV_CODEC_CAP_FRAME_THREADS,
    .hw_configs            = (const AVCodecHWConfigInternal*[]) {  // 硬件加速配置
#if CONFIG_H264_DXVA2_HWACCEL
                               HWACCEL_DXVA2(h264),  // DXVA2 硬件加速
#endif
#if CONFIG_H264_D3D11VA_HWACCEL
                               HWACCEL_D3D11VA(h264),  // D3D11VA 硬件加速
#endif
#if CONFIG_H264_D3D11VA2_HWACCEL
                               HWACCEL_D3D11VA2(h264),  // D3D11VA2 硬件加速
#endif
#if CONFIG_H264_NVDEC_HWACCEL
                               HWACCEL_NVDEC(h264),  // NVDEC 硬件加速
#endif
#if CONFIG_H264_VAAPI_HWACCEL
                               HWACCEL_VAAPI(h264),  // VAAPI 硬件加速
#endif
#if CONFIG_H264_VDPAU_HWACCEL
                               HWACCEL_VDPAU(h264),  // VDPAU 硬件加速
#endif
#if CONFIG_H264_VIDEOTOOLBOX_HWACCEL
                               HWACCEL_VIDEOTOOLBOX(h264),  // VideoToolbox 硬件加速
#endif
                               NULL  // 结束标记
                           },
    .caps_internal         = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING,  // 内部能力标志
    .flush                 = flush_dpb,  // 刷新函数
    .init_thread_copy      = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),  // 线程复制初始化
    .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),  // 更新线程上下文
    .profiles              = NULL_IF_CONFIG_SMALL(ff_h264_profiles),  // 配置文件
    .priv_class            = &h264_class,  // 私有类
};

源码地址 : https://github.com/FFmpeg/FFmpeg/blob/release/4.0/libavcodec/h264dec.c Line1047 ~ 1089

FFmpeg 4.0 版本源码地址 : https://github.com/FFmpeg/FFmpeg/tree/release/4.0


3、FFmpeg 编解码器 ID 和 名称


下面分析 编解码器的 名称 和 编码器 ID 字段 ;

avcodec_find_decoder 函数 根据 编解码器ID 查找 FFmpeg 中的编解码器的 ;

avcodec_find_decoder_by_name 函数 是根据 编解码器名称 查找 FFmpeg 中的编解码器的 ;

.name = "h264", // 编解码器名称 就是为该编解码器定义的名称 , 可以直接在 FFmpeg 命令中通过该 名称 " h264 " 调用该 编解码器 ;

.id = AV_CODEC_ID_H264, // 编解码器ID 是 编解码器的 ID , 这是一个 枚举 , 定义在 libavcodec/avcodec.h 代码中 , AV_CODEC_ID_H264 是 FFmpeg 对 H.264 编码的 内部表示 , 只要是 H.264 规范的编解码器 , 不管是哪家厂商开发 H.264 的编解码器 , id 字段的值必须是 AV_CODEC_ID_H264 ;

/**
 * 标识比特流的语法和语义。
 * 原则大致是:
 * 具有相同ID的两个解码器可以解码相同的流。
 * 具有相同ID的两个编码器可以编码兼容的流。
 * 由于实现细节,可能会略有偏差。
 *
 * 如果你向此列表添加一个编解码器ID,请遵循以下原则:
 * 1. 现有编解码器ID的值不发生变化(这会破坏ABI的兼容性),
 * 2. 新增的ID尽可能靠近相似的编解码器。
 *
 * 在添加新的编解码器ID后,不要忘记在编解码器描述符列表中添加条目,
 * 并增加 libavcodec 的次版本号。
 */
enum AVCodecID {
    AV_CODEC_ID_NONE,    // 无编解码器ID

    /* 视频编解码器 */
    AV_CODEC_ID_H264,    // H.264视频编解码器
}

在这里插入图片描述
源码地址 : https://github.com/FFmpeg/FFmpeg/blob/release/4.0/libavcodec/avcodec.h Line 245





二、FFmpeg 解码器相关 结构体 / 函数




1、AVFormatContext 结构体


音视频 解码 靠 " 解码器 " 进行 ;

与 解封装 类似 , 解封装器 工作 需要使用 AVFormatContext 格式上下文结构体 , 解封装相关信息都封装在该结构体中 ;

解码器 对音视频数据进行解码 , 也有一个 解码器上下文结构体 AVCodecContext ;

最新版本的 FFmpeg 使用 avcodec_alloc_context3 函数 , 分配 编解码器上下文 AVCodecContext 结构体 ;

  • avcodec_alloc_context 和 avcodec_alloc_context2 是 早期版本 FFmpeg 中分配编解码器上下文结构体 的函数 , 现在已经弃用 , 如果在代码中遇到这两个函数需要注意 ;

AVCodecContext 用于存储 解码 或 编码 过程中使用的编解码器上下文信息 , 包含了大量的字段和配置信息 , 允许用户控制解码和编码的行为 ;

下面的代码中 列举了 一些重要的 AVCodecContext 结构体字段 ;

typedef struct AVCodecContext {
    const AVClass *av_class;            // 指向AVClass的指针,用于支持FFmpeg的日志和调试功能

    int log_level_offset;                // 日志级别偏移量

    enum AVMediaType codec_type;         // 媒体类型,例如视频、音频、字幕等

    const struct AVCodec *codec;         // 指向编码器或解码器的指针

    int codec_id;                        // 编解码器ID,指定要使用的编解码器类型(例如AV_CODEC_ID_H264)

    void *priv_data;                     // 指向私有数据的指针,用于存储编解码器特定的配置信息

    int bit_rate;                        // 目标码率(以比特/秒为单位)

    int width, height;                   // 视频宽度和高度

    int gop_size;                        // 视频的GOP(Group of Pictures)大小,设置I帧之间的间隔

    int max_b_frames;                    // 最大B帧数,B帧是一种双向预测的帧

    enum AVPixelFormat pix_fmt;          // 视频的像素格式(例如YUV420P)

    int sample_rate;                     // 音频采样率(以赫兹为单位)

    int channels;                        // 音频通道数

    enum AVSampleFormat sample_fmt;      // 音频采样格式(例如AV_SAMPLE_FMT_FLTP表示浮点格式)

    int64_t channel_layout;              // 音频通道布局(例如立体声、5.1声道)

    int frame_size;                      // 每个音频帧的采样数

    int profile;                         // 编解码器的配置文件,例如H.264的Baseline、Main或High Profile

    int level;                           // 编解码器的级别,例如H.264中的Level 4.0

    int thread_count;                    // 线程数量,用于并行编码或解码

    int flags;                           // 编解码器的通用标志,用于设置不同的编码/解码选项

    int flags2;                          // 额外的标志选项

    AVRational time_base;                // 基准时间,用于表示帧的时间戳

    AVRational framerate;                // 视频帧率(以帧/秒表示)

    int64_t bit_rate_tolerance;          // 比特率容忍度,用于VBR(可变比特率)编码

    int rc_buffer_size;                  // 码率控制缓冲区大小

    int rc_max_rate;                     // 最大比特率

    int rc_min_rate;                     // 最小比特率

    float qcompress;                     // 帧间压缩,用于调整比特率的波动

    float qblur;                         // 帧间模糊,用于平滑比特率变化

    int qmin;                            // 最小量化参数,用于控制编码质量

    int qmax;                            // 最大量化参数

    int64_t channel_layout;              // 音频通道布局(例如立体声、5.1声道)

    // 省略许多其他字段
} AVCodecContext;

2、avcodec_find_decoder 函数 - 根据 ID 查找 解码器


avcodec_find_decoder 函数 用于 根据 解码器 ID 查找 指定解码器 , 函数原型如下 :

const AVCodec *avcodec_find_decoder(enum AVCodecID id);
  • enum AVCodecID id 参数 : 指定要查找的 解码器的 ID , 这是一个枚举类型 , 用于标识各种支持的编解码器 , 如 : AV_CODEC_ID_H264 ;
  • AVCodec * 返回值 :
    • 如果 找到 对应的解码器 , 返回指向 AVCodec 结构体的指针 ;
    • 如果 没有找到 对应的解码器 , 返回 NULL ;

代码示例 : 下面的代码是查找 H.264 解码器 的代码 ;

// 查找 H.264 解码器
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
    fprintf(stderr, "查找解码器失败\n");
    return -1;
}

3、avcodec_find_decoder_by_name 函数 - 根据 名称 查找 解码器


avcodec_find_decoder_by_name 函数 用于通过 解码器名称 查找 解码器 , 函数原型如下 :

const AVCodec *avcodec_find_decoder_by_name(const char *name);
  • const char *name 参数 : 解码器名称 , H.264 解码器名称是 h264 ;
  • AVCodec * 返回值 :
    • 如果 找到 对应的解码器 , 返回指向 AVCodec 结构体的指针 ;
    • 如果 没有找到 对应的解码器 , 返回 NULL ;

代码示例 : 下面的代码是根据 h264 名称查找解码器 ;

// 查找名为 "h264" 的解码器
const AVCodec *codec = avcodec_find_decoder_by_name("h264");
if (!codec) {
    fprintf(stderr, "查找解码器失败\n");
    return -1;
}

4、avcodec_alloc_context3 函数 - 初始化编解码上下文结构体


avcodec_alloc_context3 函数 用于分配并初始化一个 AVCodecContext 结构体 , 该结构体用于存储编解码器的上下文信息 , 函数原型如下 :

AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
  • const AVCodec *codec 参数 : 指向 AVCodec 结构体的指针 , 该参数用于指定要使用的编解码器 , 如果为 NULL,会创建一个空的上下文,可以稍后再设置编码器 ;
  • AVCodecContext * 类型返回值 : 如果初始化成功 , 则返回 编解码上下文结构体指针 , 如果初始化失败 , 返回 NULL ;

代码示例 : 下面的代码中 , 首先查找 h264 编解码器 , 然后为该编解码器分配上下文对象 ;

// 查找名为 "h264" 的解码器
const AVCodec *codec = avcodec_find_decoder_by_name("h264");
if (!codec) {
    fprintf(stderr, "查找解码器失败\n");
    return -1;
}

// 分配编解码器上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {
    fprintf(stderr, "分配编解码器上下文失败\n");
    return -1;
}

5、avcodec_parameters_to_context 函数 - 拷贝 编解码上下文结构体 数据


avcodec_parameters_to_context 函数 用于将 AVCodecParameters 中的参数拷贝到 AVCodecContext 中 , 函数原型如下 :

int avcodec_parameters_to_context(AVCodecContext *codec_ctx, const AVCodecParameters *par);
  • AVCodecContext *codec_ctx 参数 : 指向 拷贝目的地 的 编解码器上下文 结构体 的 指针 , 这是 被拷贝的数据赋值的对象 ;
  • const AVCodecParameters *par 参数 : 要 拷贝 的 编解码器参数 的 源结构体 , 数据从该结构体拷贝出来的 , 是数据源 ;
  • int 返回值 :
    • 拷贝成功 , 返回 0 ;
    • 拷贝失败 , 返回负值 错误码 ;

代码示例 :

// 初始化 编解码器 上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
// 从 媒体流 中获取 编解码器参数 , 一般是被解码的音视频流
AVCodecParameters *par = stream->codecpar;

// 将参数从 par 复制到 codec_ctx
if (avcodec_parameters_to_context(codec_ctx, par) < 0) {
    fprintf(stderr, "拷贝参数失败\n");
    return -1;
}

6、avcodec_open2 函数 - 打开编解码器


avcodec_open2 函数 用于打开指定的 编解码器 , 并与 AVCodecContext 关联 , 函数原型如下 :

int avcodec_open2(AVCodecContext *codec_ctx, const AVCodec *codec, AVDictionary **options);
  • AVCodecContext *codec_ctx 参数 : 指向 AVCodecContext 结构体的指针 , 用于存储编解码器的上下文信息 ;
  • const AVCodec *codec 参数 : 指向 AVCodec 结构体的指针 , 这是要打开的编解码器 ;
  • AVDictionary **options 参数 : 指向 AVDictionary 的指针 , 这是用于传递编解码器的附加选项 , 一般都设置为 NULL ;
  • int 返回值 :
    • 打开成功 , 返回 0 ;
    • 打开失败 , 返回负值 错误码 ;

代码示例 :

// 查找名为 "h264" 的解码器
const AVCodec *codec = avcodec_find_decoder_by_name("h264");
if (!codec) {
    fprintf(stderr, "查找解码器失败\n");
    return -1;
}

// 分配编解码器上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {
    fprintf(stderr, "分配编解码器上下文失败\n");
    return -1;
}

// 打开解码器
if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
    fprintf(stderr, "打开解码器失败\n");
    return -1;
}

7、avcodec_send_packet 函数 和 avcodec_receive_frame 函数 - 解码组合函数


avcodec_send_packet 函数 用于将 压缩数据包 发送到解码器 , 压缩数据包就是未解码的音视频帧 , 不能用于播放 , 函数原型如下 :

int avcodec_send_packet(AVCodecContext *codec_ctx, const AVPacket *pkt);
  • AVCodecContext *codec_ctx 参数 : 指向 编解码器 上下文 结构体的指针 ;
  • const AVPacket *pkt 参数 : 指向 AVPacket 结构体的指针 , 其中包含编码数据的包 , 如果设置为 NULL , 表示解码器接收的输入数据已经结束 ;
  • int 类型 返回值 :
    • 发送成功 返回 0 ;
    • 发送失败 返回 负值错误码 ;

avcodec_receive_frame 函数 用于从 解码器 获取 解码后的 音视频帧 , 该 音视频帧 可直接用于播放 , 函数原型如下 :

int avcodec_receive_frame(AVCodecContext *codec_ctx, AVFrame *frame);
  • AVCodecContext *codec_ctx 参数 : 指向 编解码器 上下文 结构体的指针 ;
  • AVFrame *frame 参数 : 接收到的 解码后的 音视频帧 数据 ;
  • int 类型 返回值 :
    • 接收成功 返回 0 ;
    • 接收失败 返回 负值错误码 ;

代码示例 :

// 初始化解码器上下文 codec_ctx,并确保其已成功打开

AVPacket pkt;  // 定义一个 AVPacket 结构,用于存储压缩数据包
AVFrame *frame = av_frame_alloc();  // 分配一个 AVFrame 结构,用于存储解码后的帧
int ret;  // 定义返回值变量,用于存储函数返回的状态

// 向解码器发送压缩包
ret = avcodec_send_packet(codec_ctx, &pkt);  // 将压缩数据包发送给解码器
if (ret < 0) {  // 如果发送数据包失败
    fprintf(stderr, "Error sending packet to decoder\n");  // 输出错误信息
}

// 从解码器接收解码后的帧
while (ret >= 0) {
    ret = avcodec_receive_frame(codec_ctx, frame);  // 从解码器接收解码后的帧
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {  // 如果没有更多帧可以接收或已到文件末尾
        break;  // 退出循环
    } else if (ret < 0) {  // 如果接收解码帧过程中出错
        fprintf(stderr, "Error during decoding\n");  // 输出错误信息
        break;  // 退出循环
    }

    // 使用解码后的 frame(例如渲染或进行其他处理)
    av_frame_unref(frame);  // 清除帧数据,准备接收下一帧
}

av_frame_free(&frame);  // 释放 frame 结构,避免内存泄漏

8、avcodec_free_context 函数 和 avcodec_close 函数 - 释放编解码器上下文结构体


avcodec_free_context 函数 用于释放 AVCodecContext 结构体并清除其中的所有资源 , 函数原型如下 :

void avcodec_free_context(AVCodecContext **codec_ctx);
  • AVCodecContext **codec_ctx 参数 : 指向 AVCodecContext 指针的 指针 , 这是要释放的编解码器上下文 , 该函数会将 *codec_ctx 置为 NULL ;

avcodec_free_context 函数 会自动调用 avcodec_close 函数 ;


avcodec_close 函数 用于关闭一个打开的 AVCodecContext 结构体 , 释放与其关联的编解码器资源 , 函数原型如下 :

int avcodec_close(AVCodecContext *codec_ctx);
  • AVCodecContext **codec_ctx 参数 : 指向 AVCodecContext 指针的 指针 , 这是要释放的编解码器上下文 , 该函数会将 *codec_ctx 置为 NULL ;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux完结

学习视频笔记均来自B站UP主" 泷羽sec",如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 【linux基础之病毒编写&#xff08;完结&#xff09;】 https://www.bilibili.com/video…

分享三个python爬虫案例

一、爬取豆瓣电影排行榜Top250存储到Excel文件 近年来&#xff0c;Python在数据爬取和处理方面的应用越来越广泛。本文将介绍一个基于Python的爬虫程序&#xff0c;用于抓取豆瓣电影Top250的相关信息&#xff0c;并将其保存为Excel文件。 获取网页数据的函数&#xff0c;包括以…

【计网】数据链路层笔记

【计网】数据链路层 数据链路层概述 数据链路层在网络体系结构中所处的地位 链路、数据链路和帧 链路(Link)是指从一个节点到相邻节点的一段物理线路(有线或无线)&#xff0c;而中间没有任何其他的交换节点。 数据链路(Data Link)是基于链路的。当在一条链路上传送数据时&a…

docker 拉取MySQL8.0镜像以及安装

目录 一、docker安装MySQL镜像 搜索images 拉取MySQL镜像 二、数据挂载 在/root/mysql/conf中创建 *.cnf 文件 创建容器,将数据,日志,配置文件映射到本机 检查MySQL是否启动成功&#xff1a; 三、DBeaver数据库连接 问题一、Public Key Retrieval is not allowed 问题…

【c++篇】:栈、队列、优先队列:容器世界里的秩序魔法 - stack,queue与priority_queue探秘

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;c篇–CSDN博客 文章目录 前言一.容器stack1.介绍2.成员函数3.模拟实现4.注意事项 二.容器qu…

实现uniapp-微信小程序 搜索框+上拉加载+下拉刷新

pages.json 中的配置 { "path": "pages/message", "style": { "navigationBarTitleText": "消息", "enablePullDownRefresh": true, "onReachBottomDistance": 50 } }, <template><view class…

无人机培训机型有哪些?CAAC考证选3类还是4类

无人机培训是一个涵盖多个方面的综合性过程&#xff0c;旨在培养具备无人机操作技能和相关知识的人才。 无人机培训机型 无人机培训通常涵盖多种机型&#xff0c;以满足不同领域和应用场景的需求。常见的无人机培训机型包括&#xff1a; 1. 多旋翼无人机&#xff1a;也称为多…

95.【C语言】数据结构之双向链表的头插,头删,查找,中间插入,中间删除和销毁函数

目录 1.双向链表的头插 方法一 方法二 2.双向链表的头删 3.双向链表的销毁 4.双向链表的某个节点的数据查找 5.双向链表的中间插入 5.双向链表的中间删除 6.对比顺序表和链表 承接94.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删文章 1.双向链表的头插 方法…

[极客大挑战 2019]PHP 1

[极客大挑战 2019]PHP 1 审题 猜测备份在www.zip中&#xff0c;输入下载文件。 知识点 反序列化 解题 查看代码 看到index.php中包含了class.php,直接看class.php中的代码 查看条件 当usernameadmin&#xff0c;password100时输出flag 构造反序列化 输入select中&#…

C++面试基础知识:排序算法 C++实现

上周实习面试&#xff0c;手撕代码快排没写出来&#xff0c;非常丢人&#xff0c;把面试官都给逗笑了。 基础不牢&#xff0c;地动山摇&#xff0c;基础的算法还是要牢记于心的。 插入排序 分为有序区和无序区&#xff0c;每次从无序区中选出一个&#xff0c;放到有序区域中。…

yarn报错`warning ..\..\package.json: No license field`:已解决

出现这个报错有两个原因 1、项目中没有配置许可证 在项目根目录package.json添加 {"name": "next-starter","version": "1.0.0",# 添加这一行"license": "MIT", }或者配置私有防止发布到外部仓库 {"priv…

批量缓存模版

批量缓存模版 缓存通常有两种使用方式&#xff0c;一种是Cache-Aside&#xff0c;一种是cache-through。也就是旁路缓存和缓存即数据源。 一般一种用于读&#xff0c;另一种用于读写。参考后台服务架构高性能设计之道。 最典型的Cache-Aside的样例&#xff1a; //读操作 da…

亚信安全并购亚信科技交易正式完成

亚信安全与亚信科技联合宣布&#xff0c;亚信安全正式完成对亚信科技的控股权收购&#xff0c;由此&#xff0c;规模近百亿的中国最大的软件企业之一诞生&#xff01;双方将全面实现公司发展战略&#xff0c;以及优势能力与资源的深度融合&#xff0c;形成业界独有的“懂网、懂…

MybatisPlus入门(十)MybatisPlus-逻辑删除和多记录操作

一、Mybatis-Plus 多记录操作 按照主键删除多条记录 List<Long> ids Arrays.asList(new Long[]{2,3}) userDao.deleteBatchIds(ids); 示例代码如下: Testvoid testDelete(){//删除指定多条数据List<Long> list new ArrayList<>();list.add(14025513424818…

【css】overflow: hidden效果

1. 不添加overflow: hidden 1.1 效果 上面无圆角 1.2 代码 <template><view class"parent"><view class"child1">child1</view><view class"child2">child2</view></view></template><…

「QT」几何数据类 之 QPolygonF 浮点型多边形类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid…

架构篇(04理解架构的演进)

目录 学习前言 一、架构演进 1. 初始阶段的网站架构 2. 应用服务和数据服务分离 3. 使用缓存改善网站性能 4. 使用应用服务器集群改善网站的并发处理能力 5. 数据库读写分离 6. 使用反向代理和CDN加上网站相应 7. 使用分布式文件系统和分布式数据库系统 8. 使用NoSQL和…

OpenCV基础05_GUI和PyMsql

目录 一、PySimpleGUI 1、布局和窗口 2、文本框组件 3、视频处理 4、图片处理 二、pymsql 1、数据库操作 2、数据采集 3、人脸识别 一、PySimpleGUI PySimpleGUI 是一个用于简化 GUI 编程的 Python 包&#xff0c;它封装了多种底层 GUI 框架&#xff08;如 tkinter、…

ModuleNotFoundError: No module named ‘_ssl‘ centos7中的Python报错

报错 ModuleNotFoundError: No module named ‘_ssl’ 解决步骤&#xff1a; 1.下载openssl wget https://www.openssl.org/source/openssl-3.0.7.tar.gz tar -zxvf openssl-3.0.7.tar.gz cd openssl-3.0.72.编译安装 ./config --prefix/usr/local/openssl make make install3…

外呼系统只需这 3 种功能,电销效率快速提升

在当今竞争激烈的商业环境中&#xff0c;电销团队面临着诸多挑战。如何提高电销效率&#xff0c;成为了企业关注的焦点。今天&#xff0c;小编就给大家介绍&#xff0c;沃创云三种外呼系统功能&#xff0c;让你的电销效率快速提升。 一、智能拨号功能 传统的电销方式中&#x…