音频应用

 找回密码
 快速注册

QQ登录

只需一步,快速开始

阅读: 27228|回复: 0

[技术] 浅析DirectShow音视频同步解决完整方案

[复制链接]

162

积分

0

听众

0

音贝

音频应用新手发布

Rank: 3

积分
162
发表于 2004-9-25 | |阅读模式
音频应用公众号资讯免费发布推广
浅析DirectShow音视频同步解决完整方案, h( d' c0 U9 X. B. c3 V

& W1 a9 y2 |, v) _' u% x多媒体处理,不可避免地要解决音视频的同步问题。DirectShow是怎么来实现的呢?我们一起来学习一下。
1 D8 c9 q1 A& U% K3 r  大家知道,DirectShow结构最核心的部分是Filter Graph Manager:向下控制Graph中的所有Filter,向上对τ贸绦蛱峁┍喑探涌凇F渲校現ilter Graph Manager实现的很重要一个功能,就是同步音视频的处理。简单地说,就是选一个公共的参考时钟,并且要求给每个Sample都打上时间戳,Video Renderer或Audio Renderer根据Sample的时间戳来控制播放。如果到达Renderer的Sample晚了,则加快Sample的播放;如果早了,则Renderer等待,一直到Sample时间戳的开始时间再开始播放。这个控制过程还引入一个叫Quality Control的反馈机制。5 M& V0 U0 B0 E& H& z
  下面,我们来看一下参考时钟(Reference Clock)。所有Filter都参照于同一个时钟,才能统一步调。DirectShow引入了两种时钟时间:Reference time和Stream time。前者是从参考时钟返回的绝对时间(IReferenceClock::GetTime),数值本身的意义取决于参考时钟的内部实现,利用价值不大;后者是两次从参考时钟读取的数值的差值,实际应用于Filter Graph内部的同步。Stream time在Filter Graph不同状态的取值为:( v. c6 D% O; J2 }+ a
  1. Filter Graph运行时,取值为当前参考时钟时间减去Filter Graph启动时的时间(启动时间是通过调用Filter上的IMediaFilter::Run来设置的);
0 B# W  E! O5 R. v# n( E  b( n  2. Filter Graph暂停时,保持为暂停那一刻的Stream time;5 G( f% k  r+ P0 m
  3. 执行完一次Seek操作后,复位至零;
8 W1 d3 s; G5 Y7 M0 M  4. Filter Graph停止时,取值不确定。
3 Z6 O' p* U' V) J  那么,参考时钟究竟是什么东西呢?其实,它只是一个实现了IReferenceClock接口的对象。也就是说,任何一个实现了IReferenceClock接口的对象都可以成为参考时钟。在Filter Graph中,这个对象一般就是一个Filter。(在GraphEdit中,实现了参考时钟的Filter上会显示一个时钟的图标;如果同一个Graph中有多个Fiter实现了参考时钟,当前被Filter Graph Manager使用的那个会高亮度显示。)而且大多数情况下,参考时钟是由Audio Renderer这个Filter提供的,因为声卡上本身带有了硬件定时器资源。接下来的问题是,如果Filter Graph中有多个对象实现了IReferenceClock接口,Filter Graph Manager是如何做出选择的呢?默认的算法如下:' O, h5 j9 |; L, O. Z" z
  1. 如果应用程序设置了一个参考时钟,则直接使用这个参考时钟。(应用程序通过IMediaFilter:: SetSyncSource设置参考时钟,参数即为参考时钟;如果参数值为NULL,表示Filter Graph不使用参考时钟,以最快的速度处理Sample;可以调用IFilterGraph:: SetDefaultSyncSource来恢复Filter Graph Manager默认的参考时钟。值得注意的是,这时候的IMediaFilter接口应该从Filter Graph Manager上获得,而不是枚举Graph中所有的Filter并分别调用Filter上的这个接口方法。)/ U* h8 v- x- o; @, ?& }# c
  2. 如果Graph中有支持IReferenceClock接口的Live Source,则选择这个Live Source。
+ g- i. j1 j0 |5 b( Y  3. 如果Graph中没有Live Source,则从Renderer依次往上选择一个实现IReferenceClock接口的Filter。如果连接着的Filter都不能提供参考时钟,则再从没有连接的Filter中选择。这一步算法中还有一个优先情况,就是如果Filter Graph中含有一个Audio Render的链路,则直接选择Audio Renderer这个Filter(原因上面已经提及)。0 H. k1 D/ d/ |5 O; I  m
  4. 如果以上方法都找不到一个适合的Filter,则选取系统参考时钟。(System Reference Clock,通过CoCreateInstance创建,CLSID为CLSID_SystemClock。). _  n  N3 ^3 j0 F
  我们再来看一下Sample的时间戳(Time Stamp)。需要注意的是,每个Sample上可以设置两种时间戳:IMediaSample::SetTime和IMediaSample::SetMediaTime。我们通常讲到时间戳,一般是指前者,它又叫Presentation time,Renderer正是根据这个时间戳来控制播放;而后者对于Filter来说不是必须的,Media time有没有用取决于你的实现,比如你给每个发出去的Sample依次打上递增的序号,在后面的Filter接收时就可以判断传输的过程中是否有Sample丢失。我们再看一下IMediaSample::SetTime的参数,两个参数类型都是REFERENCE_TIME,千万不要误解这里的时间是Reference time,其实它们用的是Stream time。还有一点,就是并不是所有的Sample都要求打上时间戳。对于一些压缩数据,时间戳是很难打的,而且意义也不是很大(不过压缩数据经过Decoder出来之后到达Renderer之前,一般都会打好时间戳了)。时间戳包括两个时间,开始时间和结束时间。当Renderer接收到一个Sample时,一般会将Sample的开始时间和当前的Stream time作比较,如果Sample来晚了或者没有时间戳,则马上播放这个Sample;如果Sample来得早了,则通过调用参考时钟的IReferenceClock::AdviseTime等待Sample的开始时间到达后再将这个Sample播放。Sample上的时间戳一般由Source Filter或Parser Filter来设置,设置的方法有如下几种情况:; H$ C0 k7 [+ c. i9 y6 V! _7 @
  1. 文件回放(File playback):第一个Sample的时间戳从0开始打起,后面Sample的时间戳根据Sample有效数据的长度和回放速率来定。; b# ^" \( y& M: I9 A- L8 b7 L
  2. 音视频捕捉(Video and audio capture):原则上,采集到的每一个Sample的开始时间都打上采集时刻的Stream time。对于视频帧,Preview pin出来的Sample是个例外,因为如果按上述方法打时间戳的话,每个Sample通过Filter链路传输,最后到达Video Renderer的时候都将是迟到的;Video Renderer通过Quality Control反馈给Source Filter,会导致Source Filter丢帧。所以,Preview pin出来的Sample都不打时间戳。对于音频采集,需要注意的是,Audio Capture Filter与声卡驱动程序两者各自使用了不同的缓存,采集的数据是定时从驱动程序缓存拷贝到Filter的缓存的,这里面有一定时间的消耗。
" m: ^- @& m6 s- `  3. 合成(Mux Filters):取决于Mux后输出的数据类型,可以打时间戳,也可以不打时间戳。4 D$ B+ r4 l( h$ Q5 q
  大家可以看到,Sample的时间戳对于保证音视频同步是很重要的。Video Renderer和Audio Renderer作为音视频同步的最终执行者,需要做很多工作。我们或许要开发其它各种类型的Filter,但一般这两个Filter是不用再开发的。一是因为Renderer Filter本身的复杂性,二是因为微软会对这两个Filter不断升级,集成DirectX中其它模块的最新技术(如DirectSound、DirectDraw、Direct3D等)。
: g7 U1 r# D8 _9 Q) t' ], Z  最后,我们再来仔细看一下Live Source的情况。Live Source又叫Push source,包括Video /Audio Capture Filter、网络广播接收器等。Filter Graph Manager是如何知道一个Filter是Live Source的呢?通过如下任何一个条件判断:
: y, ~1 \3 j3 u9 Z  1. 调用Filter上的IAMFilterMiscFlags::GetMiscFlags返回有AM_FILTER_MISC_FLAGS_IS_SOURCE标记,并且至少有一个Output pin实现了IAMPushSource接口。2 x: ]2 f& `" F
  2. Filter实现了IKsPropertySet接口,并且有一个Capture output pin(Pin的类型为PIN_CATEGORY_CAPTURE)。0 I3 \- N) I+ Z
  Live Source对于音视频同步的影响主要是以下两个方面:Latency和Rate Matching。Latency是指Filter处理一个Sample花费的时间,对于Live Source来说,主要取决于使用缓存的大小,比如采集30fps的视频一般采集完一帧后才将数据以一个Sample发送出去,则这个Filter的Latency为33ms,而Audio一般缓存500ms后才发送一个Sample,则它的Latency就为500ms。这样的话,Audio与Video到达Renderer就会偏差470ms,造成音视频的不同步。默认情况下,Filter Graph Manager是不会对这种情况进行调整的。当然,应用程序可以通过IAMPushSource接口来进行Latency的补偿,方法是调用IAMGraphStreams::SyncUsingStreamOffset函数。Filter Graph Manager的实现如下:对所有实现IAMPushSource接口的Filter调用IAMLatency::GetLatency得到各个Source的Latency值,记下所有Latency值中的最大值,然后调用IAMPushSource::SetStreamOffset对各个Source设置偏移值。! |6 N# Q7 H# n$ v1 W2 q
  这样,在Source Filter产生Sample时,打的时间戳就会加上这个偏移量。Rate Matching问题的引入,主要是由于Renderer Filter和Source Filter使用的是不同的参考时钟。这种情况下,Renderer对数据的播放要么太快,要么太慢。另外,一般Live Source不能控制输出数据的速率,所以必须在Renderer上进行播放速率的匹配。因为人的听觉敏感度要大于视觉敏感度,所以微软目前只在Audio Renderer上实现了Rate Matching。实现Rate Matching的算法是比较复杂的,这里就不再赘述。
. ^* M8 `3 `$ r, P  看到这里,大家应该对DirectShow是如何解决音视频同步问题的方案有一点眉目了吧。深层次的研究,还需要更多的测试、Base class源码阅读,以及DirectShow相关控制机制的理解,比如Quality Control Management等。
欢迎厂家入驻,推文!免费!微信:yinpinyingyong
您需要登录后才可以回帖 登录 | 快速注册

本版积分规则

音频应用搜索

小黑屋|手机版|音频应用官网微博|音频招标|音频应用 (鄂ICP备16002437号)

Powered by Audio app

快速回复 返回顶部 返回列表