甘肃兴华建设集团网站,长春关键词搜索排名,网页设计特效网站,衡水建网站费用音频重采样#xff08;audio resampling#xff09;是指改变音频信号的采样率的过程。采样率#xff08;sample rate#xff09;是指每秒钟采集的音频样本数#xff0c;通常以赫兹#xff08;Hz#xff09;或每秒样本数#xff08;samples per second#xff09;表示。…音频重采样audio resampling是指改变音频信号的采样率的过程。采样率sample rate是指每秒钟采集的音频样本数通常以赫兹Hz或每秒样本数samples per second表示。例如CD音频的标准采样率是44.1kHz表示每秒钟采集44,100个样本。 文章目录 什么时候会用到重采样重采样的技术使用FFmpeg命令行对MP3文件进行重采样参数说明更详细的命令 FFmpeg 对MP3文件重采样代码实现流程图关键步骤对MP3文件进行解码初始化重采样参数初始化编码器音频帧转换 完整代码效果展示 什么时候会用到重采样 设备兼容性 不同的音频设备可能支持不同的采样率。例如某些音频接口或播放设备可能只支持特定的采样率。在这种情况下需要将音频文件重采样到兼容的采样率。 节省存储空间 高采样率的音频文件占用更多的存储空间。如果需要节省存储空间或带宽可以将音频文件重采样到较低的采样率。 音频质量调整 有时候需要在高质量和低质量之间进行平衡。例如音频工程师可能会将录音采样率设置得很高以捕捉更多的细节但在混音或发行时可能会选择稍低的采样率以便于处理和分发。 多媒体项目 在多媒体项目中不同来源的音频文件可能具有不同的采样率。为了保持项目的一致性通常需要对音频文件进行重采样使它们具有相同的采样率。 音频处理和分析 某些音频处理和分析工具对输入的采样率有特定的要求。在使用这些工具之前可能需要对音频进行重采样。
重采样的技术
重采样涉及插值算法例如 线性插值 是最简单的一种插值方法但可能会引入较多的失真。 多项式插值如三次样条插值 能提供更高的精度但计算复杂度较高。 窗函数插值如Lanczos窗 常用于高质量音频重采样能够有效减少伪影和失真。
通过这些算法重采样过程可以在不同的采样率之间平滑过渡尽量保持原音频信号的质量。
使用FFmpeg命令行对MP3文件进行重采样
一个名为input.mp3的MP3文件并且你想将其采样率更改为48kHz48000Hz可以使用以下命令
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表示音频编码格式是 MP3MPEG Audio Layer III。
44100 Hz
表示音频采样率为 44.1 kHz44100 赫兹即每秒钟采集 44100 次音频样本。
stereo
表示音频是立体声双声道即音频信号包含两个独立的声道。
fltp
表示音频采样格式是 float planar浮点平面格式即每个音频样本用浮点数表示并且每个声道的数据存储在单独的平面中。
320 kb/s
表示音频比特率为 320 kbps千比特每秒即每秒钟有 320000 比特的数据传输速率。
经过重采样之后,再次查看参数
音频采样率改为了 48 kHz48000 赫兹并且正常播放