当前位置: 首页 > news >正文

【FFmpeg】avcodec_send_frame函数

目录

  • 1.avcodec_send_frame
    • 1.1 将输入的frame存入内部buffer(encode_send_frame_internal)
      • 1.1.1 frame的引用函数(av_frame_ref )
        • 1.1.1.1 帧属性的拷贝(frame_copy_props)
        • 1.1.1.2 buffer的引用函数(av_buffer_ref)
    • 1.2 将frame送入编码器(encode_receive_packet_internal)
      • 1.2.1 软编入口(encode_simple_receive_packet)
        • 1.2.1.1 实际编码(codec->cb.encode)
        • 1.2.1.2 创建pkt的refcount缓冲buffer(encode_make_refcounted)
  • 2.小结

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】avcodec_open2函数

【推流】
【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_send_frame的函数调用关系为
在这里插入图片描述

1.avcodec_send_frame

avcodec_send_frame函数的主要功能是将外部输入的frame送入到编码器中实现编码,函数的实现位于libavcodec\encode.c中,从代码看,主要进行的流程为:
(1)输入信息avctx的检查
(2)如果输入的帧frame不存在,设置draining为1,随后将输入的frame存入avctx的buffer中,存入的过程使用encode_send_frame_internal实现
(3)将frame送入编码器进行编码,使用encode_receive_packet_internal实现

/*** Supply a raw video or audio frame to the encoder. Use avcodec_receive_packet()* to retrieve buffered output packets.** @param avctx     codec context* @param[in] frame AVFrame containing the raw audio or video frame to be encoded.*                  Ownership of the frame remains with the caller, and the*                  encoder will not write to the frame. The encoder may create*                  a reference to the frame data (or copy it if the frame is*                  not reference-counted).*                  It can be NULL, in which case it is considered a flush*                  packet.  This signals the end of the stream. If the encoder*                  still has packets buffered, it will return them after this*                  call. Once flushing mode has been entered, additional flush*                  packets are ignored, and sending frames will return*                  AVERROR_EOF.**                  For audio:*                  If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame*                  can have any number of samples.*                  If it is not set, frame->nb_samples must be equal to*                  avctx->frame_size for all frames except the last.*                  The final frame may be smaller than avctx->frame_size.* @retval 0                 success* @retval AVERROR(EAGAIN)   input is not accepted in the current state - user must*                           read output with avcodec_receive_packet() (once all*                           output is read, the packet should be resent, and the*                           call will not fail with EAGAIN).* @retval AVERROR_EOF       the encoder has been flushed, and no new frames can*                           be sent to it* @retval AVERROR(EINVAL)   codec not opened, it is a decoder, or requires flush* @retval AVERROR(ENOMEM)   failed to add packet to internal queue, or similar* @retval "another negative error code" legitimate encoding errors*/
// 向编码器提供原始视频或音频帧。使用avcodec_receive_packet()检索缓冲的输出数据包
int attribute_align_arg avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame)
{AVCodecInternal *avci = avctx->internal;int ret;// 1.输入的检查if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))return AVERROR(EINVAL);if (avci->draining)return AVERROR_EOF;if (avci->buffer_frame->buf[0])return AVERROR(EAGAIN);// 如果输出不存在,draining设为1if (!frame) {avci->draining = 1;} else { // 2.将输入的frame存入avctx的buffer中ret = encode_send_frame_internal(avctx, frame);if (ret < 0)return ret;}// 3.将frame送入编码器进行编码if (!avci->buffer_pkt->data && !avci->buffer_pkt->side_data) {ret = encode_receive_packet_internal(avctx, avci->buffer_pkt);if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)return ret;}avctx->frame_num++;return 0;
}

1.1 将输入的frame存入内部buffer(encode_send_frame_internal)

函数的功能是将输入的frame填充到avctx的buffer中,首先对音频进行检查,随后使用av_frame_ref将输入的frame填充到avctx的buffer中,如果是视频文件,最后还执行了encode_generate_icc_profile,但是这个函数没有被实现,默认会返回0

static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src)
{AVCodecInternal *avci = avctx->internal;EncodeContext     *ec = encode_ctx(avci);AVFrame *dst = avci->buffer_frame;int ret;// 音频if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {/* extract audio service type metadata */AVFrameSideData *sd = av_frame_get_side_data(src, AV_FRAME_DATA_AUDIO_SERVICE_TYPE);if (sd && sd->size >= sizeof(enum AVAudioServiceType))avctx->audio_service_type = *(enum AVAudioServiceType*)sd->data;/* check for valid frame size */if (!(avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) {/* if we already got an undersized frame, that must have been the last */if (ec->last_audio_frame) {av_log(avctx, AV_LOG_ERROR, "frame_size (%d) was not respected for a non-last frame\n", avctx->frame_size);return AVERROR(EINVAL);}if (src->nb_samples > avctx->frame_size) {av_log(avctx, AV_LOG_ERROR, "nb_samples (%d) > frame_size (%d)\n", src->nb_samples, avctx->frame_size);return AVERROR(EINVAL);}if (src->nb_samples < avctx->frame_size) {ec->last_audio_frame = 1;if (!(avctx->codec->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME)) {int pad_samples = avci->pad_samples ? avci->pad_samples : avctx->frame_size;int out_samples = (src->nb_samples + pad_samples - 1) / pad_samples * pad_samples;if (out_samples != src->nb_samples) {ret = pad_last_frame(avctx, dst, src, out_samples);if (ret < 0)return ret;goto finish;}}}}}// 将src拷贝到dst中ret = av_frame_ref(dst, src);if (ret < 0)return ret;finish:if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) {// 函数还未实现,默认会返回0ret = encode_generate_icc_profile(avctx, dst);if (ret < 0)return ret;}// unset frame duration unless AV_CODEC_FLAG_FRAME_DURATION is set,// since otherwise we cannot be sure that whatever value it has is in the// right timebase, so we would produce an incorrect value, which is worse// than none at allif (!(avctx->flags & AV_CODEC_FLAG_FRAME_DURATION))dst->duration = 0;return 0;
}

下面看看av_frame_ref的实现过程

1.1.1 frame的引用函数(av_frame_ref )

int av_frame_ref(AVFrame *dst, const AVFrame *src)
{int ret = 0;av_assert1(dst->width == 0 && dst->height == 0);av_assert1(dst->ch_layout.nb_channels == 0 &&dst->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC);// 长宽和format的配置dst->format         = src->format;dst->width          = src->width;dst->height         = src->height;// 这个帧描述的音频采样数(每个通道)dst->nb_samples     = src->nb_samples;// 拷贝一些属性值ret = frame_copy_props(dst, src, 0);if (ret < 0)goto fail;// 拷贝音频声道数ret = av_channel_layout_copy(&dst->ch_layout, &src->ch_layout);if (ret < 0)goto fail;/* duplicate the frame data if it's not refcounted */// 如果src没有被引用,即只有1份。没有进行拷贝操作// refcounted可以看做是一种编程模式,用于管理动态分配的资源,如内存、句柄、文件描述等// 这种模式通过维护一个引用计数器来跟踪资源的使用者数量,以确保资源在不再需要时被正确释放// 例如,当这个资源首次被创建或分配时,它的引用计数器被初始化为1,当有一个新的使用者或引用使用这个资源时// 引用计数器就会加1,当使用者不再需要使用这个资源时,它会减少引用计数器,并检查计数器是否为0// 如果这个计数器为0,这说明没有其他的使用者了,可以进行安全的释放if (!src->buf[0]) {// 分配一个新的bufferret = av_frame_get_buffer(dst, 0);if (ret < 0)goto fail;// 将帧数据从src复制到dstret = av_frame_copy(dst, src);if (ret < 0)goto fail;return 0;}/* ref the buffers */for (int i = 0; i < FF_ARRAY_ELEMS(src->buf); i++) {if (!src->buf[i])continue;// buffer的ref操作dst->buf[i] = av_buffer_ref(src->buf[i]);if (!dst->buf[i]) {ret = AVERROR(ENOMEM);goto fail;}}// extended_buf用于存储额外的数据缓冲区,这个字段通常用于存储与视频帧相关的额外信息,例如字幕、时间戳等if (src->extended_buf) {dst->extended_buf = av_calloc(src->nb_extended_buf,sizeof(*dst->extended_buf));if (!dst->extended_buf) {ret = AVERROR(ENOMEM);goto fail;}dst->nb_extended_buf = src->nb_extended_buf;for (int i = 0; i < src->nb_extended_buf; i++) {dst->extended_buf[i] = av_buffer_ref(src->extended_buf[i]);if (!dst->extended_buf[i]) {ret = AVERROR(ENOMEM);goto fail;}}}// 硬件frame上下文信息if (src->hw_frames_ctx) {dst->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);if (!dst->hw_frames_ctx) {ret = AVERROR(ENOMEM);goto fail;}}/* duplicate extended data */if (src->extended_data != src->data) {int ch = dst->ch_layout.nb_channels;if (!ch) {ret = AVERROR(EINVAL);goto fail;}dst->extended_data = av_malloc_array(sizeof(*dst->extended_data), ch);if (!dst->extended_data) {ret = AVERROR(ENOMEM);goto fail;}memcpy(dst->extended_data, src->extended_data, sizeof(*src->extended_data) * ch);} elsedst->extended_data = dst->data;memcpy(dst->data,     src->data,     sizeof(src->data));memcpy(dst->linesize, src->linesize, sizeof(src->linesize));return 0;fail:// 如果失败,解引用av_frame_unref(dst);return ret;
}
1.1.1.1 帧属性的拷贝(frame_copy_props)
static int frame_copy_props(AVFrame *dst, const AVFrame *src, int force_copy)
{int ret;#if FF_API_FRAME_KEY
FF_DISABLE_DEPRECATION_WARNINGSdst->key_frame              = src->key_frame;
FF_ENABLE_DEPRECATION_WARNINGS
#endifdst->pict_type              = src->pict_type;dst->sample_aspect_ratio    = src->sample_aspect_ratio;dst->crop_top               = src->crop_top;dst->crop_bottom            = src->crop_bottom;dst->crop_left              = src->crop_left;dst->crop_right             = src->crop_right;dst->pts                    = src->pts;dst->duration               = src->duration;dst->repeat_pict            = src->repeat_pict;
#if FF_API_INTERLACED_FRAME
FF_DISABLE_DEPRECATION_WARNINGSdst->interlaced_frame       = src->interlaced_frame;dst->top_field_first        = src->top_field_first;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
#if FF_API_PALETTE_HAS_CHANGED
FF_DISABLE_DEPRECATION_WARNINGSdst->palette_has_changed    = src->palette_has_changed;
FF_ENABLE_DEPRECATION_WARNINGS
#endifdst->sample_rate            = src->sample_rate;dst->opaque                 = src->opaque;dst->pkt_dts                = src->pkt_dts;
#if FF_API_FRAME_PKT
FF_DISABLE_DEPRECATION_WARNINGSdst->pkt_pos                = src->pkt_pos;dst->pkt_size               = src->pkt_size;
FF_ENABLE_DEPRECATION_WARNINGS
#endifdst->time_base              = src->time_base;dst->quality                = src->quality;dst->best_effort_timestamp  = src->best_effort_timestamp;dst->flags                  = src->flags;dst->decode_error_flags     = src->decode_error_flags;dst->color_primaries        = src->color_primaries;dst->color_trc              = src->color_trc;dst->colorspace             = src->colorspace;dst->color_range            = src->color_range;dst->chroma_location        = src->chroma_location;av_dict_copy(&dst->metadata, src->metadata, 0);for (int i = 0; i < src->nb_side_data; i++) {const AVFrameSideData *sd_src = src->side_data[i];AVFrameSideData *sd_dst;if (   sd_src->type == AV_FRAME_DATA_PANSCAN&& (src->width != dst->width || src->height != dst->height))continue;if (force_copy) {sd_dst = av_frame_new_side_data(dst, sd_src->type,sd_src->size);if (!sd_dst) {frame_side_data_wipe(dst);return AVERROR(ENOMEM);}memcpy(sd_dst->data, sd_src->data, sd_src->size);} else {AVBufferRef *ref = av_buffer_ref(sd_src->buf);sd_dst = av_frame_new_side_data_from_buf(dst, sd_src->type, ref);if (!sd_dst) {av_buffer_unref(&ref);frame_side_data_wipe(dst);return AVERROR(ENOMEM);}}av_dict_copy(&sd_dst->metadata, sd_src->metadata, 0);}ret = av_buffer_replace(&dst->opaque_ref, src->opaque_ref);ret |= av_buffer_replace(&dst->private_ref, src->private_ref);return ret;
}
1.1.1.2 buffer的引用函数(av_buffer_ref)
AVBufferRef *av_buffer_ref(const AVBufferRef *buf)
{AVBufferRef *ret = av_mallocz(sizeof(*ret));if (!ret)return NULL;*ret = *buf;// 引用之后,refcount + 1atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);return ret;
}

1.2 将frame送入编码器(encode_receive_packet_internal)

函数的主要功能是将frame送入到编码器中,先检查图像的尺寸是否有效,随后看是否是硬编还是软编,如果是硬编则使用receive_packet获取硬编码器输出的信息,否则调用encode_simple_receive_packet进行软编

static int encode_receive_packet_internal(AVCodecContext *avctx, AVPacket *avpkt)
{AVCodecInternal *avci = avctx->internal;int ret;if (avci->draining_done)return AVERROR_EOF;av_assert0(!avpkt->data && !avpkt->side_data);if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) {if ((avctx->flags & AV_CODEC_FLAG_PASS1) && avctx->stats_out)avctx->stats_out[0] = '\0';// 1.检查图像的给定维度是否有效,这意味着具有指定pix_fmt的图像平面的所有字节都可以用带符号的int寻址if (av_image_check_size2(avctx->width, avctx->height, avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx))return AVERROR(EINVAL);}// FF_CODEC_CB_TYPE_RECEIVE_PACKET表示编解码器是使用receive_packet回调的编码器;仅限音频和视频编解码器// 2.cb_type为这个类型的都是硬件编码器的回调,例如amf,nvidia,vaapi平台if (ffcodec(avctx->codec)->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET) {ret = ffcodec(avctx->codec)->cb.receive_packet(avctx, avpkt);if (ret < 0)av_packet_unref(avpkt);else// Encoders must always return ref-counted buffers.// Side-data only packets have no data and can be not ref-counted.av_assert0(!avpkt->data || avpkt->buf);} elseret = encode_simple_receive_packet(avctx, avpkt); // 3.使用软件编码器进行编码if (ret >= 0)avpkt->flags |= encode_ctx(avci)->intra_only_flag; // 是否是intra flagif (ret == AVERROR_EOF)avci->draining_done = 1;return ret;
}

1.2.1 软编入口(encode_simple_receive_packet)

函数很简单,做一下检查之后,直接调用encode_simple_internal进行内部编码

static int encode_simple_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
{int ret;while (!avpkt->data && !avpkt->side_data) {ret = encode_simple_internal(avctx, avpkt);if (ret < 0)return ret;}return 0;
}

encode_simple_internal的定义如下

static int encode_simple_internal(AVCodecContext *avctx, AVPacket *avpkt)
{AVCodecInternal   *avci = avctx->internal;AVFrame          *frame = avci->in_frame;const FFCodec *const codec = ffcodec(avctx->codec);int got_packet;int ret;if (avci->draining_done)return AVERROR_EOF;// 如果引用计数为0,需要先获取1帧if (!frame->buf[0] && !avci->draining) {av_frame_unref(frame);// 由编码器调用以获取下一帧进行编码ret = ff_encode_get_frame(avctx, frame);if (ret < 0 && ret != AVERROR_EOF)return ret;}if (!frame->buf[0]) {if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||avci->frame_thread_encoder))return AVERROR_EOF;// Flushing is signaled with a NULL frameframe = NULL;}got_packet = 0;av_assert0(codec->cb_type == FF_CODEC_CB_TYPE_ENCODE);// 多线程编码if (CONFIG_FRAME_THREAD_ENCODER && avci->frame_thread_encoder)/* This will unref frame. */ret = ff_thread_video_encode_frame(avctx, avpkt, frame, &got_packet);else {// 编码cbret = ff_encode_encode_cb(avctx, avpkt, frame, &got_packet);}if (avci->draining && !got_packet)avci->draining_done = 1;return ret;
}

ff_encode_encode_cb的定义如下,调用encode进行实际编码,对于libx264格式,会调用X264_frame()函数进行编码,随后会调用encode_make_refcounted来创建refcount缓冲buffer

int ff_encode_encode_cb(AVCodecContext *avctx, AVPacket *avpkt,AVFrame *frame, int *got_packet)
{const FFCodec *const codec = ffcodec(avctx->codec);int ret;// 执行编码,对于libx264格式,会调用X264_frame()函数进行编码// 编码后的压缩数据存储在avpkt的data中ret = codec->cb.encode(avctx, avpkt, frame, got_packet);emms_c();av_assert0(ret <= 0);if (!ret && *got_packet) {if (avpkt->data) {// 创建avpkt的refcount缓冲bufferret = encode_make_refcounted(avctx, avpkt);if (ret < 0)goto unref;// Date returned by encoders must always be ref-countedav_assert0(avpkt->buf);}// set the timestamps for the simple no-delay case// encoders with delay have to set the timestamps themselves// 为简单的无延迟情况设置时间戳,带有延迟的编码器必须自己设置时间戳if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY) ||(frame && (codec->caps_internal & FF_CODEC_CAP_EOF_FLUSH))) {if (avpkt->pts == AV_NOPTS_VALUE)avpkt->pts = frame->pts;if (!avpkt->duration) {if (frame->duration)avpkt->duration = frame->duration;else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {avpkt->duration = ff_samples_to_time_base(avctx,frame->nb_samples);}}// 注释翻译:根据需要将用户不透明的值从帧传播到avctx/pkt// 这里应该是将AVFrame结构体中记录重新排序的opaque数据,主要用于编码过程中对视频帧的重排序ret = ff_encode_reordered_opaque(avctx, avpkt, frame);if (ret < 0)goto unref;}// dts equals pts unless there is reordering// there can be no reordering if there is no encoder delayif (!(avctx->codec_descriptor->props & AV_CODEC_PROP_REORDER) ||!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)        ||(codec->caps_internal & FF_CODEC_CAP_EOF_FLUSH))avpkt->dts = avpkt->pts;} else {
unref:av_packet_unref(avpkt);}if (frame)av_frame_unref(frame); // 这里不再使用,解引用return ret;
}
1.2.1.1 实际编码(codec->cb.encode)

本函数实际执行了编码过程,以libx264编码器为例,会调用X264_frame()函数进行编码,从定义的结构体可以看出

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_frame()的定义如下,主要工作流程为:
(1)根据外部输入的frame,初始化x264格式的pic_in,这个pic_in会被送入x264编码器进行编码(setup_frame)
(2)进行264编码(x264_encoder_encode)
(3)一些编码后信息的处理
(4)处理私有数据
(5)确定帧类型
(6)计算编码结束的psnr质量信息
主要函数的功能都用注释标出,不再具体对底层函数分析,另外x264_encoder_encode在【x264】系列记录中可以看到具体的实现

static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,int *got_packet)
{X264Context *x4 = ctx->priv_data;x264_nal_t *nal;int nnal, ret;x264_picture_t pic_out = {0}, *pic_in;int pict_type;int64_t wallclock = 0;X264Opaque *out_opaque;// 1.根据外部输入的frame,来初始化x264格式的pic_in,从而进行x264编码ret = setup_frame(ctx, frame, &pic_in);if (ret < 0)return ret;do {// 2.x264的接口函数,具体执行264编码过程if (x264_encoder_encode(x4->enc, &nal, &nnal, pic_in, &pic_out) < 0)return AVERROR_EXTERNAL;// 3.下面是一些编码后信息的处理if (nnal && (ctx->flags & AV_CODEC_FLAG_RECON_FRAME)) {AVCodecInternal *avci = ctx->internal;av_frame_unref(avci->recon_frame);// csp: colorspaceavci->recon_frame->format = csp_to_pixfmt(pic_out.img.i_csp);if (avci->recon_frame->format == AV_PIX_FMT_NONE) {av_log(ctx, AV_LOG_ERROR,"Unhandled reconstructed frame colorspace: %d\n",pic_out.img.i_csp);return AVERROR(ENOSYS);}avci->recon_frame->width  = ctx->width;avci->recon_frame->height = ctx->height;// 将编码输出的重建帧(img)信息给到recon_framefor (int i = 0; i < pic_out.img.i_plane; i++) {avci->recon_frame->data[i]     = pic_out.img.plane[i];avci->recon_frame->linesize[i] = pic_out.img.i_stride[i];}// 确保帧数据是可写的,尽可能避免数据复制// 如果avframe当中的buf->flag不是READ_ONLY则可写ret = av_frame_make_writable(avci->recon_frame);if (ret < 0) {av_frame_unref(avci->recon_frame);return ret;}}// 将编码之后的nal信息,写入到pkt之中// pic_out.img当中没有存储nal信息,nal信息存储在了ctx之中ret = encode_nals(ctx, pkt, nal, nnal);if (ret < 0)return ret;// 返回当前延迟(缓冲)帧的数量,这应该在流结束时使用,以知道何时拥有所有编码帧} while (!ret && !frame && x264_encoder_delayed_frames(x4->enc)); // if (!ret)return 0;pkt->pts = pic_out.i_pts;pkt->dts = pic_out.i_dts;// 4.处理私有数据,opaque是一种通用的未指定具体含义的数据// 通常与视频帧或编码器上下文相关联,可以存储一些自定义数据,统计数据等等out_opaque = pic_out.opaque;if (out_opaque >= x4->reordered_opaque &&out_opaque < &x4->reordered_opaque[x4->nb_reordered_opaque]) {wallclock = out_opaque->wallclock;pkt->duration = out_opaque->duration;if (ctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) {pkt->opaque                  = out_opaque->frame_opaque;pkt->opaque_ref              = out_opaque->frame_opaque_ref;out_opaque->frame_opaque_ref = NULL;}opaque_uninit(out_opaque);} else {// Unexpected opaque pointer on picture outputav_log(ctx, AV_LOG_ERROR, "Unexpected opaque pointer; ""this is a bug, please report it.\n");}// 5.确定帧类型switch (pic_out.i_type) {case X264_TYPE_IDR:case X264_TYPE_I:pict_type = AV_PICTURE_TYPE_I;break;case X264_TYPE_P:pict_type = AV_PICTURE_TYPE_P;break;case X264_TYPE_B:case X264_TYPE_BREF:pict_type = AV_PICTURE_TYPE_B;break;default:av_log(ctx, AV_LOG_ERROR, "Unknown picture type encountered.\n");return AVERROR_EXTERNAL;}pkt->flags |= AV_PKT_FLAG_KEY*pic_out.b_keyframe;// 6.计算编码结束的psnr质量信息if (ret) {int error_count = 0;int64_t *errors = NULL;int64_t sse[3] = {0};if (ctx->flags & AV_CODEC_FLAG_PSNR) {const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(ctx->pix_fmt);double scale[3] = { 1,(double)(1 << pix_desc->log2_chroma_h) * (1 << pix_desc->log2_chroma_w),(double)(1 << pix_desc->log2_chroma_h) * (1 << pix_desc->log2_chroma_w),};error_count = pix_desc->nb_components;for (int i = 0; i < pix_desc->nb_components; ++i) {double max_value = (double)(1 << pix_desc->comp[i].depth) - 1.0;double plane_size = ctx->width * (double)ctx->height / scale[i];/* psnr = 10 * log10(max_value * max_value / mse) */double mse = (max_value * max_value) / pow(10, pic_out.prop.f_psnr[i] / 10.0);/* SSE = MSE * width * height / scale -> because of possible chroma downsampling */sse[i] = (int64_t)floor(mse * plane_size + .5);};errors = sse;}ff_side_data_set_encoder_stats(pkt, (pic_out.i_qpplus1 - 1) * FF_QP2LAMBDA,errors, error_count, pict_type);if (wallclock)ff_side_data_set_prft(pkt, wallclock);}*got_packet = ret;return 0;
}
1.2.1.2 创建pkt的refcount缓冲buffer(encode_make_refcounted)

函数的主要功能是为avpkt分配refcounted的缓冲buffer

static int encode_make_refcounted(AVCodecContext *avctx, AVPacket *avpkt)
{uint8_t *data = avpkt->data;int ret;// 如果avpkt中存在buf,直接返回if (avpkt->buf)return 0;avpkt->data = NULL;// 获取encode的bufferret = ff_get_encode_buffer(avctx, avpkt, avpkt->size, 0);if (ret < 0)return ret;memcpy(avpkt->data, data, avpkt->size);return 0;
}

ff_get_encode_buffer的定义如下,其核心就是调用了get_encode_buffer进行encode_buffer的创建,默认的创建函数为avcodec_default_get_encode_buffer

int ff_get_encode_buffer(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int flags)
{int ret;if (size < 0 || size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)return AVERROR(EINVAL);av_assert0(!avpkt->data && !avpkt->buf);avpkt->size = size;// 默认使用的get_encode_buffer函数由avcodec_default_get_encode_buffer指定ret = avctx->get_encode_buffer(avctx, avpkt, flags);if (ret < 0)goto fail;if (!avpkt->data || !avpkt->buf) {av_log(avctx, AV_LOG_ERROR, "No buffer returned by get_encode_buffer()\n");ret = AVERROR(EINVAL);goto fail;}memset(avpkt->data + avpkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE);ret = 0;
fail:if (ret < 0) {av_log(avctx, AV_LOG_ERROR, "get_encode_buffer() failed\n");av_packet_unref(avpkt);}return ret;
}

avcodec_default_get_encode_buffer的定义如下,核心的地方是使用av_buffer_realloc为avpkt分配buf的空间

int avcodec_default_get_encode_buffer(AVCodecContext *avctx, AVPacket *avpkt, int flags)
{int ret;if (avpkt->size < 0 || avpkt->size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)return AVERROR(EINVAL);if (avpkt->data || avpkt->buf) {av_log(avctx, AV_LOG_ERROR, "avpkt->{data,buf} != NULL in avcodec_default_get_encode_buffer()\n");return AVERROR(EINVAL);}ret = av_buffer_realloc(&avpkt->buf, avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE);if (ret < 0) {av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet of size %d\n", avpkt->size);return ret;}avpkt->data = avpkt->buf->data;return 0;
}

2.小结

avcodec_send_frame函数主要功能是将外部输入的frame送入到编码器中进行编码,送入之前需要将输入信息转换成对应的格式(例如x264格式),编码完成之后获取重建信息进行计算和存储。在这个过程中,refcount这个概念很重要,这个技术应用的核心目的是良好的管理FFmpeg当中资源的生命周期(例如AVFrame,AVPacket等),每多一个访问者,该项资源的refcount数量加1,每减少一个访问者,refcount减1

粗略来说,使用refcount有几点好处:
(1)内存空间
在程序运行过程中,为了节省开销,许多地方没有必要重复分配空间,使用refcount可以通过浅拷贝的方式,访问同一资源

(2)内存管理
程序中,往往有很多地方会访问相同的地址,如果操作不当,可能会出现误操作如提前释放或双重释放,refcount提供了一个计数器,只有当计数器清零的时候才会释放这个变量

(3)代码清晰
将资源的生命周期管理封装起来,使得代码更好维护

CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen

相关文章:

【FFmpeg】avcodec_send_frame函数

目录 1.avcodec_send_frame1.1 将输入的frame存入内部buffer&#xff08;encode_send_frame_internal&#xff09;1.1.1 frame的引用函数&#xff08;av_frame_ref &#xff09;1.1.1.1 帧属性的拷贝&#xff08;frame_copy_props&#xff09;1.1.1.2 buffer的引用函数&#xf…...

python获取字符编码

在Python中&#xff0c;您可以使用内置的ord()函数获取单个字符的Unicode编码&#xff0c;使用encode()方法获取字符串的字节编码。 获取单个字符的Unicode编码: char a unicode_code ord(char) print(unicode_code) # 输出字符的Unicode编码 获取字符串的字节编码: tex…...

通过MATLAB控制TI毫米波雷达的工作状态之实时数据采集

前言 前一章博主介绍了如何基于MATLAB的各种前面板组件结合MATLAB代码来发送CFG指令控制毫米波雷达的工作状态,这一章节博主将介绍如何基于这些组件结合MATLAB代码来实现TI毫米波雷达数据的实时采集。目前大部分TI毫米波雷达的数据采集均是仅可以采集一段数据又或者利用DAC10…...

华为HCIP Datacom H12-821 卷21

1.单选题 以下关于PIM-SM中SPT切换的描述,错误的是哪一项? A、若所有组播流量都经过RP路由器,则RP路由器可能成为数据转发的瓶颈 B、SPT路径最短,转发性能更优 C、SPT 切换完成后,组播流量依然经过 ReT 树 D、RPT 树可能不是组播流量转发的最优路径 正确答案: C 解析…...

MySQL之应用层优化(二)

应用层优化 Web服务器问题 寻找最优并发度 每个Web服务器都有一个最佳并发度——就是说&#xff0c;让进程处理请求尽可能快&#xff0c;并且不超过系统负载的最优的并发连接数。这就是前面说的最大系统容量。进行一个简单的测量和建模&#xff0c;或者只是反复试验&#xf…...

Java源码解读之常量52429

文章目录 为什么有52429的常量呢&#xff1f;对于为什么选择52429?那么为什么不再选几位呢&#xff1f; 在JDK8源码中 java.lang.Integer有52429作为常量出现&#xff0c; 为什么有52429的常量呢&#xff1f; static void getChars(int i, int index, char[] buf) {int q, r;…...

“Photoshop AI插件:StartAI的全面使用攻略

随着人工智能技术的飞速发展&#xff0c;Photoshop作为设计师们不可或缺的工具&#xff0c;也在不断地融入AI技术&#xff0c;以提升设计效率和效果。在2024年&#xff0c;PSAI插件StartAI因其强大的功能和易用性&#xff0c;成为了Photoshop用户的得力帮手。下面来给大家详细介…...

入门Axure:快速掌握原型设计技能

2002 年&#xff0c;维克托和马丁在旧金山湾区的一家初创公司工作&#xff0c;发现自己一再被软件开发生命周期的限制所困扰&#xff0c;而且产品团队在编写规范之前很难评估他们的解决方案&#xff0c;开发人员经常不理解&#xff08;或不阅读&#xff09;给出的规范&#xff…...

Java中的序列化与反序列化详解

Java中的序列化与反序列化详解 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 什么是序列化与反序列化&#xff1f; 序列化&#xff08;Serialization&#…...

在鸿蒙开发中如何实现皮肤切换?

在鸿蒙开发中&#xff0c;实现主题皮肤切换可以通过以下步骤&#xff1a; 1. 创建不同的主题样式文件&#xff0c;例如theme_light.json和theme_dark.json。 2. 在应用程序的config.json文件中&#xff0c;引入这些主题样式文件。 3. 在应用程序的入口文件&#xff08;例如main…...

FlowUs新一代内容创作营销平台|FlowUs息流国产 好用 不限速

FlowUs 作为一个知识管理和协作平台&#xff0c;知识库功能可以被视为一个强大的学习工具&#xff01; 为什么FlowUs知识库可以成为学习利器呢&#xff1f;原因有以下几点 集中化知识存储&#xff1a;FlowUs允许我们将所有相关信息和资料集中在一个地方&#xff0c;便于访问和复…...

WebSocket解决方案(springboot 基于Redis发布订阅)

WebSocket 因为一般的请求都是HTTP请求&#xff08;单向通信&#xff09;&#xff0c;HTTP是一个短连接&#xff08;非持久化&#xff09;&#xff0c;且通信只能由客户端发起&#xff0c;HTTP协议做不到服务器主动向客户端推送消息。WebSocket确能很好的解决这个问题&…...

如何优化网站SEO排名?

选择那些容易排名的关键词。使用工具找到那些竞争少但有流量的词语。其次&#xff0c;内部链接非常重要。通过合理的内部链接&#xff0c;可以提升各个页面的权重。 增加FAQ部分能帮助你捕捉更多的长尾关键词流量。争取出现在精选摘要的位置&#xff0c;可以直接提升你的曝光率…...

基于Java的音乐网站系统-计算机毕业设计源码01239

目 录 摘要 1 绪论 1.1 研究背景 1.2系统开发目标、意义 1.3研究内容 2 相关技术介绍 2.1 MySQL数据库 2.2 Java编程语言 2.3 SpringBoot框架介绍 3 系统需求分析与设计 3.1 可行性分析 3.1.1 技术可行性分析 3.1.2 经济可行性分析 3.1.3 法律可行性分析 3.2 需…...

云原生之容器编排实践-OpenEuler23.09在线安装Kubernetes与KubeSphere

背景 前几篇文章中介绍了如何将 ruoyi-cloud 项目部署到 Kubernetes 集群中&#xff0c;包括网关服务、认证服务和系统服务并且对全部服务采用 YAML 文件的方式来进行部署&#xff0c;这虽然有助于理解 K8S 组织管理资源的风格与底层机制&#xff0c;但是对于团队中不太熟悉命…...

Ubuntu 截图shutter,图像编辑 gimp,录屏kazam

1.截图&#xff1a; Shutter 安装shutter命令&#xff1a; sudo add-apt-repository ppa:shutter/ppasudo apt-get updatesudo apt-get install shutter 2.图片编辑&#xff1a;Gimp, Kolourpaint, Pinta gimp全名为&#xff1a;GNU Image Manipulation Program&#xff0c…...

WSO2 products 文件上传漏洞(CVE-2022-29464)

前言 CVE-2022-29464 是一个影响多个 WSO2 产品的严重远程代码执行&#xff08;RCE&#xff09;漏洞。这些产品包括 WSO2 API Manager、WSO2 Identity Server 和 WSO2 Enterprise Integrator 等。由于用户输入验证不当&#xff0c;该漏洞允许未经身份验证的攻击者在服务器上上…...

YOLOv8改进 | 卷积模块 | SAConv可切换空洞卷积

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…...

使用Python下载并合并HLS视频片段

下载和合并视频片段的实用方法 在日常工作中&#xff0c;我们经常会遇到需要从网上下载视频并将其合并成一个完整视频的需求。本文将介绍如何使用 Python 下载多个视频片段&#xff0c;并使用 ffmpeg 将这些片段合并成一个完整的视频文件。以下是具体步骤和代码实现。 完整代…...

常见的九种二极管

常见的九种二极管 文章目录 常见的九种二极管1、普通二极管2、光电二极管&#xff08;LED&#xff09;3、变容二级管4、发光二极管5、恒流二极管6、快恢复二极管&#xff08;FRD&#xff09;7、肖特基二极管8、瞬态电压抑制二极管(TVS)9、齐纳二极管&#xff08;稳压&#xff0…...

竞赛选题 python的搜索引擎系统设计与实现

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python的搜索引擎系统设计与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;5分创新点&#xff1a;3分 该项目较为新颖&#xff…...

大模型技术方向夏令营1期-对话分角色要素提取挑战赛

#AI夏令营 #Datawhale #夏令营 一、 baseline 跑通 Baseline 本身挑战性有限&#xff0c;关键是熟悉 LLM-centric 相关任务 coding 层面的流程方法&#xff0c;比如: 大模型 API&#xff08;这里为科大讯飞 Spark&#xff09;调用token消耗的理解如何调用大模型实现针对给定…...

类和对象(封装、继承、多态、友元)

c面相对象的三大特性为&#xff1a;封装、继承、多态 c 认为万事万物都皆为对象&#xff0c;对象上有其属性和行为 一、类和对象&#xff08;封装&#xff09; &#xff08;一&#xff09;封装的意义 封装是c面相对象的三大特性之一 封装的意义&#xff1a; 将属性和行为…...

关于Yolov8我踩过的那些坑

按照报错频次梳理&#xff1a; 致命反斜杠‘\’ 调用模型时&#xff0c;我喜欢‘copy relative location’&#xff0c;然后win系统默认反斜杠&#xff01; 就导致路径读取错误&#xff01;各种报错&#xff01;&#xff01; debug到崩溃然后发现是斜杠的问题&#xff0c;本吗喽…...

Linux——shell原理和文件权限

1.shell原理 在我们使用云服务器时&#xff0c;需要通过shell进行使用&#xff0c;而shell则是一种外壳程序。 我们提到过&#xff0c;大部分的指令实际上就是文件&#xff0c;当用户需要执行某种功能时&#xff0c;由于用户不擅长和操作系统直接交互&#xff08;操作复杂&…...

网络工程师需要熟悉Docker吗?我觉得不需要精通,但是得懂基础

你好&#xff0c;这里是网络技术联盟站&#xff0c;我是瑞哥。 Docker&#xff0c;这个字眼大家不陌生吧&#xff0c;不过作为网络工程师可能平时接触不到&#xff0c;如果在看文章的是运维人员&#xff0c;那么70%以上的运维人员都会跟Docker打交道。即使网工用不到&#xff…...

c++初级-2-引用

文章目录 引用一、引用的定义二、引用做函数参数三、引用作为返回对象四、引用的本质五、常量引用 引用 即给一个变量起别名。 一、引用的定义 int a 10;//引用int& b a;cout << "a " << a << endl;cout << "b " <&l…...

如何清理电脑内存?让电脑运行如飞!

电脑内存&#xff08;RAM&#xff09;的清理对于维持系统的流畅运行至关重要。随着使用时间的增加&#xff0c;系统内存会被各种应用程序和后台进程占用&#xff0c;导致系统响应变慢&#xff0c;甚至出现卡顿现象。通过有效地清理内存&#xff0c;可以提升电脑的性能&#xff…...

[数据集][目标检测]人员状态跑睡抽烟打电话跌倒检测数据集4943张5类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4943 标注数量(xml文件个数)&#xff1a;4943 标注数量(txt文件个数)&#xff1a;4943 标注…...

Java8 - Stream API 处理集合数据

Java 8的Stream API提供了一种功能强大的方式来处理集合数据&#xff0c;以函数式和声明式的方式进行操作。Stream API允许您对元素集合执行操作&#xff0c;如过滤、映射和归约&#xff0c;以简洁高效的方式进行处理。 下面是Java 8 Stream API的一些关键特性和概念&#xff…...

漫步5G-A City,一份独属于上海的浪漫

作家亨利詹姆斯曾写道&#xff0c;“城市漫步&#xff0c;让我接触到了这个世界上最好的东西”。 用漫无目的地行走&#xff0c;来体验和观察一座城市&#xff0c;上海凭借丰富多元的文化特质&#xff0c;成为citywalk这种浪漫生活方式的流行地。 无论你是漫步在美术馆、画廊林…...

SpringBoot 如何处理跨域请求?你说的出几种方法?

引言&#xff1a;在现代的Web开发中&#xff0c;跨域请求&#xff08;Cross-Origin Resource Sharing&#xff0c;CORS&#xff09;是一个常见的挑战。随着前后端分离架构的流行&#xff0c;前端应用通常运行在一个与后端 API 不同的域名或端口上&#xff0c;这就导致了浏览器的…...

OV SSL证书年度成本概览:为企业安全护航的经济之选

在当今数字化时代&#xff0c;企业网站不仅是品牌展示的窗口&#xff0c;更是与客户沟通的桥梁。然而&#xff0c;随着网络威胁的不断升级&#xff0c;保护网站安全成为了企业不可忽视的任务。SSL证书&#xff0c;特别是OV SSL证书&#xff0c;因其对企业身份的严格验证&#x…...

歌尔气压计SPA06-003在无人机的创新应用

随着科技的不断进步&#xff0c;各类智能设备的功能日益强大&#xff0c;其中气压计作为一种能够测量大气压力的传感器&#xff0c;已被广泛应用于多种领域。歌尔气压计以其高精度、低功耗的特点&#xff0c;在无人机和智能手表上的应用尤为突出&#xff0c;为这两个领域的产品…...

python3多文件操作

1 介绍 有两个.py文件&#xff0c;分别为main.py和util.py&#xff0c;执行main.py时&#xff0c;调用util.py当中的函数。 main.py内容如下&#xff0c; import util if __name__ "__main__":a [3.0,4.0]length util.get_length_from_vec(a)print(f"leng…...

312. 戳气球

312. 戳气球 题目链接&#xff1a;312. 戳气球 代码如下&#xff1a; //参考链接:https://leetcode.cn/problems/burst-balloons/solutions/336390/chuo-qi-qiu-by-leetcode-solution class Solution { public:int maxCoins(vector<int>& nums) {int nnums.size()…...

深入理解C++中的锁

目录 1.基本互斥锁&#xff08;std::mutex&#xff09; 2.递归互斥锁&#xff08;std::recursive_mutex&#xff09; 3.带超时机制的互斥锁&#xff08;std::timed_mutex&#xff09; 4.带超时机制的递归互斥锁&#xff08;std::recursive_timed_mutex&#xff09; 5.共享…...

压缩pdf文件大小,压缩pdf文件大小软件哪个好

在数字化时代&#xff0c;PDF文件因其卓越的跨平台兼容性和稳定性而成为工作与学习的好帮手。然而&#xff0c;当PDF文件体积过大时&#xff0c;传输和存储便成了一项挑战。别担心&#xff0c;本文将为你揭秘如何快速压缩PDF文件&#xff0c;让你的文档轻装上路&#xff01; 压…...

难道 Java 已经过时了?

当一门技术已经存在许多年了&#xff0c;它可能会失去竞争力&#xff0c;而后黯然退场&#xff0c;默默地离开&#xff0c;这对大部分的人来说就已经算是过时了。 Java 于 1995 年正式上线&#xff0c;至今已经走过了 27 个年头&#xff0c;在众多编程技术里算是年龄比较大的语…...

华为OD机考题(​HJ32 密码截取)

前言 经过前期的数据结构和算法学习&#xff0c;开始以OD机考题作为练习题&#xff0c;继续加强下熟练程度。有需要的可以同步练习下。 描述 Catcher是MCA国的情报员&#xff0c;他工作时发现敌国会用一些对称的密码进行通信&#xff0c;比如像这些ABBA&#xff0c;ABA&…...

【高考志愿】测绘科学与技术

目录 一、专业介绍 1.1 专业概述 1.2 专业方向 1.3 课程内容 二、就业前景 三、报考注意事项 四、测绘科学与技术专业排名 五、职业规划与未来发展 高考志愿选择测绘科学与技术专业&#xff0c;对于许多有志于空间信息技术领域发展的学生来说&#xff0c;无疑是一个极具…...

SpringBoot异步接口实现 提升吞吐量

前言 Servlet 3.0之前&#xff1a;HTTP请求由单一线程处理。Servlet 3.0之后&#xff1a;支持异步处理&#xff0c;提高系统吞吐量。 SpringBoot 异步接口实现方式 AsyncContext&#xff1a;Servlet层级&#xff0c;不常用。Callable&#xff1a;使用java.util.concurrent.C…...

C语言快速学习笔记

学习网站&#xff1a;C 语言教程 | 菜鸟教程 (runoob.com)C 语言教程 | 菜鸟教程 (runoob.com)C 语言教程 | 菜鸟教程 (runoob.com) 这个网站知识完整&#xff0c;讲解清晰。 在线C语言编程工具&#xff1a;菜鸟教程在线编辑器 (runoob.com) 国外学习网站&#xff1a;C语言介…...

如何选择易用性高的项目管理软件?

随着项目管理在各行各业的广泛应用&#xff0c;选择一款易用性高的项目管理软件变得越来越重要。易用性高的软件可以帮助企业提高工作效率&#xff0c;降低管理成本&#xff0c;同时还能提升团队之间的协作能力。那么&#xff0c;如何选择一款易用性高的项目管理软件呢&#xf…...

vue3基于uni-app 封装小程序request请求

const BASE_URL https://47.122.26.142; // 替换为你的 API 基础 URL const token uni.getStorageSync(token);const request (url: string, method: any, data {}, headers {}) > {return new Promise((resolve, reject) > {uni.request({url: ${BASE_URL}${url},m…...

YOLO在目标检测与视频轨迹追踪中的应用

YOLO在目标检测与视频轨迹追踪中的应用 引言 在计算机视觉领域&#xff0c;目标检测与视频轨迹追踪是两个至关重要的研究方向。随着深度学习技术的飞速发展&#xff0c;尤其是卷积神经网络&#xff08;CNN&#xff09;的广泛应用&#xff0c;目标检测与视频轨迹追踪的性能得到…...

版本控制系统:Git 纯应用(持续更新)

基本操作 ctrl上行键&#xff1a;上次代码 本地仓库&#xff1a;Git init 新建文件&#xff1a;touch xxxx.xxx 查看状态&#xff1a;Git status 文件从工作区——暂存区&#xff1a;Git add ./文件名(.是通配符代表所有) 暂存区——仓库&#xff1a;Git commit -m &…...

从0开始搭建vue项目

#先查下电脑有没有安装过node和npm node -v npm -v #安装vue npm install -g vue #安装webpack npm install webpack -g 都安装好后&#xff0c;进入你想创建的文件夹内 创建名字&#xff1a;vue init webpack <project_name> 就默认回车 然后根据项目需求Y/n 比如…...

Java框架常见面试题

在Java框架面试中&#xff0c;面试官通常会考察候选人对常见Java框架的理解、使用经验以及解决问题的能力。以下是一些常见的Java框架面试题及其详细回答&#xff1a; 1. Spring框架相关问题 问题&#xff1a;Spring框架的核心组件有哪些&#xff1f;它们各自的作用是什么&am…...

linux c 应用编程定时器函数

在 Linux C 应用编程中&#xff0c;对于多线程编程中的定时器函数使用&#xff0c;通常可以借助 pthread 库和系统提供的定时器相关的函数来实现。 首先&#xff0c;常见的定时器函数有 setitimer() 和 alarm() 。setitimer() 函数可以更精确地设置定时器&#xff0c;它可以设…...

springboot的企业培训管理系统-计算机毕业设计源码50804

摘要 本研究旨在开发一套高效、安全、易用的springboot企业培训管理系统。该系统致力于提升企业的培训管理效率&#xff0c;确保培训质量与效果的持续优化。通过整合先进的AI技术&#xff0c;在企业培训管理中发挥着不可或缺的作用。 通过采用Java作为主要开发语言&#xff0c;…...

Apache Flink架构介绍

目录 一、Apache Flink架构组件栈 1.1 概述 1.2 架构图 1.3 架构分层组件说明 1.3.1 物理部署层 1.3.2 Runtime 核心层 1.3.3 API & Libraries层 二、Flink运行时架构 2.1 概述 2.2 架构图 2.3 架构角色和组件 2.3.1 Flink Clients客户端 2.3.2 JobManager 2.…...

使用 docker buildx 构建跨平台镜像

buildx是Docker官方提供的一个构建工具&#xff0c;它可以帮助用户快速、高效地构建Docker镜像&#xff0c;并支持多种平台的构建。使用buildx&#xff0c;用户可以在单个命令中构建多种架构的镜像&#xff0c;例如x86和arm架构&#xff0c;而无需手工操作多个构建命令。此外bu…...

2024微信小程序期末大作业-点奶茶微信小程序(后端nodejs-server)(附下载链接)_微信小程序期末大作业百度网盘下载

菜单展示 购物车展示&#xff1a; 提交订单&#xff1a; 支付详情页展示&#xff1a; 订单查看&#xff1a; 查看历史消费&#xff1a; 部分代码展示&#xff1a; <!--pages/home/home.wxml--> <block wx:for"{{listData}}" wx:key"itemlist&qu…...

Elasticsearch优化索引映射和设置

在Elasticsearch的世界中&#xff0c;优化索引的映射&#xff08;mapping&#xff09;和设置&#xff08;settings&#xff09;对于提高搜索性能、存储效率和系统稳定性至关重要。本文将带您深入了解如何针对Elasticsearch的索引进行优化&#xff0c;帮助您构建更高效、更可靠的…...

【基于R语言群体遗传学】-6-表型计算等位基因频率、最大似然估计方法

到目前为止&#xff0c;我们主要讨论了等位基因和基因型频率&#xff0c;以及我们如何可以从一个推断出另一个。但是&#xff0c;如果我们不知道等位基因频率&#xff0c;只知道种群中存在哪些表型呢&#xff1f;如果我们足够幸运&#xff0c;知道哪些表型对应哪些基因型&#…...

vb.net,C#强制结束进程,“优雅”的退出方式

在VB.NET中&#xff0c;Application.Exit()和Environment.Exit(0)都用于结束程序&#xff0c;但它们的使用场景和背后的逻辑略有不同。 **Application.Exit()**&#xff1a; Application.Exit()通常用于Windows Forms应用程序中。当调用Application.Exit()时&#xff0c;它会触…...

MFC CList<CRect, CRect> m_listRect;的用法

CList<CRect, CRect&> 是 MFC&#xff08;Microsoft Foundation Classes&#xff09;中定义的一个双向链表模板类&#xff0c;用于存储 CRect 对象。在使用 CList 时&#xff0c;你可以执行多种操作&#xff0c;比如添加、移除、查找和遍历元素。以下是一些常见的用法…...

哈工大机器人竞技队成立22年来4次获国际冠军

另获得中国冠/亚/季军13次、国家级科技奖励60余次、省级科技竞赛奖励50余次、多次获得“最佳技术奖”。早在2009年就力克群雄获得唯一一张亚太大学生机器人大赛的入场卷&#xff0c;代表中国在东京5场全胜获得冠军。 2013届队员王永锟填报高考志愿时&#xff0c;恰巧看到哈工大…...

Firefox浏览器网页上的按钮点击无效解决办法

我在github下点下载经常不好使&#xff0c;查了原因&#xff0c;原来是浏览器的问题。在Firefox浏览器的设置里面&#xff0c;去掉一些cookies的禁用即可。之后&#xff0c;就可以点击按钮成功响应了。...

kotlin基础之高阶函数

Kotlin中的高阶函数、内联函数以及noinline和crossinline关键字是函数式编程中的重要概念。下面我将逐一解释这些概念的定义、实现原理、使用场景以及noinline和crossinline关键字的具体用法。 高阶函数 定义&#xff1a;高阶函数是接受一个或多个函数作为参数&#xff0c;或…...

d20(184-190)-勇敢开始Java,咖啡拯救人生

目录 网络通信 网络通信三要素&#xff08;IP地址&#xff0c;端口号&#xff0c;协议 IP地址 InetAddress 端口号 协议 传输层的两个通信协议 UDP通信 java.net.Datagramsocket类 客户端 服务端 UDP通信多收多发 客户端 服务端 TCP通信 java.net.Socket类 客…...