Ffmpeg视频开发教程(六)——基于ffmpeg4.0的音频重采样和编码为mp3

上一篇(https://blog.csdn.net/zhangamxqun/article/details/80466371)讲了pcm编码为mp2.    使用的pcm数据是AV_SAMPLE_FMT_S16格式,双声道,采样率是44100。但是这种格式ffmpeg的mp3编码器并不支持,所以在本文中,我们先对pcm数据进行重采样,转成AV_SAMPLE_FMT_S16P格式的pcm数据,然后再编码为mp3数据。

使用的原始AV_SAMPLE_FMT_S16格式pcm测试数据还是https://download.csdn.net/download/zhangamxqun/10440053

详细的代码如下,lib库的路径请自行按自己的配置修改。

/** 
实现FFMPEG4.0把AV_SAMPLE_FMT_S16格式双声道pcm数据编码为mp3数据,作者自己测试正确可用,test.pcm数据放于exe目录下
作者:明天继续
使用的ffmpeg版本:ffmpeg-20180508-293a6e8-win32 
开发工具:vs2012 
**/  
  
#include "stdafx.h"  
#include 
#include 
#include 
#include 
#include 
extern "C" {  
#include   
#include   
#include 
#include 
#include 
#include 
#include 
#include 
 
}  

//lib库的路径请按照自己的项目重新设置
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avcodec.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avformat.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avfilter.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avutil.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/swscale.lib") 
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/swresample.lib")
  
#if _MSC_VER  
#define snprintf _snprintf_s  
#define PRIx64       "I64x"  
#define PRIX64       "I64X"  
#endif  

static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)  
{  
	const enum AVSampleFormat *p = codec->sample_fmts;  

	while (*p != AV_SAMPLE_FMT_NONE) {  
		if (*p == sample_fmt)  
			return 1;  
		p++;  
	}  
	return 0;  
}  

static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt,  
				   FILE *output)  
{  
	int ret;  

	/* 编码frame */  
	ret = avcodec_send_frame(ctx, frame);  
	if (ret < 0) {  
		fprintf(stderr, "Error sending the frame to the encoder\n");  
		exit(1);  
	}  

	/* 读取编码出的数据包,通常不止一个 */  
	while (ret >= 0) {  
		ret = avcodec_receive_packet(ctx, pkt);  
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)  
			return;  
		else if (ret < 0) {  
			fprintf(stderr, "Error encoding audio frame\n");  
			exit(1);  
		}  

		fwrite(pkt->data, 1, pkt->size, output);  
		av_packet_unref(pkt);  
	}  
}  


int _tmain(int argc, _TCHAR* argvec[])  
{  
	////////////////////////////////////重采样,准备相关变量  
	int64_t src_ch_layout = AV_CH_LAYOUT_STEREO, dst_ch_layout = AV_CH_LAYOUT_STEREO;  
	int src_rate = 44100, dst_rate = 44100;  
	uint8_t **src_data = NULL, **dst_data = NULL;  
	int src_nb_channels = 0, dst_nb_channels = 0;  
	int src_linesize, dst_linesize;  
	int src_nb_samples = 1152, dst_nb_samples, max_dst_nb_samples;  
	enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_S16, dst_sample_fmt = AV_SAMPLE_FMT_S16P;  
	//int dst_bufsize;  
	struct SwrContext *swr_ctx;  
	int ret;  
	///////////////////////////////////////////////////////////  

	av_register_all();  


	//////////////////////////////////////////////////mp3编码准备相关变量  
	//编码器  
	const AVCodec *codecMp3;  
	//编码器上下文  
	AVCodecContext *contexMp3= NULL;  
	//pcm数据帧  
	AVFrame *frameMp3In;  
	//编码后的mp3数据包  
	AVPacket *pktMp3_Out;  
	/* 找到MP3的编码器 */  
	codecMp3 = avcodec_find_encoder(AV_CODEC_ID_MP3);  
	if (!codecMp3) {  
		fprintf(stderr, "Codec not found\n");  
		exit(1);  
	}  

	//生成编码器上下文  
	contexMp3 = avcodec_alloc_context3(codecMp3);  
	if (!contexMp3) {  
		fprintf(stderr, "Could not allocate audio codec context\n");  
		exit(1);  
	}  
	/* 设置编码器上下文的bit率 */  
	contexMp3->bit_rate = 64000;   
	contexMp3->sample_fmt = AV_SAMPLE_FMT_S16P;  
	if (!check_sample_fmt(codecMp3, contexMp3->sample_fmt)) {  
		fprintf(stderr, "Encoder does not support sample format %s",  
			av_get_sample_fmt_name(contexMp3->sample_fmt));  
		exit(1);  
	}  
	/* 设置编码器的必要参数 */  
	contexMp3->sample_rate    = dst_rate;  
	contexMp3->channel_layout = AV_CH_LAYOUT_STEREO;  
	contexMp3->channels       = av_get_channel_layout_nb_channels(contexMp3->channel_layout);  
	if (avcodec_open2(contexMp3, codecMp3, NULL) < 0) {  
		fprintf(stderr, "Could not open codec\n");  
		exit(1);  
	}  
	FILE *mp3File = fopen("outmp3.mp3", "wb");  
	/* 初始化mp3编码结果数据的存储包 */  
	pktMp3_Out = av_packet_alloc();  
	if (!pktMp3_Out) {  
		fprintf(stderr, "could not allocate the packet\n");  
		exit(1);  
	}  
	/* 初始化存放pcm数据的frame */  
	frameMp3In = av_frame_alloc();  
	if (!frameMp3In) {  
		fprintf(stderr, "Could not allocate audio frame\n");  
		exit(1);  
	}  
	//设置pcm数据frame的一些参数  
	//frame size 是数据中有多少个样本,每个样本包含两个通道各一个数据,每个数据占16位2个字节。所以frame得实际大小是 frame_size * sizeof(uint16_t)*2  
	frameMp3In->nb_samples     = contexMp3->frame_size;  
	frameMp3In->format         = contexMp3->sample_fmt;  
	frameMp3In->channel_layout = contexMp3->channel_layout;  
	/* 分配frame的内存空间 */  
    ret = av_frame_get_buffer(frameMp3In, 0);  
	if (ret < 0) {  
		fprintf(stderr, "Could not allocate audio data buffers\n");  
		exit(1);  
	}  
	///////////////////////////////////////////////////////////////////  

	//src_nb_samples = getAV_SAMPLE_FMT_S16FrameSize();  


	/* 创建重采样上下文 */  
	swr_ctx = swr_alloc();  
	if (!swr_ctx) {  
		fprintf(stderr, "Could not allocate resampler context\n");  
		ret = AVERROR(ENOMEM);  
		goto end;  
	}  

	/* 设置选项 */  
	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);  

	/* 初始化重采样上下文 */  
	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);  
	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;  
	}  

	/* 计算转换后的样本数量 */  
	max_dst_nb_samples = dst_nb_samples =  
		av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);  

	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);  
	if (ret < 0) {  
		fprintf(stderr, "Could not allocate destination samples\n");  
		goto end;  
	}  

	FILE * inputfile = NULL;  
	inputfile = fopen("test.pcm", "rb");  
	uint8_t * tempdata = frameMp3In->data[0];  
	uint8_t * tempdata1 = frameMp3In->data[1];  
	while(!feof(inputfile))  
	{  
		int readsize = fread(src_data[0],1,src_nb_channels*src_nb_samples*sizeof(uint16_t),inputfile);  
		src_nb_samples = readsize / (src_nb_channels*sizeof(uint16_t));  
		if (!readsize)  
			break;  
		/* 计算目标样本数量 */  
		dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, src_rate) +  
			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;  
		}  
		frameMp3In->nb_samples = dst_nb_samples;  
		contexMp3->frame_size = dst_nb_samples;  
		frameMp3In->data[0] = dst_data[0]; 
		frameMp3In->data[1] = dst_data[1];

		//重采样后的结果编码为mp3  
		encode(contexMp3, frameMp3In, pktMp3_Out, mp3File);  
		fprintf(stdout, "encode data size:%d\n",readsize);  

	}  

	frameMp3In->data[0] = tempdata; 
	frameMp3In->data[0] = tempdata1;

end:  

	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);  


	fclose(inputfile);  

	/////////////////////////////////////////编码mp3  
	fclose(mp3File);  
	av_frame_free(&frameMp3In);  
	av_packet_free(&pktMp3_Out);  
	avcodec_free_context(&contexMp3);  

	return 0;  
}



你可能感兴趣的:(音视频开发)