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了