ffmpeg命令行是如何打开vf_scale滤镜的

news2024/7/11 13:13:43

前言

在ffmpeg命令行中,ffmpeg -i test -pix_fmt rgb24 test.rgb,会自动打开ff_vf_scale滤镜,本章主要追踪这个流程。

通过gdb可以发现其基本调用栈如下:
在这里插入图片描述
可以看到,query_formats()中创建的vf_scale滤镜,
这是ffmpeg滤镜框架中的操作,当avfilter进行query format的时候,如果发现前后两个filter的pixformat不一致的时候就会在中间插入一个vf_scale的滤镜。这就是标题的答案。
但是本章内容主要讨论ffmpeg工具里面是如何调用avfilter的,也就是从哪里开始创建,哪里开始销毁,以及中间是如何传递信息的。特别是当命令行中没有vf_scale的操作时,ffmpeg工具是否会打开filter?

为了分析上述问题,我们先用一定使用到vf_scale的命令:

ffmpeg -i test  -pix_fmt rgb24  test.rgb

先找到调用avfilter的两个函数:

ret = av_buffersrc_add_frame_flags(ifilter->filter, frame, buffersrc_flags);
ret = av_buffersink_get_frame_flags(filter, filtered_frame,AV_BUFFERSINK_FLAG_NO_REQUEST);

ffmpeg.h中定义了有关filter的三个结构体:

typedef struct InputFilter {
    AVFilterContext    *filter;
    struct InputStream *ist;
    struct FilterGraph *graph;
    uint8_t            *name;
    enum AVMediaType    type;   // AVMEDIA_TYPE_SUBTITLE for sub2video

    AVFifoBuffer *frame_queue;

    // parameters configured for this input
    int format;

    int width, height;
    AVRational sample_aspect_ratio;

    int sample_rate;
    int channels;
    uint64_t channel_layout;

    AVBufferRef *hw_frames_ctx;
    int32_t *displaymatrix;

    int eof;
} InputFilter;

typedef struct OutputFilter {
    AVFilterContext     *filter;
    struct OutputStream *ost;
    struct FilterGraph  *graph;
    uint8_t             *name;

    /* temporary storage until stream maps are processed */
    AVFilterInOut       *out_tmp;
    enum AVMediaType     type;

    /* desired output stream properties */
    int width, height;
    AVRational frame_rate;
    int format;
    int sample_rate;
    uint64_t channel_layout;

    // those are only set if no format is specified and the encoder gives us multiple options
    // They point directly to the relevant lists of the encoder.
    const int *formats;
    const uint64_t *channel_layouts;
    const int *sample_rates;
} OutputFilter;

typedef struct FilterGraph {
    int            index;
    const char    *graph_desc;

    AVFilterGraph *graph;
    int reconfiguration;
    // true when the filtergraph contains only meta filters
    // that do not modify the frame data
    int is_meta;

    InputFilter   **inputs;
    int          nb_inputs;
    OutputFilter **outputs;
    int         nb_outputs;
} FilterGraph;

我们通过上述线索寻找buffersrc和buffersink是在哪里创建的。
首先看buffersink,通过:

decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_t *duration_pts, int eof,int *decode_failed)
	decode(ist->dec_ctx, decoded_frame, got_output, pkt)
	send_frame_to_filters(ist, decoded_frame)
		ifilter_send_frame(ist->filters[i], decoded_frame, i < ist->nb_filters - 1)
			configure_filtergraph(fg)//reinit
			av_buffersrc_add_frame_flags(ifilter->filter, frame, buffersrc_flags)

通过上面结构可知其中的configure_filtergraph(fg)//reinit是关键,在这里初始化整个avfilter。
在这里,我们回顾一下avfilter的关键调用流程:

	char args[512];
    int ret = 0;
    const AVFilter *buffersrc  = avfilter_get_by_name("buffer");
    const AVFilter *buffersink = avfilter_get_by_name("buffersink");
    AVFilterInOut *outputs = avfilter_inout_alloc();
    AVFilterInOut *inputs  = avfilter_inout_alloc();
    AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base;
    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };

    filter_graph = avfilter_graph_alloc();
    if (!outputs || !inputs || !filter_graph) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    /* buffer video source: the decoded frames from the decoder will be inserted here. */
    snprintf(args, sizeof(args),
            "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
            dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
            time_base.num, time_base.den,
            dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);

    ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
                                       args, NULL, filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
        goto end;
    }

    /* buffer video sink: to terminate the filter chain. */
    ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
                                       NULL, NULL, filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
        goto end;
    }

    ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
                              AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
        goto end;
    }
    outputs->name       = av_strdup("in");
    outputs->filter_ctx = buffersrc_ctx;
    outputs->pad_idx    = 0;
    outputs->next       = NULL;

    /*
     * The buffer sink input must be connected to the output pad of
     * the last filter described by filters_descr; since the last
     * filter output label is not specified, it is set to "out" by
     * default.
     */
    inputs->name       = av_strdup("out");
    inputs->filter_ctx = buffersink_ctx;
    inputs->pad_idx    = 0;
    inputs->next       = NULL;

    if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
                                    &inputs, &outputs, NULL)) < 0)
        goto end;

    if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
        goto end;
        
     ...

回到ifilter_send_frame()中来:


static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_reference)
{
    FilterGraph *fg = ifilter->graph;
    AVFrameSideData *sd;
    int need_reinit, ret;
    int buffersrc_flags = AV_BUFFERSRC_FLAG_PUSH;

    if (keep_reference)
        buffersrc_flags |= AV_BUFFERSRC_FLAG_KEEP_REF;

    /* determine if the parameters for this input changed */
    //如果输入和frame中的format不一致,就会引起reinit
    need_reinit = ifilter->format != frame->format;

    switch (ifilter->ist->st->codecpar->codec_type) {
    case AVMEDIA_TYPE_VIDEO:
        need_reinit |= ifilter->width  != frame->width ||
                       ifilter->height != frame->height;
        break;
    }

    if (!ifilter->ist->reinit_filters && fg->graph)
        need_reinit = 0;

    if (!!ifilter->hw_frames_ctx != !!frame->hw_frames_ctx ||
        (ifilter->hw_frames_ctx && ifilter->hw_frames_ctx->data != frame->hw_frames_ctx->data))
        need_reinit = 1;

    if (sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) {
        if (!ifilter->displaymatrix || memcmp(sd->data, ifilter->displaymatrix, sizeof(int32_t) * 9))
            need_reinit = 1;
    } else if (ifilter->displaymatrix)
        need_reinit = 1;

    if (need_reinit) {
    	//ifilter从这里获取到w,h,pix等信息
        ret = ifilter_parameters_from_frame(ifilter, frame);
        if (ret < 0)
            return ret;
    }

    /* (re)init the graph if possible, otherwise buffer the frame and return */
    if (need_reinit || !fg->graph) {
        ret = configure_filtergraph(fg);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n");
            return ret;
        }
    }

    ret = av_buffersrc_add_frame_flags(ifilter->filter, frame, buffersrc_flags);
    if (ret < 0) {
        if (ret != AVERROR_EOF)
            av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret));
        return ret;
    }

    return 0;
}

继续回到onfigure_filtergraph(fg) 删除了与本题无关的代码:

int configure_filtergraph(FilterGraph *fg)
{
    AVFilterInOut *inputs, *outputs, *cur;
    //这里判断是否是simple,因为我们的命令行中没有avfilter,故此为true
    int ret, i, simple = filtergraph_is_simple(fg);
    //int filtergraph_is_simple(FilterGraph *fg)
	//{return !fg->graph_desc;}
	//这里其实就是NULL
    const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter :fg->graph_desc;

    cleanup_filtergraph(fg);
    //创建图
    if (!(fg->graph = avfilter_graph_alloc()))
        return AVERROR(ENOMEM);

    if (simple) {
    //获取到outputstream
        OutputStream *ost = fg->outputs[0]->ost;
        char args[512];
        const AVDictionaryEntry *e = NULL;
    } else {
        fg->graph->nb_threads = filter_complex_nbthreads;
    }
	//这里在我们这里可以跳过,因为graph_desc为null
    if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0)
        goto fail;
        
	//这里是配置buffersrc的地方
    for (cur = inputs, i = 0; cur; cur = cur->next, i++){
        if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) {
            avfilter_inout_free(&inputs);
            avfilter_inout_free(&outputs);
            goto fail;
         }
     }
    avfilter_inout_free(&inputs);
    
	//这里是配置buffersink的地方
    for (cur = outputs, i = 0; cur; cur = cur->next, i++)
        configure_output_filter(fg, fg->outputs[i], cur);
    avfilter_inout_free(&outputs);

    if (!auto_conversion_filters)
        avfilter_graph_set_auto_convert(fg->graph, AVFILTER_AUTO_CONVERT_NONE);
        
    //avfilter的标准调用
    if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0)
        goto fail;
    fg->is_meta = graph_is_meta(fg->graph);
    return 0;
}

接下来继续看如何配置input_filter的:

static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
                                        AVFilterInOut *in)
{
    AVFilterContext *last_filter;
    //创建buffer
    const AVFilter *buffer_filt = avfilter_get_by_name("buffer");
    const AVPixFmtDescriptor *desc;
    InputStream *ist = ifilter->ist;
    InputFile     *f = input_files[ist->file_index];
    AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) :
                                         ist->st->time_base;
    AVRational fr = ist->framerate;
    AVRational sar;
    AVBPrint args;
    char name[255];
    int ret, pad_idx = 0;
    int64_t tsoffset = 0;
    AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();

    if (!par)
        return AVERROR(ENOMEM);
    memset(par, 0, sizeof(*par));
    par->format = AV_PIX_FMT_NONE;

    if (!fr.num)
        fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);

    sar = ifilter->sample_aspect_ratio;
    if(!sar.den)
        sar = (AVRational){0,1};
    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
    //这里是配置buffersrc的地方
    av_bprintf(&args,
             "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:"
             "pixel_aspect=%d/%d",
             ifilter->width, ifilter->height, ifilter->format,
             tb.num, tb.den, sar.num, sar.den);
    if (fr.num && fr.den)
        av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
    snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
             ist->file_index, ist->st->index);

	//创建filterctx
    if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
                                            args.str, NULL, fg->graph)) < 0)
        goto fail;
    par->hw_frames_ctx = ifilter->hw_frames_ctx;
    ret = av_buffersrc_parameters_set(ifilter->filter, par);
    if (ret < 0)
        goto fail;
    av_freep(&par);
    last_filter = ifilter->filter;

    desc = av_pix_fmt_desc_get(ifilter->format);
    av_assert0(desc);


    snprintf(name, sizeof(name), "trim_in_%d_%d",ist->file_index, ist->st->index);
    if (copy_ts) {
        tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time;
        if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)
            tsoffset += f->ctx->start_time;
    }
    //插入trim
    ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ?
                      AV_NOPTS_VALUE : tsoffset, f->recording_time,
                      &last_filter, &pad_idx, name);
    if (ret < 0)
        return ret;
	//链接
    if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
        return ret;
    return 0;
fail:
    av_freep(&par);

    return ret;
}

下面是configure_output_filter

static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
{
    OutputStream *ost = ofilter->ost;
    OutputFile    *of = output_files[ost->file_index];
    AVFilterContext *last_filter = out->filter_ctx;
    AVBPrint bprint;
    int pad_idx = out->pad_idx;
    int ret;
    const char *pix_fmts;
    char name[255];

    snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index);
    //创建buffersink
    ret = avfilter_graph_create_filter(&ofilter->filter,
                                       avfilter_get_by_name("buffersink"),
                                       name, NULL, NULL, fg->graph);

    if (ret < 0)
        return ret;
	//这个scale完全就是尺寸的resize
    if ((ofilter->width || ofilter->height) && ofilter->ost->autoscale) {
        char args[255];
        AVFilterContext *filter;
        const AVDictionaryEntry *e = NULL;
        //这里只有size的scale,并没有颜色空间的转换
        snprintf(args, sizeof(args), "%d:%d",ofilter->width, ofilter->height);
        while ((e = av_dict_get(ost->sws_dict, "", e,AV_DICT_IGNORE_SUFFIX))) {
            av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value);
        }

        snprintf(name, sizeof(name), "scaler_out_%d_%d",ost->file_index, ost->index);
        if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"),
                                                name, args, NULL, fg->graph)) < 0)
            return ret;
        if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)
            return ret;

        last_filter = filter;
        pad_idx = 0;
    }

    av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED);
    //如果设置了输出的pix_fmt 那么就会在这里增加一个format的的avfilter
    //这个format其实什么也不做,就是指定一个中间format,用来在协商的时候
    //确定是否增加中间的csc swscale
    if ((pix_fmts = choose_pix_fmts(ofilter, &bprint))) {
        AVFilterContext *filter;

        ret = avfilter_graph_create_filter(&filter,
                                           avfilter_get_by_name("format"),
                                           "format", pix_fmts, NULL, fg->graph);
        av_bprint_finalize(&bprint, NULL);
        if (ret < 0)
            return ret;
        if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)
            return ret;

        last_filter = filter;
        pad_idx     = 0;
    }

    if (ost->frame_rate.num && 0) {
        AVFilterContext *fps;
        char args[255];

        snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num,
                 ost->frame_rate.den);
        snprintf(name, sizeof(name), "fps_out_%d_%d",
                 ost->file_index, ost->index);
        ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"),
                                           name, args, NULL, fg->graph);
        if (ret < 0)
            return ret;

        ret = avfilter_link(last_filter, pad_idx, fps, 0);
        if (ret < 0)
            return ret;
        last_filter = fps;
        pad_idx = 0;
    }

    snprintf(name, sizeof(name), "trim_out_%d_%d",
             ost->file_index, ost->index);
    ret = insert_trim(of->start_time, of->recording_time,
                      &last_filter, &pad_idx, name);
    if (ret < 0)
        return ret;


    if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
        return ret;

    return 0;
}

所以对于ffmpeg中的graph来说,依次为:
ff_vsrc_buffer->ff_vf_null->(scale resize)->ff_vf_format(命令行中由format的参数)->(fps )->(trim)->ff_buffersink。
中间括号内的是几个选择的avfilter。
协商format的时候最后会协商一个ff_vsrc_buffer的pix fomat.

最后用一个rawenc去将frame编码为pkt…

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

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

相关文章

MacOS创建NetworkExtension 【保姆级流程】

MacOS创建NetworkExtension (保姆级流程) 因为自己工作中的项目&#xff0c;是运行在macos系统上&#xff0c;其中的一部分功能是通过NetworkExtension来获取系统中的流量来做相应的处理&#xff0c;所以也想自己创建一个NetworkExtension&#xff0c;三天&#xff0c;不知道踩…

世微AP2400 电动车 摩托车灯照明 汽车灯照明 手电筒照明LED灯降压恒流驱动IC

PCB 布板参考 1. 大电流路径走线要粗&#xff0c;铺铜走线比较好。 2. 大电路回路面积以最短、最宽路径完成比较好。 3. 开关切换连接点&#xff1a;电感 L、开关管漏级与续流肖特基二极管&#xff0c;走线要短与粗&#xff0c;铺铜走线比较好&#xff0c;但同时需要适当面积作…

Redis_分片集群

10. 分片集群 10.1简介 业务场景&#xff0c;需要存储50G的数据。对于内存和硬盘配置不足&#xff0c;选用两种方式 一种&#xff1a;纵向扩展&#xff1a;加内存&#xff0c;加硬盘&#xff0c;提高CPU。简单、直接。RDB存储效率要考虑。成本要考虑。二种&#xff1a;横向扩…

C# Linq源码分析之Take方法

概要 Take方法作为IEnumerable的扩展方法&#xff0c;具体对应两个重载方法。本文主要分析第一个接收整数参数的重载方法。 源码解析 Take方法的基本定义 public static System.Collections.Generic.IEnumerable Take (this System.Collections.Generic.IEnumerable source…

【Tomcat】tomcat的多实例和动静分离

多实例&#xff1a; 在一台服务器上有多台Tomcat&#xff1b;就算是多实例 安装telnet服务&#xff0c;可以用来测试端口通信是否正常 yum -y install telnettelnet 192.168.220.112 80 tomcat的日志文件 cd /usr/local/tomcat/logsvim catalina.out Tomcat多实例部署&…

一定要会用selenium的等待,3种等待方式解读

很多人问&#xff0c;这个下拉框定位不到、那个弹出框定位不到…各种定位不到&#xff0c;其实大多数情况下就是两种问题&#xff1a; 有frame 没有加等待 殊不知&#xff0c;你的代码运行速度是什么量级的&#xff0c;而浏览器加载渲染速度又是什么量级的&#xff0c;就好比…

CEC2009无约束多目标测试集(UF1-UF10、附Matlab代码)

目录 一、CEC2009无约束多目标测试集 二、CEC2009无约束多目标测试集UF1-UF10Matlab代码 三、多目标灰狼算法测试CEC2009无约束多目标测试集 一、CEC2009无约束多目标测试集 二、CEC2009无约束多目标测试集UF1-UF10Matlab代码 % cec09.m % The Matlab version of the test i…

C# Winform DataGridView 数据刷新问题

目录 一、问题 二、创建项目 三、绑定空的数据源 四、绑定有数据的数据源 五、修改绑定的数据源 六、解决数据源刷新问题 七、解决刷新数据界面闪烁 一、问题 DataGridView 是比较常用的表格控件&#xff0c;在 DataGridView 中显示数据&#xff0c; 一般使用 dataGrid…

自动化更新导致的各种问题解决办法

由于最近自动化频频更新导致出现各种问题&#xff0c;因此在创建驱动对象代码时改成这种方式 我最近就遇到了由于更新而导致的代码报错&#xff0c;报错信息如下&#xff1a; 复制内容如下&#xff1a; Exception in thread “main” org.openqa.selenium.remote.http.Connecti…

搭建了个腾讯滑块服务,直接取ticket的,仅供测试.

最近闲着没事搭建了个TX滑块验证码服务,C#写的. 接口是rest接口 提交任务POST http://47.104.132.20:19090/task/addTask 提交数据: { "url": "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?stylesimple&aid16&uin3557247785…

html实现iphone同款开关

一、背景 想实现一个开关的按钮&#xff0c;来触发一些操作&#xff0c;网上找了总感觉看着别扭&#xff0c;忽然想到iphone的开关挺好&#xff0c;搞一个 二、代码实现 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8&qu…

内生性的蒙特卡罗模拟

这是一个很好的例子,通过蒙特卡洛模拟展示了忽略相关变量时,回归系数估计的偏差。 %% 蒙特卡洛模拟&#xff1a;内生性会造成回归系数的巨大误差 times 300; % 蒙特卡洛的次数 R zeros(times,1); % 用来储存扰动项u和x1的相关系数 K zeros(times,1); % 用来储存遗漏了x2…

利用自动校对软件优化新闻稿件的拼写和语法

利用自动校对软件优化新闻稿件的拼写和语法&#xff0c;您可以按照以下步骤进行&#xff1a; 1.选择适合的校对软件&#xff1a;市场上有多种拼写和语法校对软件可供选择。根据您的需求和预算&#xff0c;选择一个功能强大且适合新闻稿件的软件。 2.导入稿件&#xff1a;将待校…

msvcr110.dll文件丢失的解决方法有哪些?高效率修复msvcr110.dll缺失

msvcr110.dll是一个重要的系统动态链接库&#xff08;DLL&#xff09;文件&#xff0c;它在Windows操作系统中起到关键的作用。如果出现msvcr110.dll文件缺失的情况&#xff0c;可能会导致应用程序无法正常运行、错误提示以及系统不稳定性等问题。这时候我们就需要去解决它了&a…

freeswitch的mod_xml_curl模块动态获取configuration

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 mod_xml_curl模块支持从web服务获取xml配置&#xff0c;本文介绍如何动态获取acl配置。 环境 centos&#xff1a;CentOS release 7.0 (Final)或以上版本 freeswitch&#xff1a;v1.6.20 GCC&#xff1a;4.8.5 web…

SysML V1.2 Model Elements

一、概述 SysML的ModelElements包定义了可以显示在多种SysML图类型上的通用构造。这些包括包、模型、各种类型的依赖(例如&#xff0c;导入、访问、细化、实现)、约束和注释。本章中定义的包图用于通过将模型元素划分为可打包的元素并在包内建立包和&#xff08;/或&#xff0…

【启明智显分享】基于开阳ARK630HV100的车规开发板

基于开阳ARK630HV100的车规开发板 专用HMI芯片&#xff1a;5寸智能液晶摩托车仪表采用专用的HMI芯片&#xff0c;确保高效的数据处理和显示性能。这种芯片专为摩托车仪表设计&#xff0c;具有更快的响应速度和更稳定的性能&#xff0c;可以提供流畅的用户体验。高分辨率液晶显…

nodejs+vue+elementui社区流浪猫狗救助救援网站_4a4i2

基于此背景&#xff0c;本研究结合管理员即时发布流浪猫狗救助救援信息与用户的需求&#xff0c;设计并实现了流浪猫狗救助救援网站。系统采用B/S架构&#xff0c;java语言作为主要开发语言&#xff0c;MySQL技术创建和管理数据库。系统主要分为管理员和用户两大功能模块。通过…

安装ubuntu22.04系统,配置国内源以及ssh远程登录

一、安装ubuntu22.04系统 原文连接&#xff1a;Ubuntu操作系统22.04版本安装教程-VMware虚拟机_wx63f86e949a470的技术博客_51CTO博客 1.点击界面左侧的开启此虚拟机&#xff0c;即可进入Ubuntu操作系统安装界面&#xff0c;点击​​Try or Install Ubuntu ​​即可开始安装 …

【解密算法:时间与空间的博弈】

本章重点 ​​什么是数据结构&#xff1f; 什么是算法&#xff1f; 算法效率 时间复杂度 空间复杂度 常见时间复杂度以及复杂度oj练习 1. 什么是数据结构&#xff1f; 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系…