[原创]讲一下IMedia 流播放的故事!

来源:互联网 发布:网络招聘平台哪家最好 编辑:程序博客网 时间:2024/04/30 00:53

IMedia作为强大的brew音频视频播放接口,其功能必然很完善。今天讲一下用IMedia 播放流声音,这个常用于用在网络音频在线播放等等。

这次主要针对PCM和wav格式,这类是比较基础的流播放格式。
个人理解流播放其实就是一个无头音乐的播放,网络传送来的buffer不会每次都带音频信息,所以我个人叫他“无头播放”。
而一个音乐没有头信息怎么会被播放接口失败呢?
识别了如何填充buffer持续播放?
有几种填充方式呢?

下面介绍第一种做法IFIFO实现:
#include "AEEFIFO.h"
#include "AEEIMedia.h"
//首先定义两个结构体变量:
AEEMediaDataEx MediaDataEx;//这个是用来填充IMedia 的结构体
AEEMediaWaveSpec WaveSpec;//这个就是音频格式信息
IFIFO   *m_pFIFO;
IMedia * pIMedia;
ISource *m_pISource =NULL;

ISHELL_CreateInstance(pMe->applet.m_pIShell, AEECLSID_FIFO, (void**)&m_pFIFO);  
ISHELL_CreateInstance(pMe->applet.m_pIShell,AEECLSID_MEDIAPCM,(void**)&pIMedia);



IFIFO_OpenEx(GET_IFIFO(),"fifo://shared//test", AEEFIFO_MODE_RW);//fs:/shared/test这个目录
IFIFO_QueryInterface(GET_IFIFO(), AEECLSID_SOURCE, (void**)&m_pISource);
IFIFO_SetBufSize(GET_IFIFO(), 2048*4);//大小自己决定


MEMSET(&WaveSpec, 0x00, sizeof(WaveSpec)); //初始化
WaveSpec.wSize               = sizeof(WaveSpec);//spec的size
WaveSpec.clsMedia            = AEECLSID_MEDIAPCM;//这里用PCM接口
WaveSpec.wChannels           = 1;//channel,Number of channels. One for mono, two for stereo.
WaveSpec.dwSamplesPerSec     = 8000;//Channel sample rate in samples per second(Hertz). 只能取值8000 16000 32000 这样的采样率
WaveSpec.wBitsPerSample      = 16;// Number of bits per sample 取值8 或者 16
WaveSpec.bUnsigned           = FALSE;    //这个填写false
WaveSpec.dwExtra    = 0;//填写0
//上面这一堆复制其实就是说明这个音频的格式,比如采样率,channel等等

MediaDataEx.clsData       = MMD_ISOURCE;     // pData is ISource
MediaDataEx.pData         = (void *)m_pISource;   // ISource object
MediaDataEx.dwSize        = 0;
MediaDataEx.dwStructSize  = sizeof(MediaDataEx);  // Size of AEEMediaDataEx structure
MediaDataEx.dwCaps        = 0;    // What capabilities to enable. 0 means all.  
MediaDataEx.dwBufferSize  = 0;    // Internal buffer size. 0 means use default.
MediaDataEx.bRaw          = TRUE;   // Is this Raw data? Set it to no (FALSE)
MediaDataEx.pSpec         = &WaveSpec;   // Valid only for raw data
MediaDataEx.dwSpecSize    = sizeof(WaveSpec); // Valid only for raw data
//上面赋值是用来初始化IMedia接口数组,然后传给 IMedia

IMEDIA_SetMediaDataEx(pIMedia,&MediaDataEx, 1);
IMEDIA_RegisterNotify(pIMedia,(PFNMEDIANOTIFY)CB_Play,pISource);
IMEDIA_Play(pIMedia);//开始播放,但这个时候其实是没声音的!

接下来在获得到buffer后
只要做一件事:
IFIFO_Write(m_pFIFO,(char *)music_buf, music_buf_len);//这样就看到音频播放比较流畅了

这是一种做法,另一种做法参考下面的源代码:


这个源码有些地方不是很适合,具体改动在这里:
void SMISOURCE_UpdateWriteable(SMISource *pis)//更改 by狮子//里面的判断比较生硬,这样就动态多了
{
if ( pis->nWritePos < pis->nReadPos )
{
 if (( pis->nReadPos - pis->nWritePos ) < pis->nWriteSize )
 {
  pis->bWriteable = FALSE;
 }
 else
 {
  pis->bWriteable = TRUE;
 }
}
else if ( pis->nWritePos > pis->nReadPos )
{
 if (( pis->nWritePos + pis->nWriteSize  ) >= pis->nBufSize )
 {
  if (( pis->nWritePos + pis->nWriteSize  - pis->nBufSize ) < pis->nReadPos )
  {
   pis->bWriteable = TRUE;
  }
  else
  {
   pis->bWriteable = FALSE;
  }
 }
 else
 {
  pis->bWriteable = TRUE;
 }
}
else//更改 by狮子//部分当指针相等时其实是可以写的
{
 pis->bWriteable = TRUE;
}
}

int SMISOURCE_SourceFromMemory( int nBufLen, SMISource **ppis)
{
SMISource *pis = NULL;
int nRet = 0;
//sf_dbgprintf("SMISOURCE|SMISOURCE_SourceFromMemory");
if (/*( NULL == pBuf ) || */( nBufLen <= 0 ))
 return 1;
pis = (SMISource*)MALLOC(sizeof(SMISource));
if ( NULL == pis )
{
 //sf_dbgprintf("SMISOURCE|");
 return ENOMEMORY;
}
pis->pVtbl = (ISourceVtbl*)MALLOC(sizeof(ISourceVtbl));
if ( NULL == pis->pVtbl )
{
 //sf_dbgprintf("SMISOURCE|");
 FREE(pis);
 return ENOMEMORY;
}
pis->pVtbl->AddRef = SMISOURCE_AddRef;
pis->pVtbl->Release = SMISOURCE_Release;
pis->pVtbl->QueryInterface = SMISOURCE_QueryInterface;
pis->pVtbl->Read = SMISOURCE_Read;
pis->pVtbl->Readable = SMISOURCE_Readable;
pis->pDataBuf = MALLOC(nBufLen);//更改 by狮子//使用内部buffer,外部不再关心其存储方式
pis->nBufSize = nBufLen;
MEMSET(pis->pDataBuf,0x00,nBufLen);
pis->nRefCnt = 1;
pis->bWriteable = TRUE;
pis->nDataSize = 0;
pis->nReadPos = 0;
pis->nWritePos = 0;
pis->pReadableCB = NULL;
*ppis = pis;
return SUCCESS;
}
int SMISOURCE_SourceFree(SMISource *pis)
{
//sf_dbgprintf("SMISOURCE|");
if ( NULL != pis )
{
 FREEIF(pis->pVtbl);
 FREEIF(pis->pDataBuf);
 FREE(pis);
}
return 0;
}

具体的实现可以看源代码,不过源代码是用多线程的,其实每次buffer到了调用SMISource_Write 就OK了

原创粉丝点击