FFmpeg零基础学习(四)——CPU视频解码 重新编码

目录

  • 前言
  • 正文
    • 一、核心代码
    • 二、重要的API
        • 1、avformat_alloc_context
        • 2、avformat_open_input
        • 3、avcodec_alloc_context3
        • 4、avcodec_parameters_to_context
        • 5、avformat_alloc_output_context2
        • 6、avformat_new_stream
        • 7、avcodec_parameters_from_context
    • END、总结的知识与问题
  • 参考

前言

本篇文章的功能在于对视频进行解码,然后重新编码。只不过,本篇文章并没有对获得的解码数据进行任何的操作,后续会有文章,针对这方面的内容,进行完善。

正文

一、核心代码

code

	int ret = 0;int err;//打开输入文件char filename[] = "juren-30s.mp4";//动态分配和初始化一个空的AVFormatContext对象,为后续的音视频封装和解封装操作做准备。AVFormatContext *fmt_ctx = avformat_alloc_context();if (!fmt_ctx){qDebug() << "fmt_ctx error code:"<<AVERROR(ENOMEM);return;}//打开文件if((err = avformat_open_input(&fmt_ctx, filename,NULL,NULL)) < 0){printf("can not open file %d \n",err);return;}//分配并初始化一个 AVCodecContext 结构体,该结构体用于编解码器的相关信息和参数设置。AVCodecContext *avctx = avcodec_alloc_context3(NULL);//用于将AVCodecParameters结构体的值赋值给AVCodecContext结构体的对应字段//参数值的赋值涉及到了编解码器的基本配置,例如编码类型、码率、帧率等等。这样可以方便地将参数信息传递给编解码器上下文,以供后续的编解码操作使用。ret = avcodec_parameters_to_context(avctx, fmt_ctx->streams[0]->codecpar);if (ret < 0){qDebug() << "avcodec_parameters_to_context error code:"<<ret;return;}//查找解码器AVCodec *codec = avcodec_find_decoder(avctx->codec_id);//打开解码器if ((ret = avcodec_open2(avctx, codec, NULL)) < 0){qDebug() << "avcodec_open2 error code:"<<ret;return;}//打开输出文件容器char filename_out[] = "juren-30s-5.mp4";AVFormatContext *fmt_ctx_out = NULL;//该函数会尝试通过指定的输出格式来分配一个输出格式上下文err = avformat_alloc_output_context2(&fmt_ctx_out, NULL, NULL, filename_out);if (!fmt_ctx_out){qDebug() << "error code:"<<AVERROR(ENOMEM);return;}//添加一路流到容器上下文AVStream *st = avformat_new_stream(fmt_ctx_out, NULL);st->time_base = fmt_ctx->streams[0]->time_base;//分配帧和包资源AVCodecContext *enc_ctx = NULL;AVPacket *pkt = av_packet_alloc();AVFrame *frame = av_frame_alloc();AVPacket *pkt_out = av_packet_alloc();int frame_num = 0;int read_end = 0;while(true){if(1 == read_end ){break;}ret = av_read_frame(fmt_ctx, pkt);//跳过不处理音频包if(1 == pkt->stream_index){av_packet_unref(pkt);continue;}//读取到文件的结尾了if (AVERROR_EOF == ret){//读取完文件,这时候 pkt 的 data 跟 size 应该是 nullavcodec_send_packet(avctx, NULL);}else{if( 0 != ret){qDebug() << "av_read_frame error code:"<<ret;return;}else{retry:if (avcodec_send_packet(avctx, pkt) == AVERROR(EAGAIN)){qDebug() << "Receive_frame and send_packet both returned EAGAIN, which is an API violation";//这里可以考虑休眠 0.1 秒,返回 EAGAIN 通常是 ffmpeg 的内部 api 有buggoto retry;}//释放 pkt 里面的编码数据av_packet_unref(pkt);}}//循环不断从解码器读数据,直到没有数据可读。while(true){//读取 AVFrameret = avcodec_receive_frame(avctx, frame);/* 释放 frame 里面的YUV数据,* 由于 avcodec_receive_frame 函数里面会调用 av_frame_unref,所以下面的代码可以注释。* 所以我们不需要 手动 unref 这个 AVFrame* *///av_frame_unref(frame);if(AVERROR(EAGAIN) == ret){//提示 EAGAIN 代表 解码器 需要 更多的 AVPacket//跳出 第一层 for,让 解码器拿到更多的 AVPacketbreak;}else if( AVERROR_EOF == ret ){/* 提示 AVERROR_EOF 代表之前已经往 解码器发送了一个 data 跟 size 都是 NULL 的 AVPacket* 发送 NULL 的 AVPacket 是提示解码器把所有的缓存帧全都刷出来。* 通常只有在 读完输入文件才会发送 NULL 的 AVPacket,或者需要用现有的解码器解码另一个的视频流才会这么干。** *//* 往编码器发送 null 的 AVFrame,让编码器把剩下的数据刷出来。*/ret = avcodec_send_frame(enc_ctx, NULL);for(;;){ret = avcodec_receive_packet(enc_ctx, pkt_out);//这里不可能返回 EAGAIN,如果有直接退出。if (ret == AVERROR(EAGAIN)){printf("avcodec_receive_packet error code %d \n",ret);return;}if ( AVERROR_EOF == ret ){break;}//编码出 AVPacket ,先打印一些信息,然后把它写入文件。printf("pkt_out size : %d \n",pkt_out->size);//设置 AVPacket 的 stream_index ,这样才知道是哪个流的。pkt_out->stream_index = st->index;//转换 AVPacket 的时间基为 输出流的时间基。pkt_out->pts = av_rescale_q_rnd(pkt_out->pts, fmt_ctx->streams[0]->time_base, st->time_base, static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt_out->dts = av_rescale_q_rnd(pkt_out->dts, fmt_ctx->streams[0]->time_base, st->time_base, static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt_out->duration = av_rescale_q_rnd(pkt_out->duration, fmt_ctx->streams[0]->time_base, st->time_base, static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));ret = av_interleaved_write_frame(fmt_ctx_out, pkt_out);if (ret < 0) {printf("av_interleaved_write_frame faile %d \n",ret);return;}av_packet_unref(pkt_out);}av_write_trailer(fmt_ctx_out);//跳出 第二层 for,文件已经解码完毕。read_end = 1;break;}else if(ret >= 0){//只有解码出来一个帧,才可以开始初始化编码器。if(NULL == enc_ctx){//打开编码器,并且设置 编码信息。AVCodec *encode = avcodec_find_encoder(AV_CODEC_ID_H264);enc_ctx = avcodec_alloc_context3(encode);enc_ctx->codec_type = AVMEDIA_TYPE_VIDEO;enc_ctx->bit_rate = 400000;enc_ctx->framerate = avctx->framerate;enc_ctx->gop_size = 30;enc_ctx->max_b_frames = 10;enc_ctx->profile = FF_PROFILE_H264_MAIN;/** 其实下面这些信息在容器那里也有,也可以一开始直接在容器那里打开编码器* 我从 AVFrame 里拿这些编码器参数是因为,容器的信息不一样就是最终的信息。* 因为你解码出来的 AVFrame 可能会经过 filter 滤镜,经过滤镜之后信息就会变化,但是本文没有使用滤镜。*///编码器的时间基要取 AVFrame 的时间基,因为 AVFrame 是输入。enc_ctx->time_base = fmt_ctx->streams[0]->time_base;enc_ctx->width = fmt_ctx->streams[0]->codecpar->width;enc_ctx->height = fmt_ctx->streams[0]->codecpar->height;enc_ctx->sample_aspect_ratio = st->sample_aspect_ratio = frame->sample_aspect_ratio;enc_ctx->pix_fmt = static_cast<AVPixelFormat>(frame->format);enc_ctx->color_range            = frame->color_range;enc_ctx->color_primaries        = frame->color_primaries;enc_ctx->color_trc              = frame->color_trc;enc_ctx->colorspace             = frame->colorspace;enc_ctx->chroma_sample_location = frame->chroma_location;/* 注意,这个 field_order 不同的视频的值是不一样的,这里我写死了。* 因为 本文的视频就是 AV_FIELD_PROGRESSIVE* 生产环境要对不同的视频做处理的*/enc_ctx->field_order = AV_FIELD_PROGRESSIVE;/* 现在我们需要把 编码器参数复制给流,解码的时候是 从流赋值参数给解码器。* 现在要反着来。* */ret = avcodec_parameters_from_context(st->codecpar,enc_ctx);if (ret < 0){qDebug() << "avcodec_parameters_from_context codec faile:" << ret;return;}//它用于初始化和打开音频或视频编解码器的上下文。if ((ret = avcodec_open2(enc_ctx, encode, NULL)) < 0) {qDebug() << "avcodec_open2 codec faile:" << ret;return ;}//正式打开输出文件if ((ret = avio_open2(&fmt_ctx_out->pb, filename_out, AVIO_FLAG_WRITE,&fmt_ctx_out->interrupt_callback,NULL)) < 0){qDebug() << "avio_open2 codec faile:" << ret;return;}//要先写入文件头部。ret = avformat_write_header(fmt_ctx_out,NULL);if (ret < 0){qDebug() << "avformat_write_header codec faile:" << ret;return;}}//往编码器发送 AVFrame,然后不断读取 AVPacketret = avcodec_send_frame(enc_ctx, frame);if (ret < 0){qDebug() <<  "avcodec_send_frame fail:"<<ret;return;}for(;;){ret = avcodec_receive_packet(enc_ctx, pkt_out);if (ret == AVERROR(EAGAIN)){break;}if (ret < 0){qDebug() <<  "avcodec_receive_packet fail:"<<ret;return;}//编码出 AVPacket ,先打印一些信息,然后把它写入文件。qDebug() << "pkt_out size:"<<pkt_out->size;//设置 AVPacket 的 stream_index ,这样才知道是哪个流的。pkt_out->stream_index = st->index;//转换 AVPacket 的时间基为 输出流的时间基。pkt_out->pts = av_rescale_q_rnd(pkt_out->pts, fmt_ctx->streams[0]->time_base, st->time_base, static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt_out->dts = av_rescale_q_rnd(pkt_out->dts, fmt_ctx->streams[0]->time_base, st->time_base, static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt_out->duration = av_rescale_q_rnd(pkt_out->duration, fmt_ctx->streams[0]->time_base, st->time_base, static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));ret = av_interleaved_write_frame(fmt_ctx_out, pkt_out);if (ret < 0) {qDebug() <<  "av_interleaved_write_frame fail:"<<ret;return;}av_packet_unref(pkt_out);}}else{qDebug() <<"other fail \n";return;}}}av_frame_free(&frame);av_packet_free(&pkt);av_packet_free(&pkt_out);//关闭编码器,解码器。avcodec_close(avctx);avcodec_close(enc_ctx);//释放容器内存。avformat_free_context(fmt_ctx);//必须调 avio_closep ,要不可能会没把数据写进去,会是 0kbavio_closep(&fmt_ctx_out->pb);avformat_free_context(fmt_ctx_out);qDebug() <<"done \n";

二、重要的API

1、avformat_alloc_context

avformat_alloc_context是FFmpeg库中的一个函数,用于动态分配并初始化一个AVFormatContext结构体。它的函数原型如下:

/*** Allocate an AVFormatContext.* avformat_free_context() can be used to free the context and everything* allocated by the framework within it.*/
AVFormatContext *avformat_alloc_context(void);

该函数会分配一块内存,并将其初始化为一个空的AVFormatContext结构体,然后返回指向该结构体的指针。

使用avformat_alloc_context函数可以创建一个空的AVFormatContext对象,然后可以通过设置不同的字段和参数来配置它,以便进行音视频封装或解封装操作。
总结来说,avformat_alloc_context函数用于动态分配和初始化一个空的AVFormatContext对象,为后续的音视频封装和解封装操作做准备。
所以,分配后,可以对AVFormatContext 对象进行判空,防止初始化失败。

2、avformat_open_input

avformat_open_input是FFmpeg库中的一个函数,用于打开音视频输入文件并初始化相关的输入上下文(AVFormatContext)。它的函数原型如下:

/*** Open an input stream and read the header. The codecs are not opened.* The stream must be closed with avformat_close_input().** @param ps       Pointer to user-supplied AVFormatContext (allocated by*                 avformat_alloc_context). May be a pointer to NULL, in*                 which case an AVFormatContext is allocated by this*                 function and written into ps.*                 Note that a user-supplied AVFormatContext will be freed*                 on failure.*> 传入值为avformat_alloc_context 分配的对象* @param url      URL of the stream to open.*> 要打开的流的地址* @param fmt      If non-NULL, this parameter forces a specific input format.*                 Otherwise the format is autodetected.* >输入的文件的格式,若为NULL,则自动检测* @param options  A dictionary filled with AVFormatContext and demuxer-private*                 options.*                 On return this parameter will be destroyed and replaced with*                 a dict containing options that were not found. May be NULL.** @return 0 on success, a negative AVERROR on failure.*> 返回值0,为正确,其他值为失败* @note If you want to use custom IO, preallocate the format context and set its pb field.*/
int avformat_open_input(AVFormatContext **ps, const char *url,const AVInputFormat *fmt, AVDictionary **options);

该函数的参数说明如下:
ps:指向指针的指针,用于存储分配的AVFormatContext对象。
url:输入文件的URL或文件名。
fmt:指定输入格式,如果为NULL,则由FFmpeg自动检测输入文件的格式。
options:指向包含附加选项的字典。可以在打开输入文件时提供一些特定的选项,比如设置超时时间、设置输入缓冲大小等。

1、强调关闭使用avformat_close_input
2、函数返回一个整数值,表示操作的结果。如果返回值小于0,则表示打开输入文件失败,否则返回0表示操作成功。使用avformat_open_input函数可以打开一个音视频输入文件,并将其与一个AVFormatContext对象关联起来,以便后续的音视频解封装操作。
3、avformat_open_input函数用于打开音视频输入文件,并初始化相关的输入上下文。它是进行音视频解封装操作的起点之一。

源码如下:

int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options)
{AVFormatContext *s = *ps;int i, ret = 0;AVDictionary *tmp = NULL;ID3v2ExtraMeta *id3v2_extra_meta = NULL;if (!s && !(s = avformat_alloc_context()))return AVERROR(ENOMEM);if (!s->av_class) {av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");return AVERROR(EINVAL);}if (fmt)s->iformat = fmt;if (options)av_dict_copy(&tmp, *options, 0);if (s->pb) // must be before any goto fails->flags |= AVFMT_FLAG_CUSTOM_IO;if ((ret = av_opt_set_dict(s, &tmp)) < 0)goto fail;if (!(s->url = av_strdup(filename ? filename : ""))) {ret = AVERROR(ENOMEM);goto fail;}#if FF_API_FORMAT_FILENAME
FF_DISABLE_DEPRECATION_WARNINGSav_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
FF_ENABLE_DEPRECATION_WARNINGS
#endifif ((ret = init_input(s, filename, &tmp)) < 0)goto fail;s->probe_score = ret;if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);if (!s->protocol_whitelist) {ret = AVERROR(ENOMEM);goto fail;}}if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);if (!s->protocol_blacklist) {ret = AVERROR(ENOMEM);goto fail;}}if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);ret = AVERROR(EINVAL);goto fail;}avio_skip(s->pb, s->skip_initial_bytes);/* Check filename in case an image number is expected. */if (s->iformat->flags & AVFMT_NEEDNUMBER) {if (!av_filename_number_test(filename)) {ret = AVERROR(EINVAL);goto fail;}}s->duration = s->start_time = AV_NOPTS_VALUE;/* 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;}}/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */if (s->pb)ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);#if FF_API_DEMUXER_OPENif (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
#elseif (s->iformat->read_header)
#endifif ((ret = s->iformat->read_header(s)) < 0)goto fail;if (!s->metadata) {s->metadata = s->internal->id3v2_meta;s->internal->id3v2_meta = NULL;} else if (s->internal->id3v2_meta) {av_log(s, AV_LOG_WARNING, "Discarding ID3 tags because more suitable tags were found.\n");av_dict_free(&s->internal->id3v2_meta);}if (id3v2_extra_meta) {if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||!strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0)goto close;if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0)goto close;if ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < 0)goto close;} elseav_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");}ff_id3v2_free_extra_meta(&id3v2_extra_meta);if ((ret = avformat_queue_attached_pictures(s)) < 0)goto close;#if FF_API_DEMUXER_OPENif (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
#elseif (s->pb && !s->internal->data_offset)
#endifs->internal->data_offset = avio_tell(s->pb);s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;update_stream_avctx(s);for (i = 0; i < s->nb_streams; i++)s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;if (options) {av_dict_free(options);*options = tmp;}*ps = s;return 0;close:if (s->iformat->read_close)s->iformat->read_close(s);
fail:ff_id3v2_free_extra_meta(&id3v2_extra_meta);av_dict_free(&tmp);if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))avio_closep(&s->pb);avformat_free_context(s);*ps = NULL;return ret;
}

分析可以查看雷神的这篇文章:
FFmpeg源代码简单分析:avformat_open_input()
可以看到,若打开文件失败,或是分配资源失败等等,都会将传入的AVFormatContext的对象置为NULL.

3、avcodec_alloc_context3

avcodec_alloc_context3 是 FFmpeg 库中的一个函数,用于分配并初始化一个 AVCodecContext 结构体,该结构体用于编解码器的相关信息和参数设置。
AVCodecContext 结构体是 FFmpeg 库中最为重要的结构之一,它包含了编解码器的相关信息,例如编码器类型、编码器参数、图像尺寸、音频采样率等。通过 AVCodecContext,你可以对媒体数据进行编解码操作,并设置编解码器的各种参数。
使用 avcodec_alloc_context3 函数可以创建一个空的 AVCodecContext 结构体,并将其内部字段初始化为默认值。
需要注意的是,分配的 AVCodecContext 结构体在使用完毕后,需要通过调用 avcodec_free_context 函数进行释放,以避免内存泄漏。
总而言之,avcodec_alloc_context3 函数用于分配并初始化 AVCodecContext 结构体,为后续的编解码操作提供基础。你可以使用该函数创建一个编解码器上下文,并设置相应的参数,以便进行媒体数据的编解码处理。

4、avcodec_parameters_to_context

1、用于将AVCodecParameters结构体的值赋值给AVCodecContext结构体的对应字段。AVCodecParameters结构体包含了编解码器相关的参数信息,而AVCodecContext结构体则是编解码器的上下文环境。
2、该函数会根据AVCodecParameters结构体中的值,更新AVCodecContext结构体中对应的字段。参数值的赋值涉及到了编解码器的基本配置,例如编码类型、码率、帧率等等。这样可以方便地将参数信息传递给编解码器上下文,以供后续的编解码操作使用。

5、avformat_alloc_output_context2

参数解析:

  • AVFormatContext **ctx: 输出参数,指向分配的输出格式上下文的指针。
  • AVOutputFormat *oformat: 输出参数,指定要分配的输出格式。
  • const char *format_name: 可选参数,用于指定输出格式的短名称。如果为NULL,则根据输出文件的扩展名自动推断。
  • const char *filename: 输出文件的路径或URL。

1、首先,该函数会尝试通过指定的输出格式来分配一个输出格式上下文(AVFormatContext)。如果format_name参数为NULL,函数会尝试根据输出文件的扩展名猜测输出格式。然后,分配的上下文将被赋值给ctx指针。
2、在成功分配输出格式上下文后,我们可以继续设置该上下文的各种参数,如音视频编码器、封装格式的选项等。

6、avformat_new_stream

avformat_new_stream是FFmpeg库中的一个函数,用于创建一个新的音视频流(stream)并将其添加到封装格式上下文(AVFormatContext)中。

7、avcodec_parameters_from_context

用于从已存在的编码器上下文(AVCodecContext)中获取编码器参数。

END、总结的知识与问题

参考

1、FFmpeg写入输出文件—FFmpeg API教程

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

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

相关文章

技术阅读周刊第十期

技术阅读周刊&#xff0c;每周更新。 周四加了个班&#xff0c;周五没缓过来&#xff0c;就推迟到今天更新了 历史更新 20231117&#xff1a;第六期20231124&#xff1a;第七期20231201&#xff1a;第八期20231215&#xff1a;第九期 Golang: 14 Shorthand Tricks You Might No…

LLMs 玩狼人杀:清华大学验证大模型参与复杂交流博弈游戏的能力

作者&#xff1a;彬彬 编辑&#xff1a;李宝珠&#xff0c;三羊 清华大学研究团队提出了一种用于交流游戏的框架&#xff0c;展示了大语言模型从经验中学习的能力&#xff0c;还发现大语言模型具有非预编程的策略行为&#xff0c;如信任、对抗、伪装和领导力。 近年来&#x…

设计模式——迭代器模式

引言 迭代器模式是一种行为设计模式&#xff0c; 让你能在不暴露集合底层表现形式 &#xff08;列表、 栈和树等&#xff09; 的情况下遍历集合中所有的元素。 问题 集合是编程中最常使用的数据类型之一。 尽管如此&#xff0c; 集合只是一组对象的容器而已。 大部分集合使用…

Spring 原理(一)

Spring 原理 它是一个全面的、企业应用开发一站式的解决方案&#xff0c;贯穿表现层、业务层、持久层。但是 Spring仍然可以和其他的框架无缝整合。 Spring 特点 轻量级控制反转面向切面容器框架集合 Spring 核心组件 Spring 常用模块 Spring 主要包 Spring 常用注解 bean …

达索系统SOLIDWORKS 2024 云服务新功能

“云服务 是基于互联网的相关服务的增加、使用和交互模式&#xff0c;通常涉及通过互联网来提供动态易扩展且经常是虚拟化的资源。 云是网络、互联网的一种比喻说法。过去在图中往往用云来表示电信网&#xff0c;后来也用来表示互联网和底层基础设施的抽象。云服务指通过网络以…

小贴士:知道方程的解如何求通解

1.思路&#xff1a;认知&#xff1a;题中的所有解都是特解&#xff0c;解的形式为kxb 1.如何求通解&#xff1a;a1a2是题目中提供的条件&#xff0c;根据认知它们的和是2b&#xff0c;所以b等于a1a3除2&#xff0c;而有一条认知&#xff0c;为两个特解的差为通解向量&#xff0…

python绘图总结

1 二维图像 1.1 二维曲线 plot(x, y, ls"-", lw1.5, labelNone)x, y&#xff1a;横坐标和纵坐标ls&#xff1a;颜色、点标记、线型列表&#xff0c;如 ls‘r*-’ 表示红色实线、*形点&#xff0c;ls‘g.’ 表示绿色散点lw&#xff1a;线宽度label&#xff1a;线标签…

波奇学Linux:进程等待

僵尸进程(Z状态)无法被kill指令杀死&#xff0c;通过进程等待杀掉它&#xff0c;解决内存泄漏问题&#xff08;进程处于僵尸态&#xff0c;仍然维护pcb结构体来解决问题&#xff09; 通过进程等待&#xff0c;获得进程退出情况 wait回收僵尸态进程 我们可以看到进程由五秒后子…

JavaScript高频题整理(附答案背诵版)

1、简述JavaScript中map和foreach的区别&#xff1f; map和forEach都是JavaScript数组的迭代方法&#xff0c;但它们之间存在一些关键区别。 返回值&#xff1a;map方法会返回一个新的数组&#xff0c;这个新数组是由原数组通过某个函数处理后的结果组成的。而forEach方法则没…

我们应该知道的 10 个高级 JavaScript 技巧

我们应该知道的 10 个高级 JavaScript 技巧 解构赋值 赋值解构是一种从数组或对象中提取值并将其分配给变量的简洁方法。 它简化了您的代码并提高了可读性。对于数组&#xff0c;可以使用括号表示法&#xff0c;对于对象可以使用大括号。 // 解构数组 const [firstItem, sec…

php hyperf 读取redis,存储到数据库

redis中排行榜中的数据 public function execute(string $date){$query ChannelConfig::query();$query->where(module_name, rank_reward);$rewardData $query->first();$rewards [];if( $rewardData ){$rewardContents $rewardData->content;foreach ($rewardC…

java.lang.NumberFormatException

1. 请解释NumberFormatException是什么&#xff1f; NumberFormatException是Java中的一个异常类&#xff0c;表示在将字符串转换为数字时出现了格式错误。当使用Integer.parseInt()、Double.parseDouble()等方法尝试将一个字符串转换为整数或浮点数时&#xff0c;如果该字符串…

VMware----基于 VMware 玩转 CentOS 虚拟机创建、克隆以及配置后台运行

查看原文 文章目录 一、安装 Vmware二、创建 CentOS7 系统的虚拟机三、克隆虚拟机四、设置虚拟机后台运行 一、安装 Vmware &#xff08;1&#xff09;打开VMware下载地址页面&#xff0c;滑动页面&#xff0c;找到如下界面&#xff0c;点击【下载】 &#xff08;2&#xff…

vscode如何开发微信小程序?JS与TS的主要区别?

要在 VS Code 中编写微信小程序代码并同步到 Git&#xff0c;需要安装以下插件&#xff1a; 1. 微信小程序插件&#xff08;WeChat Mini Program&#xff09;&#xff1a;此插件提供了微信小程序的语法高亮、代码提示、调试、上传等功能。 2. Git 插件&#xff08;GitLens、…

光伏电站的投资回报计算工具有哪些?

随着全球能源需求的增长和环境意识的提高&#xff0c;光伏发电作为一种可再生能源形式逐渐受到人们的关注。在短短几十年的时间里&#xff0c;光伏发电已经取得了显著的发展。光伏发电作为一项长期的投资&#xff0c;在建设时肯定会考虑投资回报问题。光伏电站的投资回报计算工…

本地电商平台商业模式 同城实体店引流获客方法

本地电商平台的商业模式通常是基于在线市场交易的。这样的平台允许本地商家在其上发布商品信息&#xff0c;消费者可以在平台上选择购买&#xff0c;并直接向卖家付款。商家可以通过平台获得更广泛的市场覆盖和销售机会&#xff0c;同时消费者也可以享受更加便利和多样化的购物…

RPC(3):HttpClient实现RPC之GET请求

1HttpClient简介 在JDK中java.net包下提供了用户HTTP访问的基本功能&#xff0c;但是它缺少灵活性或许多应用所需要的功能。 HttpClient起初是Apache Jakarta Common 的子项目。用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包&#xff0c;并且它支持 H…

Neural Network——神经网络

1.feature reusing——特征复用 1.1 什么是特征复用 回顾我们之前所学习的模型&#xff0c;本质上都是基于线性回归&#xff0c;但却都可以运用于非线性相关的数据&#xff0c;包括使用了如下方法 增加更多的特征产生新的特征&#xff08;多项式回归&#xff09;核函数 在本身…

CCF编程能力等级认证GESP—C++3级—20230611

CCF编程能力等级认证GESP—C3级—20230611 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)春游密码合规检测 答案及解析单选题判断题编程题1编程题2 单选题…

Spring IOC 原理(二)

Spring IOC 原理 概念 Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系&#xff0c;利用 Java 语言的反射功能实例化Bean 并建立 Bean 之间的依赖关系。 Spring 的 IoC 容器在完成这些底层工作的基础上&#xff0c;还提供了 Bean 实例缓存、生命周期管理、 Bean 实…