【音视频】⾳频处理基本概念及⾳频重采样
一、重采样
1.1 什么是重采样
所谓的重采样,就是改变⾳频的采样率、sample format
、声道数等参数,使之按照我们期望的参数输出。
1.2 为什么要重采样
为什么要重采样?
-
当然是原有的⾳频参数不满⾜我们的需求,⽐如在FFmpeg解码⾳频的时候,不同的⾳源有不同的格式,采样率等,在解码后的数据中的这些参数也会不⼀致(最新FFmpeg 解码⾳频后,⾳频格式为
AV_SAMPLE_FMT_FLTP
,这个参数应该是⼀致的),如果我们接下来需要使⽤解码后的⾳频数据做其他操作,⽽这些参数的不⼀致导致会有很多额外⼯作,此时直接对其进⾏重采样,获取我们制定的⾳频参数,这样就会⽅便很多。 -
再⽐如在将⾳频进⾏SDL播放时候,因为当前的SDL2.0不⽀持planar格式,也不⽀持浮点型的,⽽最新的FFMPEG 16年会将⾳频解码为AV_SAMPLE_FMT_FLTP格式,因此此时就需要我们对其重采样,使之可以在SDL2.0上进⾏播放。
1.3 可调节的参数
通过重采样,我们可以对:
- sample rate(采样率)
- sample format(采样格式)
- channel layout(通道布局,可以通过此参数获取声道数)
二、 对应参数解析
2.1 采样率
采样设备每秒抽取样本的次数
2.2 采样格式及量化精度(位宽)
每种⾳频格式有不同的量化精度(位宽),位数越多,表示值就越精确,声⾳表现⾃然就越精准。
FFMpeg中⾳频格式有以下⼏种,每种格式有其占⽤的字节数信息
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
2.3 分⽚(plane)和打包(packed)
-
以双声道为例,带P(
plane
)的数据格式在存储时,其左声道和右声道的数据是分开存储的,左声道的数据存储在data[0]
,右声道的数据存储在data[1]
,每个声道的所占⽤的字节数为linesize[0]
和linesize[1]
-
不带P(
packed
)的⾳频数据在存储时,是按照LRLRLR…的格式交替存储在data[0]
中,linesize[0]
表示总的数据量。
2.4 声道分布(channel_layout)
声道分布在FFmpeg\libavutil\channel_layout.h
中有定义,⼀般来说⽤的⽐较多的是AV_CH_LAYOUT_STEREO
(双声道)和AV_CH_LAYOUT_SURROUND
(三声道)
这两者的定义如下:
#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
2.5 ⾳频帧的数据量计算
- ⼀帧⾳频的数据量(字节)=
channel
数* nb_samples
样本数*
每个样本占⽤的字节数 - 如果该⾳频帧是FLTP格式的PCM数据,包含
1024
个样本,双声道,那么该⾳频帧包含的⾳频数据量是2*1024*4=8192
字节。 AV_SAMPLE_FMT_DBL : 2*1024*8 = 16384
2.6 ⾳频播放时间计算
-
以采样率44100Hz来计算,每秒44100个sample,⽽正常⼀帧为1024个sample,可知每帧播放时间
/1024=1000ms/44100
,得到每帧播放时间=1024*1000/44100=23.2ms
(更精确的是23.21995464852608
)。 -
⼀帧播放时间(毫秒) =
nb_samples
样本数*1000/
采样率 -
(1)
1024*1000/44100=23.21995464852608ms
->约等于23.2ms
,精度损失了0.011995464852608ms
,如果累计10
万帧,误差>1199
毫秒,如果有视频⼀起的就会有⾳视频同步的问题。 如果按着23.2
去计算pts
(0 23.2 46.4
)就会有累积误差。 -
(2)
1024*1000/48000=21.33333333333333ms
三、FFmpeg重采样API
分配⾳频重采样的上下⽂
struct SwrContext *swr_alloc(void)
当设置好相关的参数后,使⽤此函数来初始化SwrContext结构体
int swr_init(struct SwrContext *s);
分配SwrContext并设置/重置常⽤的参数。
struct SwrContext *swr_alloc_set_opts(struct SwrContext *s, // ⾳频重采样上下⽂int64_t out_ch_layout, // 输出的layout, 如:5.1声道enum AVSampleFormat out_sample_fmt, // 输出的采样格式。Float, S16,⼀般选⽤是s16 绝⼤部分声卡⽀持int out_sample_rate, //输出采样率int64_t in_ch_layout, // 输⼊的layoutenum AVSampleFormat in_sample_fmt, // 输⼊的采样格式int in_sample_rate, // 输⼊的采样率int log_offset, // ⽇志相关,不⽤管先,直接为0void *log_ctx // ⽇志相关,不⽤管先,直接为NULL
);
将输⼊的⾳频按照定义的参数进⾏转换并输出
- 返回值
<= out_count
, in
和in_count
可以设置为0,以最后刷新最后⼏个样本。
int swr_convert(struct SwrContext *s, // ⾳频重采样的上下⽂uint8_t **out, // 输出的指针。传递的输出的数组int out_count, //输出的样本数量,不是字节数。单通道的样本数量。const uint8_t **in , //输⼊的数组,AVFrame解码出来的DATAint in_count // 输⼊的单通道的样本数量。
);
释放掉SwrContext
结构体并将此结构体置为NULL
void swr_free(struct SwrContext **s);
-
与lswr的交互是通过SwrContext完成的,SwrContext被分配给swr_alloc()或
swr_alloc_set_opts()。 它是不透明的,所以所有参数必须使⽤AVOptions API设置。 -
为了使⽤lswr,你需要做的第⼀件事就是分配SwrContext。 这可以使⽤swr_alloc()或swr_alloc_set_opts()来完成。 如果您使⽤前者,则必须通过AVOptions API设置选项。 后⼀个函数提供了相同的功能,但它允许您在同⼀语句中设置⼀些常⽤选项。
-
例如,以下代码将设置从平⾯浮动样本格式到交织的带符号16位整数的转换,从48kHz到44.1kHz的下采样,以及从5.1声道到⽴体声的下混合(使⽤默认混合矩阵)。 这是使⽤swr_alloc()函数。
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_POINT1, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);av_opt_set_int(swr, "in_sample_rate", 48000, 0);
av_opt_set_int(swr, "out_sample_rate", 44100, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
同样的⼯作也可以使⽤swr_alloc_set_opts():
SwrContext *swr = swr_alloc_set_opts(NULL, // we're allocating a new_contextAV_CH_LAYOUT_STEREO, // out_ch_layoutAV_SAMPLE_FMT_S16, // out_sample_fmt44100, // out_sample_rateAV_CH_LAYOUT_5POINT1, // in_ch_layoutAV_SAMPLE_FMT_FLTP, // in_sample_fmt48000, // in_sample_rate0, // log_offsetNULL); // log_ctx
-
⼀旦设置了所有值,它必须⽤swr_init()初始化。 如果需要更改转换参数,可以使⽤AVOptions来更改参数,如上⾯第⼀个例⼦所述; 或者使⽤swr_alloc_set_opts(),但是第⼀个参数是分配的上下⽂。 您必须再次调⽤swr_init()。
-
转换本身通过重复调⽤swr_convert()来完成。 请注意,如果提供的输出空间不⾜或采样率转换完成后,样本可能会在swr中缓冲,这需要“未来”样本。 可以随时通过使⽤swr_convert()(in_count可以设置为0)来检索不需要将来输⼊的样本。 在转换结束时,可以通过调⽤具有NULL in和in incount的swr_convert()来刷新重采样缓冲区。
实现流程
添加输出文件
在main
函数参数中加入输出文件,这里设置为out.pcm
打开输出文件
二进制写方式打开
FILE *dst_file;
dst_filename = argv[1];
dst_file = fopen(dst_filename, "wb");
设置输入参数和输出参数
- 这里的输入源是我们手动生成了,生成的是一个正弦波
- 手动设置一下输入源的参数信息
// 输入参数
int64_t src_ch_layout = AV_CH_LAYOUT_STEREO;
int src_rate = 48000;
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_DBL;
int src_nb_channels = 0;
uint8_t **src_data = NULL; // 二级指针
int src_linesize;
int src_nb_samples = 1024;
- 设置输出参数,改变采样率和采样格式
// 输出参数
int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;
int dst_rate = 44100;
enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
int dst_nb_channels = 0;
uint8_t **dst_data = NULL; //二级指针
int dst_linesize;
int dst_nb_samples;
int max_dst_nb_samples;
- 创建重采样上下文对象
- 分配重采样上下文内存
- 设置重采样参数
- 初始化重采样
struct SwrContext *swr_ctx;
swr_ctx = swr_alloc();
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);/* initialize the resampling context */
if ((ret = swr_init(swr_ctx)) < 0) {fprintf(stderr, "Failed to initialize the resampling context\n");goto end;
}
- 需要根据通道格式获取通道的数量
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
- 根据输入(输出)参数(采样率、音频格式、通道数)分配内存
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, src_nb_channels,src_nb_samples, src_sample_fmt, 0);dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
// 分配输出缓存内存
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels,dst_nb_samples, dst_sample_fmt, 0);
- 根据输入的采样率和输入的采样点计算出输出文件的总采样数量
- 这里的采样点指的是一帧采样的总采样点,是循环进行这样每一帧的采样
AV_ROUND_UP
是上取整,因为存在小数
max_dst_nb_samples = dst_nb_samples =av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
生成输入输出数据
- 生成输入源,因为输入格式使用的是
packet
模式的格式 - 每次生成数据计算时间
t
,我们只生成10s
数据
fill_samples((double *)src_data[0], src_nb_samples, src_nb_channels, src_rate, &t);
fill_samples
函数用于生成一个440Hz
频率的正弦波- 这里是打包模式,只有
src_data[0]
有数据,并且每个采样点依次输入到每个channel
中
static void fill_samples(double *dst, int nb_samples, int nb_channels, int sample_rate, double *t)
{int i, j;double tincr = 1.0 / sample_rate, *dstp = dst;const double c = 2 * M_PI * 440.0;/* generate sin tone with 440Hz frequency and duplicated channels */for (i = 0; i < nb_samples; i++) {*dstp =sin(c * *t);for (j = 1; j < nb_channels; j++)dstp[j] = dstp[0];dstp += nb_channels;*t += tincr;}
}
计算采样点偏差
- 由于我们转换后的采样点不能整除,因此会在重采样器中缓存一部分采样点
- 我们就需要动态拿出这个缓存的采样点,如果采样点大于之前的最大采样点,那么我就需要重新分配输出样本的内存,并且重新设置最大采样点
int64_t delay = swr_get_delay(swr_ctx, src_rate);dst_nb_samples = av_rescale_rnd(delay + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);if (dst_nb_samples > max_dst_nb_samples) {av_freep(&dst_data[0]);ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,dst_nb_samples, dst_sample_fmt, 1);if (ret < 0)break;max_dst_nb_samples = dst_nb_samples;
重采样转换
- 根据输入输出的样本数将输入数据重采样转换到输出数据
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);
if (ret < 0) {fprintf(stderr, "Error while converting\n");goto end;
}
写入文件
- 获取
dst
数据的大小,要根据通道数、音频格式,以及linesize
来推断 - 获取数据大小后,写入相应大小的数据到文件
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);
if (ret < 0) {fprintf(stderr, "Error while converting\n");goto end;
}
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,ret, dst_sample_fmt, 1);
if (dst_bufsize < 0) {fprintf(stderr, "Could not get sample buffer size\n");goto end;
}
fwrite(dst_data[0], 1, dst_bufsize, dst_file);
整体转换代码
- 整体转换的代码如下,只重采样
10s
的数据
t = 0;do {/* generate synthetic audio */// 生成输入源fill_samples((double *)src_data[0], src_nb_samples, src_nb_channels, src_rate, &t);/* compute destination number of samples */int64_t delay = swr_get_delay(swr_ctx, src_rate);dst_nb_samples = av_rescale_rnd(delay + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);if (dst_nb_samples > max_dst_nb_samples) {av_freep(&dst_data[0]);ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,dst_nb_samples, dst_sample_fmt, 1);if (ret < 0)break;max_dst_nb_samples = dst_nb_samples;}// int fifo_size = swr_get_out_samples(swr_ctx,src_nb_samples);// printf("fifo_size:%d\n", fifo_size);// if(fifo_size < 1024)// continue;/* convert to destination format */// ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);if (ret < 0) {fprintf(stderr, "Error while converting\n");goto end;}dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,ret, dst_sample_fmt, 1);if (dst_bufsize < 0) {fprintf(stderr, "Could not get sample buffer size\n");goto end;}printf("t:%f in:%d out:%d\n", t, src_nb_samples, ret);fwrite(dst_data[0], 1, dst_bufsize, dst_file);} while (t < 10);
冲刷重采样器
- 冲刷重采样,主要是为了将剩余的数据写入到文件中
- 输入数据写为
NULL
即可
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, NULL, 0);
if (ret < 0) {fprintf(stderr, "Error while converting\n");goto end;
}
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,ret, dst_sample_fmt, 1);
if (dst_bufsize < 0) {fprintf(stderr, "Could not get sample buffer size\n");goto end;
}fwrite(dst_data[0], 1, dst_bufsize, dst_file);
结束工作
- 释放重采样器内存、释放输入输出缓冲区内存
- 关闭输出文件
fclose(dst_file);if (src_data)av_freep(&src_data[0]);
av_freep(&src_data);if (dst_data)av_freep(&dst_data[0]);
av_freep(&dst_data);swr_free(&swr_ctx);
完整代码
main.c
/** Copyright (c) 2012 Stefano Sabatini** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:** The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.*//*** @example resampling_audio.c* libswresample API use example.*/#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>static int get_format_from_sample_fmt(const char **fmt,enum AVSampleFormat sample_fmt)
{int i;struct sample_fmt_entry {enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le;} sample_fmt_entries[] = {{ AV_SAMPLE_FMT_U8, "u8", "u8" },{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
};*fmt = NULL;for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {struct sample_fmt_entry *entry = &sample_fmt_entries[i];if (sample_fmt == entry->sample_fmt) {*fmt = AV_NE(entry->fmt_be, entry->fmt_le);return 0;}}fprintf(stderr,"Sample format %s not supported as output format\n",av_get_sample_fmt_name(sample_fmt));return AVERROR(EINVAL);
}/*** Fill dst buffer with nb_samples, generated starting from t. 交错模式的*/
static void fill_samples(double *dst, int nb_samples, int nb_channels, int sample_rate, double *t)
{int i, j;double tincr = 1.0 / sample_rate, *dstp = dst;const double c = 2 * M_PI * 440.0;/* generate sin tone with 440Hz frequency and duplicated channels */for (i = 0; i < nb_samples; i++) {*dstp =sin(c * *t);for (j = 1; j < nb_channels; j++)dstp[j] = dstp[0];dstp += nb_channels;*t += tincr;}
}int main(int argc, char **argv)
{// 输入参数int64_t src_ch_layout = AV_CH_LAYOUT_STEREO;int src_rate = 48000;enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_DBL;int src_nb_channels = 0;uint8_t **src_data = NULL; // 二级指针int src_linesize;int src_nb_samples = 1024;// 输出参数int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;int dst_rate = 44100;enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;int dst_nb_channels = 0;uint8_t **dst_data = NULL; //二级指针int dst_linesize;int dst_nb_samples;int max_dst_nb_samples;// 输出文件const char *dst_filename = NULL; // 保存输出的pcm到本地,然后播放验证FILE *dst_file;int dst_bufsize;const char *fmt;// 重采样实例struct SwrContext *swr_ctx;double t;int ret;if (argc != 2) {fprintf(stderr, "Usage: %s output_file\n""API example program to show how to resample an audio stream with libswresample.\n""This program generates a series of audio frames, resamples them to a specified ""output format and rate and saves them to an output file named output_file.\n",argv[0]);exit(1);}dst_filename = argv[1];dst_file = fopen(dst_filename, "wb");if (!dst_file) {fprintf(stderr, "Could not open destination file %s\n", dst_filename);exit(1);}// 创建重采样器/* create resampler context */swr_ctx = swr_alloc();if (!swr_ctx) {fprintf(stderr, "Could not allocate resampler context\n");ret = AVERROR(ENOMEM);goto end;}// 设置重采样参数/* set options */// 输入参数av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);// 输出参数av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0);av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);// 初始化重采样/* initialize the resampling context */if ((ret = swr_init(swr_ctx)) < 0) {fprintf(stderr, "Failed to initialize the resampling context\n");goto end;}/* allocate source and destination samples buffers */// 计算出输入源的通道数量src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);// 给输入源分配内存空间ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, src_nb_channels,src_nb_samples, src_sample_fmt, 0);if (ret < 0) {fprintf(stderr, "Could not allocate source samples\n");goto end;}/* compute the number of converted samples: buffering is avoided* ensuring that the output buffer will contain at least all the* converted input samples */// 计算输出采样数量max_dst_nb_samples = dst_nb_samples =av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);printf("max_dst_nb_samples = %d\n",max_dst_nb_samples);/* buffer is going to be directly written to a rawaudio file, no alignment */dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);// 分配输出缓存内存ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels,dst_nb_samples, dst_sample_fmt, 0);printf("linesize = %d\n",dst_linesize);if (ret < 0) {fprintf(stderr, "Could not allocate destination samples\n");goto end;}t = 0;do {/* generate synthetic audio */// 生成输入源fill_samples((double *)src_data[0], src_nb_samples, src_nb_channels, src_rate, &t);/* compute destination number of samples */int64_t delay = swr_get_delay(swr_ctx, src_rate);dst_nb_samples = av_rescale_rnd(delay + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);if (dst_nb_samples > max_dst_nb_samples) {av_freep(&dst_data[0]);ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,dst_nb_samples, dst_sample_fmt, 1);if (ret < 0)****break;max_dst_nb_samples = dst_nb_samples;}// int fifo_size = swr_get_out_samples(swr_ctx,src_nb_samples);// printf("fifo_size:%d\n", fifo_size);// if(fifo_size < 1024)// continue;/* convert to destination format */// ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);if (ret < 0) {fprintf(stderr, "Error while converting\n");goto end;}dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,ret, dst_sample_fmt, 1);if (dst_bufsize < 0) {fprintf(stderr, "Could not get sample buffer size\n");goto end;}printf("t:%f in:%d out:%d\n", t, src_nb_samples, ret);fwrite(dst_data[0], 1, dst_bufsize, dst_file);} while (t < 10);ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, NULL, 0);if (ret < 0) {fprintf(stderr, "Error while converting\n");goto end;}dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,ret, dst_sample_fmt, 1);if (dst_bufsize < 0) {fprintf(stderr, "Could not get sample buffer size\n");goto end;}printf("dst_bufsize = %d\n",dst_bufsize);printf("flush in:%d out:%d\n", 0, ret);fwrite(dst_data[0], 1, dst_bufsize, dst_file);if ((ret = get_format_from_sample_fmt(&fmt, dst_sample_fmt)) < 0)goto end;fprintf(stderr, "Resampling succeeded. Play the output file with the command:\n""ffplay -f %s -channel_layout %"PRId64" -channels %d -ar %d %s\n",fmt, dst_ch_layout, dst_nb_channels, dst_rate, dst_filename);end:fclose(dst_file);if (src_data)av_freep(&src_data[0]);av_freep(&src_data);if (dst_data)av_freep(&dst_data[0]);av_freep(&dst_data);swr_free(&swr_ctx);return ret < 0;
}
更多资料:https://github.com/0voice
相关文章:
【音视频】⾳频处理基本概念及⾳频重采样
一、重采样 1.1 什么是重采样 所谓的重采样,就是改变⾳频的采样率、sample format、声道数等参数,使之按照我们期望的参数输出。 1.2 为什么要重采样 为什么要重采样? 当然是原有的⾳频参数不满⾜我们的需求,⽐如在FFmpeg解码⾳频的时候…...
自然语言处理将如何颠覆未来教育?个性化学习新纪元
教育领域正经历着自印刷术发明以来最深刻的变革。自然语言处理(NLP)技术突破传统教育框架的桎梏,正在重塑人类知识传递的基本范式。这场变革的实质不在于教学工具的数字化升级,而在于重新定义了"教"与"学"的本…...
4月25日星期五今日早报简报微语报早读
4月25日星期五,农历三月廿八,早报#微语早读。 1、祝贺!神舟二十号载人飞船发射取得圆满成功; 2、文旅部:今年一季度国内出游人次17.94亿,同比增长26.4%; 3、2025五一档新片预售票房破1000万&…...
秒级到毫秒:BFD的速度革命
一、BFD技术概述 双向转发检测(BFD)是一种轻量级的网络协议,专门用于快速检测、监控网络链路或IP路由的连通性状态。作为网络领域的"心跳检测器",BFD通过毫秒级(默认1000ms)的快速探测机…...
systemctl 命令详解与常见问题解决
在 Linux 系统中,service 命令和 chkconfig 命令一直用于管理服务,但随着 systemd 的引入,systemctl 命令逐渐成为主流。systemctl 命令不仅功能强大,而且使用简单。本文将详细介绍 systemctl 命令的作用以及常见问题的解决方法。…...
5.6 Microsoft Semantic Kernel:专注于将LLM集成到现有应用中的框架
5.6.1 Semantic Kernel概述 Microsoft Semantic Kernel(以下简称SK)是一个开源的软件开发工具包(SDK),旨在帮助开发者将大型语言模型(LLM)无缝集成到现有的应用程序中。它支持C#、Python和Java…...
vite+vue构建的网站项目localhost:5173打不开
原因:关掉了cmd命令提示符,那个端口就没有被配置上,打开就是这样的。 解决方法:重新在工作目录下打开cmd,输入npm run dev重新启动项目。 重新出现这样的界面说明已经成功启动项目,再次在浏览器中刷新并输入…...
电脑屏幕录制软件Captura源码编译(Win10,VS2022)
屏幕录像的意义: 教育教学方面 制作教学资源:教师可以通过录制屏幕来制作教学视频,演示软件操作、讲解复杂的知识点等。学生可以随时观看这些视频,便于复习和巩固知识,尤其对于一些抽象的概念或难以在课堂上一次性掌握…...
【版本控制】SVN + TortoiseSVN版本管理实用教程(附安装+开发常用操作)
摘要: 本文将带你从零开始掌握 SVN 版本控制系统,结合 TortoiseSVN 图形客户端工具,深入学习包括安装、检出、提交、更新、回滚、冲突解决等常用开发操作,快速上手团队协作! 🧩 什么是 SVN? SV…...
常见网络安全攻击类型深度剖析(二):SQL注入攻击——原理、漏洞利用演示与代码加固方法
常见网络安全攻击类型深度剖析(二):SQL注入攻击——原理、漏洞利用演示与代码加固方法 在Web应用安全领域,SQL注入(SQL Injection)是历史最悠久、危害最广泛的攻击类型之一。据OWASP(开放式Web应用安全项目)统计,SQL注入连续多年稳居“OWASP Top 10”漏洞榜单前列,每…...
DeepSeek智能时空数据分析(三):专业级地理数据可视化赏析-《杭州市国土空间总体规划(2021-2035年)》
序言:时空数据分析很有用,但是GIS/时空数据库技术门槛太高 时空数据分析在优化业务运营中至关重要,然而,三大挑战仍制约其发展:技术门槛高,需融合GIS理论、SQL开发与时空数据库等多领域知识;空…...
day49—双指针+贪心—验证回文串(LeetCode-680)
题目描述 给你一个字符串 s,最多 可以从中删除一个字符。 请你判断 s 是否能成为回文字符串:如果能,返回 true ;否则,返回 false 。 示例 1: 输入:s "aba" 输出:true…...
AI电视里的达摩
2025年,所有电视都搭载了AI功能,所有电视厂商都在宣传AI能力。但问题是,消费者依旧没有对AI电视做出什么积极的回应。“AI电视是鸡肋”“AI只是电视的又一轮泡沫”等观点层出不穷。 为什么明明AI技术能够解决电视的很多问题,但AI电…...
算力网络(CFN)在跨校联合科研中的应用:安全性挑战与联邦调度实践
引言:科研协作的算力困境 上海交通大学与麻省理工学院联合开展的高能物理模拟实验,因算力资源分配不均导致部分节点连续72小时处于空转状态。这个典型案例揭示了当前跨机构科研协作的痛点:算力资源无法实现安全可信的细粒度共享。算力网…...
面向对象编程核心:封装、继承、多态与 static 关键字深度解析
面向对象编程核心:封装、继承、多态与 static 关键字深度解析 一、封装:数据安全与接口规范 1. 封装的本质与作用 核心定义:将数据(属性)与操作数据的方法(行为)绑定在类中,隐藏内…...
c++进阶——类与继承
文章目录 继承继承的基本概念继承的基本定义继承方式继承的一些注意事项 继承类模板 基类和派生类之间的转换继承中的作用域派生类的默认成员函数默认构造函数拷贝构造赋值重载析构函数默认成员函数总结 不能被继承的类继承和友元继承与静态成员多继承及其菱形继承问题继承模型…...
【CODEMATE】进制转换(transform) 粤港澳青少年信息学创新大赛 C/C++/Python 解题思路
目录 问题描述做题思路,解决过程思路:踩过的坑:核心代码C 语言 / C 切片:C 语言 / C 判断 ‘A’ 数量:Python 切片:Python 判断 ‘A’ 数量: 完整代码C 语言 完整代码C 完整代码Python 完整代码…...
window和ubuntu自签证书
window下 以管理员身份 运行 Windows PowerShell # CN192.168.0.100 (换成自己的IP或者域名) # O(组织) OU(组织单位) # Cert:\LocalMachine\My:证书存储位置 # test_10:自定义证书名称 .AddYears(10): 证书过期时间 10 年 $cert New-SelfSi…...
ES历史版本下载
下载地址 Past Releases of Elastic Stack Software | Elastic 安装步骤参考 windows 安装 Elasticsearch_windows安装elasticsearch-CSDN博客...
技术面试一面标准流程
0. 自我介绍 ...... 1. 拷打项目 项目干了啥? 难点是啥? 问项目中用到的东西? 扩展? ...... 2. 基础知识 数据结构、C基础、设计模式 数据结构: 堆? unordered_map 和 布隆过滤器 都是用于查找…...
第14篇:Linux设备驱动程序入门<一>
Q:如何简单的理解DE1-SoC-UP Linux系统的设备驱动程序? A:设备驱动程序(Device Driver),简称驱动程序(Driver)。DE1-SoC-UP Linux系统中的设备驱动程序允许系统软件与DE1-SoC开发板…...
软件设计模式与体系结构:基于Java实现管道-过滤器架构
软件设计模式与体系结构:基于Java实现管道-过滤器架构 前言 在软件架构中,数据流风格是一种常见的架构模式,特别适用于需要对数据进行一系列处理的场景。管道-过滤器(Pipe and Filter)*架构是数据流风格的典型代表&a…...
Node.js 包管理工具介绍
Node.js 包管理工具介绍 Node.js 是一个基于 Chrome V8 JavaScript 引擎的服务器端运行环境,它允许开发者使用 JavaScript 进行后端开发。为了方便管理和维护项目中使用的第三方库和模块,Node.js 提供了多种包管理工具。本文将详细介绍几种常用的 Node.…...
Node.js 应用场景
Node.js 应用场景 引言 Node.js 是一个基于 Chrome V8 JavaScript 引擎的开源、跨平台 JavaScript 运行环境。它主要用于服务器端开发,通过非阻塞 I/O 模型实现了高并发处理能力。本文将详细介绍 Node.js 的应用场景,帮助你了解其在实际项目中的应用。…...
C/C++线程详解
一、C语言线程创建(POSIX线程) 1. 基本创建方法 POSIX线程(pthread)是C语言中创建线程的标准API: #include <pthread.h> #include <stdio.h>void* thread_func(void* arg) {printf("Thread runnin…...
动态ip与静态ip的概念、区别、应用场景
动态ip与静态ip的区别 前言 一、IP地址的概念和作用 1.1、IP地址的定义 1.2、IP地址的作用 二、动态IP和静态IP的区别 2.1、动态IP和静态IP的定义 2.2、动态IP和静态IP的特点 2.3、动态IP和静态IP的优缺点比较 三、动态IP和静态IP的应用场景 3.1. 动态IP的应用场景 3.2. 静态IP…...
P12167 [蓝桥杯 2025 省 C/Python A] 倒水
P12167 [蓝桥杯 2025 省 C/Python A] 倒水 题目描述 小蓝有 n n n 个装了水的瓶子,从左到右摆放,第 i i i 个瓶子里装有 a i a_i ai 单位的水。为了美观,小蓝将水循环染成了 k k k 种颜色,也就是说,第 i i i …...
Appium自动化开发环境搭建
自动化 文章目录 自动化前言 前言 Appium是一款开源工具,用于自动化iOS、Android和Windows桌面平台上的本地、移动web和混合应用程序。原生应用是指那些使用iOS、Android或Windows sdk编写的应用。移动网页应用是通过移动浏览器访问的网页应用(appum支持iOS和Chrom…...
【金仓数据库征文】金仓数据库:国产化浪潮下的技术突破与行业实践
目录 前言 技术突破:从追赶国际到引领创新 行业深耕:从医疗到航空航天的多领域落地 事务管理与ACID特性 事务管理概述 索引优化与性能调优 安全性与备份恢复策略 Json构造函数 总结 前言 在数字化转型的全球趋势下,数据库作为信息系…...
计算机操作系统
1. T0 时刻是否为安全状态? 步骤 1: 计算当前可用资源 总资源数量: A: 17B: 5C: 20 已分配资源: P1: (2, 1, 2)P2: (4, 0, 2)P3: (4, 0, 5)P4: (2, 0, 4)P5: (3, 1, 4) 当前可用资源: A: 17 - (2 4 4 2 3) 2B: 5 - (1 0 …...
linux系统问题杂谈
1.配置好anaconda之后,在一个终端中编辑好环境变量之后能够正常使用conda命令,但是新打开一个中断使用conda命令报错"无法识别conda"。 原因:使用“export PATH"/home/username/anaconda3/bin:$PATH"命令,临…...
百度打响第一枪!通用超级智能体时代,真的来了
Create2025百度AI开发者大会在武汉举行,K哥受邀参加,看到了许多有趣的创新技术和产品。其中最令我印象深刻的是一款全新发布的通用超级智能体——心响App。 这款App通过多智能体复杂组合、协作,满足用户能够「一站式」解决复杂问题的使用诉求…...
FWFT_FIFO和Standard_FIFO对比仿真
在FPGA中使用FIFO时,如果使用FPGA厂商提供的FIFO IP,一般都会有First Word Fall Through FIFO和Standard FIFO类型选项,那么这两种FIFO有什么差异么。两种FIFO的端口是一样的,看不出区别,只有通过仿真,才能…...
【网络原理】TCP提升效率机制(二):流量控制和拥塞控制
目录 一. 前言 二. 流量控制 三. 拥塞控制 一. 前言 TCP的可靠传输依靠确认应答机制,超时重传机制是对确认应答的一种补充,解决了丢包问题 为了提高传输效率,避免大量的时间都浪费在等待应答的过程,故引入了滑动窗口机制&…...
DeepSeek+Cline:开启自动化编程新纪元
目录 一、引言:AI 编程时代的曙光二、认识 DeepSeek 和 Cline2.1 DeepSeek 是什么2.2 Cline 详解2.3 两者结合的魅力 三、DeepSeek Cline 安装与配置全流程3.1 安装 VS Code3.2 安装 Cline 插件3.3 获取 DeepSeek API Key3.4 配置 Cline 与 DeepSeek 连接 四、实战演…...
【RedisLockRegistry】分布式锁
RedisLockRegistry分布式锁 介绍 RedisLockRegistry是Spring框架提供的一种分布式锁机制,它基于Redis来实现对共享资源的保护,防止多个进程同时对同一资源进行修改,从而避免数据不一致或其他问题 基本原理 RedisLockRegistry通过Redi…...
脚本批量启动Node服务器
创建文件start-projects.ps1 定义项目路径(使用PowerShell中更可靠的路径表示方式) $变量A “E:/XXXX文件根目录” $变量B “E:/XXXX” $变量C “E:/XXXX” 打开变量A并执行npm run dev Start-Process powershell -ArgumentList “-NoExit”, “-Com…...
使用命令行加密混淆C#程序
C#作为托管语言编译生成的IL中间代码极易被反编译工具还原源码。据统计,超过83%的商业软件曾遭遇过代码逆向风险,导致核心算法泄露、授权被跳过. 因此对于C#语言开发的程序来说, 在发布前进行混淆和加密非常有必要. 本文主要介绍如何使用恒盾C#混淆加密…...
零基础上手Python数据分析 (23):NumPy 数值计算基础 - 数据分析的加速“引擎”
写在前面 —— 超越原生 Python 列表,解锁高性能数值计算,深入理解 Pandas 的底层依赖 在前面一系列关于 Pandas 的学习中,我们已经领略了其在数据处理和分析方面的强大威力。我们学会了使用 DataFrame 和 Series 来高效地操作表格数据。但是,你是否好奇,Pandas 为何能够…...
深度学习实战106-大模型LLM+股票MCP Server的股票分析和投资建议应用场景
大家好,我是微学AI,今天给大家介绍一下深度学习实战106-大模型LLM+股票MCP Server的股票分析和投资建议应用场景。 文章目录 一、项目背景(一)大型语言模型(LLM)在金融领域的应用趋势(二)模型上下文协议(MCP)的兴起(三)大模型LLM+股票MCP服务的需求二、开发流程(…...
IDEA配置将Servlet真正布署到Tomcat
刚开始只能IDEA运行完Servlet web application 并保持IDEA运行才能通过浏览器访问到我的Servlet,跟想象中的不一样,不应该是IDEA运行完项目以后只要打开Tomcat就能访问吗?事实时运行完项目只要关掉IDEA就不能再访问到应用了,而且T…...
交叉编译paho.mqtt.c和paho.mqtt.cpp(MQTT客户端)
一、参考资料 【MQTT】paho.mqtt.cpp 库的 介绍、下载、交叉编译、MQTT客户端例子源码-CSDN博客 【MQTT】paho.mqtt.c 库的“介绍、下载、交叉编译” 详解,以及编写MQTT客户端例子源码-CSDN博客 二、准备工作 1. 重要说明 paho.mqtt.cpp与paho.mqtt.cÿ…...
Prometheus中部署Alertmanager
部署Alertmanager 是 Prometheus 生态系统中的一个重要步骤,用于管理和处理 Prometheus生成的告警。Alertmanager和Prometheus Server一样均采用Golang实现,并且没有第三方依赖。一般来说我们可以通过以下几种方式来部署Alertmanager:二进制包…...
van-field组件设置为textarea属性被软键盘遮挡问题
在移动端van-field 输入框当type为text时,调出软键盘输入框会被顶上去,但type为textarea时不会被顶上去,可以用下面方法来实现: 1. 来2个van-field type为text的输入框z-index: 1 type为textarea的输入框z-index: 9999&#x…...
websheet之 编辑器
一、默认编辑器 该单元格编辑器是控件自带的编辑器,用户不需要指定。 二、下拉选择 该单元格编辑器是控件自带的编辑器的一种。该控件需要你指定下拉的数据源。在下面的例子中,我们给C3和C6单元格指定了币种的下拉选择编辑器。参数见:六、 参…...
氢气泄漏应急预案应包括哪些内容?
氢气泄漏应急预案是科研实验室中应对氢气泄漏事故的重要文件,其内容需要全面覆盖预防、检测、响应和善后处理等环节,确保在紧急情况下能够快速、有序地采取措施,最大限度地减少事故风险和损失。以下是氢气泄漏应急预案应包括的主要内容&#…...
【每天一个知识点】IPv4(互联网协议版本4)和IPv6(互联网协议版本6)
IPv4(互联网协议版本4)和IPv6(互联网协议版本6)是用于在互联网上标识和定位设备的两种主要协议。它们的主要区别在于地址空间、结构、以及一些附加功能。以下是两者的对比: 1. 地址长度 IPv4: 地址长度为32位…...
【高频考点精讲】前端构建工具对比:Webpack、Vite、Rollup和Parcel
前端构建工具大乱斗:Webpack、Vite、Rollup和Parcel谁是你的菜? 【初级】前端开发工程师面试100题(一) 【初级】前端开发工程师面试100题(二) 【初级】前端开发工程师的面试100题(速记版) 最近在后台收到不少同学提问:“老李啊,现在前端构建工具这么多,我该选哪个?…...
牛客小白月赛115-B题:签到题
题目传送门牛客网竞赛题目 一、题目描述 给定n道题目,每道题难度为aᵢ。要从中选出m道题组成比赛,使得难度最低的题目(签到题)数量尽可能多。求签到题的最大可能数量。 输入: 第一行两个整数n,m(1≤m≤n≤210⁵)第…...
【QQMusic项目复习笔记——音乐管理模块详解】第四章
🌹 作者: 云小逸 🤟 个人主页: 云小逸的主页 🤟 motto: 要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在&…...