近来要把WinCE.Net平台的的程序移植到Pocket PC上,出现了一点小麻烦。原有工程中有个DirectShow的封装类,用来播放MP3格式的音频,而Pocket PC 2003不支持DirectShow,为了和老系统兼容看来要改一下这段代码。首先就排除了WMPlayer控件,太耗资源(DirectShow也一样)。libmad是跨平台的MPEG音频解码库,使用整数运算来模拟浮点运算,实为嵌入式平台MP3解码器之翘首。下载最新的libmad包,其中两个例子代码minimad和madplay,minimad只是在控制台输出解码后的PCM信息而madplay详细演示了libmad的用法。
libmad解码分为同步解码和异步解码,异步解码我没有仔细研究也缺少例子,同步的流程都在run_sync()这个函数里面,基本流程是:数据流-->分析帧头,将数据流分解为数据帧-->逐帧解码-->滤波-->合成PCM。不过你不需要自己写这个过程,libmad已经给你做了,你需要的只是简单实现几个回调函数就可以了。
我们要实现的大致代码就是
struct mad_decoder m_Decoder;
/* initialize decoder */
//这里填入6个回调函数,其中输入输出是必填的
mad_decoder_init(&m_Decoder, this, decode_input,NULL, decode_filter,decode_output, decode_error, NULL);
int options = MAD_OPTION_IGNORECRC; //忽略CRC校验
mad_decoder_options(&m_Decoder, options);
/* start decoding */
int result = mad_decoder_run(&m_Decoder, MAD_DECODER_MODE_SYNC);
/* release the decoder */
mad_decoder_finish(&m_Decoder);
类定义代码
#define MAX_RESAMPLEFACTOR (6)
#define MAX_NSAMPLES (1152 * MAX_RESAMPLEFACTOR)
#define MAX_OUTPUTBUFFER (48000 * 4 * 2)
class ClibmadWrap
{
public:
ClibmadWrap();
virtual ~ClibmadWrap();
// user type
struct outputbuffer
{
outputbuffer()
{
length = 0;
data = NULL;
}
~outputbuffer()
{
if(data)
{
free(data);
data = NULL;
}
length = 0;
}
unsigned int length;
unsigned char* data;
};
struct data_block
{
data_block()
{
dataflag = 0;
start = NULL;
length = 0;
pNext = NULL;
}
long dataflag;
unsigned char* start;
unsigned long length;
data_block* pNext;
void release()
{
if(start)
{
free(start);
start = NULL;
}
length = 0;
}
};
struct pcm_block
{
pcm_block()
{
memset(&pWaveHdr, 0, sizeof(pWaveHdr));
lplaying = 0;
m_hPlayEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
}
~pcm_block()
{
if(m_hPlayEvt!=NULL && m_hPlayEvt!=INVALID_HANDLE_VALUE)
CloseHandle(m_hPlayEvt);
m_hPlayEvt = NULL;
}
WAVEHDR pWaveHdr;
long lplaying;
HANDLE m_hPlayEvt;
void release()
{
if(pWaveHdr.lpData)
free(pWaveHdr.lpData);
pWaveHdr.lpData = NULL;
lplaying = 0;
}
};
struct audio_stats
{
audio_stats()
{
clipped_samples = 0;
peak_clipping = peak_sample = 0;
}
unsigned long clipped_samples;
mad_fixed_t peak_clipping;
mad_fixed_t peak_sample;
};
//Method
// for player
BOOL Create();
void Release();
BOOL Render(unsigned char* pInput, long lsize);
void Play();
BOOL IsPlaying()
{
return m_bOpenDev;
}
// for decoder
enum mad_flow Filter(struct mad_stream const *stream, struct mad_frame *frame);
enum mad_flow Error(struct mad_stream *stream, struct mad_frame *frame);
enum mad_flow Input(struct mad_stream *stream);
enum mad_flow Output(struct mad_header const *header, struct mad_pcm *pcm);
private:
BOOL open_dev();
void close_dev();
int write_dev(struct pcm_block *pFrame);
void setformat(unsigned int channels, unsigned int speed, unsigned int bits);
void releaseinput();
void Decoder();
struct mad_decoder m_Decoder;
pcm_block m_pOutput[2];
// bCreate is first attribute should be initialized.
BOOL m_bCreate;
outputbuffer m_output;
long m_nChannels; /* number of channels (i.e. mono, stereo...) */
long m_nSamplesPerSec; /* sample rate */
long m_nAvgBytesPerSec; /* for data_block estimation */
long m_wBitsPerSample; /* number of bits per sample of mono data */
long m_nBlockAlign;
volatile HANDLE m_hEventDecode;
volatile HANDLE m_hEventKill;
HANDLE m_hDThread;
HWAVEOUT m_phwo;
volatile BOOL m_bOpenDev;
BOOL m_bEof;
CRITICAL_SECTION m_csDataAccess;
data_block* m_pInput;
int m_index;
audio_stats m_stats;
friend void WINAPI DecodeThread( ClibmadWrap * pPlaySound );
friend void CALLBACK waveOutProc( HWAVEOUT hwo, UINT uMsg,
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 );
};
主要实现代码
void WINAPI DecodeThread( ClibmadWrap* pPlaySound )
{
while(WaitForSingleObject(pPlaySound->m_hEventKill,IGNORE)==WAIT_TIMEOUT)
{
WaitForSingleObject(pPlaySound->m_hEventDecode,INFINITE);
pPlaySound->Decoder();
}
}
void CALLBACK waveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance,
DWORD dwParam1, DWORD dwParam2 )
{
switch(uMsg)
{
case WOM_DONE:
{
WAVEHDR *header = (WAVEHDR *) dwParam1;
ClibmadWrap::pcm_block* pPCM = (ClibmadWrap::pcm_block*)header->dwUser;
SetEvent(pPCM->m_hPlayEvt);
}
break;
case WOM_OPEN:
case WOM_CLOSE:
break;
}
}
BOOL ClibmadWrap::Create()
{
if(m_bCreate)
return m_bCreate;
m_bCreate = FALSE;
if(waveOutGetNumDevs()==0)
{
//不能打开音频设备
return FALSE;
}
memset(&m_bCreate, 0, sizeof(ClibmadWrap) - offsetof(ClibmadWrap, m_bCreate));
m_bEof = TRUE;
m_hEventKill = CreateEvent(NULL, TRUE/* manual reset */, FALSE/* initial state */, NULL);
m_hEventDecode = CreateEvent(NULL, FALSE, FALSE, NULL);
DWORD hThreadId;
m_hDThread = CreateThread(NULL, 0, (unsigned long(__stdcall *)(void *))DecodeThread,this,0,&hThreadId);
SetThreadPriority(m_hDThread, THREAD_PRIORITY_BELOW_NORMAL);
InitializeCriticalSection(&m_csDataAccess);
if(m_output.data==NULL)
m_output.data = (unsigned char*)malloc(MAX_OUTPUTBUFFER);
/* initialize decoder */
mad_decoder_init(&m_Decoder, this, decode_input,
NULL, decode_filter,
decode_output, decode_error, NULL);
m_bCreate = TRUE;
return m_bCreate;
}
BOOL ClibmadWrap::Render(unsigned char* pInput, long lsize)
{
if(!m_bCreate || pInput==NULL || lsize<MAD_BUFFER_GUARD)
return FALSE;
EnterCriticalSection(&m_csDataAccess);
if(m_pInput==NULL)
{
m_pInput = (data_block*)malloc(sizeof(data_block));
m_pInput->start = (unsigned char*)malloc(sizeof(char)*lsize);
memcpy(m_pInput->start, pInput, lsize);
m_pInput->length = lsize;
m_pInput->pNext = NULL;
m_pInput->dataflag = 0;
}
else
{
data_block* p = m_pInput;
while(p->pNext) p = p->pNext;
p->pNext = (data_block*)malloc(sizeof(data_block));
p->pNext->start = (unsigned char*)malloc(sizeof(char)*lsize);
memcpy(p->pNext->start, pInput, lsize);
p->pNext->length = lsize;
p->pNext->pNext = NULL;
p->pNext->dataflag = 0;
}
m_bEof = FALSE;
LeaveCriticalSection(&m_csDataAccess);
return TRUE;
}
void ClibmadWrap::Decoder()
{
if(!m_bCreate) return;
int options = MAD_OPTION_IGNORECRC;
mad_decoder_options(&m_Decoder, options);
/* start decoding */
int result = mad_decoder_run(&m_Decoder, MAD_DECODER_MODE_SYNC);
}
enum mad_flow ClibmadWrap::Input(struct mad_stream *stream)
{
if(!m_bCreate)
return MAD_FLOW_STOP;
data_block* pInput = m_pInput;
data_block* pPerBuffer = NULL;
unsigned char* ppos = (unsigned char*)stream->next_frame;
EnterCriticalSection(&m_csDataAccess);
while( pInput && (pInput->dataflag&1) )
{
if(pPerBuffer)
{
pPerBuffer->release();
}
pPerBuffer = pInput;
pInput = pInput->pNext;
}
LeaveCriticalSection(&m_csDataAccess);
if(pInput)
{
// 由于项目中是同时输入多个完成的MP3文件流,加上下列代码会产生杂音,
//如果是输入一个MP3流的多个数据段则需要加上下列代码
// if(ppos)
// {
// int ileave = &pPerBuffer->start[pPerBuffer->length] - ppos;
// if(ileave>MAD_BUFFER_GUARD)
// {
// unsigned char* pnewdata = (unsigned char*)malloc(ileave + pInput->length);
// memcpy(pnewdata, ppos, ileave);
// memcpy(pnewdata+ileave, pInput->start, pInput->length);
// pInput->length = ileave + pInput->length;
// free(pInput->start);
// pInput->start = pnewdata;
// }
// }
mad_stream_buffer(stream, pInput->start, pInput->length);
pInput->dataflag |= 1;
}
else
{
if(ppos)
{
int ileave = &pPerBuffer->start[pPerBuffer->length] - ppos;
if(ileave>MAD_BUFFER_GUARD)
{
unsigned char* pnewdata = (unsigned char*)malloc(ileave + MAD_BUFFER_GUARD);
memcpy(pnewdata, ppos, ileave);
memset(pnewdata+ileave, 0, MAD_BUFFER_GUARD);
pPerBuffer->length = ileave + MAD_BUFFER_GUARD;
free(pPerBuffer->start);
pPerBuffer->start = pnewdata;
mad_stream_buffer(stream, pPerBuffer->start, pPerBuffer->length);
pPerBuffer->dataflag |= 1;
m_bEof = TRUE;
}
else
{
m_bEof = TRUE;
EnterCriticalSection(&m_csDataAccess);
releaseinput();
LeaveCriticalSection(&m_csDataAccess);
return MAD_FLOW_STOP;
}
}
else
{
m_bEof = TRUE;
EnterCriticalSection(&m_csDataAccess);
releaseinput();
LeaveCriticalSection(&m_csDataAccess);
return MAD_FLOW_STOP;
}
}
return MAD_FLOW_CONTINUE;
}
enum mad_flow ClibmadWrap::Output(struct mad_header const *header, struct mad_pcm *pcm)
{
if(!m_bCreate)
return MAD_FLOW_STOP;
unsigned int nchannels, nsamples;
mad_fixed_t const *left_ch, *right_ch;
nchannels = pcm->channels;
nsamples = pcm->length;
left_ch = pcm->samples[0];
right_ch = pcm->samples[1];
if (nchannels==1)
{
right_ch = 0;
}
pcm_block* pPCM = &m_pOutput[m_index];
if(pPCM->lplaying==1)
{
if(WaitForSingleObject(pPCM->m_hPlayEvt, INFINITE)!=WAIT_TIMEOUT)
{
waveOutUnprepareHeader(m_phwo, &pPCM->pWaveHdr, sizeof(pPCM->pWaveHdr));
pPCM->lplaying = 0;
pPCM->release();
}
}
//把左右声道合到一个PCM流中
int len = audio_pcm_s16le(&m_output.data[m_output.length], nsamples, left_ch, right_ch, &m_stats);
m_output.length += len;
if(m_output.length + MAX_NSAMPLES * (samplesize/8) * 2 > pcm->samplerate * (samplesize/8) * 2 || m_bEof)
{
char* pBuffer = (char*)malloc(m_output.length);
memcpy(pBuffer, m_output.data, m_output.length);
pPCM->pWaveHdr.lpData = pBuffer;
pPCM->pWaveHdr.dwBufferLength = m_output.length;
pPCM->pWaveHdr.dwUser = (DWORD)pPCM;
pPCM->pWaveHdr.dwFlags = 0;
pPCM->pWaveHdr.dwLoops = 0;
if(!m_bOpenDev)
{
setformat(nchannels, header->samplerate, samplesize);
if(!open_dev())
return MAD_FLOW_STOP;
}
write_dev(pPCM);
pPCM->lplaying = 1;
m_index = (m_index + 1) % 2;
m_output.length = 0;
}
if(m_bEof)
{
WaitForSingleObject(pPCM->m_hPlayEvt, INFINITE);
waveOutUnprepareHeader(m_phwo, &pPCM->pWaveHdr, sizeof(pPCM->pWaveHdr));
pPCM->lplaying = 0;
pPCM->release();
close_dev();
return MAD_FLOW_STOP;
}
return MAD_FLOW_CONTINUE;
}
由于代码只需要满足我的程序所以有些地方处理得很简单,测试效果能流畅地顺序播放多个MP3音频文件流,占用内存比以前少8M左右。