FAAD2解码AAC得到PCM数据帧采用Microsoft.DirectX.DirectSound播放时有嘟嘟声、噪音的问题

DirectX(Direct eXtension,简称DX)是由微软公司创建的多媒体编程接口,是一种应用程序接口(API)

问题描述:

1.PC端产测软件,通过P2P接收到设备发送过来的音频数据帧(AAC,16KHZ,16bit位宽、单通道),使用faad/faad2解码库解码后的音频帧,

播放出来有频率很快的一直嘟嘟嘟的声音,听起来断断续续(不知道怎么描述)

2.关于faad解码后的数据总是双通道的问题,见我上一篇博客(https://blog.csdn.net/spy_007_/article/details/109177862),不过最后我是直接

使用解码出来的双通道PCM数据进行播放的(该篇后续的整个过程都是使用的双通道数据,读者如果使用单通道数据进行播放可以结合上一篇博客进行修改)

 

问题解决:

将PCM数据先提交给DirectX底层接口播放时(waveOutPrepareHeader),提交完之后就会返回,并不是等到本次提交的数据完全播放结束才会返回,

也就是说传入的数据buf,A:我们上层并不能立马释放,B:也不能就只使用一块BUF循环接收数据,否则,底层播放的数据就会遭到破坏,声音异常。

解决办法就是多申请几块buf,让循环使用:

 

注意:上图函数OnWriteSoundData接收的PCM数据buf 每次都是不同的,上层申请了15个buf,依次循环使用,如下:

 

 源码如下:

#pragma once

#include "pch.h"
#include "hi_voice_api.h"

//标志使用哪一种解码方式
#define AUDIO_DECODE_USE_AAC	1
#define AUDIO_DECODE_USE_G711	0

typedef struct _AudioFrame
{
	char*pcm;
	int pcm_len;
	int pts;

}AudioFrame;


#if AUDIO_DECODE_USE_AAC
#include "faad.h"
class AudioDecode_AAC
{
public:
	NeAACDecHandle decoder = NULL;
public:
	AudioDecode_AAC()
	{
	}

	~AudioDecode_AAC()
	{
		
	}

	//AAC解码器初始化,需要传入一帧数据帧(带ADTS帧头),作为初始化的入参
	long AudioDecode_AAC_Init(
		unsigned char *frame,
		unsigned long size,
		unsigned long *samplerate,
		unsigned char *channels);

	int AudioDecode_AAC_Exit();

	 void* AudioDecode_AAC_Decode(
		NeAACDecFrameInfo *hInfo,
		unsigned char *buffer,
		unsigned long buffer_size);

};

#endif

#if AUDIO_DECODE_USE_G711

class AudioDecode_g711
{

private:
	hiVOICE_G711_STATE_S vgs;
	bool ready;
public:
	AudioDecode_g711():ready(false)
	{
	}

	~AudioDecode_g711()
	{
	}

	int Create();
	int Decode(void* buf, int len, int pts, int audType, AudioFrame*af);
	int malloc_buf(int pcm_len, AudioFrame*ret_buf);
	int free_buf(AudioFrame*buf);
	int Destroy();

};

#endif
#include "pch.h"
#include "AudioDecode.h"
#include "typeport.h"



#if AUDIO_DECODE_USE_AAC

#define MAX_CHANNELS 2

static int adts_sample_rates[] = { 96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0 };

//用于接收AAC解码出来的pcm数据:
#define MAX_PCM_BUF_NUM	(15)		
#define ONE_PCM_BUF_LEN	(2048*2)
static char* pcm_buf[MAX_PCM_BUF_NUM] = {0};
static int pb_producer_index = 0; //生产者使用的索引号


long AudioDecode_AAC::AudioDecode_AAC_Init(
	unsigned char *frame,
	unsigned long size,
	unsigned long *samplerate,
	unsigned char *channels)
{
	if (!decoder)
	{
		//初始化PCM接收Buf:
		int i;
		for (i=0;i< MAX_PCM_BUF_NUM;i++)
		{
			pcm_buf[i] = (char*)malloc(ONE_PCM_BUF_LEN);
			if (!pcm_buf[i])
			{
				printf("malloc failed!");
				return -1;
			}
			memset(pcm_buf[i],0, ONE_PCM_BUF_LEN);
		}
		//open decoder
		decoder = NeAACDecOpen();
		NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);

		conf->defObjectType = LC;
		conf->defSampleRate = 8000;//8000; //real samplerate/2
		conf->outputFormat = FAAD_FMT_16BIT; //
		conf->downMatrix = 0; //不进行自动扩展到双通道 ???
		conf->useOldADTSFormat = 0; //ADTS长度为0:56bit(1代表是58bit)
		conf->dontUpSampleImplicitSBR = 1;

		NeAACDecSetConfiguration(decoder, conf);


		//initialize decoder
		return NeAACDecInit(decoder, frame,size, samplerate, channels);
	}
	else
	{
		ERROR_LOG("AAC decoder already inited!\n");
		return -1;
	}

}

int AudioDecode_AAC::AudioDecode_AAC_Exit()
{
	int i;
	for (i = 0; i < MAX_PCM_BUF_NUM; i++)
	{
		free(pcm_buf[i]);
		pcm_buf[i] = NULL;
	}

	NeAACDecClose(decoder);
	decoder = NULL;
	return 0;
}


/**
 * fetch one ADTS frame
 */
int check_ADTS_len(unsigned char* buffer, size_t buf_size)
{
	size_t size = 0;

	if (!buffer)
	{
		perror("illegall parameter!\n");
		return -1;
	}

	if (buf_size < 7)
	{
		perror("illegall parameter!\n");
		return -1;
	}

	if ((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0))
	{
		// profile; 2 uimsbf
		// sampling_frequency_index; 4 uimsbf
		// private_bit; 1 bslbf
		// channel_configuration; 3 uimsbf
		// original/copy; 1 bslbf
		// home; 1 bslbf
		// copyright_identification_bit; 1 bslbf
		// copyright_identification_start; 1 bslbf
		// frame_length; 13 bslbf

		size |= (((buffer[3] & 0x03)) << 11);//high 2 bit
		size |= (buffer[4] << 3);//middle 8 bit
		size |= ((buffer[5] & 0xe0) >> 5);//low 3bit

		//printf("len1=%x\n", (buffer[3] & 0x03));
		//printf("len2=%x\n", buffer[4]);
		//printf("len3=%x\n", (buffer[5] & 0xe0) >> 5);
		//printf("get_one_ADTS_frame buf_size(%d) parse ADTS-->size(%d)\n", buf_size,(int)size);
			
	}
	
	//int samplerate = adts_sample_rates[(buffer[2] & 0x3c) >> 2]; //解析ADTS中的采样率信息
	//printf("samplerate = %d\n", samplerate); //16000

	if (buf_size != size)
	{
		printf("parse ADTS : buf_size(%d) != size(%d)\n", buf_size, size);
		return -1;
	}

	return 0;
}

unsigned int parse_ADTS_len(unsigned char* buffer)
{
	size_t size = 0;

	if (!buffer)
	{
		perror("illegall parameter!\n");
		return -1;
	}

	if ((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0))
	{
		// profile; 2 uimsbf
		// sampling_frequency_index; 4 uimsbf
		// private_bit; 1 bslbf
		// channel_configuration; 3 uimsbf
		// original/copy; 1 bslbf
		// home; 1 bslbf
		// copyright_identification_bit; 1 bslbf
		// copyright_identification_start; 1 bslbf
		// frame_length; 13 bslbf

		size |= (((buffer[3] & 0x03)) << 11);//high 2 bit
		size |= (buffer[4] << 3);//middle 8 bit
		size |= ((buffer[5] & 0xe0) >> 5);//low 3bit

		//printf("len1=%x\n", (buffer[3] & 0x03));
		//printf("len2=%x\n", buffer[4]);
		//printf("len3=%x\n", (buffer[5] & 0xe0) >> 5);
		printf(" parse ADTS-->size(%d)\n", (int)size);

	}
	else
	{
		return -1;
	}

	//int samplerate = adts_sample_rates[(buffer[2] & 0x3c) >> 2]; //解析ADTS中的采样率信息
	//printf("samplerate = %d\n", samplerate); //16000


	return 0;
}

/***********
 * 左右声道合并
 * data:出入的待处理的数据
 * len:传入数据的长度
 * right_left:0,合并到左声道
 *             1,合并到右声道
 *********/
int my_audio_digital_Channel_merging_add(void *data, unsigned int  len, unsigned char right_left)
{
	if (data == NULL) {
		return -1;
	}

	int  valuetemp;
	short *buf;

	buf = (short *)data;
	len >>= 1; //byte to point

	for (unsigned int  i = 0; i < len; i += 2) {

		valuetemp = (buf[i] + buf[i + 1]);
		//防止16位数据溢出
		if (valuetemp < -32768) {
			valuetemp = -32768;
		}
		else if (valuetemp > 32767) {
			valuetemp = 32767;
		}
		//或者也可以全部数据除2
		//valuetemp = valuetemp / 2;
		buf[i + right_left] = (short)valuetemp;
		buf[i + 1 - right_left] = (short)0;
	}


	return 0;

}

//成功:返回解码得到的PCM数据指针 ; 失败 :NULL
//frame_info:返回帧信息参数
 void* AudioDecode_AAC::AudioDecode_AAC_Decode(
	NeAACDecFrameInfo *frame_info,
	unsigned char *frame,
	unsigned long size)
{
	check_ADTS_len(frame,size);

	/*----进行解码操作----------------------------------------*/
	//解析下一帧数据长度
	char* return_pcm_data = NULL;
	void* pcm_data = NeAACDecDecode(decoder, frame_info, frame, size);
	//printf(" frame_info->samples = %d frame_info->channels = %d\n", frame_info->samples,frame_info->channels);//初始化中dontUpSampleImplicitSBR = 1时返回2048;dontUpSampleImplicitSBR=0时返回4096
	if (size != frame_info->bytesconsumed)//每次传入一帧数据,这两个值每次都相等
	{
		printf("error!$$$$$$$$$$$$$$$$$$$$$$$$$size(%d) frame_info->bytesconsumed(%d)\n", size, frame_info->bytesconsumed);
	}

	if (frame_info->error > 0)
	{
		printf("error!$$$$$$$$$ %s\n", NeAACDecGetErrorMessage(frame_info->error));
	}
	else if (pcm_data && frame_info->samples > 0)
	{

#if 1	//直接返回双通道的数据

		//对数据进行备份到缓存buf
		if (frame_info->samples * sizeof(short) > ONE_PCM_BUF_LEN)
		{
			printf("PCM buf overflow!!!!\n");
			return NULL;
		}
		memset(pcm_buf[pb_producer_index],0, ONE_PCM_BUF_LEN);
		memcpy(pcm_buf[pb_producer_index], pcm_data, frame_info->samples*sizeof(short));
		return_pcm_data = pcm_buf[pb_producer_index];
		pb_producer_index++;
		if (pb_producer_index >= MAX_PCM_BUF_NUM)
		{
			pb_producer_index = 0;
		}

#else	//转换成单通道数据(faad解码总是强制性变成双通道输出)
		if (frame_info->channels == 2) //双通道数据转换成单通道
		{
			if (frame_info->samples/2 * sizeof(short) > ONE_PCM_BUF_LEN)
			{
				printf("PCM buf overflow!!!!\n");
				return NULL;
			}
			memset(pcm_buf[pb_producer_index], 0, ONE_PCM_BUF_LEN);
			return_pcm_data = pcm_buf[pb_producer_index];
			pb_producer_index++;
			if (pb_producer_index >= MAX_PCM_BUF_NUM)
			{
				pb_producer_index = 0;
			}

			//从双声道的数据中提取单通道 
			int i, j;
			for (i = 0, j = 0; i < 4096 && j < 2048; i += 4, j += 2)
			{
				//每次拷贝2字节数据到frame_mono(16bit位宽,即每个通道数据一个采样2字节)
				return_pcm_data[j] = ((char*)pcm_data)[i];
				return_pcm_data[j + 1] = ((char*)pcm_data)[i + 1];
			}
			frame_info->samples = frame_info->samples/2;//1024; //只留下单通道数据
			frame_info->channels = 1;
		}
	
#endif	
		return (void*)return_pcm_data;
		
	}

	return NULL;
}


#endif


#if AUDIO_DECODE_USE_G711
int AudioDecode_g711::Create()
{
	int ret = HI_VOICE_DecReset(&vgs, G711_A);
	if (HI_SUCCESS != ret)
	{
		ERROR_LOG("HI_VOICE_DecReset fail: %#x\n", ret);
		return -1;
	} 

	ready = true;
	return 0;
}

int AudioDecode_g711::malloc_buf(int pcm_len, AudioFrame*ret_buf)
{
	ret_buf->pcm_len = pcm_len;
	ret_buf->pcm = (char*)malloc(pcm_len);
	if (!ret_buf->pcm)
	{
		ERROR_LOG("malloc failed!\n");
		return -1;
	}
	return 0;
}

int AudioDecode_g711::free_buf(AudioFrame*buf)
{
	if (buf)
	{
		free(buf->pcm);
		buf->pcm = NULL;
	}
	return 0;
}

int AudioDecode_g711::Decode(void* buf, int len, int pts, int audType, AudioFrame*af)
{
	if (NULL == af)
	{
		return -1;
	}

	audType = audType;
	if (!ready) return -1;

	char pcm[1024];
	HI_S16 l = len / 2;
	int ret = HI_VOICE_DecodeFrame(&vgs, (HI_S16*)buf, (HI_S16*)pcm, &l);
	if (HI_SUCCESS == ret)
	{
		af->pcm = (char*)malloc(l * 2);
		if (malloc_buf(l * 2, af) < 0)
		{
			return -1;
		}
		memcpy(af->pcm, pcm, l * 2);
		af->pts = pts;
		return 0;
		  
	}

	return -1;
}

int AudioDecode_g711::Destroy()
{
	ready = false;
	return 0;
}

#endif 
#pragma once

#include<mmsystem.h>
#include<mmreg.h>
#pragma  comment(lib, "winmm.lib")

#define WM_PLAYSOUND_STARTPLAYING	WM_USER+600
#define WM_PLAYSOUND_STOPPLAYING	WM_USER+601
#define WM_PLAYSOUND_PLAYBLOCK		WM_USER+602
#define WM_PLAYSOUND_ENDTHREAD		WM_USER+603


#define MAX_PCM_LPHDR_NUM 15

// CPlaySound
class CPlaySound : public CWinThread
{
	DECLARE_DYNCREATE(CPlaySound)
public:
	CPlaySound();
	~CPlaySound();
	virtual BOOL InitInstance();
	virtual int ExitInstance();
private:
	void displayError(int code, char mesg[]);
	WAVEFORMATEX		m_WaveFormatEx;
	BOOL				m_IsPlaying;
	HWAVEOUT			m_hPlay;
	CStdioFile			m_PlayLog;
	WAVEHDR pcm_lpHdr[MAX_PCM_LPHDR_NUM] = {0};	//用于接收帧的缓存buf数组
	int cur_pcm_lpHdr_index = 0;	//当前用于接收传入数据的buf下标
protected:
	afx_msg void OnStartPlaying(WPARAM wParam, LPARAM lParam);
	afx_msg void OnStopPlaying(WPARAM wParam, LPARAM lParam);
	afx_msg void OnEndPlaySoundData(WPARAM wParam, LPARAM lParam);
	afx_msg void OnWriteSoundData(WPARAM wParam, LPARAM lParam);
	afx_msg void OnEndThread(WPARAM wParam, LPARAM lParam);
	DECLARE_MESSAGE_MAP()
};


// PlaySound.cpp : 实现文件
//

#include "pch.h"
#include "PlaySound.h"
#include "typeport.h"


// CPlaySound

IMPLEMENT_DYNCREATE(CPlaySound, CWinThread)

CPlaySound::CPlaySound()
{
	//打开播放日志
	m_PlayLog.Open(TEXT("playsound.log"), CFile::modeCreate | CFile::modeWrite);
	m_PlayLog.WriteString(TEXT("\n In the constructor of Play sound"));

	//初始化音频格式结构体
	memset(&m_WaveFormatEx, 0, sizeof(m_WaveFormatEx));
	m_WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
	m_WaveFormatEx.nChannels = 2;//2;//1;
	m_WaveFormatEx.wBitsPerSample = 16;//8;
	m_WaveFormatEx.nSamplesPerSec = 16000;//16000; //16000;//8000;
	m_WaveFormatEx.nBlockAlign = m_WaveFormatEx.nChannels * m_WaveFormatEx.wBitsPerSample / 8;
	m_WaveFormatEx.nAvgBytesPerSec = m_WaveFormatEx.nSamplesPerSec * m_WaveFormatEx.nBlockAlign;	//8000;
	m_WaveFormatEx.cbSize = 0;


	m_IsPlaying = FALSE;
}

CPlaySound::~CPlaySound()
{

}

BOOL CPlaySound::InitInstance()
{
	// TODO: 在此执行任意逐线程初始化
	return TRUE;
}

int CPlaySound::ExitInstance()
{
	// TODO: 在此执行任意逐线程清理
	return CWinThread::ExitInstance();
}

void CPlaySound::OnStartPlaying(WPARAM wParam, LPARAM lParam)
{
	MMRESULT mmReturn = 0;

	if (m_IsPlaying) //已经开始播放则直接返回
		return; //FALSE;
	m_PlayLog.WriteString(TEXT("\n Starting playing"));


	//打开音频输出设备
	mmReturn = ::waveOutOpen(&m_hPlay, WAVE_MAPPER,
		&m_WaveFormatEx, ::GetCurrentThreadId(), 0, CALLBACK_THREAD);
	if (mmReturn) //打开设备失败
	{
		DEBUG_LOG("audio waveOutOpen failed!\n");
		displayError(mmReturn, "PlayStart");
	}
	else
	{
		m_IsPlaying = TRUE;
		DWORD volume = 0xffffffff;
		waveOutSetVolume(m_hPlay, volume);//设置输出设备的输出量
	}
}

void CPlaySound::displayError(int code, char mesg[])
{

	TCHAR errorbuffer[MAX_PATH];
	TCHAR errorbuffer1[MAX_PATH];

	waveOutGetErrorText(code, errorbuffer, MAX_PATH);
	wsprintf(errorbuffer1, TEXT("PLAY : %s :%x:%s"), mesg, code, errorbuffer);
	AfxMessageBox(errorbuffer1);
}

/*
6、结束输出前先用waveOutReset重置输出设备,重置能够使输出设备全部buffer输出结束,
所以在waveOutReset后要延迟一段时间,然后调用waveOutClose关闭设备。
*/
void CPlaySound::OnStopPlaying(WPARAM wParam, LPARAM lParam)
{

	MMRESULT mmReturn = 0;

	if (m_IsPlaying == FALSE)
		return;// FALSE;

	//m_PlayLog.WriteString(TEXT("\n Stopped  playing"));
	DEBUG_LOG("Audio Stopped  playing !\n");

	mmReturn = ::waveOutReset(m_hPlay);//重置输出设备,重置能够使输出设备全部buffer输出结束

	if (!mmReturn)
	{
		m_IsPlaying = FALSE;
		Sleep(300); //等待所有buffer输出完成
		mmReturn = ::waveOutClose(m_hPlay);//关闭设备
	}
}

/*5、当提交给设备的数据输出结束,设备会发送一条MM_WOM_DONE消息反馈给设备,
设备应该用waveOutUnprepareHeader将提交给设备输出的数据清除。
*/
void CPlaySound::OnEndPlaySoundData(WPARAM wParam, LPARAM lParam)
{
	LPWAVEHDR lpHdr = (LPWAVEHDR)lParam;

	if (lpHdr)
	{
		::waveOutUnprepareHeader(m_hPlay, lpHdr, sizeof(WAVEHDR));//音频输出结束,清空buffer
	}
	return;//ERROR_SUCCESS;
}

void CPlaySound::OnWriteSoundData(WPARAM wParam, LPARAM lParam)
{
	MMRESULT mmResult = 0;

	if (m_IsPlaying == FALSE)
	{
		ERROR_LOG("m_IsPlaying == FALSE");
		return; //FALSE;
	}
		

	//m_PlayLog.WriteString(TEXT("\nplaying sound data...."));
	//DEBUG_LOG("playing sound data.... length(%d)\n", length);

	// Prepare wave header for playing 
	WAVEHDR *lpHdr = &pcm_lpHdr[cur_pcm_lpHdr_index];
	cur_pcm_lpHdr_index++;
	if (cur_pcm_lpHdr_index >= MAX_PCM_LPHDR_NUM)
	{
		cur_pcm_lpHdr_index = 0;
	}

	memset(lpHdr, 0, sizeof(WAVEHDR));
	lpHdr->lpData = (char *)lParam;
	lpHdr->dwBufferLength = (int)wParam;
	//printf("lpHdr->dwBufferLength = %d\n", lpHdr->dwBufferLength);


	//将要输出的数据写入buffer
	mmResult = ::waveOutPrepareHeader(m_hPlay, lpHdr, sizeof(WAVEHDR));
	if (mmResult)
	{
		m_PlayLog.WriteString(TEXT("\nError while preparing header"));
		ERROR_LOG("Error while preparing header\n");
		return;//ERROR_SUCCESS;
	}

	//将输出数据发送给输出设备
	mmResult = ::waveOutWrite(m_hPlay, lpHdr, sizeof(WAVEHDR));

	if (mmResult)
	{
		ERROR_LOG("Error while writing to device");
		m_PlayLog.WriteString(TEXT("\nError while writing to device"));
		return;//ERROR_SUCCESS;				
	}
	

	return;//ERROR_SUCCESS;
}

void CPlaySound::OnEndThread(WPARAM wParam, LPARAM lParam)
{

	// If already playing then stop it...
	if (m_IsPlaying)
		OnStopPlaying(0, 0);

	m_PlayLog.WriteString(TEXT("\nEnding the play device"));
	DEBUG_LOG("Audio Ending the play device\n");

	// Quit this thread...
	::PostQuitMessage(0);

	return;//TRUE;
}

BEGIN_MESSAGE_MAP(CPlaySound, CWinThread)
	ON_THREAD_MESSAGE(WM_PLAYSOUND_STARTPLAYING, OnStartPlaying)
	ON_THREAD_MESSAGE(WM_PLAYSOUND_STOPPLAYING, OnStopPlaying)
	ON_THREAD_MESSAGE(WM_PLAYSOUND_PLAYBLOCK, OnWriteSoundData)
	ON_THREAD_MESSAGE(MM_WOM_DONE, OnEndPlaySoundData)
	ON_THREAD_MESSAGE(WM_PLAYSOUND_ENDTHREAD, OnEndThread)
END_MESSAGE_MAP()


// CPlaySound 消息处理程序

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值