音视频开发—FFmpeg 音频重采样详解
音频重采样(audio resampling)是指改变音频信号的采样率的过程。采样率(sample rate)是指每秒钟采集的音频样本数,通常以赫兹(Hz)或每秒样本数(samples per second)表示。例如,CD音频的标准采样率是44.1kHz,表示每秒钟采集44,100个样本。
文章目录
- 什么时候会用到重采样
- 重采样的技术
- 使用FFmpeg命令行对MP3文件进行重采样
- 参数说明
- 更详细的命令
- FFmpeg 对MP3文件重采样代码实现
- 流程图
- 关键步骤:
- 对MP3文件进行解码
- 初始化重采样参数
- 初始化编码器
- 音频帧转换
- 完整代码
- 效果展示
什么时候会用到重采样
-
设备兼容性:
不同的音频设备可能支持不同的采样率。例如,某些音频接口或播放设备可能只支持特定的采样率。在这种情况下,需要将音频文件重采样到兼容的采样率。 -
节省存储空间:
高采样率的音频文件占用更多的存储空间。如果需要节省存储空间或带宽,可以将音频文件重采样到较低的采样率。 -
音频质量调整:
有时候需要在高质量和低质量之间进行平衡。例如,音频工程师可能会将录音采样率设置得很高,以捕捉更多的细节,但在混音或发行时,可能会选择稍低的采样率以便于处理和分发。 -
多媒体项目:
在多媒体项目中,不同来源的音频文件可能具有不同的采样率。为了保持项目的一致性,通常需要对音频文件进行重采样,使它们具有相同的采样率。 -
音频处理和分析:
某些音频处理和分析工具对输入的采样率有特定的要求。在使用这些工具之前,可能需要对音频进行重采样。
重采样的技术
重采样涉及插值算法,例如:
-
线性插值:
是最简单的一种插值方法,但可能会引入较多的失真。 -
多项式插值(如三次样条插值):
能提供更高的精度,但计算复杂度较高。 -
窗函数插值(如Lanczos窗):
常用于高质量音频重采样,能够有效减少伪影和失真。
通过这些算法,重采样过程可以在不同的采样率之间平滑过渡,尽量保持原音频信号的质量。
使用FFmpeg命令行对MP3文件进行重采样
一个名为input.mp3的MP3文件,并且你想将其采样率更改为48kHz(48000Hz),可以使用以下命令:
ffmpeg -i input.mp3 -ar 48000 output.mp3
参数说明
-i input.mp3:指定输入文件。-ar 48000:指定新的采样率,这里是48000Hz。output.mp3:指定输出文件名。
更详细的命令
如果你还想保留原始的比特率(bitrate)和声道数(channels),可以使用更多参数:
ffmpeg -i input.mp3 -ar 48000 -ab 192k -ac 2 output.mp3
-ab 192k:指定音频比特率为192kbps。-ac 2:指定音频通道数为2(立体声)。
FFmpeg 对MP3文件重采样代码实现
流程图

关键步骤:
对MP3文件进行解码
具体流程如下:

打开输入文件: 调用 avformat_open_input 函数打开输入文件,并获得格式上下文 fmt_ctx。
读取流信息: 调用 avformat_find_stream_info 函数读取文件的流信息,以便后续处理。
查找最佳音频流: 调用 av_find_best_stream 函数查找输入文件中的最佳音频流,并返回其索引 audio_stream_index 和对应的解码器 codec。
分配解码器上下文: 调用 avcodec_alloc_context3 函数为找到的解码器分配解码器上下文 codec_ctx。
将流的参数复制到解码器上下文: 调用 avcodec_parameters_to_context 函数将输入文件中音频流的参数复制到解码器上下文中。
打开解码器: 调用 avcodec_open2 函数打开解码器,准备解码音频数据。
代码实现:
int initialize_decoder(const char *input_filename, AVFormatContext **fmt_ctx, AVCodecContext **codec_ctx, int *audio_stream_index)
{AVCodec *codec = NULL;if (avformat_open_input(fmt_ctx, input_filename, NULL, NULL) < 0){fprintf(stderr, "Could not open input file\n");return -1;}if (avformat_find_stream_info(*fmt_ctx, NULL) < 0){fprintf(stderr, "Could not find stream information\n");return -1;}*audio_stream_index = av_find_best_stream(*fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);if (*audio_stream_index < 0){fprintf(stderr, "Could not find audio stream in input file\n");return -1;}*codec_ctx = avcodec_alloc_context3(codec);if (!(*codec_ctx)){fprintf(stderr, "Could not allocate audio codec context\n");return -1;}if (avcodec_parameters_to_context(*codec_ctx, (*fmt_ctx)->streams[*audio_stream_index]->codecpar) < 0){fprintf(stderr, "Could not copy codec parameters to codec context\n");return -1;}if (avcodec_open2(*codec_ctx, codec, NULL) < 0){fprintf(stderr, "Could not open codec\n");return -1;}return 0; // 成功
}
初始化重采样参数
具体流程如下

代码实现
int initialize_resampler(SwrContext **swr_ctx, AVCodecContext *codec_ctx, AVFrame **swr_frame)
{*swr_ctx = swr_alloc_set_opts(NULL, // ctxAV_CH_LAYOUT_STEREO, // 输出的channel 的布局AV_SAMPLE_FMT_FLTP, // 输出的采样格式48000, // 输出的采样率codec_ctx->channel_layout, // 输入的channel布局codec_ctx->sample_fmt, // 输入的采样格式codec_ctx->sample_rate, // 输入的采样率0, NULL);if (!(*swr_ctx)){fprintf(stderr, "Could not allocate resampler context\n");return -1;}if (swr_init(*swr_ctx) < 0){fprintf(stderr, "Could not initialize the resampling context\n");swr_free(swr_ctx);return -1;}*swr_frame = av_frame_alloc();if (!(*swr_frame)){fprintf(stderr, "Could not allocate resampled frame\n");swr_free(swr_ctx);return -1;}return 0; // 成功
}
初始化编码器
大体流程如下:
设置输出文件格式和路径: 调用 av_guess_format 函数猜测输出文件的格式,并根据输出文件名设置格式。
分配输出格式的上下文: 调用 avformat_alloc_output_context2 函数为输出文件分配格式上下文。
创建输出流: 调用 avformat_new_stream 函数为输出文件创建一个新的音频流。
查找编码器: 调用 avcodec_find_encoder 函数查找MP3编码器。
创建编码器上下文: 调用 avcodec_alloc_context3 函数为编码器分配上下文。
设置编码器参数: 设置编码器的比特率、采样格式、采样率和声道布局等参数。
打开编码器: 调用 avcodec_open2 函数打开编码器。
给输出流设置编码器参数: 调用 avcodec_parameters_from_context 函数将编码器的参数复制到输出流。
打开输出文件: 如果输出格式不支持直接输出到文件,则调用 avio_open 函数打开输出文件。
写入文件头部: 调用 avformat_write_header 函数写入输出文件的头部信息。
初始化输出数据包: 调用 av_packet_alloc 函数初始化一个数据包,用于存储编码后的音频数据。
代码实现
int initialize_encoder(const char *output_filename, AVFormatContext **out_fmt_ctx, AVCodecContext **enc_ctx, AVStream **out_stream,AVPacket **outpack)
{AVOutputFormat *out_fmt = NULL;AVCodec *encoder = NULL;AVCodecContext *encoder_ctx = NULL;AVStream *stream = NULL;// 设置输出文件的格式与路径out_fmt = av_guess_format(NULL, output_filename, NULL);if (!out_fmt){fprintf(stderr, "could not guess file format\n");return -1;}// 打开输出格式的上下文if (avformat_alloc_output_context2(out_fmt_ctx, out_fmt, NULL, output_filename) < 0){fprintf(stderr, "could not create output context\n");return -1;}// 创建输出流stream = avformat_new_stream(*out_fmt_ctx, NULL);if (!stream){fprintf(stderr, "could not create output stream\n");return -1;}// 查找编码器encoder = avcodec_find_encoder(AV_CODEC_ID_MP3);if (!encoder){fprintf(stderr, "Codec not found\n");return -1;}// 创建编码器上下文encoder_ctx = avcodec_alloc_context3(encoder);if (!encoder_ctx){fprintf(stderr, "Could not allocate audio codec context\n");return -1;}// 设置编码器参数encoder_ctx->bit_rate = 192000; // 比特率为192kbpsencoder_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; // 采样格式encoder_ctx->sample_rate = 48000; // 采样率encoder_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // 双声道布局encoder_ctx->channels = 2;// 打开编码器if (avcodec_open2(encoder_ctx, encoder, NULL) < 0){fprintf(stderr, "Could not open codec\n");avcodec_free_context(&encoder_ctx);return -1;}// 给输出流设置编码器if (avcodec_parameters_from_context(stream->codecpar, encoder_ctx) < 0){fprintf(stderr, "Failed to copy encoder parameters to output stream\n");avcodec_free_context(&encoder_ctx);return -1;}// 打开输出文件if (!((*out_fmt_ctx)->oformat->flags & AVFMT_NOFILE)){if (avio_open(&(*out_fmt_ctx)->pb, output_filename, AVIO_FLAG_WRITE) < 0){fprintf(stderr, "Could not open output file\n");return -1;}}// 写入文件头部if (avformat_write_header((*out_fmt_ctx), NULL) < 0){fprintf(stderr, "Error occurred when opening output file\n");return -1;}*enc_ctx = encoder_ctx;*out_stream = stream;//初始化输出的数据包*outpack = av_packet_alloc();return 0; // 成功
}
音频帧转换
需要转换的帧要与编码器的参数注意对应,否则编码器无法编码
重点是swr_convert_frame
swr_convert_frame 是 FFmpeg 中用于将一个音频帧从一种格式转换为另一种格式的函数。它使用 SwrContext(重采样上下文)来执行转换,包括采样率、通道布局和采样格式的转换。
函数定义
int swr_convert_frame(SwrContext *swr_ctx, AVFrame *out_frame, const AVFrame *in_frame);
参数说明
swr_ctx:指向SwrContext的指针,包含重采样所需的上下文信息。这个上下文在之前需要通过swr_alloc_set_opts和swr_init等函数进行初始化。out_frame:指向目标AVFrame的指针,存储转换后的音频数据。这个AVFrame需要预先分配并设置好其参数,例如采样率、通道布局和采样格式等。in_frame:指向源AVFrame的指针,包含需要转换的音频数据。
返回值
函数返回一个整数,成功时返回0,失败时返回负数。
使用步骤
- 初始化
SwrContext: 使用swr_alloc_set_opts和swr_init函数初始化重采样上下文,设置输入和输出的采样率、通道布局和采样格式等。 - 分配并设置
AVFrame: 分配并设置输入和输出AVFrame,并确保out_frame的参数与SwrContext的输出参数一致。 - 调用
swr_convert_frame: 调用swr_convert_frame函数进行音频数据的转换。 - 处理转换后的音频数据: 转换完成后,可以使用
out_frame中的数据进行后续处理。
// 确保有足够的缓冲区来容纳转换后的数据swr_frame->channel_layout = AV_CH_LAYOUT_STEREO;swr_frame->format = AV_SAMPLE_FMT_FLTP;swr_frame->sample_rate = 48000;swr_frame->nb_samples = encodec_ctx->frame_size; //与编码器帧大小保持一致// 分配缓冲区if (av_frame_get_buffer(swr_frame, 0) < 0){fprintf(stderr, "Could not allocate output frame samples\n");av_frame_free(&swr_frame);return -1;}// 执行重采样if (swr_convert_frame(swr_ctx, swr_frame, frame) < 0){fprintf(stderr, "Error while converting\n");av_frame_free(&swr_frame);return -1;}// 将重采样后的帧发送给编码器if (avcodec_send_frame(encodec_ctx, swr_frame) == 0){while (avcodec_receive_packet(encodec_ctx, out_packet) == 0){// 正确设置数据包中的流索引out_packet->stream_index = out_stream->index;// 调整时间戳,使其基于输出流的时间基av_packet_rescale_ts(out_packet, encodec_ctx->time_base, out_stream->time_base);// 写入一个编码的数据包到输出文件if (av_interleaved_write_frame(out_fmt_ctx, out_packet) < 0){fprintf(stderr, "Error while writing output packet\n");break;}}}av_frame_unref(swr_frame); // 清理帧数据以便重用
完整代码
extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libswresample/swresample.h>
}
#include <string>
#include <iostream>
using namespace std;int initialize_encoder(const char *output_filename, AVFormatContext **out_fmt_ctx, AVCodecContext **enc_ctx, AVStream **out_stream,AVPacket **outpack)
{AVOutputFormat *out_fmt = NULL;AVCodec *encoder = NULL;AVCodecContext *encoder_ctx = NULL;AVStream *stream = NULL;// 设置输出文件的格式与路径out_fmt = av_guess_format(NULL, output_filename, NULL);if (!out_fmt){fprintf(stderr, "could not guess file format\n");return -1;}// 打开输出格式的上下文if (avformat_alloc_output_context2(out_fmt_ctx, out_fmt, NULL, output_filename) < 0){fprintf(stderr, "could not create output context\n");return -1;}// 创建输出流stream = avformat_new_stream(*out_fmt_ctx, NULL);if (!stream){fprintf(stderr, "could not create output stream\n");return -1;}// 查找编码器encoder = avcodec_find_encoder(AV_CODEC_ID_MP3);if (!encoder){fprintf(stderr, "Codec not found\n");return -1;}// 创建编码器上下文encoder_ctx = avcodec_alloc_context3(encoder);if (!encoder_ctx){fprintf(stderr, "Could not allocate audio codec context\n");return -1;}// 设置编码器参数encoder_ctx->bit_rate = 192000; // 比特率为192kbpsencoder_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; // 采样格式encoder_ctx->sample_rate = 48000; // 采样率encoder_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // 双声道布局encoder_ctx->channels = 2;// 打开编码器if (avcodec_open2(encoder_ctx, encoder, NULL) < 0){fprintf(stderr, "Could not open codec\n");avcodec_free_context(&encoder_ctx);return -1;}// 给输出流设置编码器if (avcodec_parameters_from_context(stream->codecpar, encoder_ctx) < 0){fprintf(stderr, "Failed to copy encoder parameters to output stream\n");avcodec_free_context(&encoder_ctx);return -1;}// 打开输出文件if (!((*out_fmt_ctx)->oformat->flags & AVFMT_NOFILE)){if (avio_open(&(*out_fmt_ctx)->pb, output_filename, AVIO_FLAG_WRITE) < 0){fprintf(stderr, "Could not open output file\n");return -1;}}// 写入文件头部if (avformat_write_header((*out_fmt_ctx), NULL) < 0){fprintf(stderr, "Error occurred when opening output file\n");return -1;}*enc_ctx = encoder_ctx;*out_stream = stream;//初始化输出的数据包*outpack = av_packet_alloc();return 0; // 成功
}int initialize_decoder(const char *input_filename, AVFormatContext **fmt_ctx, AVCodecContext **codec_ctx, int *audio_stream_index)
{AVCodec *codec = NULL;if (avformat_open_input(fmt_ctx, input_filename, NULL, NULL) < 0){fprintf(stderr, "Could not open input file\n");return -1;}if (avformat_find_stream_info(*fmt_ctx, NULL) < 0){fprintf(stderr, "Could not find stream information\n");return -1;}*audio_stream_index = av_find_best_stream(*fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);if (*audio_stream_index < 0){fprintf(stderr, "Could not find audio stream in input file\n");return -1;}*codec_ctx = avcodec_alloc_context3(codec);if (!(*codec_ctx)){fprintf(stderr, "Could not allocate audio codec context\n");return -1;}if (avcodec_parameters_to_context(*codec_ctx, (*fmt_ctx)->streams[*audio_stream_index]->codecpar) < 0){fprintf(stderr, "Could not copy codec parameters to codec context\n");return -1;}if (avcodec_open2(*codec_ctx, codec, NULL) < 0){fprintf(stderr, "Could not open codec\n");return -1;}return 0; // 成功
}int initialize_resampler(SwrContext **swr_ctx, AVCodecContext *codec_ctx, AVFrame **swr_frame)
{*swr_ctx = swr_alloc_set_opts(NULL, // ctxAV_CH_LAYOUT_STEREO, // 输出的channel 的布局AV_SAMPLE_FMT_FLTP, // 输出的采样格式48000, // 输出的采样率codec_ctx->channel_layout, // 输入的channel布局codec_ctx->sample_fmt, // 输入的采样格式codec_ctx->sample_rate, // 输入的采样率0, NULL);if (!(*swr_ctx)){fprintf(stderr, "Could not allocate resampler context\n");return -1;}if (swr_init(*swr_ctx) < 0){fprintf(stderr, "Could not initialize the resampling context\n");swr_free(swr_ctx);return -1;}*swr_frame = av_frame_alloc();if (!(*swr_frame)){fprintf(stderr, "Could not allocate resampled frame\n");swr_free(swr_ctx);return -1;}return 0; // 成功
}int main()
{AVFormatContext *fmt_ctx = NULL;AVOutputFormat *out_fmt = NULL; // 输出格式AVFormatContext *out_fmt_ctx = NULL; // 输出格式上下文AVCodecContext *codec_ctx = NULL; // 解码器上下文AVCodec *codec = NULL; // 解码器AVCodec *encodec = NULL; // 编码器AVCodecContext *encodec_ctx = NULL; // 编码器上下文AVPacket *packet;AVFrame *frame;AVStream *out_stream = NULL; // 输出流AVPacket *out_packet;int audio_stream_index;SwrContext *swr_ctx;AVFrame *swr_frame;int ret;ret = initialize_encoder("output.mp3", &out_fmt_ctx, &encodec_ctx, &out_stream,&out_packet);if (ret != 0){fprintf(stderr, "init encode failed\n");return -1;}// 初始化解码器ret = initialize_decoder("test.mp3", &fmt_ctx, &codec_ctx, &audio_stream_index);if (ret != 0){fprintf(stderr, "init decode failed\n");return -1;}packet = av_packet_alloc(); // 初始化数据包frame = av_frame_alloc();// 初始化重采样上下文ret = initialize_resampler(&swr_ctx, codec_ctx, &swr_frame);if (ret != 0){fprintf(stderr, "init resampler failed\n");return -1;}// 解码 ---- 重采样 -----编码while (av_read_frame(fmt_ctx, packet) >= 0){if (packet->stream_index == audio_stream_index){if (avcodec_send_packet(codec_ctx, packet) == 0){while (avcodec_receive_frame(codec_ctx, frame) == 0){// 确保有足够的缓冲区来容纳转换后的数据swr_frame->channel_layout = AV_CH_LAYOUT_STEREO;swr_frame->format = AV_SAMPLE_FMT_FLTP;swr_frame->sample_rate = 48000;swr_frame->nb_samples = encodec_ctx->frame_size; //与编码器帧大小保持一致// 分配缓冲区if (av_frame_get_buffer(swr_frame, 0) < 0){fprintf(stderr, "Could not allocate output frame samples\n");av_frame_free(&swr_frame);return -1;}// 执行重采样if (swr_convert_frame(swr_ctx, swr_frame, frame) < 0){fprintf(stderr, "Error while converting\n");av_frame_free(&swr_frame);return -1;}// 将重采样后的帧发送给编码器if (avcodec_send_frame(encodec_ctx, swr_frame) == 0){while (avcodec_receive_packet(encodec_ctx, out_packet) == 0){// 正确设置数据包中的流索引out_packet->stream_index = out_stream->index;// 调整时间戳,使其基于输出流的时间基av_packet_rescale_ts(out_packet, encodec_ctx->time_base, out_stream->time_base);// 写入一个编码的数据包到输出文件if (av_interleaved_write_frame(out_fmt_ctx, out_packet) < 0){fprintf(stderr, "Error while writing output packet\n");break;}}}av_frame_unref(swr_frame); // 清理帧数据以便重用}}}av_packet_unref(packet);}// 收尾工作// 写入文件尾部信息if (av_write_trailer(out_fmt_ctx) < 0){fprintf(stderr, "Error writing trailer of the output file\n");}// 关闭输出文件和释放输出上下文if (!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE)){avio_closep(&out_fmt_ctx->pb);}avformat_free_context(out_fmt_ctx);// 其他资源清理av_packet_free(&out_packet);av_frame_free(&frame);av_frame_free(&swr_frame);av_packet_free(&packet);avcodec_close(codec_ctx);avcodec_close(encodec_ctx);avformat_close_input(&fmt_ctx);swr_free(&swr_ctx);return 0;
}
效果展示
ffmpeg 可以查看MP3文件的具体参数
命令:ffmpeg -i test.mp3

Stream #0:0:
Stream:表示这是一个流。#0:0:表示这是第一个输入文件中的第一个流(通常第一个流是视频流,第二个流是音频流,依此类推)。
Audio: mp3:
Audio:表示这个流是一个音频流。mp3:表示音频编码格式是 MP3(MPEG Audio Layer III)。
44100 Hz:
- 表示音频采样率为 44.1 kHz(44100 赫兹),即每秒钟采集 44100 次音频样本。
stereo:
- 表示音频是立体声(双声道),即音频信号包含两个独立的声道。
fltp:
- 表示音频采样格式是
float planar(浮点平面格式),即每个音频样本用浮点数表示,并且每个声道的数据存储在单独的平面中。
320 kb/s:
- 表示音频比特率为 320 kbps(千比特每秒),即每秒钟有 320000 比特的数据传输速率。
经过重采样之后,再次查看参数

音频采样率改为了 48 kHz(48000 赫兹),并且正常播放
相关文章:
音视频开发—FFmpeg 音频重采样详解
音频重采样(audio resampling)是指改变音频信号的采样率的过程。采样率(sample rate)是指每秒钟采集的音频样本数,通常以赫兹(Hz)或每秒样本数(samples per second)表示。…...
统计本地端口占用情况
要查看MongoDB是否正在备份,可以通过以下几种方法: 查看MongoDB的进程列表: 使用命令ps -ef | grep mongo,这将列出所有正在运行的MongoDB进程。在输出的列表中,你可以查看是否有与备份相关的进程或任务正在运行。 查…...
【MySQL精通之路】SQL优化(1)-查询优化(9)-外部联接优化
主博客: 【MySQL精通之路】SQL优化(1)-查询优化-CSDN博客 上一篇: 【MySQL精通之路】SQL优化(1)-查询优化(8)-嵌套联接优化-CSDN博客 下一篇: 【MySQL精通之路】SQL优化(1)-查询优化(10)-外部联接简化-CSDN博客 外部联接包括LEFT JOIN和…...
Python应用开发——30天学习Streamlit Python包进行APP的构建(1)
关于 #30天学Streamlit #30天学Streamlit 是一个旨在帮助你学习构建 Streamlit 应用的编程挑战。 你将学会: 如何搭建一个编程环境用于构建 Streamlit 应用构建你的第一个 Streamlit 应用学习所有好玩的、能用在 Streamlit 应用里的输入输出组件🗓️ 天 1 设置本地开发环境…...
轻兔推荐 —— 一个好用的软件服务推荐平台
给大家推荐一个好用的的软件服务推荐平台:轻兔推荐 网站界面简洁大方,没有太多杂七杂八的功能和页面,有明暗主题色可以选择,默认为亮色,可在网站上方手动切换。 每工作日都会推荐一款软件,有时会加更&…...
LeetCode hot100-57-G
17. 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。不会,放IDEA里执行了一下大概理解了流程 …...
基于Vue uni-app的自定义列表表格信息展示组件
摘要:随着软件技术的不断发展,前端开发面临着越来越多的挑战。特别是在业务场景复杂多变的情况下,如何提高开发效率和降低维护成本成为了关键。本文旨在探讨组件化开发在前端应用中的重要性,并以Vue uni-app自定义列表表格为例&am…...
计网(部分在session学习章)
TCP/UDP TCP:面向连接,先三次握手建立连接,可靠传输。 UDP:无连接,不可靠,传递的快。 TCP可靠传输 1.分块编号传输; 2.校验和,校验首部和数据的检验和,检测数据在传输中的变化; 3.丢弃重复数据; 4.流量控制,TCP 利⽤滑动窗⼝实现流量控制。TCP的拥塞控制采⽤…...
TypeScript 枚举
什么是 TypeScript 枚举? 简单来说,枚举是一种用于命名一组常量的数据类型。在 TypeScript 中,枚举允许我们定义一个命名的常量集合,并为这些常量分配相关的数值。通过枚举,我们可以为一组相关的值提供一个友好的名字…...
(1) 初识QT5
文章目录 Qt Quickdemo信号的命名方式 qml语言一个很重要的概念 qt 模块 Qt Quick Qt Quick是Qt5中⽤户界⾯技术的涵盖。Qt Quick⾃⾝包含了以下⼏种技术: QML-使⽤于⽤户界⾯的标识语⾔JavaScript-动态脚本语⾔Qt C具有⾼度可移植性的C库. 类似HTML语⾔…...
2024年认证杯二阶段数学建模赛题浅析
一图流 问题模型复杂度数据收集难度数据处理难度实现难度专业知识需求A题中高中中中材料科学、热物理、机械工程B题高高高高生物力学、神经学、医学成像C题高高高高环境科学、气象学、气候工程D题中中高高中高机器学习、数据科学、AI设计 【腾讯文档】2024年认证杯二阶段资料助…...
Redis教程(十八):Redis的Redisson的看门狗机制
传送门:Redis教程汇总篇,让你从入门到精通 Redisson的看门狗机制 Redisson的看门狗机制主要是指客户端在获取到锁之后,通过后台线程或定时任务自动续期的功能,以避免在锁持有期间因为处理时间过长而导致锁自动释放,进而确保操作的安全性与原子性。 这个机制的工作原理是…...
docker-compose 映射端口失败! docker端口映射失败 ,docker映射只能使用老端口,映射无法使用
1. 现象 使用docker-compose 启动项目,发现映射端口出现问题,不能映射端口! 如图: 使用原来端口是可以使用的 2. 问题原因: 使用了docker-mode 为host模式,所以不能换端口,只能写为"8086:…...
AIGC笔记--基于PEFT库使用LoRA
1--相关讲解 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS LoRA 在 Stable Diffusion 中的三种应用:原理讲解与代码示例 PEFT-LoRA 2--基本原理 固定原始层,通过添加和训练两个低秩矩阵,达到微调模型的效果; 3--简单代…...
yolo 算法 易主
标题:YOLOv10: Real-Time End-to-End Object Detection 论文:https://arxiv.org/pdf/2405.14458ethttps%3A//arxiv.org/pdf/2405.14458.zhihu.com/?targethttps%3A//arxiv.org/pdf/2405.14458 源码:https://github.com/THU-MIG/yolov10 分析…...
用这8种方法在海外媒体推广发稿平台上获得突破-华媒舍
在今天的数字时代,海外媒体推广发稿平台已经成为了许多机构和个人宣传和推广的有效途径。如何在这些平台上获得突破并吸引更多的关注是一个关键问题。本文将介绍8种方法,帮助您在海外媒体推广发稿平台上实现突破。 1. 确定目标受众 在开始使用海外媒体推…...
怎么调试前端文件:一步步揭开前端调试的神秘面纱
怎么调试前端文件:一步步揭开前端调试的神秘面纱 在前端开发的世界中,调试是一项至关重要的技能。它能够帮助我们定位并解决代码中的错误,提升应用的性能和用户体验。本文将从四个方面、五个方面、六个方面和七个方面,为你揭示前…...
【深入学习Redis丨第一篇】Redis服务器部署详解
前言 小伙伴们大家好,我是陈橘又青,今天起 《深入学习Redis》 专栏开始更新。本专栏将专为希望深入了解Redis的开发者、系统架构师以及数据库爱好者而写的免费专栏。从Redis的基本概念入手,逐步深入到其内部实现和高级用法。希望能帮助你更好…...
git教程(IDEA + 命令行)
首先假设你已经安装 git 且 已经初始化完成: // 初始化git config --global user.name "你的用户名" git config --global user.email "你的邮箱"在当前文件夹下创建一个仓库,且该文件夹下会有多个项目 首先在当前文件夹下新建git…...
树莓派部署harbor_arm64
文章目录 树莓派4b部署Harbor-arm64版本docker-compose维护命令访问harbor 192.168.1.111认用户名密码admin/Harbor12345 树莓派4b部署Harbor-arm64版本 harbor-arm版本 部署:参考 wget https://github.com/hzliangbin/harbor-arm64/releases/download/v1.9.3/ha…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
