sdl在一些電腦上無法播放出聲音

公司任務是從流媒體發送內存aac數據通過解碼,調用ffmpeg進行解碼,使用sdl進行播放,本來順理成章的播放很是沒有問題,但在老大的電腦上怎麼也播不出聲音來,糾結好幾天終於找到問題了,在sdl初始化之前需要進行com庫初始化,使其支持子線程進行音頻的播放。附源碼:

#include "Stdafx.h"
#include "aac.h"
#include <process.h>
#include "objbase.h"


#define MAX_AUDIO_FRAME_SIZE 192000
#define CHANNEL 2
#define BITS_PER_SAMPLE 	16
#define SAMPLE_PER_SEC 44100

CRITICAL_SECTION g_csLock;
static int g_countLock = 0;

/* The audio function callback takes the following parameters: 
 * stream: A pointer to the audio buffer to be filled 
 * len: The length (in bytes) of the audio buffer 
*/ 
void  AACPlay::fill_audio(void *udata,Uint8 *stream,int len){ 
	AACPlay* pThis = (AACPlay*)udata;
	if(pThis->m_audio_len==0)		/*  Only  play  if  we  have  data  left  */ 
		return; 
	//SDL 2.0
	EnterCriticalSection(&g_csLock);
	SDL_memset(stream, 0, len);
	len=(len>pThis->m_audio_len?pThis->m_audio_len:len);	/*  Mix  as  much  data  as  possible  */ 
	SDL_MixAudio(stream,pThis->m_audio_pos,len,SDL_MIX_MAXVOLUME);
	LeaveCriticalSection(&g_csLock);
	pThis->m_audio_pos += len; 
	pThis->m_audio_len -= len; 
}

AACPlay::AACPlay(void)
{	
	///解碼
	m_pDecodeFormatCtx	= NULL;
	m_pDecodeCodecCtx		= NULL;
	m_pDecodeCodec			= NULL;
	m_pDecodeFrame			= NULL;	
	m_pDecodeSwrCtx			= NULL;
	m_nAudioStreamIndex	=-1;
	m_nDecodeBuffSize		= 0;
	m_pDecodeFrameBuff	= NULL;
	m_memblockPool			= NULL;

	m_audio_len						= 0;
	m_audio_chunk				= NULL;
	m_audio_pos					= NULL;
	m_bOpenSDLorNot			= FALSE;
	
	m_nLastErrorCode					= ITAV_NoEro;
	m_pDecodeTempOutBuff		= NULL;
	m_decodeCodecID					= AV_CODEC_ID_NONE;
	m_pDecodeCodecParserCtx = NULL;
	m_ThrHandle							= NULL;
	
	if(!g_countLock)
	{
		InitializeCriticalSection(&g_csLock);
		g_countLock ++;
	}
}


AACPlay::~AACPlay(void)
{
	SDL_CloseAudio();//Close SDL
	SDL_Quit();
	if(m_memblockPool)
	{
		delete m_memblockPool;
		m_memblockPool = NULL;
	}
	deleteAllDecodeInfo();
	g_countLock --;
	if(g_countLock <= 0)
		DeleteCriticalSection(&g_csLock);
}

//************************************
//函數名:  initDecode
//描述:初始化解碼函數
//參數:無
//返回值:int
//時間:2015/7/31 WZQ
//************************************
int AACPlay::initDecode()
{	
	resetLastErrorCode();	
	avcodec_register_all();
	m_pDecodeFormatCtx = avformat_alloc_context();	
	if(m_decodeCodecID == AV_CODEC_ID_NONE)
		m_decodeCodecID = AV_CODEC_ID_AAC;
	m_pDecodeCodec = avcodec_find_decoder(m_decodeCodecID);
	if (!m_pDecodeCodec)
	{
		m_nLastErrorCode = ITAV_FindDecoderEro;		
		return m_nLastErrorCode;
	}

	m_pDecodeCodecCtx =  avcodec_alloc_context3(m_pDecodeCodec);
	if (!m_pDecodeCodecCtx)
	{
		fprintf(stderr, "Could not allocate video codec context\n");
		m_nLastErrorCode = ITAV_DecodeAllocCtxFailed;
		return m_nLastErrorCode;
	}	
	m_pDecodeCodecParserCtx = av_parser_init(m_decodeCodecID);
	if (!m_pDecodeCodecParserCtx)
	{
		printf("Could not allocate video parser context\n");
		m_nLastErrorCode = ITAV_DecodeCodecParserCtxFailed;
		return m_nLastErrorCode;
	}
	if (avcodec_open2(m_pDecodeCodecCtx, m_pDecodeCodec,NULL) < 0)
	{
		m_nLastErrorCode = ITAV_OpenDecoderEro;		
		return m_nLastErrorCode;
	}

	av_init_packet(&m_decodePacket);
	m_pDecodeFrame = av_frame_alloc();
	if(!m_pDecodeFrame)
	{
		m_nLastErrorCode = ITAV_DecodeAllocFrameFailed;
		return m_nLastErrorCode;
	}	
	///設置輸出音頻格式	
	m_decodeSampleFmt=AV_SAMPLE_FMT_S16;	
	m_pDecodeFrameBuff = (uint8_t *)av_malloc(1000000);	
	m_pDecodeTempOutBuff = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);
        ///在這進行com的初始化爲支持多線程
       CoInitializeEx(NULL, COINIT_MULTITHREADED);
	//Init
	if(SDL_Init(/*SDL_INIT_VIDEO | */SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  
		printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
		m_nLastErrorCode = ITAV_SDLINITERROR;
		return m_nLastErrorCode;
	}
	
	//SDL_AudioSpec
	m_wanted_spec.freq = SAMPLE_PER_SEC; 
	m_wanted_spec.format = AUDIO_S16SYS; 
	m_wanted_spec.channels = CHANNEL; 
	m_wanted_spec.silence = 0; 
	m_wanted_spec.samples = m_pDecodeCodecCtx->frame_size; 
	m_wanted_spec.callback = &AACPlay::fill_audio; 
	m_wanted_spec.userdata = this;

	m_memblockPool = new INMSMemBlockPool(4096);
	if(!m_memblockPool)
	{
		m_nLastErrorCode = ITAV_MallocEro;
		return m_nLastErrorCode;
	}
	m_hEventInput			= CreateEvent(NULL,FALSE,FALSE,NULL);
	m_hEventPlay			= CreateEvent(NULL,FALSE,FALSE,NULL);
	m_hEventPlayStop	= CreateEvent(NULL,FALSE,FALSE,NULL);


	return m_nLastErrorCode;
}

//************************************     
// 函數名稱: Play     
// 函數說明:開始播放,啓動解碼播放          
// 返 回 值: void     
//************************************  
void AACPlay::Play()
{
	unsigned tmpid;
	m_ThrHandle=reinterpret_cast<HANDLE>(_beginthreadex(0,0,DecodeAudioThread,this,0,&tmpid));
	if(m_ThrHandle)
	{
		SetEvent(m_hEventInput);
	}
}

//************************************     
// 函數名稱: DecodeAudioThread     
// 函數說明:解碼播放線程         
// 返 回 值: unsigned     
// 參    數: void * pParam     
//************************************  
unsigned AACPlay::DecodeAudioThread(void* pParam)
{
	AACPlay* pthis = (AACPlay*)pParam;
	pthis->decodeOutput();
	return 0;
}
//************************************     
// 函數名稱: decodeInput     
// 函數說明:解碼輸入          
// 返 回 值: int     
// 參    數: unsigned char * strSrcBuf     
// 參    數: int nSrcLen     
//************************************  
int AACPlay::decodeInput(unsigned char* strSrcBuf,int nSrcLen)
{
	resetLastErrorCode();
	if(WAIT_OBJECT_0 == WaitForSingleObject(m_hEventInput,INFINITE))
	{
		if(m_memblockPool!=NULL && strSrcBuf != NULL && nSrcLen > 0)
		{
			INMSMemBlock* tempblock = new INMSMemBlock(nSrcLen);
			tempblock->addData(strSrcBuf,nSrcLen);
			m_memblockPool->pushMemBlock(tempblock);
			SetEvent(m_hEventPlay);
		}
	}
	return ITAV_NoEro;
}
//************************************
//函數名:  decodeInput(char* strSrcBuf,int nSrcLen,char** strDestBuf,int &nDestLen)
//描述:解碼數據  輸出
//參數:輸入數據,輸入大小,輸出的指針,輸出大小
//返回值:int <0 失敗  
//注意:此函數不支持外部多線程編程,因爲此部分未線程鎖
//時間:2015/7/23 WZQ
//************************************
int AACPlay::decodeOutput()
{
	resetLastErrorCode();
	int cur_size,got_frame,ret;
	const int in_buffer_size=4096;	
	uint8_t *cur_ptr;
	while (1) 
	{		
		HANDLE hEvent[2] = {m_hEventPlay,m_hEventPlayStop};
		int ret = WaitForMultipleObjects(2,hEvent,FALSE,INFINITE);
		if(ret == WAIT_OBJECT_0)
		{
			INMSMemBlock* memBlock = m_memblockPool->popMemBlock();
			cur_size = memBlock->dataSize();
			cur_ptr=memBlock->data();//m_pDecodeFrameBuff;
			while (cur_size>0)
			{
				///解析一包數據
				int len = av_parser_parse2(m_pDecodeCodecParserCtx,m_pDecodeCodecCtx,&m_decodePacket.data,
					&m_decodePacket.size,cur_ptr,cur_size,AV_NOPTS_VALUE,AV_NOPTS_VALUE,AV_NOPTS_VALUE);
				cur_ptr += len;
				cur_size -= len;
				if(m_decodePacket.size==0)
					continue;
				//Some Info from AVCodecParserContext
				///打印當前解析的一些信息
				printf("[Packet]Size:%6d\t",m_decodePacket.size);			
										 //AV_SAMPLE_FMT_FLTP
				ret = avcodec_decode_audio4(m_pDecodeCodecCtx, m_pDecodeFrame,&got_frame, &m_decodePacket);
				if (ret < 0) 
				{
					m_nLastErrorCode = ITAV_DecodeEro;
					break;
				}
				if(got_frame)
				{				
					if(!m_pDecodeSwrCtx)
					{
						m_pDecodeSwrCtx = swr_alloc();
						int64_t channelslayout = av_get_default_channel_layout(m_pDecodeFrame->channels);
						AVSampleFormat out_fmt = AV_SAMPLE_FMT_S16;//目標格式  
						m_pDecodeSwrCtx=swr_alloc_set_opts(m_pDecodeSwrCtx,av_get_default_channel_layout(m_pDecodeFrame->channels), out_fmt, m_pDecodeFrame->sample_rate,
							av_get_default_channel_layout(m_pDecodeCodecCtx->channels),m_pDecodeCodecCtx->sample_fmt , m_pDecodeCodecCtx->sample_rate,0, NULL);
						swr_init(m_pDecodeSwrCtx);					
					}
					///如果需要重採樣,把下面函數打開,把此三行屏蔽即可
					swr_convert(m_pDecodeSwrCtx,&m_pDecodeTempOutBuff,  swr_get_out_samples(m_pDecodeSwrCtx,m_pDecodeFrame->nb_samples) ,(const uint8_t **)m_pDecodeFrame->data,m_pDecodeFrame->nb_samples);
					int out_nb_samples=m_pDecodeFrame->nb_samples;	///還是用的老的採樣率

					
					//printf("%d\n",m_pDecodeCodecCtx->codec->sample_fmts[0]);
					if(!m_bOpenSDLorNot)
					{
						m_wanted_spec.freq			= m_pDecodeFrame->sample_rate;
						//AUDIO_U16LSB; 
						m_wanted_spec.channels	= m_pDecodeFrame->channels; 
						switch(m_pDecodeCodecCtx->sample_fmt)
						{
						case	AV_SAMPLE_FMT_S16P: 
						case	AV_SAMPLE_FMT_FLTP:  
						case	AV_SAMPLE_FMT_S16:
						case	AV_SAMPLE_FMT_NONE:
						case	AV_SAMPLE_FMT_FLT:
							m_wanted_spec.format = AUDIO_S16;
							break;
						case AV_SAMPLE_FMT_U8:
							m_wanted_spec.format = AUDIO_U8;
							break;
						case AV_SAMPLE_FMT_S32:
							m_wanted_spec.format	= AUDIO_F32;
							break;///< signed 32 bits
						case AV_SAMPLE_FMT_DBL:
							m_wanted_spec.format	= AUDIO_F32;
							break;///< double
						case AV_SAMPLE_FMT_DBLP:
							m_wanted_spec.format	= AUDIO_F32;
							break;///< double, planar

						default:
							m_wanted_spec.format		= AUDIO_S16LSB;
							break;
						}
						m_wanted_spec.silence		= 0; 
						m_wanted_spec.samples	= /*m_pDecodeFrame->nb_samples*//*m_pDecodeCodecCtx->frame_size*/out_nb_samples;
						m_bOpenSDLorNot				= TRUE;
						if (SDL_OpenAudio(&m_wanted_spec, NULL)<0){ 
							SDL_ClearError();
							printf("Failed to open audio: %s\n", SDL_GetError());
							m_nLastErrorCode = ITAV_SDLOPENERROR;
							return m_nLastErrorCode; 
						} 
					}
					//獲取的大小也應該爲目標格式
					m_nDecodeBuffSize = av_samples_get_buffer_size(NULL,m_pDecodeCodecCtx->channels ,out_nb_samples,AV_SAMPLE_FMT_S16,1);
					///如果只使用默認轉換 打開上面三行 屏蔽下面一行即可
					//	m_nDecodeBuffSize = AudioResampling(m_pDecodeCodecCtx,m_pDecodeFrame,m_decodeSampleFmt,m_decodeCodecFmt.nDstChannels,m_decodeCodecFmt.nDstSampleRate,m_pDecodeTempOutBuff);
					m_nDecodeFrameCount++;
					printf("Succeed to decode 1 frame!\n");
				}
				m_audio_chunk	= (Uint8 *) m_pDecodeTempOutBuff;
				m_audio_len			=  m_nDecodeBuffSize;
				m_audio_pos		= m_audio_chunk;
				SDL_PauseAudio(0);
				while(m_audio_len>0)//Wait until finish
					SDL_Delay(1); 
			}
			SetEvent(m_hEventInput);
		}
		else if(ret == WAIT_OBJECT_0+1)
			break;
	}	
	return m_nLastErrorCode;	
}

//************************************     
// 函數名稱: Stop     
// 函數說明:停止音頻播放線程          
// 返 回 值: void     
//************************************  
void AACPlay::Stop()
{
	SetEvent(m_hEventPlayStop);
	::WaitForSingleObject(m_ThrHandle,INFINITE);
	if	(m_ThrHandle!=0)
	{
		::CloseHandle(m_ThrHandle);
		m_ThrHandle = 0;
	}
	CloseHandle(m_hEventInput);
	CloseHandle(m_hEventPlay);
	CloseHandle(m_hEventPlayStop);
	deleteAllDecodeInfo();
}

//************************************     
// 函數名稱: resetLastErrorCode     
// 函數說明:重置上一個錯誤          
// 返 回 值: void     
//************************************  
void AACPlay::resetLastErrorCode()
{
	m_nLastErrorCode = ITAV_NoEro;
}

//************************************     
// 函數名稱: deleteAllDecodeInfo     
// 函數說明:釋放ffmpeg結構體相關內存          
// 返 回 值: void     
//************************************  
void AACPlay::deleteAllDecodeInfo()
{
	// Close file	///釋放申請的輸出buffer
	if(m_pDecodeFrameBuff)
	{
		av_free(m_pDecodeFrameBuff);
		m_pDecodeFrameBuff = NULL;
	}
	if(m_pDecodeTempOutBuff)
	{
		av_free(m_pDecodeTempOutBuff);
		m_pDecodeTempOutBuff = NULL;
	}
	if(m_pDecodeSwrCtx)
	{
		///保留 重採樣模塊需要用,現在是每次都申請,後續可改爲一次初始 多次使用最後釋放
		swr_free(&m_pDecodeSwrCtx);
		m_pDecodeSwrCtx = NULL;
	}
	if(m_pDecodeFrame)
	{
		av_frame_free(&m_pDecodeFrame);
		m_pDecodeFrame = NULL;
	}
	if(m_pDecodeCodecParserCtx)
	{
		av_parser_close(m_pDecodeCodecParserCtx);
		m_pDecodeCodecParserCtx = NULL;
	}
	
	// Close the codec	///釋放獲取的解碼指針
	if(m_pDecodeCodecCtx)
	{
		avcodec_close(m_pDecodeCodecCtx);
		av_free(m_pDecodeCodecCtx);
		m_pDecodeCodecCtx = NULL;
	}
	// Close the video file	釋放格式化指針
	if(m_pDecodeFormatCtx)
	{
		avformat_free_context(m_pDecodeFormatCtx);
		m_pDecodeFormatCtx = NULL;
	}
	
	m_decodePacket.data = NULL;
	m_decodePacket.size = 0;
	m_nAudioStreamIndex=-1;
	m_nDecodeBuffSize=0;
	m_decodeCodecID=AV_CODEC_ID_NONE;			///當前解碼時選擇id	
	m_nDecodeFrameCount=0;
}


發佈了23 篇原創文章 · 獲贊 23 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章