int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;
int src_rate = 8000;
int dst_rate = 44100;
AVSampleFormat src_fmt = AV_SAMPLE_FMT_S16;
AVSampleFormat dst_fmt = AV_SAMPLE_FMT_S32P;
const char *input_file = "./beijingbeijing_8000_mono_s16le.pcm";
const char *output_packpet_outfile = "./beijingbeijing_44100_stereo_s32le_packet.pcm";
对上面的PCM数据进行重采样,通过API swr_convert
//return number of samples output per channel
int ret = swr_convert(swr, out_data, dst_nb_samples, (const uint8_t **)in_data, src_nb_samples);
之前的设置是如下的
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", src_ch_layout, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", dst_ch_layout, 0);
av_opt_set_int(swr, "in_sample_rate", src_rate, 0);
av_opt_set_int(swr, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", src_fmt, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", dst_fmt, 0);
/* initialize the resampling context */
if ((ret = swr_init(swr)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
return -1;
}
av_opt_set_sample_fmt(swr, "out_sample_fmt", dst_fmt, 0); 设置了格式是AV_SAMPLE_FMT_S16P,所以重采样后的PCM数据是Planar: L L L L R R R R,我们需要转换为Packet: L R L R L R L R,并保存到文件中.。如果格式是AV_SAMPLE_FMT_S32,那么生成的格式就是packet模式:L R L R L R L R。
另外注意一点:重采样后的PCM数据是要从planar格式转到packet格式然后写到pcm文件中才能播放。
如果是直接写到文件中LLLLLLLLLLRRRRRRRR,这样的数据ffplay是无法播放的。因为ffplay程序无法得知有多少个samples L。所以ffplay只能播放packet数据。(这是我个人的理解,如有错误,请指正)。
源码:
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavdevice/avdevice.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
}
AVFrame *frame = NULL;
AVCodecContext *codec_ctx = NULL;
AVPacket *pkt = NULL;
int main(int argc, char *argv[])
{
int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;
int src_rate = 8000;
int dst_rate = 44100;
AVSampleFormat src_fmt = AV_SAMPLE_FMT_S16;
AVSampleFormat dst_fmt = AV_SAMPLE_FMT_S32P;
const char *input_file = "./beijingbeijing_8000_mono_s16le.pcm";
const char *output_packpet_outfile = "./beijingbeijing_44100_stereo_s32le_packet.pcm";
avdevice_register_all();
FILE *fp_in = NULL;
if ((fp_in = fopen(input_file, "rb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", input_file);
return -1;
}
FILE *fp_out_packet = NULL;
if ((fp_out_packet = fopen(output_packpet_outfile, "wb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", output_packpet_outfile);
return -1;
}
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", src_ch_layout, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", dst_ch_layout, 0);
av_opt_set_int(swr, "in_sample_rate", src_rate, 0);
av_opt_set_int(swr, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", src_fmt, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", dst_fmt, 0);
/* initialize the resampling context */
int ret = -1;
if ((ret = swr_init(swr)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
return -1;
}
uint8_t **out_data = NULL;
uint8_t **in_data = NULL;
int dst_linesize, src_linesize;
int src_nb_samples = 1024;
int max_dst_nb_samples, dst_nb_samples;
int src_channels = av_get_channel_layout_nb_channels(src_ch_layout);
int dst_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
av_log(NULL, AV_LOG_INFO, "src_channels = %d, dst_channels = %d\n", src_channels, dst_channels);
max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
av_samples_alloc_array_and_samples(&in_data, &src_linesize, src_channels, src_nb_samples, src_fmt, 0);
av_samples_alloc_array_and_samples(&out_data, &dst_linesize, dst_channels, dst_nb_samples, dst_fmt, 0);
while (feof(fp_in) == 0)
{
//输入一帧数据的长度,这儿这是PCM转换的话,length是无论大小的。后续的编码需要按照一帧数据的长度来编码
int length = src_nb_samples * av_get_bytes_per_sample(src_fmt) * src_channels;
fread(in_data[0], 1, length, fp_in);
/* compute destination number of samples */
dst_nb_samples = av_rescale_rnd(swr_get_delay(swr, src_rate) + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
//return number of samples output per channel
int convert_samples = swr_convert(swr, out_data, dst_nb_samples, (const uint8_t **)in_data, src_nb_samples);
int dst_bufsize = av_samples_get_buffer_size(NULL, dst_channels, convert_samples, dst_fmt, 1);
fprintf(stderr, "writing %d data\n", dst_bufsize);
int dst_fmt_size = av_get_bytes_per_sample(dst_fmt);
if (av_sample_fmt_is_planar(dst_fmt))
{
//conver into packet mode
for (int i = 0; i < convert_samples; i++)
for (int ch = 0; ch < dst_channels; ch++)
fwrite(out_data[ch] + dst_fmt_size * i, 1, dst_fmt_size, fp_out_packet);
}
else
{
fwrite(out_data[0], 1, dst_bufsize, fp_out_packet);
}
fflush(fp_out_packet);
}
fclose(fp_out_packet);
fclose(fp_in);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
int src_nb_samples = 1024;只是一个参考值,实际情况中可以设置如下frame->nb_samples
测试结果: