音频重采样,生成packet格式的PCM数据

    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

测试结果:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值