Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2716634
  • 博文数量: 416
  • 博客积分: 10220
  • 博客等级: 上将
  • 技术积分: 4193
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-15 09:47
文章分类

全部博文(416)

文章存档

2022年(1)

2021年(1)

2020年(1)

2019年(5)

2018年(7)

2017年(6)

2016年(7)

2015年(11)

2014年(1)

2012年(5)

2011年(7)

2010年(35)

2009年(64)

2008年(48)

2007年(177)

2006年(40)

我的朋友

分类: WINDOWS

2007-01-15 11:21:13

作者: 陆其明   

摘自《Windows Media编程导向》 陆其明 编著  清华大学出版社

  媒体内容在播放时,最令人头痛的就是音视频不同步。从技术上来说,解决音视频同步问题的最佳方案就是时间戳:首先选择一个参考时钟(要求参考时钟上的时间是线性递增的);生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间);在播放时,读取数据块上的时间戳,同时参考当前参考时钟上的时间来安排播放(如果数据块的开始时间大于当前参考时钟上的时间,则不急于播放该数据块,直到参考时钟达到数据块的开始时间;如果数据块的开始时间小于当前参考时钟上的时间,则“尽快”播放这块数据或者索性将这块数据“丢弃”,以使播放进度追上参考时钟)。

图2.8 解决音视频同步问题的时间戳方案

    可见,避免音视频不同步现象有两个关键——一是在生成数据流时要打上正确的时间戳。如果数据块上打的时间戳本身就有问题,那么播放时再怎么调整也于事无补。如图2.8,视频流内容是从0s开始的,假设10s时有人开始说话,要求配上音频流,那么音频流的起始时间应该是10s,如果时间戳从0s或其它时间开始打,则这个混合的音视频流在时间同步上本身就出了问题。打时间戳时,视频流和音频流都是参考参考时钟的时间,而数据流之间不会发生参考关系;也就是说,视频流和音频流是通过一个中立的第三方(也就是参考时钟)来实现同步的。第二个关键的地方,就是在播放时基于时间戳对数据流的控制,也就是对数据块早到或晚到采取不同的处理方法。图2.8中,参考时钟时间在0-10s内播放视频流内容过程中,即使收到了音频流数据块也不能立即播放它,而必须等到参考时钟的时间达到10s之后才可以,否则就会引起音视频不同步问题。

基于时间戳的播放过程中,仅仅对早到的或晚到的数据块进行等待或快速处理,有时候是不够的。如果想要更加主动并且有效地调节播放性能,需要引入一个反馈机制,也就是要将当前数据流速度太快或太慢的状态反馈给“源”,让源去放慢或加快数据流的速度。熟悉DirectShow的读者一定知道,DirectShow中的质量控制(Quality Control)就是这么一个反馈机制。DirectShow对于音视频同步的解决方案是相当出色的。但WMF SDK在播放时只负责将ASF数据流读出并解码,而并不负责音视频内容的最终呈现,所以它也缺少这样的一个反馈机制。

    为了更好地理解基于时间戳的音视频同步方案,下面举一个生活中的例子。假设你和你的一个朋友约好了今天18:00在沪上广场见面,然后一起吃饭,再去打游戏。实际上,这个18:00就是你和你朋友保持同步的一个时间点。结果你17:50就到了沪上广场,那么你必须等你的朋友。10分钟过后,你的朋友还没有到,这时他打来电话说有事耽搁了,要晚一点才能到。你没办法,因为你已经在旁边的餐厅预订了位置,如果不马上赶过去,预订就会被取消,于是你告诉你的朋友直接到餐厅碰头吧,要他加快点。于是在餐厅将来的某个时间点就成为你和你朋友的又一个同步点。虽然具体时间不定(要看你朋友赶过来的速度),但这样努力的方向是对的,你和你朋友肯定能在餐厅见到面。结果呢?你朋友终于在18:30赶过来了,你们最终“同步”了。吃完饭19:30了,你临时有事要处理一下,于是跟你朋友再约好了20:00在附近的一家游戏厅碰头。你们又不同步了,但在游戏厅将来的某个时间点你们还是会再次同步的。

    悟出什么道理了没有?其实,同步是一个动态的过程,是一个有人等待、有人追赶的过程。同步只是暂时的,而不同步才是常态。人们总是在同步的水平线上振荡波动,但不会偏离这条基线太远。

二.    单一视频流

Filter Graph如图2:

图2 单一视频流的Filter Graph

注意:紧靠Video Renderer的上一级Filter的Video输出Pin,其GetMediaType函数提供的Media Type的VIDEOINFOHEADER结构要求填写完整,不仅包括图像的宽度、高度、像素位数,还包括BITMAPINFOHEADER结构的大小(biSize,指定为sizeof(BITMAPINFOHEADER))、平面数(biPlanes,指定为1)。如果需要调色板,BITMAPINFOHEADER数据结构后面还要带上调色板数据;如果是16位的RGB格式,BITMAPINFOHEADER数据结构后面则要带上RGB分量提取的掩码。代码参考如下:

VIDEOINFO    mVideoInfo;
ZeroMemory(&mVideoInfo, sizeof(mVideoInfo));
mVideoInfo.AvgTimePerFrame = 333667;
mVideoInfo.bmiHeader.biSize     = sizeof(BITMAPINFOHEADER);
mVideoInfo.bmiHeader.biWidth    = 352;
mVideoInfo.bmiHeader.biHeight   = 240;
mVideoInfo.bmiHeader.biBitCount = 16;
mVideoInfo.bmiHeader.biPlanes   = 1;
mVideoInfo.bmiHeader.biCompression = BI_BITFIELDS;
mVideoInfo.bmiHeader.biSizeImage   = mVideoInfo.bmiHeader.biWidth *
    mVideoInfo.bmiHeader.biHeight * mVideoInfo.bmiHeader.biBitCount / 8;
for (int i = 0; i < 3; i++) // Important for 16 bit RGB!
{
    mVideoInfo.dwBitMasks[i] = bits565[i];
}

1.    使用Filter Graph Manager默认的参考时钟

注:如果Filter Graph中没有一个Filter实现IReferenceClock接口,则该Filter Graph默认使用系统时钟作为参考时钟。

1.1    Video Sample不打时间戳、连续送出
现象:视频以最快的速度播放。Video Renderer不发送Quality Control消息。流时间线性增加。
Msiavsrc.ax(tid 920)     2307 : stream time: 120000
Msiavsrc.ax(tid 920)     2309 : stream time: 140000
Msiavsrc.ax(tid 920)     2311 : stream time: 160000
Msiavsrc.ax(tid 920)     2314 : stream time: 190000
Msiavsrc.ax(tid 920)     2316 : stream time: 210000
Msiavsrc.ax(tid 920)     2318 : stream time: 230000
Msiavsrc.ax(tid 920)     2320 : stream time: 250000
……
Msiavsrc.ax(tid 920)     3569 : stream time: 12740000(最后一个Sample)

1.2    Video Sample不打时间戳、间隙送出(模拟网络阻塞情况)
现象:视频播放一顿一顿。Video Renderer不发送Quality Control消息。视频数据流的阻塞不会影响流时间。流时间线性增加,间隙休眠的时间反映在前后两次获得的流时间上。
Msiavsrc.ax(tid 970)     2385 : stream time: 680000
Msiavsrc.ax(tid 970)     2389 : stream time: 720000
Msiavsrc.ax(tid 970)     4390 : Sleep(2000)...(出现视频播放的停顿)
Msiavsrc.ax(tid 970)     4391 : stream time: 20740000(流时间反映出Sleep的2s)
Msiavsrc.ax(tid 970)     4393 : stream time: 20760000
Msiavsrc.ax(tid 970)     4395 : stream time: 20780000
……
Msiavsrc.ax(tid 970)    39826 : stream time: 375090000(最后一个Sample)

1.3    Video Sample打(连续)时间戳、连续送出
现象:视频连续播放。Video Renderer发送Quality Control消息进行反馈控制。流时间线性增加,两次获取的流时间差大致是一帧的显示时间。
Msiavsrc.ax(tid 970)     4105 : stream time: 3600000
Msiavsrc.ax(tid 970)     4106 : Quality control (Famine) received.
Msiavsrc.ax(tid 970)     4139 : stream time: 3940000
Msiavsrc.ax(tid 970)     4140 : Quality control (Famine) received.
Msiavsrc.ax(tid 970)     4171 : stream time: 4260000
Msiavsrc.ax(tid 970)     4172 : Quality control (Famine) received.
Msiavsrc.ax(tid 970)     4205 : stream time: 4600000
Msiavsrc.ax(tid 970)     4206 : Quality control (Famine) received.
Msiavsrc.ax(tid 970)     4238 : stream time: 4930000
Msiavsrc.ax(tid 970)     4239 : Quality control (Famine) received.
……
Msiavsrc.ax(tid 970)    21922 : stream time: 181770000(最后一个Sample)

1.4    Video Sample打时间戳(中途复位一次,时间戳从0重打)、连续送出
现象:播放较连续。Video Renderer发送Quality Control消息进行反馈控制。时间戳复位后有一个“快镜头”,相邻两次Sample发送的时间差较小(正常时应该是一帧图像的显示时间),说明Video Renderer收到Sample后马上返回,以最快速度处理Sample,使Sample时间戳能够再次赶上流时间。
Msiavsrc.ax(tid 964)     2133 : stream time: 12280000
Msiavsrc.ax(tid 964)     2165 : stream time: 12600000
Msiavsrc.ax(tid 964)     2166 : Reset time stamp...
Msiavsrc.ax(tid 964)     2199 : stream time: 12940000(流时间递增缓慢)
Msiavsrc.ax(tid 964)     2201 : stream time: 12960000
Msiavsrc.ax(tid 964)     2203 : stream time: 12980000
Msiavsrc.ax(tid 964)     2205 : stream time: 13000000
Msiavsrc.ax(tid 964)     2208 : stream time: 13030000
Msiavsrc.ax(tid 964)     2210 : stream time: 13050000
Msiavsrc.ax(tid 964)     2212 : stream time: 13070000
Msiavsrc.ax(tid 964)     2214 : stream time: 13090000
……
Msiavsrc.ax(tid 964)    17748 : stream time: 168430000(最后一个Sample)

1.5    Video Sample打时间戳(时间戳超前流时间一个delta)、连续送出
现象:等待delta后播放连续。Video Renderer发送Quality Control消息进行反馈控制。
Msiavsrc.ax(tid 848)      821 : stream time: 4167195720000(随机值,此Sample送出后被Video Renderer阻塞)
Msiavsrc.ax(tid 848)     5828 : stream time: 49940000(delta在此反映出来)
Msiavsrc.ax(tid 848)     5860 : stream time: 50250000
Msiavsrc.ax(tid 848)     5892 : stream time: 50580000
Msiavsrc.ax(tid 848)     5927 : stream time: 50930000

……
Msiavsrc.ax(tid 848)    22677 : stream time: 218430000(最后一个Sample)

1.6    Video Sample打时间戳(时间戳随机乱序)、连续送出
现象:播放有断续、有快镜头。Video Renderer发送Quality Control消息进行反馈控制。整个播放过程中,如果新Sample的时间戳比流时间超前,则Sample会被Video Renderer阻塞住,直到流时间到达Sample时间后才开始播放;如果新Sample的时间戳比流时间滞后,Sample到达Video Renderer后会被立即播放,因此出现快镜头,直到Sample的时间戳赶上流时间后视频才恢复正常速度。总之,视频流不会影响参考时钟的正常运作。
Msiavsrc.ax(tid 610)     1774 : stream time: 5590000
Msiavsrc.ax(tid 610)     1807 : stream time: 5930000
Msiavsrc.ax(tid 610)     1808 : Add 5 second delta...
Msiavsrc.ax(tid 610)     1841 : stream time: 6270000(此Sample送出后阻塞)
Msiavsrc.ax(tid 610)     6873 : stream time: 56590000(流时间到达Sample上的时间戳后再继续播放)
Msiavsrc.ax(tid 610)     6906 : stream time: 56920000
Msiavsrc.ax(tid 610)     6940 : stream time: 57260000
……
Msiavsrc.ax(tid 610)     7440 : stream time: 62260000
Msiavsrc.ax(tid 610)     7473 : stream time: 62590000
Msiavsrc.ax(tid 610)     7474 : Add -3 second delta...
Msiavsrc.ax(tid 610)     7508 : stream time: 62940000(出现快镜头,Sample以很快的速度发送给Video Renderer)
Msiavsrc.ax(tid 610)     7509 : stream time: 62950000
Msiavsrc.ax(tid 610)     7511 : stream time: 62970000
Msiavsrc.ax(tid 610)     7512 : stream time: 62980000
Msiavsrc.ax(tid 610)     7513 : stream time: 62990000
……
Msiavsrc.ax(tid 610)     7563 : stream time: 63490000
Msiavsrc.ax(tid 610)     7564 : stream time: 63500000
Msiavsrc.ax(tid 610)     7566 : Do not add delta...
Msiavsrc.ax(tid 610)     7566 : stream time: 63520000
Msiavsrc.ax(tid 610)     7567 : stream time: 63530000
Msiavsrc.ax(tid 610)     7568 : stream time: 63540000
Msiavsrc.ax(tid 610)     7570 : stream time: 63560000
……
Msiavsrc.ax(tid 610)     7719 : stream time: 65050000
Msiavsrc.ax(tid 610)     7721 : stream time: 65070000
Msiavsrc.ax(tid 610)     7722 : stream time: 65080000
Msiavsrc.ax(tid 610)     7746 : stream time: 65320000(Sample时间戳终于赶上了流时间)
Msiavsrc.ax(tid 610)     7779 : stream time: 65650000
……
Msiavsrc.ax(tid 610)    19390 : stream time: 181760000(最后一个Sample)

1.7    Video Sample打(连续)时间戳、间隙送出(模拟网络阻塞情况)
现象:视频播放一顿一顿。Video Renderer发送Quality Control消息进行反馈控制。无数据时,流时间仍然在走;于是紧接着下一个Sample到达Video Renderer时已经“迟到”,所以会出现快镜头。
Msiavsrc.ax(tid 678)     1667 : stream time: 8940000
Msiavsrc.ax(tid 678)     1700 : stream time: 9270000
Msiavsrc.ax(tid 678)     3701 : Sleep(2000)...(此时阻塞显示上一个Sample的图像)
Msiavsrc.ax(tid 678)     3702 : stream time: 29290000(出现快镜头)
Msiavsrc.ax(tid 678)     3704 : stream time: 29310000
Msiavsrc.ax(tid 678)     3706 : stream time: 29330000
……
Msiavsrc.ax(tid 678)     3762 : stream time: 29890000
Msiavsrc.ax(tid 678)     3766 : stream time: 29930000
Msiavsrc.ax(tid 678)     5767 : Sleep(2000)... (此时阻塞显示上一个Sample的图像)
Msiavsrc.ax(tid 678)     5768 : stream time: 49950000(出现快镜头)
Msiavsrc.ax(tid 678)     5770 : stream time: 49970000
Msiavsrc.ax(tid 678)     5772 : stream time: 49990000
……
Msiavsrc.ax(tid 678)    38788 : stream time: 380150000(最后一个Sample)

2.    Filter Graph不使用参考时钟

现象:不管Video Sample打不打时间戳,也不管时间戳打得是否正确,视频都是以最快的速度播放。并且Video Renderer不发送Quality Control消息。如果Video Sample送出过程中有间隙性停顿,视频也会出现间隙性的停顿。

3.    Filter Graph中残留(处于未连接状态)一个Audio Renderer
注:默认情况下,Audio Renderer会被选中为Filter Graph的参考时钟。

Filter Graph如图3:

图3 残留一个Audio Renderer的单一视频流Filter Graph

测试结果均与以系统时钟作为Filter Graph的参考时钟的情况类似。

小结:

v    在任何时候,Video Sample上的时间戳都不会影响Filter Graph的流时间。
v    如果Video Sample上没有时间戳,则Video Renderer以最快速度处理Sample数据;如果有时间戳,则根据时间戳以及当前的流时间来安排Sample内容(视频图像)的显示。若Sample上的时间戳超前流时间,Video Renderer将该Sample阻塞,直到流时间到达Sample时间戳后再开始播放;若Sample上的时间戳滞后于流时间,Video Renderer将Sample内容立即显示后返回,以最快速度处理Sample,以使Sample时间戳尽快追赶流时间。
v    视频流播放过程中有一个Quality Control机制;Quality Control消息发送者是Video Renderer,反馈给数据发送线程,以加快或减慢数据发送速度,试图提高服务质量。

三.    混合音视频流

注:既然视频流不会影响Filter Graph的流时间,则视频流的播放情况不会影响到音频流的播放。本节主要讨论音频流播放情况对视频流播放的影响。

Filter Graph如图4:

图4 混合音视频流的Filter Graph

1.    Filter Graph不使用参考时钟

现象:音频正常、连续播放,视频快镜头播放。音视频不同步!

2.    Filter Graph使用参考时钟

2.1 Audio Sample不打时间戳
Audio Sample不打时间戳,音频流就不会影响Filter Graph的流时间。不管以Audio Renderer还是以系统时钟作为参考时钟,音视频均能同步、连续播放。

2.2 Audio Sample正常打时间戳
(1)Audio Renderer作为参考时钟
现象:音视频同步、连续播放。
(2)系统时钟作为参考时钟
现象:音视频同步、连续播放。

2.3 Audio Sample打时间戳(中途复位一次,时间戳从0重打)、连续送出
(1)Audio Renderer作为参考时钟
现象:Audio Sample时间戳复位后音视频不同步。由于Audio Sample时间戳复位后流时间不走了,所以视频播放停止(Video Sample的时间戳因超前流时间而被阻塞住),但音频仍然正常播放。因此造成音视频不同步。
(2)系统时钟作为参考时钟
现象:视频能够不受干扰地连续播放。音频在时间戳复位后有一部分数据(时间戳小于流时间的数据)被丢弃,所以播放中有一个跳变。

2.4 Audio Sample打时间戳(时间戳随机乱序)、连续送出
(1)Audio Renderer作为参考时钟
现象:音频连续播放。由于Audio Sample的时间戳将影响Filter Graph的流时间,导致视频的播放有时候出现快镜头,有时候出现停顿。最终音视频不同步。
(2)系统时钟作为参考时钟
现象:视频连续播放,音频播放不连续。因为Filter Graph的流时间独立运行,所以视频能连续播放。音频数据Sample时间戳小于流时间的将被丢弃,大于流时间的将被阻塞,因此总的播放效果就是断断续续。

2.5 Audio Sample打(连续)时间戳、间隙送出(模拟网络阻塞情况)
(1)Audio Renderer作为参考时钟
现象:音视频均不能连续播放。因为在音频无数据送出时,Filter Graph的流时间是不走的,导致视频也停止播放(Video Sample的时间戳因超前流时间而被阻塞住);直到音频有数据了,流时间继续走动,视频也恢复播放。音频数据虽不能连续播放,但音频数据没有被丢失。
(2)系统时钟作为参考时钟
现象:视频连续播放,音频断断续续,并且部分音频数据被跳过不播放。因为Filter Graph的流时间独立运行,所以视频能连续播放。音频数据Sample时间戳小于流时间的将被丢弃。

阅读(1657) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~