【FFmpeg】avcodec_open2函数
目录
- 1. avcodec_open2
- 1.1 编解码器的预初始化(ff_encode_preinit & ff_decode_preinit)
- 1.2 编解码器的初始化(init)
- 1.3 释放编解码器(ff_codec_close)
FFmpeg相关记录:
示例工程:
【FFmpeg】调用ffmpeg库实现264软编
【FFmpeg】调用ffmpeg库实现264软解
【FFmpeg】调用ffmpeg库进行RTMP推流和拉流
【FFmpeg】调用ffmpeg库进行SDL2解码后渲染
流程分析:
【FFmpeg】编码链路上主要函数的简单分析
【FFmpeg】解码链路上主要函数的简单分析
结构体分析:
【FFmpeg】AVCodec结构体
【FFmpeg】AVCodecContext结构体
【FFmpeg】AVStream结构体
【FFmpeg】AVFormatContext结构体
【FFmpeg】AVIOContext结构体
【FFmpeg】AVPacket结构体
函数分析:
【通用】
【FFmpeg】avcodec_find_encoder和avcodec_find_decoder
【FFmpeg】关键结构体的初始化和释放(AVFormatContext、AVIOContext等)
【推流】
【FFmpeg】avformat_open_input函数
【FFmpeg】avformat_find_stream_info函数
【FFmpeg】avformat_alloc_output_context2函数
【FFmpeg】avio_open2函数
【FFmpeg】avformat_write_header函数
【FFmpeg】av_write_frame函数
avcodec_open2的函数调用关系为

1. avcodec_open2
/*** Initialize the AVCodecContext to use the given AVCodec. Prior to using this* function the context has to be allocated with avcodec_alloc_context3().** The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),* avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for* retrieving a codec.** Depending on the codec, you might need to set options in the codec context* also for decoding (e.g. width, height, or the pixel or audio sample format in* the case the information is not available in the bitstream, as when decoding* raw audio or video).** Options in the codec context can be set either by setting them in the options* AVDictionary, or by setting the values in the context itself, directly or by* using the av_opt_set() API before calling this function.** Example:* @code* av_dict_set(&opts, "b", "2.5M", 0);* codec = avcodec_find_decoder(AV_CODEC_ID_H264);* if (!codec)* exit(1);** context = avcodec_alloc_context3(codec);** if (avcodec_open2(context, codec, opts) < 0)* exit(1);* @endcode** In the case AVCodecParameters are available (e.g. when demuxing a stream* using libavformat, and accessing the AVStream contained in the demuxer), the* codec parameters can be copied to the codec context using* avcodec_parameters_to_context(), as in the following example:** @code* AVStream *stream = ...;* context = avcodec_alloc_context3(codec);* if (avcodec_parameters_to_context(context, stream->codecpar) < 0)* exit(1);* if (avcodec_open2(context, codec, NULL) < 0)* exit(1);* @endcode** @note Always call this function before using decoding routines (such as* @ref avcodec_receive_frame()).** @param avctx The context to initialize.* @param codec The codec to open this context for. If a non-NULL codec has been* previously passed to avcodec_alloc_context3() or* for this context, then this parameter MUST be either NULL or* equal to the previously passed codec.* @param options A dictionary filled with AVCodecContext and codec-private* options, which are set on top of the options already set in* avctx, can be NULL. On return this object will be filled with* options that were not found in the avctx codec context.** @return zero on success, a negative value on error* @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),* av_dict_set(), av_opt_set(), av_opt_find(), avcodec_parameters_to_context()*/
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
函数的定义位于libavcodec\avcodec.c中,从注释上看,主要的功能是使用给定的AVCodec情况下,来初始化AVCodecContext。在使用此函数之前,必须使用avcodec_alloc_context3来分配上下文,可以在外面配置options,作为codec初始化的参数
int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{int ret = 0;AVCodecInternal *avci;const FFCodec *codec2;// 1.输入检查// 已经打开,直接返回if (avcodec_is_open(avctx))return 0;// 一些错误信息打印if (!codec && !avctx->codec) {av_log(avctx, AV_LOG_ERROR, "No codec provided to avcodec_open2()\n");return AVERROR(EINVAL);}if (codec && avctx->codec && codec != avctx->codec) {av_log(avctx, AV_LOG_ERROR, "This AVCodecContext was allocated for %s, ""but %s passed to avcodec_open2()\n", avctx->codec->name, codec->name);return AVERROR(EINVAL);}if (!codec)codec = avctx->codec;codec2 = ffcodec(codec);if ((avctx->codec_type != AVMEDIA_TYPE_UNKNOWN && avctx->codec_type != codec->type) ||(avctx->codec_id != AV_CODEC_ID_NONE && avctx->codec_id != codec->id)) {av_log(avctx, AV_LOG_ERROR, "Codec type or id mismatches\n");return AVERROR(EINVAL);}avctx->codec_type = codec->type;avctx->codec_id = codec->id;avctx->codec = codec;if (avctx->extradata_size < 0 || avctx->extradata_size >= FF_MAX_EXTRADATA_SIZE)return AVERROR(EINVAL);// 2.部分结构体内存分配avci = av_codec_is_decoder(codec) ?ff_decode_internal_alloc() :ff_encode_internal_alloc();if (!avci) {ret = AVERROR(ENOMEM);goto end;}avctx->internal = avci;avci->buffer_frame = av_frame_alloc();avci->buffer_pkt = av_packet_alloc();if (!avci->buffer_frame || !avci->buffer_pkt) {ret = AVERROR(ENOMEM);goto free_and_end;}if (codec2->priv_data_size > 0) {if (!avctx->priv_data) {avctx->priv_data = av_mallocz(codec2->priv_data_size);if (!avctx->priv_data) {ret = AVERROR(ENOMEM);goto free_and_end;}if (codec->priv_class) {*(const AVClass **)avctx->priv_data = codec->priv_class;av_opt_set_defaults(avctx->priv_data);}}if (codec->priv_class && (ret = av_opt_set_dict(avctx->priv_data, options)) < 0)goto free_and_end;} else {avctx->priv_data = NULL;}if ((ret = av_opt_set_dict(avctx, options)) < 0)goto free_and_end;// 3.一些琐碎的检查// 白名单的检查if (avctx->codec_whitelist && av_match_list(codec->name, avctx->codec_whitelist, ',') <= 0) {av_log(avctx, AV_LOG_ERROR, "Codec (%s) not on whitelist \'%s\'\n", codec->name, avctx->codec_whitelist);ret = AVERROR(EINVAL);goto free_and_end;}// only call ff_set_dimensions() for non H.264/VP6F/DXV codecs so as not to overwrite previously setup dimensions// 对于非H.264/VP6F/DXV编解码器,只调用ff_set_dimensions(),以免覆盖先前设置的尺寸if (!(avctx->coded_width && avctx->coded_height && avctx->width && avctx->height &&(avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_VP6F || avctx->codec_id == AV_CODEC_ID_DXV))) {if (avctx->coded_width && avctx->coded_height)ret = ff_set_dimensions(avctx, avctx->coded_width, avctx->coded_height);else if (avctx->width && avctx->height)ret = ff_set_dimensions(avctx, avctx->width, avctx->height);if (ret < 0)goto free_and_end;}// 检查宽高if ((avctx->coded_width || avctx->coded_height || avctx->width || avctx->height)&& ( av_image_check_size2(avctx->coded_width, avctx->coded_height, avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx) < 0|| av_image_check_size2(avctx->width, avctx->height, avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx) < 0)) {av_log(avctx, AV_LOG_WARNING, "Ignoring invalid width/height values\n");ff_set_dimensions(avctx, 0, 0);}// 检查宽高比if (avctx->width > 0 && avctx->height > 0) {if (av_image_check_sar(avctx->width, avctx->height,avctx->sample_aspect_ratio) < 0) {av_log(avctx, AV_LOG_WARNING, "ignoring invalid SAR: %u/%u\n",avctx->sample_aspect_ratio.num,avctx->sample_aspect_ratio.den);avctx->sample_aspect_ratio = (AVRational){ 0, 1 };}}// 检查采样率if (avctx->sample_rate < 0) {av_log(avctx, AV_LOG_ERROR, "Invalid sample rate: %d\n", avctx->sample_rate);ret = AVERROR(EINVAL);goto free_and_end;}if (avctx->block_align < 0) {av_log(avctx, AV_LOG_ERROR, "Invalid block align: %d\n", avctx->block_align);ret = AVERROR(EINVAL);goto free_and_end;}/* AV_CODEC_CAP_CHANNEL_CONF is a decoder-only flag; so the code below* in particular checks that nb_channels is set for all audio encoders. */if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && !avctx->ch_layout.nb_channels&& !(codec->capabilities & AV_CODEC_CAP_CHANNEL_CONF)) {av_log(avctx, AV_LOG_ERROR, "%s requires channel layout to be set\n",av_codec_is_decoder(codec) ? "Decoder" : "Encoder");ret = AVERROR(EINVAL);goto free_and_end;}// 检查音频的声道数if (avctx->ch_layout.nb_channels && !av_channel_layout_check(&avctx->ch_layout)) {av_log(avctx, AV_LOG_ERROR, "Invalid channel layout\n");ret = AVERROR(EINVAL);goto free_and_end;}if (avctx->ch_layout.nb_channels > FF_SANE_NB_CHANNELS) {av_log(avctx, AV_LOG_ERROR, "Too many channels: %d\n", avctx->ch_layout.nb_channels);ret = AVERROR(EINVAL);goto free_and_end;}avctx->frame_num = 0;avctx->codec_descriptor = avcodec_descriptor_get(avctx->codec_id);// 如果codec的能力包括了experimentalif ((avctx->codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) &&avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {const char *codec_string = av_codec_is_encoder(codec) ? "encoder" : "decoder";const AVCodec *codec2;av_log(avctx, AV_LOG_ERROR,"The %s '%s' is experimental but experimental codecs are not enabled, ""add '-strict %d' if you want to use it.\n",codec_string, codec->name, FF_COMPLIANCE_EXPERIMENTAL);codec2 = av_codec_is_encoder(codec) ? avcodec_find_encoder(codec->id) : avcodec_find_decoder(codec->id);if (!(codec2->capabilities & AV_CODEC_CAP_EXPERIMENTAL))av_log(avctx, AV_LOG_ERROR, "Alternatively use the non experimental %s '%s'.\n",codec_string, codec2->name);ret = AVERROR_EXPERIMENTAL;goto free_and_end;}if (avctx->codec_type == AVMEDIA_TYPE_AUDIO &&(!avctx->time_base.num || !avctx->time_base.den)) {avctx->time_base.num = 1;avctx->time_base.den = avctx->sample_rate;}// 4.编解码器的预初始化if (av_codec_is_encoder(avctx->codec))ret = ff_encode_preinit(avctx);elseret = ff_decode_preinit(avctx);if (ret < 0)goto free_and_end;if (HAVE_THREADS && !avci->frame_thread_encoder) {/* Frame-threaded decoders call FFCodec.init for their child contexts. */lock_avcodec(codec2);ret = ff_thread_init(avctx);unlock_avcodec(codec2);if (ret < 0) {goto free_and_end;}}if (!HAVE_THREADS && !(codec2->caps_internal & FF_CODEC_CAP_AUTO_THREADS))avctx->thread_count = 1;if (!(avctx->active_thread_type & FF_THREAD_FRAME) ||avci->frame_thread_encoder) {if (codec2->init) {lock_avcodec(codec2);// 5.编解码器的初始化ret = codec2->init(avctx);unlock_avcodec(codec2);if (ret < 0) {avci->needs_close = codec2->caps_internal & FF_CODEC_CAP_INIT_CLEANUP;goto free_and_end;}}avci->needs_close = 1;}ret=0;// 如果是解码器if (av_codec_is_decoder(avctx->codec)) {if (!avctx->bit_rate)avctx->bit_rate = get_bit_rate(avctx);/* validate channel layout from the decoder */// 从解码器验证声道布局if ((avctx->ch_layout.nb_channels && !av_channel_layout_check(&avctx->ch_layout)) ||avctx->ch_layout.nb_channels > FF_SANE_NB_CHANNELS) {ret = AVERROR(EINVAL);goto free_and_end;}if (avctx->bits_per_coded_sample < 0) {ret = AVERROR(EINVAL);goto free_and_end;}}if (codec->priv_class)av_assert0(*(const AVClass **)avctx->priv_data == codec->priv_class);end:return ret;
free_and_end:// 6.创建失败则释放codecff_codec_close(avctx);goto end;
}
从代码中看,函数主要的流程分为几个步骤:
(1)一些检查,例如codec是否给入,检查codec type等等
(2)部分结构体内存的分配
(3)一些琐碎的检查
(4)编解码器的预初始化(ff_encode_preinit和ff_decode_preinit)
(5)编解码器的初始化(codec2->init)
(6)创建失败则释放codec(ff_codec_close)
1.1 编解码器的预初始化(ff_encode_preinit & ff_decode_preinit)
两个预初始化函数都定义在libavcodec\encode.c中
(1)编码器的预初始化(ff_encode_preinit)
/** Perform encoder initialization and validation.* Called when opening the encoder, before the FFCodec.init() call.*/
// 执行编码器初始化和验证
// 在打开编码器时调用,在FFCodec.init()调用之前
int ff_encode_preinit(AVCodecContext *avctx)
{AVCodecInternal *avci = avctx->internal;EncodeContext *ec = encode_ctx(avci);int ret = 0;// 检查编码器的时间基if (avctx->time_base.num <= 0 || avctx->time_base.den <= 0) {av_log(avctx, AV_LOG_ERROR, "The encoder timebase is not set.\n");return AVERROR(EINVAL);}if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE &&!(avctx->codec->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE)) {av_log(avctx, AV_LOG_ERROR, "The copy_opaque flag is set, but the ""encoder does not support it.\n");return AVERROR(EINVAL);}switch (avctx->codec_type) {case AVMEDIA_TYPE_VIDEO: ret = encode_preinit_video(avctx); break; // 视频预初始化case AVMEDIA_TYPE_AUDIO: ret = encode_preinit_audio(avctx); break;}if (ret < 0)return ret;// 检查码率是否太小if ( (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO)&& avctx->bit_rate>0 && avctx->bit_rate<1000) {av_log(avctx, AV_LOG_WARNING, "Bitrate %"PRId64" is extremely low, maybe you mean %"PRId64"k\n", avctx->bit_rate, avctx->bit_rate);}if (!avctx->rc_initial_buffer_occupancy)avctx->rc_initial_buffer_occupancy = avctx->rc_buffer_size * 3LL / 4;// AV_CODEC_PROP_INTRA_ONLY表示只进行帧内压缩,仅用于video和audioif (avctx->codec_descriptor->props & AV_CODEC_PROP_INTRA_ONLY)ec->intra_only_flag = AV_PKT_FLAG_KEY;// FF_CODEC_CB_TYPE_ENCODER表示编解码器是使用encode回调的编码器;仅限音频和视频编解码器if (ffcodec(avctx->codec)->cb_type == FF_CODEC_CB_TYPE_ENCODE) {avci->in_frame = av_frame_alloc();if (!avci->in_frame)return AVERROR(ENOMEM);}// AV_CODEC_FLAG_RECON_FRAME表示请求编码器输出重构的帧if ((avctx->flags & AV_CODEC_FLAG_RECON_FRAME)) {if (!(avctx->codec->capabilities & AV_CODEC_CAP_ENCODER_RECON_FRAME)) {av_log(avctx, AV_LOG_ERROR, "Reconstructed frame output requested ""from an encoder not supporting it\n");return AVERROR(ENOSYS);}avci->recon_frame = av_frame_alloc();if (!avci->recon_frame)return AVERROR(ENOMEM);}if (CONFIG_FRAME_THREAD_ENCODER) {ret = ff_frame_thread_encoder_init(avctx);if (ret < 0)return ret;}return 0;
}
上面的代码中,主要是进行一些检查和配置,如时间基,码率,编码flag等,还会调用encode_preinit_video进行编码信息的预初始化,如下所示,主要进行了pix fmt的检查和color_range的设置
static int encode_preinit_video(AVCodecContext *avctx)
{const AVCodec *c = avctx->codec;const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(avctx->pix_fmt);int i;if (!av_get_pix_fmt_name(avctx->pix_fmt)) {av_log(avctx, AV_LOG_ERROR, "Invalid video pixel format: %d\n",avctx->pix_fmt);return AVERROR(EINVAL);}// 检查编码格式if (c->pix_fmts) {for (i = 0; c->pix_fmts[i] != AV_PIX_FMT_NONE; i++)if (avctx->pix_fmt == c->pix_fmts[i])break;if (c->pix_fmts[i] == AV_PIX_FMT_NONE) {// 编码格式为NONEav_log(avctx, AV_LOG_ERROR,"Specified pixel format %s is not supported by the %s encoder.\n",av_get_pix_fmt_name(avctx->pix_fmt), c->name);// 输出支持的formatav_log(avctx, AV_LOG_ERROR, "Supported pixel formats:\n");for (int p = 0; c->pix_fmts[p] != AV_PIX_FMT_NONE; p++) {av_log(avctx, AV_LOG_ERROR, " %s\n",av_get_pix_fmt_name(c->pix_fmts[p]));}return AVERROR(EINVAL);}// 设置color_rangeif (c->pix_fmts[i] == AV_PIX_FMT_YUVJ420P ||c->pix_fmts[i] == AV_PIX_FMT_YUVJ411P ||c->pix_fmts[i] == AV_PIX_FMT_YUVJ422P ||c->pix_fmts[i] == AV_PIX_FMT_YUVJ440P ||c->pix_fmts[i] == AV_PIX_FMT_YUVJ444P)avctx->color_range = AVCOL_RANGE_JPEG;}// bit depth的检查if ( avctx->bits_per_raw_sample < 0|| (avctx->bits_per_raw_sample > 8 && pixdesc->comp[0].depth <= 8)) {av_log(avctx, AV_LOG_WARNING, "Specified bit depth %d not possible with the specified pixel formats depth %d\n",avctx->bits_per_raw_sample, pixdesc->comp[0].depth);avctx->bits_per_raw_sample = pixdesc->comp[0].depth;}if (avctx->width <= 0 || avctx->height <= 0) {av_log(avctx, AV_LOG_ERROR, "dimensions not set\n");return AVERROR(EINVAL);}#if FF_API_TICKS_PER_FRAME
FF_DISABLE_DEPRECATION_WARNINGSif (avctx->ticks_per_frame && avctx->time_base.num &&avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) {av_log(avctx, AV_LOG_ERROR,"ticks_per_frame %d too large for the timebase %d/%d.",avctx->ticks_per_frame,avctx->time_base.num,avctx->time_base.den);return AVERROR(EINVAL);}
FF_ENABLE_DEPRECATION_WARNINGS
#endifif (avctx->hw_frames_ctx) {AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;if (frames_ctx->format != avctx->pix_fmt) {av_log(avctx, AV_LOG_ERROR,"Mismatching AVCodecContext.pix_fmt and AVHWFramesContext.format\n");return AVERROR(EINVAL);}// 软编格式的检查if (avctx->sw_pix_fmt != AV_PIX_FMT_NONE &&avctx->sw_pix_fmt != frames_ctx->sw_format) {av_log(avctx, AV_LOG_ERROR,"Mismatching AVCodecContext.sw_pix_fmt (%s) ""and AVHWFramesContext.sw_format (%s)\n",av_get_pix_fmt_name(avctx->sw_pix_fmt),av_get_pix_fmt_name(frames_ctx->sw_format));return AVERROR(EINVAL);}avctx->sw_pix_fmt = frames_ctx->sw_format;}return 0;
}
(2)解码器的预初始化(ff_decode_preinit)
int ff_decode_preinit(AVCodecContext *avctx)
{AVCodecInternal *avci = avctx->internal;DecodeContext *dc = decode_ctx(avci);int ret = 0;/* if the decoder init function was already called previously,* free the already allocated subtitle_header before overwriting it */// 如果先前已经调用了解码器init函数,则在覆盖之前释放已经分配的subtitle_headerav_freep(&avctx->subtitle_header);// lowres的检查if (avctx->codec->max_lowres < avctx->lowres || avctx->lowres < 0) {av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",avctx->codec->max_lowres);avctx->lowres = avctx->codec->max_lowres;}// sub_charenc表示输入字幕文件的字符编码if (avctx->sub_charenc) {if (avctx->codec_type != AVMEDIA_TYPE_SUBTITLE) {av_log(avctx, AV_LOG_ERROR, "Character encoding is only ""supported with subtitles codecs\n");return AVERROR(EINVAL);} else if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) {av_log(avctx, AV_LOG_WARNING, "Codec '%s' is bitmap-based, ""subtitles character encoding will be ignored\n",avctx->codec_descriptor->name);avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_DO_NOTHING;} else {/* input character encoding is set for a text based subtitle* codec at this point */if (avctx->sub_charenc_mode == FF_SUB_CHARENC_MODE_AUTOMATIC)avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_PRE_DECODER;if (avctx->sub_charenc_mode == FF_SUB_CHARENC_MODE_PRE_DECODER) {
#if CONFIG_ICONViconv_t cd = iconv_open("UTF-8", avctx->sub_charenc);if (cd == (iconv_t)-1) {ret = AVERROR(errno);av_log(avctx, AV_LOG_ERROR, "Unable to open iconv context ""with input character encoding \"%s\"\n", avctx->sub_charenc);return ret;}iconv_close(cd);
#elseav_log(avctx, AV_LOG_ERROR, "Character encoding subtitles ""conversion needs a libavcodec built with iconv support ""for this codec\n");return AVERROR(ENOSYS);
#endif}}}dc->pts_correction_num_faulty_pts =dc->pts_correction_num_faulty_dts = 0;dc->pts_correction_last_pts =dc->pts_correction_last_dts = INT64_MIN;if ( !CONFIG_GRAY && avctx->flags & AV_CODEC_FLAG_GRAY&& avctx->codec_descriptor->type == AVMEDIA_TYPE_VIDEO)av_log(avctx, AV_LOG_WARNING,"gray decoding requested but not enabled at configuration time\n");if (avctx->flags2 & AV_CODEC_FLAG2_EXPORT_MVS) {avctx->export_side_data |= AV_CODEC_EXPORT_DATA_MVS;}if (avctx->nb_side_data_prefer_packet == 1 &&avctx->side_data_prefer_packet[0] == -1)dc->side_data_pref_mask = ~0ULL;else {// 检查side datafor (unsigned i = 0; i < avctx->nb_side_data_prefer_packet; i++) {int val = avctx->side_data_prefer_packet[i];if (val < 0 || val >= AV_PKT_DATA_NB) {av_log(avctx, AV_LOG_ERROR, "Invalid side data type: %d\n", val);return AVERROR(EINVAL);}for (unsigned j = 0; j < FF_ARRAY_ELEMS(sd_global_map); j++) {if (sd_global_map[j].packet == val) {val = sd_global_map[j].frame;// this code will need to be changed when we have more than// 64 frame side data typesif (val >= 64) {av_log(avctx, AV_LOG_ERROR, "Side data type too big\n");return AVERROR_BUG;}dc->side_data_pref_mask |= 1ULL << val;}}}}// 分配pkt的内存avci->in_pkt = av_packet_alloc();avci->last_pkt_props = av_packet_alloc();if (!avci->in_pkt || !avci->last_pkt_props)return AVERROR(ENOMEM);// 解码的bitstream filter初始化ret = decode_bsfs_init(avctx);if (ret < 0)return ret;#if FF_API_DROPCHANGEDif (avctx->flags & AV_CODEC_FLAG_DROPCHANGED)av_log(avctx, AV_LOG_WARNING, "The dropchanged flag is deprecated.\n");
#endifreturn 0;
}
1.2 编解码器的初始化(init)
在进行了前面的参数检查和配置之后,需要进行编解码器的初始化,这是依据具体编解码器进行的,参考雷博的文章,记录一下libx264的初始化过程,参考下面的代码,初始化使用的是X264_init()函数
FFCodec ff_libx264_encoder = {.p.name = "libx264",CODEC_LONG_NAME("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.p.type = AVMEDIA_TYPE_VIDEO,.p.id = AV_CODEC_ID_H264,.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |AV_CODEC_CAP_OTHER_THREADS |AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE |AV_CODEC_CAP_ENCODER_FLUSH |AV_CODEC_CAP_ENCODER_RECON_FRAME,.p.priv_class = &x264_class,.p.wrapper_name = "libx264",.priv_data_size = sizeof(X264Context),.init = X264_init,FF_CODEC_ENCODE_CB(X264_frame),.flush = X264_flush,.close = X264_close,.defaults = x264_defaults,
#if X264_BUILD < 153.init_static_data = X264_init_static,
#else.p.pix_fmts = pix_fmts_all,
#endif.caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS
#if X264_BUILD < 158| FF_CODEC_CAP_NOT_INIT_THREADSAFE
#endif,
};
X264_init函数的定义位于libavcodec\libx264.c中,进行x264编码器的初始化,下面配置参数都是x264当中一些参数,包括帧间预测、码控、编码profile、编码level等等一些信息的初始化
static av_cold int X264_init(AVCodecContext *avctx)
{X264Context *x4 = avctx->priv_data;AVCPBProperties *cpb_props;int sw,sh;int ret;if (avctx->global_quality > 0)av_log(avctx, AV_LOG_WARNING, "-qscale is ignored, -crf is recommended.\n");#if CONFIG_LIBX262_ENCODERif (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) {x4->params.b_mpeg2 = 1;x264_param_default_mpeg2(&x4->params);} else
#endif// 配置默认的参数,x4从actx->priv_data中来x264_param_default(&x4->params);// 是否进行环路滤波x4->params.b_deblocking_filter = avctx->flags & AV_CODEC_FLAG_LOOP_FILTER;// 检查是否配置了preset档位或者是tune优化if (x4->preset || x4->tune)if (x264_param_default_preset(&x4->params, x4->preset, x4->tune) < 0) {int i;av_log(avctx, AV_LOG_ERROR, "Error setting preset/tune %s/%s.\n", x4->preset, x4->tune);av_log(avctx, AV_LOG_INFO, "Possible presets:");for (i = 0; x264_preset_names[i]; i++)av_log(avctx, AV_LOG_INFO, " %s", x264_preset_names[i]);av_log(avctx, AV_LOG_INFO, "\n");av_log(avctx, AV_LOG_INFO, "Possible tunes:");for (i = 0; x264_tune_names[i]; i++)av_log(avctx, AV_LOG_INFO, " %s", x264_tune_names[i]);av_log(avctx, AV_LOG_INFO, "\n");return AVERROR(EINVAL);}if (avctx->level > 0)x4->params.i_level_idc = avctx->level;// log信息配置x4->params.pf_log = X264_log;x4->params.p_log_private = avctx;x4->params.i_log_level = X264_LOG_DEBUG;x4->params.i_csp = convert_pix_fmt(avctx->pix_fmt);
#if X264_BUILD >= 153x4->params.i_bitdepth = av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].depth;
#endifPARSE_X264_OPT("weightp", wpredp);// 码控参数配置if (avctx->bit_rate) {if (avctx->bit_rate / 1000 > INT_MAX || avctx->rc_max_rate / 1000 > INT_MAX) {av_log(avctx, AV_LOG_ERROR, "bit_rate and rc_max_rate > %d000 not supported by libx264\n", INT_MAX);return AVERROR(EINVAL);}x4->params.rc.i_bitrate = avctx->bit_rate / 1000;x4->params.rc.i_rc_method = X264_RC_ABR;}x4->params.rc.i_vbv_buffer_size = avctx->rc_buffer_size / 1000;x4->params.rc.i_vbv_max_bitrate = avctx->rc_max_rate / 1000;x4->params.rc.b_stat_write = avctx->flags & AV_CODEC_FLAG_PASS1;if (avctx->flags & AV_CODEC_FLAG_PASS2) {x4->params.rc.b_stat_read = 1;} else {if (x4->crf >= 0) {x4->params.rc.i_rc_method = X264_RC_CRF;x4->params.rc.f_rf_constant = x4->crf;} else if (x4->cqp >= 0) {x4->params.rc.i_rc_method = X264_RC_CQP;x4->params.rc.i_qp_constant = x4->cqp;}if (x4->crf_max >= 0)x4->params.rc.f_rf_constant_max = x4->crf_max;}if (avctx->rc_buffer_size && avctx->rc_initial_buffer_occupancy > 0 &&(avctx->rc_initial_buffer_occupancy <= avctx->rc_buffer_size)) {x4->params.rc.f_vbv_buffer_init =(float)avctx->rc_initial_buffer_occupancy / avctx->rc_buffer_size;}PARSE_X264_OPT("level", level);if (avctx->i_quant_factor > 0)x4->params.rc.f_ip_factor = 1 / fabs(avctx->i_quant_factor);if (avctx->b_quant_factor > 0)x4->params.rc.f_pb_factor = avctx->b_quant_factor;if (x4->chroma_offset)x4->params.analyse.i_chroma_qp_offset = x4->chroma_offset;if (avctx->gop_size >= 0)x4->params.i_keyint_max = avctx->gop_size;if (avctx->max_b_frames >= 0)x4->params.i_bframe = avctx->max_b_frames;if (x4->scenechange_threshold >= 0)x4->params.i_scenecut_threshold = x4->scenechange_threshold;// 编码质量参数if (avctx->qmin >= 0)x4->params.rc.i_qp_min = avctx->qmin;if (avctx->qmax >= 0)x4->params.rc.i_qp_max = avctx->qmax;if (avctx->max_qdiff >= 0)x4->params.rc.i_qp_step = avctx->max_qdiff;if (avctx->qblur >= 0)x4->params.rc.f_qblur = avctx->qblur; /* temporally blur quants */if (avctx->qcompress >= 0)x4->params.rc.f_qcompress = avctx->qcompress; /* 0.0 => cbr, 1.0 => constant qp */if (avctx->refs >= 0)x4->params.i_frame_reference = avctx->refs;else if (x4->params.i_level_idc > 0) {int i;int mbn = AV_CEIL_RSHIFT(avctx->width, 4) * AV_CEIL_RSHIFT(avctx->height, 4);int scale = X264_BUILD < 129 ? 384 : 1;for (i = 0; i<x264_levels[i].level_idc; i++)if (x264_levels[i].level_idc == x4->params.i_level_idc)x4->params.i_frame_reference = av_clip(x264_levels[i].dpb / mbn / scale, 1, x4->params.i_frame_reference);}// 运动搜索参数配置if (avctx->trellis >= 0)x4->params.analyse.i_trellis = avctx->trellis;if (avctx->me_range >= 0)x4->params.analyse.i_me_range = avctx->me_range;if (x4->noise_reduction >= 0)x4->params.analyse.i_noise_reduction = x4->noise_reduction;if (avctx->me_subpel_quality >= 0)x4->params.analyse.i_subpel_refine = avctx->me_subpel_quality;if (avctx->keyint_min >= 0)x4->params.i_keyint_min = avctx->keyint_min;if (avctx->me_cmp >= 0)x4->params.analyse.b_chroma_me = avctx->me_cmp & FF_CMP_CHROMA;if (x4->aq_mode >= 0)x4->params.rc.i_aq_mode = x4->aq_mode;if (x4->aq_strength >= 0)x4->params.rc.f_aq_strength = x4->aq_strength;PARSE_X264_OPT("psy-rd", psy_rd);PARSE_X264_OPT("deblock", deblock);PARSE_X264_OPT("partitions", partitions);PARSE_X264_OPT("stats", stats);if (x4->psy >= 0)x4->params.analyse.b_psy = x4->psy;if (x4->rc_lookahead >= 0)x4->params.rc.i_lookahead = x4->rc_lookahead;if (x4->weightp >= 0)x4->params.analyse.i_weighted_pred = x4->weightp;if (x4->weightb >= 0)x4->params.analyse.b_weighted_bipred = x4->weightb;if (x4->cplxblur >= 0)x4->params.rc.f_complexity_blur = x4->cplxblur;if (x4->ssim >= 0)x4->params.analyse.b_ssim = x4->ssim;if (x4->intra_refresh >= 0)x4->params.b_intra_refresh = x4->intra_refresh;if (x4->bluray_compat >= 0) {x4->params.b_bluray_compat = x4->bluray_compat;x4->params.b_vfr_input = 0;}if (x4->avcintra_class >= 0)
#if X264_BUILD >= 142x4->params.i_avcintra_class = x4->avcintra_class;
#elseav_log(avctx, AV_LOG_ERROR,"x264 too old for AVC Intra, at least version 142 needed\n");
#endifif (x4->avcintra_class > 200) {
#if X264_BUILD < 164av_log(avctx, AV_LOG_ERROR,"x264 too old for AVC Intra 300/480, at least version 164 needed\n");return AVERROR(EINVAL);
#else/* AVC-Intra 300/480 only supported by Sony XAVC flavor */x4->params.i_avcintra_flavor = X264_AVCINTRA_FLAVOR_SONY;
#endif}// 帧结构if (x4->b_bias != INT_MIN)x4->params.i_bframe_bias = x4->b_bias;if (x4->b_pyramid >= 0)x4->params.i_bframe_pyramid = x4->b_pyramid;if (x4->mixed_refs >= 0)x4->params.analyse.b_mixed_references = x4->mixed_refs;// dct变换if (x4->dct8x8 >= 0)x4->params.analyse.b_transform_8x8 = x4->dct8x8;if (x4->fast_pskip >= 0)x4->params.analyse.b_fast_pskip = x4->fast_pskip;if (x4->aud >= 0)x4->params.b_aud = x4->aud;// 是否启用mbtreeif (x4->mbtree >= 0)x4->params.rc.b_mb_tree = x4->mbtree;if (x4->direct_pred >= 0)x4->params.analyse.i_direct_mv_pred = x4->direct_pred;if (x4->slice_max_size >= 0)x4->params.i_slice_max_size = x4->slice_max_size;if (x4->fastfirstpass)x264_param_apply_fastfirstpass(&x4->params);x4->profile = x4->profile_opt;/* Allow specifying the x264 profile through AVCodecContext. */// 检查profileif (!x4->profile)switch (avctx->profile) {case AV_PROFILE_H264_BASELINE:x4->profile = "baseline";break;case AV_PROFILE_H264_HIGH:x4->profile = "high";break;case AV_PROFILE_H264_HIGH_10:x4->profile = "high10";break;case AV_PROFILE_H264_HIGH_422:x4->profile = "high422";break;case AV_PROFILE_H264_HIGH_444:x4->profile = "high444";break;case AV_PROFILE_H264_MAIN:x4->profile = "main";break;default:break;}if (x4->nal_hrd >= 0)x4->params.i_nal_hrd = x4->nal_hrd;if (x4->motion_est >= 0)x4->params.analyse.i_me_method = x4->motion_est;if (x4->coder >= 0)x4->params.b_cabac = x4->coder;if (x4->b_frame_strategy >= 0)x4->params.i_bframe_adaptive = x4->b_frame_strategy;if (x4->profile)if (x264_param_apply_profile(&x4->params, x4->profile) < 0) {int i;av_log(avctx, AV_LOG_ERROR, "Error setting profile %s.\n", x4->profile);av_log(avctx, AV_LOG_INFO, "Possible profiles:");for (i = 0; x264_profile_names[i]; i++)av_log(avctx, AV_LOG_INFO, " %s", x264_profile_names[i]);av_log(avctx, AV_LOG_INFO, "\n");return AVERROR(EINVAL);}// 长宽和fps的设置x4->params.i_width = avctx->width;x4->params.i_height = avctx->height;av_reduce(&sw, &sh, avctx->sample_aspect_ratio.num, avctx->sample_aspect_ratio.den, 4096);x4->params.vui.i_sar_width = sw;x4->params.vui.i_sar_height = sh;x4->params.i_timebase_den = avctx->time_base.den;x4->params.i_timebase_num = avctx->time_base.num;if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {x4->params.i_fps_num = avctx->framerate.num;x4->params.i_fps_den = avctx->framerate.den;} else {x4->params.i_fps_num = avctx->time_base.den;
FF_DISABLE_DEPRECATION_WARNINGSx4->params.i_fps_den = avctx->time_base.num
#if FF_API_TICKS_PER_FRAME* avctx->ticks_per_frame
#endif;
FF_ENABLE_DEPRECATION_WARNINGS}x4->params.analyse.b_psnr = avctx->flags & AV_CODEC_FLAG_PSNR;x4->params.i_threads = avctx->thread_count;if (avctx->thread_type)x4->params.b_sliced_threads = avctx->thread_type == FF_THREAD_SLICE;x4->params.b_interlaced = avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT;x4->params.b_open_gop = !(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP);x4->params.i_slice_count = avctx->slices;if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED)x4->params.vui.b_fullrange = avctx->color_range == AVCOL_RANGE_JPEG;else if (avctx->pix_fmt == AV_PIX_FMT_YUVJ420P ||avctx->pix_fmt == AV_PIX_FMT_YUVJ422P ||avctx->pix_fmt == AV_PIX_FMT_YUVJ444P)x4->params.vui.b_fullrange = 1;if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED)x4->params.vui.i_colmatrix = avctx->colorspace;if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED)x4->params.vui.i_colorprim = avctx->color_primaries;if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED)x4->params.vui.i_transfer = avctx->color_trc;if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED)x4->params.vui.i_chroma_loc = avctx->chroma_sample_location - 1;handle_side_data(avctx, &x4->params);if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)x4->params.b_repeat_headers = 0;if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME)x4->params.b_full_recon = 1;if(x4->x264opts){const char *p= x4->x264opts;while(p){char param[4096]={0}, val[4096]={0};if(sscanf(p, "%4095[^:=]=%4095[^:]", param, val) == 1){ret = parse_opts(avctx, param, "1");if (ret < 0)return ret;} else {ret = parse_opts(avctx, param, val);if (ret < 0)return ret;}p= strchr(p, ':');if (p) {++p;}}}#if X264_BUILD >= 142/* Separate headers not supported in AVC-Intra mode */if (x4->avcintra_class >= 0)x4->params.b_repeat_headers = 1;
#endif{AVDictionaryEntry *en = NULL;while (en = av_dict_get(x4->x264_params, "", en, AV_DICT_IGNORE_SUFFIX)) {if ((ret = x264_param_parse(&x4->params, en->key, en->value)) < 0) {av_log(avctx, AV_LOG_WARNING,"Error parsing option '%s = %s'.\n",en->key, en->value);
#if X264_BUILD >= 161if (ret == X264_PARAM_ALLOC_FAILED)return AVERROR(ENOMEM);
#endif}}}x4->params.analyse.b_mb_info = x4->mb_info;// update AVCodecContext with x264 parametersavctx->has_b_frames = x4->params.i_bframe ?x4->params.i_bframe_pyramid ? 2 : 1 : 0;if (avctx->max_b_frames < 0)avctx->max_b_frames = 0;avctx->bit_rate = x4->params.rc.i_bitrate*1000LL;// 打开编码器x4->enc = x264_encoder_open(&x4->params);if (!x4->enc)return AVERROR_EXTERNAL;if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {ret = set_extradata(avctx);if (ret < 0)return ret;}// cpb信息配置cpb_props = ff_encode_add_cpb_side_data(avctx);if (!cpb_props)return AVERROR(ENOMEM);cpb_props->buffer_size = x4->params.rc.i_vbv_buffer_size * 1000;cpb_props->max_bitrate = x4->params.rc.i_vbv_max_bitrate * 1000LL;cpb_props->avg_bitrate = x4->params.rc.i_bitrate * 1000LL;// Overestimate the reordered opaque buffer size, in case a runtime// reconfigure would increase the delay (which it shouldn't).x4->nb_reordered_opaque = x264_encoder_maximum_delayed_frames(x4->enc) + 17;x4->reordered_opaque = av_calloc(x4->nb_reordered_opaque,sizeof(*x4->reordered_opaque));if (!x4->reordered_opaque) {x4->nb_reordered_opaque = 0;return AVERROR(ENOMEM);}return 0;
}
1.3 释放编解码器(ff_codec_close)
函数的主要功能是释放编解码器,其核心是调用了close进行编解码器的关闭,对于libx264而言,调用的是X264_close()函数
av_cold void ff_codec_close(AVCodecContext *avctx)
{int i;if (!avctx)return;if (avcodec_is_open(avctx)) {AVCodecInternal *avci = avctx->internal;if (CONFIG_FRAME_THREAD_ENCODER &&avci->frame_thread_encoder && avctx->thread_count > 1) {ff_frame_thread_encoder_free(avctx);}if (HAVE_THREADS && avci->thread_ctx)ff_thread_free(avctx);if (avci->needs_close && ffcodec(avctx->codec)->close)ffcodec(avctx->codec)->close(avctx); // 关闭编解码器avci->byte_buffer_size = 0;av_freep(&avci->byte_buffer);av_frame_free(&avci->buffer_frame);av_packet_free(&avci->buffer_pkt);av_packet_free(&avci->last_pkt_props);av_packet_free(&avci->in_pkt);av_frame_free(&avci->in_frame);av_frame_free(&avci->recon_frame);ff_refstruct_unref(&avci->pool);ff_hwaccel_uninit(avctx);av_bsf_free(&avci->bsf);#if FF_API_DROPCHANGEDav_channel_layout_uninit(&avci->initial_ch_layout);
#endif#if CONFIG_LCMS2ff_icc_context_uninit(&avci->icc);
#endifav_freep(&avctx->internal);}for (i = 0; i < avctx->nb_coded_side_data; i++)av_freep(&avctx->coded_side_data[i].data);av_freep(&avctx->coded_side_data);avctx->nb_coded_side_data = 0;av_buffer_unref(&avctx->hw_frames_ctx);av_buffer_unref(&avctx->hw_device_ctx);if (avctx->priv_data && avctx->codec && avctx->codec->priv_class)av_opt_free(avctx->priv_data);av_opt_free(avctx);av_freep(&avctx->priv_data);if (av_codec_is_encoder(avctx->codec)) {av_freep(&avctx->extradata);avctx->extradata_size = 0;} else if (av_codec_is_decoder(avctx->codec))av_freep(&avctx->subtitle_header);avctx->codec = NULL;avctx->active_thread_type = 0;
}
X264_close函数的定义位于libavcodec\libx264.c中,如下所示,先释放了sei信息和opaque信息,随后调用x264_param_cleanup()清理x264相关的参数,最后调用x264_encoder_close来关闭x264的encoder
static av_cold int X264_close(AVCodecContext *avctx)
{X264Context *x4 = avctx->priv_data;av_freep(&x4->sei);for (int i = 0; i < x4->nb_reordered_opaque; i++)opaque_uninit(&x4->reordered_opaque[i]);av_freep(&x4->reordered_opaque);#if X264_BUILD >= 161// 清理x264相关参数x264_param_cleanup(&x4->params);
#endifif (x4->enc) {// 关闭x264的encoderx264_encoder_close(x4->enc);x4->enc = NULL;}return 0;
}
CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen
相关文章:
【FFmpeg】avcodec_open2函数
目录 1. avcodec_open21.1 编解码器的预初始化(ff_encode_preinit & ff_decode_preinit)1.2 编解码器的初始化(init)1.3 释放编解码器(ff_codec_close) FFmpeg相关记录: 示例工程ÿ…...
matlab:对带参数a关于x的方程求解
题目 讲解 简洁对各个式子的内部含义用浅显易懂的话语总结出来了,耐心体会 f(a) (x)exp(x)x^ax^(sqrt(x))-100;%因为下面的fzero的第一个数需要一个fun,所以这里有两个句柄,第一个a是输入的,第二个x是需要被解出的 A0:0.1:2;%创…...
Yolov10训练,转化onnx,推理
yolov10对于大目标的效果好,小目标不好 一、如果你训练过yolov5,yolov8,的话那么你可以直接用之前的环境就行 目录 一、如果你训练过yolov5,yolov8,的话那么你可以直接用之前的环境就行 二、配置好后就可以配置文件…...
GEE代码实例教程详解:洪水灾害监测
简介 在本篇博客中,我们将使用Google Earth Engine (GEE) 进行洪水灾害监测。通过分析Sentinel-1雷达数据,我们可以识别特定时间段内的洪水变化情况。 背景知识 Sentinel-1数据集 Sentinel-1是欧洲空间局提供的雷达卫星数据集,它能够提供…...
运维锅总详解系统设计原则
本文对CAP、BASE、ACID、SOLID 原则、12-Factor 应用方法论等12种系统设计原则进行分析举例,希望对您在进行系统设计、理解系统运行背后遵循的原理有所帮助! 一、CAP、BASE、ACID简介 以下是 ACID、CAP 和 BASE 系统设计原则的详细说明及其应用举例&am…...
深度学习笔记: 最详尽解释预测系统的分类指标(精确率、召回率和 F1 值)
欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家! 预测系统的分类指标(精确率、召回率和 F1 值) 简介 让我们来谈谈预测系统的分类指标以及对精确率、召回…...
GEE代码实例教程详解:MODIS土地覆盖分类与面积计算
简介 在本篇博客中,我们将使用Google Earth Engine (GEE) 对MODIS土地覆盖数据进行分析。通过MODIS/061/MCD12Q1数据集,我们可以识别不同的土地覆盖类型,并计算每种类型的总面积。 背景知识 MODIS MCD12Q1数据集 MODIS/061/MCD12Q1是NASA…...
LT86101UXE 国产原装 HDMI2.0 / DVI中继器方案 分辨率 4Kx2K 用于多显示器 DVI/HDMI电缆扩展模块
1. 描述 Lontium LT86101UXE HDMI2.0 / DVI中继器特性高速中继器符合HDMI2.0/1.4规范,最大6 gbps高速数据率、自适应均衡RX输入和pre-emphasized TX输出支持长电缆应用程序,没有晶体在船上保存BOM成本,内部灵活的PCB TX巷交换路由。 LT86101UXE HDMI2.0/DVI中继器自动检测线缆损…...
FastApi中的常见请求类型
FastApi中的常见请求类型 后端开发语言中,我钟情于node,高效的异步处理真是让我眼前一亮,同时,简单易懂的语法也让我非常倾心 但是但是,因为考虑要写一个深度学习算法的后端接口,所以不得不选用python作为…...
服务器,云、边缘计算概念简单理解
目录 服务器,云、边缘计算概念简单理解 一、服务器 二、云计算 三、边缘计算 服务器和云之间区别 性质 可用性 弹性扩展 管理和维护 成本 应用场景 服务器,云、边缘计算概念简单理解 一、服务器 概念简单理解: 服务器是计算机网络上最重要的设备之一,它在网络…...
【Linux系列2】Cmake安装记录
方法一 1. 查看当前cmake版本 [rootlocalhost ~]# cmake -version cmake version 2.8.12.22. 进行卸载 [rootlocalhost ~]# yum remove -y cmake3. 进行安装包的下载,也可以下载好安装包后传至相应的目录 [rootlocalhost ~]# mkdir /opt/cmake [rootlocalhost ~…...
C++ STL 多线程库用法介绍
目录 一:Atomic: 二:Thread 1. 创建线程 2. 小心移动(std::move)线程 3. 如何创建带参数的线程 4. 线程参数是引用类型时,要小心谨慎。 5. 获取线程ID 6. jthread 7. 如何在线程中使用中断 stop_token 三:如何…...
Jmeter实现接口自动化
自动化测试理论知识 什么是自动化测试? 让程序或工具代替人为执行测试用例什么样的项目适合做自动化? 1、项目周期长 --多长算长?(自己公司运营项目) 2、需求稳定(更多具体功能/模块) 3、需要…...
【大模型】多模型在大模型中的调度艺术:解锁效率与协同的新境界
多模型在大模型中的调度艺术:解锁效率与协同的新境界 引言一、多模型与大模型的概念解析二、多模型调度的必要性三、多模型调度的关键技术3.1 负载均衡与动态分配3.2 模型间通信与协作3.3 模型选择与优化 四、多模型运行优化策略4.1 异构计算平台的利用4.2 模型压缩…...
LeetCode 704, 290, 200
目录 704. 二分查找题目链接标签思路代码 290. 单词规律题目链接标签思路代码 200. 岛屿数量题目链接标签思路代码 704. 二分查找 题目链接 704. 二分查找 标签 数组 二分查找 思路 这道题是 二分查找 最经典的一道题,掌握了本题的思想就进入了 二分 思想的大…...
如何利用Java进行大数据处理?
如何利用Java进行大数据处理? 大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 1. 引言 在当今信息爆炸的时代,处理大数据是许多应用程序和系统的核心需求之一。Java作为一种…...
【论文通读】GUICourse: From General Vision Language Model to Versatile GUI Agent
GUICourse: From General Vision Language Model to Versatile GUI Agent 前言AbstractMotivationSolutionGUICourseGUIEnvGUIEnv-globalGUIEnv-local GUIActGUIAct (web-single)GUIAct (web-multi)GUIAct (smartphone) GUIChat ExperimentsMain ResultAblation Study Conclusi…...
王道考研数据机构:中缀表达式转为后缀表达式
实现方法: 初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。从左到右处理各个元素,直到末尾。可能遇到三种情况: 遇到操作数。直接加入后缀表达式遇到界限符。遇到“(”直接入栈;遇到“)”则依次弹出栈内运算符并加入后缀表达式&…...
PL/SQL安装+汉化教程
PL/SQL安装教程 一、安装: 登陆官网:PL/SQL Developer - Allround Automations下载 下载PL/SQL稳定版本12.0.7 根据自己计算机版本安装相适配的版本。我这里安装X64-bit版本 进行安装: 根据情况去更改安装,我这里全部下一步…...
Qt | Qt 线程相关类概述和举例
Qt 是一个广泛用于跨平台应用开发的框架。在 Qt 中,多线程支持是其核心特性之一,它允许开发者在不同平台上创建并发应用。以下是 Qt 中与线程相关的类概述及其使用示例。 Qt 中的线程相关类 QThread QThread 是 Qt 中用于创建和管理线程的基类。通过派生并重写 run() 函数…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
高效的后台管理系统——可进行二次开发
随着互联网技术的迅猛发展,企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心,成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统,它不仅支持跨平台应用,还能提供丰富…...
