ffmpeg面向对象——参数配置秘密探索及其设计模式

news2024/11/13 9:13:51

ffmpeg支持很多参数配置——拉流配置推流配置等等——那么庞大繁杂的配置项,如果是你,该如何实现呢?
其实看过一点点源码(不用全部)后发现,就是它的实现也是遵循一个朴素的思想——所谓“大道至简”,“万变不离其宗”——就算再多的参数,按照我们简单的思想,最开始的思维,最直接的思维,如何实现?目的很简单——把一个个的参数映射到对象的成员变量里或者全局变量里。这是一个非常简单的思想,及其朴素的思想——但是实现手段可以千变万化——fffmpeg的实现也是这样的,同样的目的,只是经历的实现过程比较“千变万化”、比较“繁杂”、比较“迷人眼”而已

朴素的表示就是:输入配置参数 ——> 更改对象成员变量/全局变量。如下图
在这里插入图片描述

思想很朴素,目的很简单。但ffmpeg的实现很复杂。

先说一个小复杂:
ffmpeg把参数统一抽象成键值对,且键和值都用字符串表示,具体内部的生效再转换成对应格式,然后配置到具体的业务对象成员变量里

再看下它复杂实现的对象流程图——这属于总—分—分的描述写法了,先结论,再原因。

0.参数配置对象流程图

在这里插入图片描述
为了实现ffmpeg的参数配置体系/机制,ffmpeg抽象了如上图5类(细分):AVDictionary字典容器类,AVDictionaryEntry字典实体类,AVOption类,AVClass类,继承AVClass *class的可配参数业务类(比如AVCodecContext/RTSPState等类);

这5大类,其中AVDictionary字典容器类,AVDictionaryEntry字典实体类作为参数传递的载体。后面3类是参数配置生效的类。

前面4类是基础、工具类、公共模块,供其他模块使用,所以放到了工具箱目录——libavutil目录下。

第5类是需要开放参数配置的业务类,在业务功能模块里定义(比较灵活,谁需要谁装配),所以就不放到工具类了——第一个成员必须是AVClass *类型的,因为ffmpeg配置参数的实现是建立在它是这样的位置的假设的,不能随意改,不然得改源码。

还有个重要的AVOption类的成员offset,这个偏移相对的是谁?如上图offset虚线箭头指向——就是AVClass *类型的宿主——可配参数实体业务类的地址。——当然可以引入linux内核第一宏就不用把AVClass放到第一个成员了,但是要改源码了。

上图左边虚框里,是第一步,保存参数配置到字典容器里(下面会有详解)——相当于寄存器(或者寄存地)。
上图右边虚框里,是第二步,将参数配置落地——把字典容器里的参数设置到可配参数业务类对象里的对应的参数成员里——最终落脚地,参数去的目的地。

ffmpeg的实现,直接将参数抽象成了AVDictionary字典容器类,然后把保存这些参数的全局变量或者对象的成员变量依然未变,参数最终映射到的是参数配置的业务类,但是变的是给增加了参数支持配置表AVOption类,而AVOption类由被AVClass类管理——AVClass类是个啥东西?我觉得称之为装饰器类,因为这用到了设计模式的装饰器设计模式——谁想增加参数可配置的功能,谁都带上AVClass类就行了。装饰器就是谁想有什么能力就去戴上那个能力就行了。AVClass类是可配置参数能力的装饰器
在这里插入图片描述

1.AVDictionary字典容器类

AVDictionary粗暴且低效地实现了python中的字典概念,或者cpp中的map容器概念——是个键值对容器。
它和AVDictionaryEntry字典实体类是什么关系?聚合关系(根据面向对象的思想)——具体见下面对象图。

粗暴低效实现,在于它在内存中搞了个指针数组,存放一个个的键值对,每次新增都会扩展这个指针数组,每次查找都是循环遍历指针数组来匹配。
又粗暴又低效,不过能用。

1.1 类定义及类图

libavutil/dict.c中

struct AVDictionary {
    int count;
    AVDictionaryEntry *elems;
};

libavutil/dict.h中

typedef struct AVDictionaryEntry {
    char *key;
    char *value;
} AVDictionaryEntry;

typedef struct AVDictionary AVDictionary;

2
从如上类图/数据结构中,就可以推测出,这个键值对保存,确实是很粗暴。——搞了个指针数组,指向各个键值对内存地址。如果想加入一个键值对,不是链表形式,而是调用realloc扩展指针数组的内存即elems成员指向的那块连续内存。

1.2 构造函数

oopc的构造也是类似c++的,但c++的类对象的内存开辟编程人员看不到由编译器编译时添加。

ffmpeg的实现是这个AVDictionary对象直接调用av_dict_set方法来构造。
在这里插入图片描述
里面包含了内存开辟。所以,直接使用即可。比如:

AVDictionary *opts = NULL;
    av_dict_set(&opts, "stimeout", "10000000", 0);

另外一个av_dict_copy也包含了构造函数。

int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags)
{
    AVDictionaryEntry *t = NULL;

    while ((t = av_dict_get(src, "", t, AV_DICT_IGNORE_SUFFIX))) 
    {
        int ret = av_dict_set(dst, t->key, t->value, flags);
        if (ret < 0)
        {
            return ret;
        }
    }

    return 0;
}

可以看到其实也是因为调用了av_dict_set函数,才具有构造功能。所以使用av_dict_copy时也可以这样:

    AVDictionary *tmp = NULL;
    av_dict_copy(&tmp, *options, 0);

这样就拷贝到tmp这个字典指向的对象了。

1.3 析构函数

av_dict_free(&opts);

1.4 设置/读取等配置参数

//设置键值对到字典类对象中——包含了构造。
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags);


//获取字典类对象中的键值对。
AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key,
                               const AVDictionaryEntry *prev, int flags);

                               
//拷贝一个字典对象的键值对到另一个字典对象中(深拷贝),包含了构造函数。                               
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags);

按照oopc来说,它这些方法就是这个类的方法,模拟的面向对象的类方法定义,第一个形参可以看着是this指针。

1.5 参数配置实例

    AVDictionary *opts = NULL;
    av_dict_set(&opts, "stimeout", "10000000", 0);          //设置超时断开连接时间 us
    av_dict_set(&opts, "buffer_size", "102400", 0);         //设置缓存大小 byte
    av_dict_set(&opts, "rtsp_transport", "tcp", 0);         //设置rtsp以tcp/udp方式打开
    av_dict_set(&opts, "threads", "0", 0);                  //设置自动开启线程数
    av_dict_set(&opts, "probesize", "2097152", 0);          //设置探测输入流数据包大小
    av_dict_set(&opts, "max_delay", "1000", 0);             //接收包间隔最大延迟 us
    av_dict_set(&opts, "analyzeduration", "1000000", 0);    //设置分析输入流所需时间 us
    av_dict_set(&opts, "max_analyze_duration", "1000", 0);  //设置分析输入流最大时长 us

这样呢,就把这些参数变成了键值对存放到了opts所指向的字典管理类对象中。那么接下来,ffmpeg就可以拿着opts去配置下去了。

到此,第0章的参数配置对象流程图中,参数配置传递完毕,接下来所讲的就是参数配置到业务对象的成员变量中的“弯弯绕绕”“繁杂”的过程。首先碰到的就是AVOption类。

2.AVOption类

AVDictionary 负责保存用户传递进来的参数(统一抽象为键值对),那么传递进来后,先不说配置到哪里,先说配置到目的地的时候是不是得过滤下?不然你瞎写参数,ffmpeg都没有支持也能配置?AVOption类应运而生——是ffmpeg能支持的参数配置表或者叫参数过滤(识别)表。

ffmpeg把那些庞杂的参数在运行的使用的内部都抽象为了一个AVOption类的表格——每个业务功能的配置都有个AVOption类的默认配置表格——以表明这个业务只能支持哪些参数配置——这样很具有扩展性,什么样的业务定义什么样的配置表——是提前定义好的,不是随便写一个配置动态现编的,程序没有那么智能——除非是那种未来高级AI程序可以自我编程动态改变的那种。

2.1 类定义

typedef struct AVOption {
    const char *name;

    /**
     * short English help text
     * @todo What about other languages?
     */
    const char *help;

    /**
     * The offset relative to the context structure where the option
     * value is stored. It should be 0 for named constants.
     */
    int offset;
    enum AVOptionType type;

    /**
     * the default value for scalar options
     */
    union {
        int64_t i64;
        double dbl;
        const char *str;
        /* TODO those are unused now */
        AVRational q;
    } default_val;
    double min;                 ///< minimum valid value for the option
    double max;                 ///< maximum valid value for the option

    int flags;
#define AV_OPT_FLAG_ENCODING_PARAM  1   ///< a generic parameter which can be set by the user for muxing or encoding
#define AV_OPT_FLAG_DECODING_PARAM  2   ///< a generic parameter which can be set by the user for demuxing or decoding
#define AV_OPT_FLAG_AUDIO_PARAM     8
#define AV_OPT_FLAG_VIDEO_PARAM     16
#define AV_OPT_FLAG_SUBTITLE_PARAM  32
/**
 * The option is intended for exporting values to the caller.
 */
#define AV_OPT_FLAG_EXPORT          64
/**
 * The option may not be set through the AVOptions API, only read.
 * This flag only makes sense when AV_OPT_FLAG_EXPORT is also set.
 */
#define AV_OPT_FLAG_READONLY        128
#define AV_OPT_FLAG_BSF_PARAM       (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering
#define AV_OPT_FLAG_RUNTIME_PARAM   (1<<15) ///< a generic parameter which can be set by the user at runtime
#define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering
#define AV_OPT_FLAG_DEPRECATED      (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information
#define AV_OPT_FLAG_CHILD_CONSTS    (1<<18) ///< set if option constants can also reside in child objects
//FIXME think about enc-audio, ... style flags

    /**
     * The logical unit to which the option belongs. Non-constant
     * options and corresponding named constants share the same
     * unit. May be NULL.
     */
    const char *unit;
} AVOption;

这个是内部所用的参数表抽象出来的类,里面包含了各种信息,其中offset偏移是比较重要的,是参数配置最终的落脚点。

针对具体业务,ffmpeg支持那些参数配置?看完本节,就不用网上搜了。 通过源码查找AVOption类的参数支持表,就知道了,也知道怎么配置了。

比如想配置rtsp的参数,那么可以找到rtsp的AVOption类的配置表格,如下,看看它支持的配置项:

static const AVClass rtsp_demuxer_class = {
    .class_name     = "RTSP demuxer",
    .item_name      = av_default_item_name,
    .option         = ff_rtsp_options,
    .version        = LIBAVUTIL_VERSION_INT,
};

可以看到rtsp的AVOption类的配置表格是ff_rtsp_options,如下

const AVOption ff_rtsp_options[] = {
    { "initial_pause",  "do not start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC },
    FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags),
    { "rtsp_transport", "set RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \
    { "udp", "UDP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP}, 0, 0, DEC|ENC, "rtsp_transport" }, \
    { "tcp", "TCP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_TCP}, 0, 0, DEC|ENC, "rtsp_transport" }, \
    { "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" },
    { "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {.i64 = (1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" },
    { "https", "HTTPS tunneling", 0, AV_OPT_TYPE_CONST, {.i64 = (1 << RTSP_LOWER_TRANSPORT_HTTPS )}, 0, 0, DEC, "rtsp_transport" },
    RTSP_FLAG_OPTS("rtsp_flags", "set RTSP flags"),
    { "listen", "wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" },
    { "prefer_tcp", "try RTP via TCP first, if available", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_PREFER_TCP}, 0, 0, DEC|ENC, "rtsp_flags" },
    { "satip_raw", "export raw MPEG-TS stream instead of demuxing", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_SATIP_RAW}, 0, 0, DEC, "rtsp_flags" },
    RTSP_MEDIATYPE_OPTS("allowed_media_types", "set media types to accept from the server"),
    { "min_port", "set minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC },
    { "max_port", "set maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC },
    { "listen_timeout", "set maximum timeout (in seconds) to wait for incoming connections (-1 is infinite, imply flag listen)", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC },
    { "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(stimeout), AV_OPT_TYPE_INT64, {.i64 = 0}, INT_MIN, INT64_MAX, DEC },
    COMMON_OPTS(),
    { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC },
    { NULL },
};

这个AVOption表格记录了rtsp支持的参数配置,其中offset的偏移量是相对RTSPState(除常量外)。

int avformat_open_input(AVFormatContext **ps, const char *filename,
                        const AVInputFormat *fmt, AVDictionary **options)
{
    AVFormatContext *s = *ps;
  
	……

  /* Allocate private data. */
    if (s->iformat->priv_data_size > 0) 
    {
        if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        if (s->iformat->priv_class) 
        {
            *(const AVClass **) s->priv_data = s->iformat->priv_class;
            av_opt_set_defaults(s->priv_data);
            if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                goto fail;
        }
    }
    ……
}

avformat_open_input中,所有的私有数据都是继承自AVClass *成员的类,它的所在类就是offset值的参考系对象。rtsp是RTSPState类,所以rtsp的AVOption类的配置表格是ff_rtsp_options中的offset偏移量就是相对RTSPState类的起始地址的偏移量,如果理解不了,可以直接看表格上面的宏定义。
在这里插入图片描述

2.2 相关操作函数

//获取AVOption表格中的一个个AVOption类成员的迭代器。 
const AVO获取ption *av_opt_next(const void *obj, const AVOption *last)

3.AVClass类

AVClass类是可配置参数能力的装饰器。有些业务类想要拥有参数可配置的能力,它的第一个成员就得是AVClass *类型的指针成员,如果不想有这个能力,把这个成员置为NULL即可。

4.可配参数业务类

自己起的名字,当经过参数配置过滤表格后,ffmpeg支持的参数要配置到哪里呢?总得有个落脚点吧?我运行的时候怎么使用它?于是可配参数业务类应运而生。

这类的形式,是如下的:

在这里插入图片描述

如果想要拥有可配参数能力,那么就定义个这个业务的参数支持装饰器AVClass,否则就把成员class置为NULL。

比如想要给rtsp拉流添加可配置参数功能,则需要定义一个rtsp参数业务配置类,第1个成员必须是AVClass *类的指针类型,再定义AVClass对象,将其指针赋给rtsp参数业务配置类的第1个成员。再定义AVOption支持可配参数的表格等,如下:
在这里插入图片描述

rtsp的可配参数装饰器是rtsp_demuxer_class,rtsp的支持可配置参数的表格是ff_rtsp_options。

2.1 相关操作函数

//把配置的参数设置到可配参数业务对象的成员变量里,
//obj就是可配置参数业务对象的地址,比如AVCodecContext/RTSPState等的地址
int av_opt_set_dict(void *obj, AVDictionary **options)

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

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

相关文章

用 Delphi 实现一个基本的网页邮件抓取和发送功能

如何用 Delphi 实现一个基本的网页邮件抓取和发送功能。以下示例仅作为概念验证&#xff0c;实际应用中需要考虑更多的细节和技术问题。 示例&#xff1a;从简单网页抓取邮件并发送 1. 环境准备 假设你已经安装了 Delphi&#xff0c;并且安装了 Indy 组件库。Indy 是一个用于…

用Python提取PowerPoint演示文稿中的音频和视频

将多种格式的媒体内容进行重新利用&#xff08;如PowerPoint演示中的音频和视频&#xff09;是非常有价值的。无论是创建独立的音频文件、提取视频以便在线分发&#xff0c;还是为了未来的使用需求进行资料归档&#xff0c;从演示文稿中提取这些媒体文件可以为多媒体内容的多次…

linux 系统是如何收发数据包

目录 1. 背景 1.1 协议栈的构成 1. 应用层: 2. Socket 层: 3. 传输层 (TCP/UDP): 4. 网络层 (IP): 5. 数据链路层 (MAC): 6. 物理层 (网卡驱动): 1.2 数据包的组成 2. 接收网络数据包的流程 2.1 数据包接收流程概述 2.2 详细步骤说明 2.2.1 网卡接收数据包 2.2.2…

JVM 虚拟机的编译器、类加载过程、类加载器有哪些?

JVM 虚拟机的编译器 编译器可以分为&#xff1a;前端编译器、JIT 编译器、AOT编译器。 前端编译器&#xff1a;源代码 --> 字节码 在Java语言中&#xff0c;JDK安装目录中的javac就是编译器。它负责将Java源代码编译为字节码。因为处于编译的前期&#xff0c;javac也叫做前…

C语言 | Leetcode C语言题解之第417题太平洋大西洋水流问题

题目&#xff1a; 题解&#xff1a; static const int dirs[4][2] {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};void bfs(int row, int col, bool ** ocean, const int ** heights, int m, int n) {if (ocean[row][col]) {return;}ocean[row][col] true;int * queue (int *)malloc…

如何安装和注册 GitLab Runner

如何安装和注册 GitLab Runner GitLab Runner 是一个用于运行 GitLab CI/CD (Continuous Integration/Continuous Deployment) 作业。它是一个与 GitLab 配合使用的应用程序&#xff0c;可以在本地或云中运行。Runner 可以执行不同类型的作业&#xff0c;例如编译代码、运行测…

有效安全计划评估的基本指标

衡量安全计划成功与否的最有效指标是什么&#xff1f; 最直接的指标是&#xff1a;您的组织是否遭到入侵&#xff1f;如果答案是肯定的&#xff0c;那么显然还有工作要做。如果答案是否定的&#xff0c;那么您的状况就更好了——但情况比这更复杂。 即使您没有遭到入侵&#…

视频理解大模型最新进展

文章目录 Video-LLaMAVision-Language BranchAudio-Language Branch Video-ChatGPTMiniGPT4-videoCogVLM2-Video&#xff08;1&#xff09;Pre-training&#xff08;2&#xff09;Post-training Qwen2-VLMA-LMMChat-UniVi大模型对比 Video-LLaMA 2023&#xff1a;阿里达摩院的…

JAVA虚拟机----JVM

(一)认识JVM JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。 虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。 常⻅的虚拟机&#xff1a;JVM、VMwave、Virtual Box。 &#xff08;二&#xff09;JVM运…

2017年国赛高教杯数学建模C题颜色与物质浓度辨识解题全过程文档及程序

2017年国赛高教杯数学建模 C题 颜色与物质浓度辨识 比色法是目前常用的一种检测物质浓度的方法&#xff0c;即把待测物质制备成溶液后滴在特定的白色试纸表面&#xff0c;等其充分反应以后获得一张有颜色的试纸&#xff0c;再把该颜色试纸与一个标准比色卡进行对比&#xff0c…

如何查看电脑什么时候被人动过及看过的文件?

一、查看Windows事件查看器 Windows系统具有强大的日志记录功能&#xff0c;通过“事件查看器”可以查看电脑的使用记录。具体步骤如下&#xff1a; 按下Win R组合键打开运行窗口&#xff0c;输入eventvwr.msc命令并回车&#xff0c;打开事件查看器。 在事件查看器中&#x…

solidwork镜像实体

效果如下&#xff1a; 可以看到这两条线是对称的。 第一步&#xff0c;点击这条要镜像的边&#xff0c;接着点击镜像实体。 然后选择镜像轴&#xff0c;即可

OpenHarmony标准系统mipi摄像头适配

OpenHarmony标准系统mipi摄像头适配 本文档以rk3568为例&#xff0c;讲述如何在OpenHarmony 标准系统rk设备上适配mipi摄像头。 开发环境 OpenHarmony标准系统4.1rrk3568设备摄像头ov5648,ov8858 文档约定&#xff1a;4.1r_3568为OpenHarmony标准系统源码根目录 1.适配准备:得…

苹果CMS插件:优化蜘蛛访问内容,提升百度收录率

确保蜘蛛抓取原始内容 专为苹果CMS设计的广告管理插件&#xff0c;能够智能识别搜索引擎蜘蛛与普通访客&#xff0c;确保蜘蛛访问时展示原始内容&#xff0c;从而提升被百度等搜索引擎收录的几率。 广告显示提升收益 对于普通访客&#xff0c;该插件则优先显示广告内容&#…

UnLua扩展C++函数和蓝图自定义事件

一、通过BlueprintImplementableEvent标记扩展C函数 1、 这个标记表示C不需要实现&#xff0c;让蓝图/Lua重写。 2、首先在C中将LuaImp函数标记为BlueprintImplementableEvent&#xff0c;不需要实现&#xff0c;然后再GetIndex中调用该函数。 MyBaseActor.h UFUNCTION(Bluepr…

电力电塔电线缺陷检测数据集 voc yolo

电力 电塔电线缺陷检测数据集 10000张 带标注 voc yolo 电力电塔电线缺陷检测数据集 数据集描述 该数据集旨在用于电力电塔和电线的缺陷检测任务&#xff0c;涵盖多种常见的缺陷类型。数据集包含了大量的图像及其对应的标注信息&#xff0c;可用于训练计算机视觉模型&#x…

DEPLOT: One-shot visual language reasoning by plot-to-table translation论文阅读

文章链接&#xff1a;https://arxiv.org/abs/2308.01979http://arxiv.org/abs/2212.10505https://arxiv.org/abs/2308.01979 源码链接&#xff1a;https://github.com/cse-ai-lab/RealCQA Abstract 理解图表需要很强的推理能力&#xff0c;之前的最先进 &#xff08;SOTA&…

圆周阵列元件的间距增加操作方法

在进行器件圆周阵列时&#xff0c;内圈的角度和外圈的旋转角度都相同&#xff0c;由于内圈的圆周长小于外圈的圆周长&#xff0c;有可能在内圈造成部分元件之间有两个焊盘会有覆盖的情况&#xff0c;此时需要对内圈的元件位置进行微调&#xff0c;需要增加在同一半径位置的元件…

数据结构 ——— 算法的时间复杂度

目录 时间复杂度的概念 时间复杂度函数式 大O的渐进表示法的概念 大O的渐进表示法 时间复杂度的概念 在计算机科学中&#xff0c;算法的时间复杂度是一个函数&#xff08;数学上的函数式&#xff09;&#xff0c;它定量描述了该算法的运行时间&#xff0c;一个算法执行所耗…

Netty笔记10-Netty参数调优

文章目录 一、CONNECT_TIMEOUT_MILLISCONNECT_TIMEOUT_MILLIS设置为1秒超时CONNECT_TIMEOUT_MILLIS设置为5秒超时注意事项 二、SO_BACKLOG代码示例注意事项 三、ulimit -n(文件描述符)设置文件描述符限制在注意事项 四、TCP_NODELAY使用 TCP_NODELAY 的场景注意事项 五、SO_SND…