FFMPEG实现音频重采样

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hiwubihe/article/details/81259134

技术在于交流、沟通,转载请注明出处并保持作品的完整性。

原文:https://blog.csdn.net/hiwubihe/article/details/81259134

[音频编解码系列文章]

  1. 音频编解码基础
  2. FFMPEG实现音频重采样
  3. FFMPEG实现PCM编码(采用封装格式实现)
  4. FFMPEG实现PCM编码(不采用封装格式实现)
  5. FAAC库实现PCM编码
  6. FAAD库实现RAW格式AAC解码
  7. FAAD库实现RAW格式AAC封装成ADTS格式
  8. FAAD库实现ADTS格式解码
  9. FFMPEG实现对AAC解码(采用封装格式实现)
  10. FFMPEG实现对AAC解码(不采用封装格式实现)

音频处理中,有时不同的编解码器支持的音频格式不一样,原始采样的音频数据可能没法直接直接为编解码器支持,如FFMPEG编码MP3格式的音频,就要求样本采用AV_SAMPLE_FMT_S16P格式保存。这就需要对不同的音频格式转换,需要重采样。这里需要注意一点,如果PCM文件采用交叉存储方式,视频帧的概念可能没什么影响,因为数据都是LRLRLR...LR方式;但是如果采用平行存储方式,L...LR...RL...LR...R,一帧必须按照指定的数据读取了,如MP3读取一帧需要读取1152*2(通道数)个样本,然后前1152为左声道,后1152为右声道。

音频重采样式例代码

/*******************************************************************************
Copyright (c) wubihe Tech. Co., Ltd. All rights reserved.
--------------------------------------------------------------------------------

Date Created:	2014-10-25
Author:			wubihe QQ:1269122125 Email:[email protected]
Description:	代码实现音频重采样 把交叉存储的双声道立体声转换成平行存储的双声道
				立体声
--------------------------------------------------------------------------------
Modification History
DATE          AUTHOR          DESCRIPTION
--------------------------------------------------------------------------------

********************************************************************************/
#include <stdio.h>

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endif





int main()
{

	static char*pFormatName[]=
	{
		"FMT_U8","FMT_S16","FMT_S32","FMT_FLT","FMT_DBL",
		"FMT_U8P","FMT_S16P","FMT_S32P","FMT_FLTP","FMT_DBLP"
	};


	//原始PCM文件
	FILE *pInputFile = fopen("huangdun_r48000_FMT_S16_c2.pcm", "rb");
	uint64_t iInputLayout				= AV_CH_LAYOUT_STEREO;
	int      iInputChans				= av_get_channel_layout_nb_channels(iInputLayout);
	AVSampleFormat eInputSampleFormat   = AV_SAMPLE_FMT_S16;
	int	     iInputSampleRate			= 48000;

	//转换后PCM文件
	uint64_t iOutputLayout				= AV_CH_LAYOUT_STEREO;
	int      iOutputChans				= av_get_channel_layout_nb_channels(iOutputLayout);
	AVSampleFormat eOutputSampleFormat  = AV_SAMPLE_FMT_S16P;
	int	     iOutputSampleRate			= 48000;

	bool bOutPutPlaner=true;

	char szOutFileName[256]={0};
	sprintf(szOutFileName,"huangdun_r%d_%s_c%d.pcm",iOutputSampleRate,pFormatName[eOutputSampleFormat],iOutputChans);
	
	FILE *pOutputFile = fopen(szOutFileName, "wb");

	//初始化转换上下文
	SwrContext *pSwrCtx = swr_alloc_set_opts(NULL,iOutputLayout, eOutputSampleFormat, iOutputSampleRate,
		iInputLayout,eInputSampleFormat , iInputSampleRate,0, NULL);
	swr_init(pSwrCtx);

	//1帧数据样本数
	int iFrameSamples = 1024;

	//保存转换后的数据
	/*uint8_t **ppConvertData = (uint8_t**)calloc(iOutputChans,sizeof(*ppConvertData));
	int iConvertLineSize = 0;
	int iConvertDataSize = av_samples_alloc(ppConvertData, &iConvertLineSize,iOutputChans, iFrameSamples,eOutputSampleFormat, 0);*/

	//音频处理是以帧为单位处理的
	//分配存储原始数据内存空间 
	int iRawLineSize = 0;
	int iRawBuffSize  = av_samples_get_buffer_size(&iRawLineSize, iInputChans, iFrameSamples, eInputSampleFormat, 0);
	uint8_t *pRawBuff = (uint8_t *)av_malloc(iRawBuffSize);

	//原始数据保存在AVFrame结构体中
	AVFrame* pRawframe = av_frame_alloc();

	pRawframe->nb_samples	= iFrameSamples;
	pRawframe->format		= eInputSampleFormat;
	pRawframe->channels     = iInputChans;

	//把分配内存挂到AVFrame结构体中
	int iReturn = avcodec_fill_audio_frame(pRawframe, iInputChans, eInputSampleFormat, (const uint8_t*)pRawBuff, iRawBuffSize, 0);
	if(iReturn<0)
	{
		return -1;
	}


	// 存储转换后数据 
	int iConvertLineSize = 0;
	int iConvertBuffSize  = av_samples_get_buffer_size(&iConvertLineSize, iOutputChans, iFrameSamples, eOutputSampleFormat, 0);
	uint8_t *pConvertBuff = (uint8_t *)av_malloc(iConvertBuffSize);

	//转换后数据保存在AVFrame结构体中
	AVFrame* pConvertframe = av_frame_alloc();
	pConvertframe->nb_samples	= iFrameSamples;
	pConvertframe->format		= eOutputSampleFormat;
	pConvertframe->channels     = iOutputChans;

	iReturn = avcodec_fill_audio_frame(pConvertframe, iOutputChans, eOutputSampleFormat, (const uint8_t*)pConvertBuff, iConvertBuffSize, 0);
	if(iReturn<0)
	{
		return -1;
	}



	int iFrameNum =0;
	
	//读取一帧音频数据
	int iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile);
	while(iRealRead>0)
	{
		//完成音频帧转换
		swr_convert(pSwrCtx, (uint8_t**)pConvertframe->data, iFrameSamples ,(const uint8_t**)pRawframe->data, iFrameSamples );
		

		if(bOutPutPlaner)
		{
			//只保存一个通道数据 便于分析 因为PCM工具不支持这种平行存储方式数据查看
			//一般情况平行存储方式pConvertframe->data[i]为i声道数据pConvertframe->linesize[i]
			//为i声道长度,但是音频各个声道的数据长度是相同的,所以只有pConvertframe->linesize[0]表示长度,
			//其他pConvertframe->linesize[1]==0
			fwrite(pConvertframe->data[0],pConvertframe->linesize[0],1,pOutputFile);
		}
		
		printf("Convert Frame :%d\n",++iFrameNum);
		

		iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile);
	}

	fclose(pInputFile);
	fclose(pOutputFile);

	av_free(pRawBuff);
	av_free(pConvertBuff);

	swr_free(&pSwrCtx);

	printf("Convert Success!!\n");
	getchar();
	return 0;

}

原始文件双通道交叉存储方式huangdun_r48000_FMT_S16_c2.pcm。

转换后文件采用平行存储方式huangdun_r48000_FMT_S16P_c2.pcm,这里只保存一个通道的数据,另一个通道是一样的。

编译环境:   Win7_64bit+VS2008

DEMO下载地址:https://download.csdn.net/download/hiwubihe/10569433

猜你喜欢

转载自blog.csdn.net/hiwubihe/article/details/81259134