公司任务是从流媒体发送内存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;
}