??xml version="1.0" encoding="utf-8" standalone="yes"?>
pdf
1.
应用E序通过调用filter graph 理器方法来q接filter.
应用E序调用IFilterGraph::ConnectDirect
IGraphBuilder::Connect来指定不同的filter直接q接Q?br> 也可用IGraphBuilder::RenderFile自动实现q接
应用E序可以通过IFilterGraph::AddFilterfilter dgraph中,
当一个filter被添加到graph中时Qfilter图表理器通过IBaseFilter::JoinFilterGraph来通知filter.
q点说明, 不是filter的直接连接函数相互链?而是在以上内部调用实现的.
2. 考虑C前描q?br> FilterA ---->FilterB
的连接检查媒体类?逻辑基本是q样:
循环FilterA的输出pin,再@环FilterB的输入Pin媒体cd是否和pmt媒体cd
匚w
for (j = 0 ; j<FilterB.PinIn.MediaTypeCount; j++)
{
if (FilterB.PinIn.MediaType[j] = pmt )
{
if(FilterA.PinIn.ReceiveConnection(FilterA.PinOut, FilterB.MediaType[i]) = OK)
return TRUE;
}
}
for (i= 0; i< FilterA.PinOut.MediaTypeCount; i++)
{
if (FilterA.PinOut.MediaType[i] 是否在FilterB.PinIn中是否支?
if (FilterA.PinIn.ReceiveConnection(FilterA.PinOut, FilterA.MediaType[i]) = OK)
return TRUE;
}
在实C,调用ơ序以下q程:
filterGraph首先调用FilterA.PinOut::Connect().
FilterA.IPinOut::Connect()
原型:IPin::Connect(IPin* pReceivePin, const AM_MEDIA_TYPE * pmt)
该Connect参数?br> pReceivePin ?FilterB的输入Pin,
pmt 是FilterA的当前媒体类?
在内部调?主要)
hr = AgreeMediaType(pReceivePin, pmt);
查pReceivePin 有否pmt的媒体类?
?则自然ok
没有,p|,退函数.
则在AgreeMediaType做了以上逻辑循环.
IPin::AgreeMediaType函数处理如下:
1.判断pmt 是否是完全媒体类?是则按全媒体cd模式出来
2.非完全媒体类?br> IPin::EnumMediaTypes(IEnumMediaTypes** pEnum)
获取枚D指针(指向Pin中的媒体cd集合).
先枚举filterB的输入Pin的媒体类型的枚D?
调用TryMediaTypes 函数d断是否匹?
q不匚w,取出FilterA的枚丄指针.再调用TryMediaTyes
IPin::TryMediaType()处理
原型:
HRESULT CBasePin::TryMediaTypes(IPin*pReceivePin, const CMediaType*pmt,
IEnumMediaType *pEnum)
在该函数处理:
for (pmt in 所有该枚D集中的枚丑֪体类?)
{
AttemptConnect(pReceivePin, pmt)
}
在AttemptConnection中调?br> CBasePin::AttemptConnection(IPin* pReceivePin, const CMediaType*pmt)
查FilterA 的CheckConnect(pReceivePin)
FilterA的PInOut::CheckMediaType(pmt)
ok,return
FilterA的PinOut::SetMediaType(pmt)
pReceivePin->ReceiveConnection(...) (filterB 的PinIn)
ok,return
FilterA的PinOut::CompleteConnect(pReceivePin)
typedef struct _MediaType {
GUID majortype;
GUID subtype;
BOOL bFixedSizeSamples;
BOOL bTemporalCompression;
ULONG lSampleSize;
GUID formattype;
IUnknown *pUnk; //not use
ULONG cbFormat;
BYTE *pbFormat;
} AM_MEDIA_TYPE;
主要?br> majortype 媒体cd大致说明
subtype 更一步的l致说明
formattype
包括有以下:其对应的不同的数据格?br> FORMAT_None
FORMAT_DvInfo
FORMAT_MPEGVideo
FORMAT_MPEG2Video
FORMAT_VideoInfo
FORMAT_VideoInfo2
FORMAT_WaveFormatEx
GUID_NULL
cbForamt成员指定了格式块pbFormat的大?
pbFormat指针指向格式子块?br> pbFormat是一个void*的指针,因ؓ格式块会因ؓ媒体cd
的不同而有不同的指向。如音频填充的是WAVEFORMATEXl构
数据.
可以从中取出传来的数据格式?br>
//TWaveFormatEx l构:
TWaveFormatEx = packed record
wFormatTag: Word; {指定格式cd; 默认 WAVE_FORMAT_PCM = 1;}
nChannels: Word; {指出波Ş数据的通道? 单声道ؓ 1, 立体Cؓ 2}
nSamplesPerSec: DWORD; {指定h速率(每秒的样本数)}一般ؓ8000
nAvgBytesPerSec: DWORD; {指定数据传输的^均速率(每秒的字节数)} 每秒的字节数:
nBlockAlign: Word; {指定块对?单位字节), 块对齐是数据的最单位}
wBitsPerSample: Word; {采样大小(字节)}每个h的BIT数目Q一般ؓ16
cbSize: Word; {应该是该l构的大}
end;
nChannels : 对于pcm,其nchannels不超q?,对于非pcm格式,则超q?.
nSamplesPerSec : 通常?kHz, 11.025 kHz, 22.05 kHz, and 44.1 kHz.
nAvgBytesPerSec : 每秒传送字节数 = nSamplesPerSec * nBlockAlign
nBlockAlign : 寚w字节 = nChannels * wBitsPerSample / 8
是表示一个样本的最字?
wBitsPerSample : 在格式默认情况下,一般ؓ8,16,表示的是h的bit ?/p>
对于一??11k传输的立体声?br>nChannels = 2
nSamplesPerSec(每秒的样本数) = 11025 是取样?br>nBlockAlign = 2 * 8 / 8= 2 寚w字节Q最样本字节数
nAvgBytesPerSec = 11025 * 2 = 22050
wBitsPerSample = 8
下面的图列清楚从另一个方面表达样?br>
h1 | h2 | ...n | |
8位单声道 | 0声道 | 0声道 | |
8位立体声 | 0声道L 1声道R | 0声道L 1声R?/td> | |
16位单声道 | 0声道(低字? 0声道(高字? | 0声道(低字? 0声道(高字? | |
16位立体声 |
0声道(低字?0声道Q高字节Q?声道(? 1声道(? |
同左 |
---------
waveform-audio ~存格式
typedef struct {
LPSTR lpData; //内存指针Q放|音频pcmh数据
DWORD dwBufferLength; //长度
DWORD dwBytesRecorded; //已录音的字节长度
DWORD dwUser;
DWORD dwFlags;
DWORD dwLoops; //循环ơ数
struct wavehdr_tag* lpNext; //保留
DWORD reserved; //保留
} WAVEHDR;
其中lpdata 即ؓpcm格式h数据?br>
采样大小?位,则采L动态范围ؓ20*log(256)分贝=48db?br>h大小?6位,则采样动态范围ؓ20*log(65536)大约?6分贝
振幅大小Q?#160; 20*log(A1/A2)分贝QA1,A2Z个声音的振幅?br>则对于的音频Q?br> 8?#160; 20 * lg( lpData[0] /256)
16?#160; 20 * lg( lpData[0]--lpData[1] / 65536)
考虑到单双道Q还需要相应取出左叛_道的倹{?br> 考虑到lg求gؓ?8?之间Q则在实际{换中需?48or96.
h大小 数据格式 最大?#160; 最?
8位PCM unsigned int 256 0
16位PCM int 32767 -32767
8位音频是unsigned 存放波Ş,取振q要-127.
?6位因其存放ؓint cd,直接套用公式.
audiometer左右声道音量探测E序Q?a href="http://www.shnenglu.com/Files/kenlistian/audioVu_di.rar">参考代?delphi?/a>Q?/p>
2 DiectDound几个对象
创徏一个设备对象,后通过讑֤对象创徏~冲区对象?br> 辅助~冲区由应用E序创徏和管理,DirectSound会自动地创徏和管理主~冲区,
3 播放音频文g开发的基本程
a 创徏一个设备对象,讄讑֤对象的协作度?
调用DirectSoundCreat8创徏一个支持IDirectSound8接口的对象,
q个对象通常代表~省的播放设备?br>
如果没有声音输出讑֤Q这个函数就q回errorQ或者,在VXD驱动E序下,
如果声音输出讑֤正被某个应用E序通过waveform格式的api函数所控制Q?br> 该函Cq回error?nbsp;
当创建完讑֤对象后,调用IDirectSound8::SetCooperativeLevel来设|?br> 协作度,否则听不到声?
b.创徏一个辅助BufferQ也叫后备缓冲区
(IDirectSound8::CreateSoundBuffer)
创徏的bufferUC辅助~冲区,Direcsound通过把几个后备缓冲区的声?br> 混合C~冲ZQ然后输出到声音输出讑֤上,辑ֈ混音的效果?/p>
c. 获取PCMcd的数?br>
WAV文g或者其他资源的数据d到缓冲区中?/p>
d. 数据读取到~冲?br> 其中用到以下来锁~冲区?br> IDirectSoundBuffer8::Lock
IDirectSoundBuffer8::Unlock.
e. 播放~冲Z的数?br> IDirectSoundBuffer8::Play 播放~冲Z的音频数据,
IDirectSoundBuffer8::Stop 暂停播放数据Q?br>
获取或者设|正在播攄音频的音量的大小
IDirectSoundBuffer8::GetVolume
IDirectSoundBuffer8::SetVolume
获取讄音频播放的频?br> IDirectSoundBuffer8::GetFrequency
IDirectSoundBuffer8::SetFrequency
ȝ冲区的频率不允许改动Q?/p>
讄音频在左叛_道播攄位置
IDirectSoundBuffer8::GetPan
IDirectSoundBuffer8::SetPan
包含全部音频数据的缓冲区我们UCؓ静态的~冲区,
管不同的声韛_能会反复使用同一个内存bufferQ但静态缓冲区的数据只写入一ơ?/p>
静态缓冲区只填充一ơ数据,然后可以playQ?br>
l静态缓冲区加蝲数据分下面几个步?br> 1、用IDirectSoundBuffer8::Lock函数来锁定所有的内存Q?br> 指定你锁定内存中你开始写入数据的偏移位置Qƈ且取回该偏移位置的地址?br> 2、采用标准的数据copyҎQ将音频数据复制到返回的地址?br> 3、调用IDirectSoundBuffer8::Unlock.Q解锁该地址?/p>
用static buffer 播放wavҎ
f 缓冲区播放大型的wave文g
缓冲区是播放那些比较长的音频文g,Ҏ放,边填充DirectSound~冲区?/p>
DirectSound的通知机制
因ؓStream buffer 大小只够容纳一部分数据Q在播放完缓冲区中的数据后,
DirectSound׃通知应用E序Q将新的数据填充到DirectSound的缓冲区中?/p>
当DirectSound播放到buffer?920Q?840Q?760Q?680{位|时Q?br>Directsound׃通知应用E序Q将g_event,讄为通知?
应用E序通过WaitForMultipleObjects 函数{待DirectSound的通知Q?br>数据填充到DirectSoun的辅助缓冲区?/p>
pȝ讑֤枚D器是Ҏ不同的种cL创徏的,如,音频压羃Q视频捕捉?br> 不同U类的枚丑֙对于每一U设备返回一个独立的名称QmonikerQ?/p>
下面的步骤是使用讑֤枚D器来获取讑֤Q?/p>
1) 创徏枚D器组ӞCLSID为CLSID_SystemDeviceEnum
2) 指定某一U类型设备,获取该种cL丑֙
通过ICreateDevEnum::CreateClassEnumerator获取某一U类的枚丑֙Q?br> 该函数返回一个IEnumMoniker接口指针Q?br> 通过查返回值是否ؓS_OK来判断是否获取到该种cL丑֙.
3) 用IEnumMoniker::Next枚D每一个moniker?br> q个Ҏq回一个IMoniker接口指针?br>4) 通过IMoniker::BindToStorage获取讑֤的名U?/p>
大致例子如下Q?br>
2.采用Filer Mapper?br> cM条g查询?br>
比系l设备枚丑֙QSystem Device EnumeratorQ的效率要低一些?br> 当要枚D某特定种cȝfilterӞ应采用系l设备枚丑֙ҎQ但搜烦支持某种
媒体cd的filterӞ用filter mapper.
Filter Mapper 通过IFilerMapper2接口搜烦接口Q?br> 通过调用IFilterMapper2::EnumMatchingFiltersҎQ传递一些参数来定义搜烦条gQ?br> q回一个适合条g的filter的枚丑֙Q?br>
q回的是一个IEnumMoniker接口Qƈ对于每个适合的filter都提供一个单独的moniker?/p>
例子Q?br>
DirectShow加入一个硬件FilterQ是?#8220;枚D”Q声卡Filter也不例外?br>代表声卡的Filter都注册在CLSID_AudioInputDeviceCategory目录下,
使用pȝ讑֤枚D器枚举这个目录,p发现要创建的声卡对象?br> Q如何枚举这里就不再赘述了?
当成功加入声卡Filter后,接下ȝ问题是要将q个Filter与其他Filter相连?/p>
x捉生成一个Wave文gQ采用过滤器的勾q如?br> 声卡filter--->Wave Dest Filter ---->File Writer Filter
Wave Dest Filter是微软DirectX SDK自带的过滤器
其功能是Q当l束捕捉Ӟ往Wave文g中写入一个文件头信息?br>
Filte Write Filter 是微软系l过滤器?br>用graphedit可以勾连后测试下?/p>
//采用E序来连接过滤器的大致方?
//没有处理错误
void BuildAudioCaptureGraph(void)
{
IBaseFilter *pSrc = NULL, //捕捉音频讑֤
*pWaveDest = NULL, //处理音频qo?br> *pWriter = NULL; //产生文gqo?/p>
IFileSinkFilter *pSink= NULL;
IGraphBuilder *pGraph;
// Create the Filter Graph Manager.
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);
// Add the audio capture filter.
//q里省略了枚举设备处?br> FindAudioCapture(&pSrc);
// audio capture devices and picks one.
pGraph->AddFilter(pSrc, L"Capture");
// Add the WavDest and the File Writer.
AddFilterByClsid(pGraph, L"WavDest", CLSID_WavDest, &pWavDest);
AddFilterByClsid(pGraph, L"File Writer", CLSID_FileWriter, &pWriter);
//是writer接口中属?br> pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSink);
pSink->SetFileName(L"C:\test.wav", NULL);
//q接filter
ConnectTwoFilters(pGraph, pSrc, pWavDest);
ConnectTwoFilters(pGraph, pWavDest, pWriter);
}
DirectShow功能实现Q?/p>
1.可提供高质量的多媒体的捕获和回攑֊能;
2.支持多种媒体格式Q包括ASFQAdvanced Systems FormatQ,MPEGQMotion Picture Experts GroupQ,AVIQAudio-Video InterleavedQ,MP3QMPEG Audio Layer-3Q和WAV声音文gQ?br>3.可从g上捕获媒体数据流Q?br>4.可自动检ƈ使用视频和音频加速硬件?/p>
故,DirectShow是用于多媒体应用开发?其实是一个Y~码(or解码))
它充分发挥媒体的性能Q提高运行速度Q可以简化媒体播放、媒体间的格式{?br>和媒体捕L工作。同Ӟ它还h极大的可扩展性和灉|性,可以q戯?br>创徏lgQƈ这个组件加入DirectShowl构中以支持新的格式或特D的效果?/p>
应用E序与DirectShowlg以及DirectShow所支持的Yg之间的关p?br>如图1
二。概?br>1.qo?br>qo器分Z下几U类型:
a 源过滤器Qsource filterQ:
源过滤器引入数据到过滤器图表中,数据来源可以是文件、网l、照相机{?br> 不同的源qo器处理不同类型的数据源?/p>
b 变换qo器(transform filterQ:
变换qo器的工作是获取输入流Q处理数据,q生成输出流?br> 变换qo器对数据的处理包括编解码、格式{换、压~解压羃{?/p>
c 提交qo器(renderer filterQ:
接收数据q把数据提交l外设?/p>
d 分割qo器(splitter filterQ:
把输入流分割成多个输出?br> 如,AVI分割qo器把一个AVI格式的字节流分割成视频流和音频流?/p>
e 混合qo器(mux filterQ:
把多个输入组合成一个单独的数据?br> 如,AVI混合qo器把视频和音频合成一个AVI格式的字节流?br> qo器的q些分类q不是绝对的Q如一个ASF读过滤器QASF Reader filterQ?br> 既是一个源qo器又是一个分割过滤器?br>
2 filter graph
qo器图表用来连接过滤器以控制媒体流Q它也可以将数据q回l应用程序,
q搜索所支持的过滤器?br> qo器有三种可能的状态:q行、停止和暂停?br> 暂停是一U中间状态,停止状态到q行状态必定经q暂停状态?br> 暂停可以理解为数据就l状态,是ؓ了快速切换到q行状态而设计的?nbsp;
在暂停状态下Q数据线E是启动的,但被提交qo器阻塞了?br>
通常情况下,qo器图表中所有过滤器的状态是一致的?br>
3. 引脚QpinQ?br> qo器可以和一个或多个qo器相q,
q接的接口也是COM形式的,UCؓ引脚?br>
qo器利用引脚在各个qo器间传输数据?br> 每个引脚都从Ipinq个COM对象z出来的?br> 每个引脚都是qo器的U有对象Q过滤器可以动态的创徏引脚Q销毁引脚,自由控制引脚的生存时间?br> 引脚分输入引脚(Input pinQ和输出引脚QOutput pinQ两U类型,
两个相连的引脚必L不同U类的,卌入引脚只能和输出引脚相连
qo器之间的q接Q也是引脚之间的连接)Q实际上是连接双方媒体类型(Media TypeQ协商的q程。(媒体cdQ不完全媒体cd 再下一节有讲解)
q接的大致过EؓQ?br> 如果调用q接函数时已l指定了完整的媒体类型,则用q个媒体cdq行q接Q?br> 成功与否都结束连接过E;
如果没有指定或不完全指定了媒体类型,
则枚举过E见后面.其两个filter的连接设定如?
Filter A ------------------> Filter B
------------------------------------------------------------- ------------------
说明:
媒体cdQMedia TypeQ?br> 两个qo器相q时Q必M用一致的媒体cdQ否则这两个qo器就不能相连?br>
媒体cd能识别上一U过滤器传送给下一U过滤器的数据类型,q对数据q行分类?br> 媒体cd的结?nbsp; AM_MEDIA_TYPE
AM_MEDIA_TYPE׃部分l成Q?br> Major type
Subtype
Format type
都用GUID 来唯一标示
Major type主要定性描qCU媒体类型,q种媒体cd可以是视频、音频、比Ҏ据流或MIDI数据{;
Subtype q一步细化媒体类型,
拿视频的说就是进一步指定是RGB-24Q还是RGB-32Q或是UYVY{;
Format type则用一个结构更q一步细化媒体类型?/p>
媒体cd的三个部分都指定了某个具体的GUID|则称q个媒体cd是完全指定的Q?br> 媒体cd的三个部分中有Q何一个值是GUID_NULLQ则U这个媒体类型是不完全指定的?br> GUID_NULL起通配W作?br>
pinout和pinin的连接过E可以用下面逻辑语言表达.
1.如调用连接函数时已经指定了完整的Media typeQ则用这个Media typeq行q接Q?br>成功与否都结束连接过E;
2.如没有指定或不完全指定了Media typeQ?br>则如?
BOOL CheckFilterB_PinIn()
{
for(i = 0 ; i < FilterB.FPinIn.MediaTypeCount ; i++)
{
if (IsSameMediaType(FilterA.FPinOut,FilterB.FPinIn.MediaType[i]) = True)
{
return TRUE; //Pin之间的连接成功;
}
}
return FALSE; //在Input pin不支持该媒体cd,p|.
}
q回FALSE再枚举Output pin上的所有Media typeQƈ逐一用这些Media type与Input pinq行q接?br>
for(i = 0 ; i < FilterA.FPinOut.MediaTypeCount; i++)
{
if (CheckFilteB_PinIn(FilterA.FPinOut.MediaType[i]) = True )
{
return TRUE;
}
}
return FALSE; //filterA和filterB的连接失?
Filter必须加入到Filter Graphq接入到工作链\中才能发挥作用?br>如想l过Filter Graph而直接用Filter实现的功能模块,那就要将Filter功能
UL成DirectX媒体对象QDMOQ?br>Filter?U状态:停止、暂停和q行?/p>
Filter Chain是相互连接着的一条Filter链\Qƈ且链路中的每个Filter全都有一个处?#8220;已连?#8221;状态的
输入PinQ至多有一个处?#8220;已连?#8221;状态的输出PinQ这条Filter链\中的数据不依赖链\外的其他Filter?/p>
Filter Chain通过IFilterChain接口来进行相x作?br>当Filter Graph处于q行状态下QFilter Chain可以在运行和停止状态之间切换;
当Filter Graph处于暂停状态下QFilter Chain可以在暂停和停止状态之间切换?br>Filter Chain只有两种状态{换?/p>
Filter的数据传?br>Filter之间以Sample的Ş式传送数据?br>Sample是一个封装了一定大数据内存的COMlg?br>用于数据传输的一般是输入pin上实现的IMemInputPin接口?/p>