一个简单的 libmad 包装类

鉴客 发布于 2011/10/23 09:34
阅读 1K+
收藏 1

近来要把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左右。

文章出处:http://blog.csdn.net/navi_dx/article/details/1456314

加载中
返回顶部
顶部