把握当下

不积跬步无以成千里,不积细流无以成江河。骐骥一跃,不能十步;驽马十驾,功在不舍。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::
18.1  需求定义 
    大家知道,在网络上传输音频、视频等多媒体数据,目前主要有下载和流式传输两种方案。下载的主要缺点是,必须等全部内容传输完毕,然后才能在本地机器打开;而采用流式传输方案,声音、影像或动画等时基媒体由音视频服务器向用户计算机连续、实时地传送,用户不必等到整个文件全部下载完毕,只需经过几秒或十几秒的启动延时即可进行观看。
    流式传输广泛应用于网络视频点播,以及视频监控等领域。下面,将以网络传送
MPEG流、客户端实时解码播放为例,介绍一下这个流式传输的解决方案。
18.2解决方案
    多媒体的流式传输实际上牵涉到两方面的技术。其一就是服务器与客户端的通信技
术,包括多媒体数据的传输、命令控制等;其二就是客户端对接收到的多媒体流实时解码
后播放的技术。显然,网络通信可以使用windOWS socket技术,多媒体流的解码播放可以使用DirectShow技术。
18.2.1  Windows Socket网络传输技术
    已知MPEG流是连续的,它的同步点不能随意选择。因此,为了传送这种数据,必
须使用面向连接的可靠的传输协议(TcP)。这个通信过程如图18 1所示。


图18 1计算机之间的socket通信过程
    如图18.1,首先是客户端使用一个Socket向服务器发送一个连接请求。服务器一般
有一个socket专门用于监听网络连接,当收到其他计算机的连接请求时,则根据实际情
况接受或者拒绝连接;如果服务器接受连接,则会产生一个新的Socket,与客户端的
socket建立一对连接,而实际的数据传送就是在这对socket连接上完成的。
    MFc提供了两个类:CAsyncsocket和CSocket,用以方便开发者使用Socket特性。
这两个类的继承关系如图18.2所示。


    图18.2 CAsyncSoeket和CSoeket的粪继承关系
  CAsyncSocket从较低层次封装了Windows SocketAPI,并且通过内建一个(隐藏的)
窗口,实现了适合Windows应用的异步机制。CSocket类从CAsyncSocket中派生而来,更简化了开发者对Socket的应用。但是,这两个类并不是线程安全的;如果要在多线程环境下应用Socket特性,建议还是自己封装Socket API函数。
    另外,实际的网络应用环境可能是很复杂的。在数据流传送过程中,可能还要实现其他更多的控制,比如调整服务器数据发送速率、响应客户端各种命令等。这些控制也要使用Socket通信,不过可以使用数据报(UDP)的形式来实现。

18.2.2 DirectShow技术应用
 
   客户端的多媒体数据解码,使用DirectShow技术是最方便的了。也就是说,需要写
一个能够接收服务器发来的数据、并提供给Filter Graph中其他的Filter解码的Source
Filter。这种FilterGraph可以如图18.3所示构建。


    图18.3接收并播放MPEGl流的Filter Graph
  如图18.3,标识为“0001”的Source Filter用于接收网络上发来的MPEGl数据,且
工作在拉模式下。 
  可以知道,DirectShow提供了MPEGl解码的所有Filter,包括MPEG-1 Stream
solitter、MPEG Video Decoder和MPEG Audio Decoder;但是MPEG-1 Stream Splitter只能工作在拉模式下,这样不得不把Source Fiker也设计成拉模式的。(不过,如果有第三方的可以工作在推模式下的Splitter Filter,则完全可以把Source Filter也设计成推模式的,即接到网络上发来的数据后主动传送给Splitter Filter,而不是被动地等待Splitter去读取;推模式的Source Filter实现起来要方便一点。)
1 8.2.3一种双缓冲队列技术
  客户端的数据接收也需要一定的技巧。首先,必须让Source Filter接收一定数量的数
据,并缓存起来。因为在Source Filter与spliter连接的过程中,会从Source Filter中读取
一部分数据,以获得数据的格式描述。(否则,Filter Graph是无法完成连接的。)而当完
整的FiRer Graph构建完成,并且处于运行状态后,Source Filter必须动态地接收数据,并
持续地把新的数据提供给Splitter。
    这里,可以使用一种叫双缓冲队列的技术。大致的工作原理是:建立两个队列,一个
是空闲的缓冲队列PoolList,用以接收存放数据;另一个是尚未处理的数据缓冲队列
DataList,等待Spliaer读取。当Source Filter使用Socket接收到数据后,从PoolList队列
的头上取出一个缓冲块,存放数据,然后将这个缓冲块加入到DataList的尾部,等待
Splitter的读取。而在Splitter要求读取时,Source Filter会从DataList队列的头上取出一个
缓冲块,读取数据,再将读完的缓冲块加到PoolList的尾部,“回收再利用”,等待再一
次的数据接收。
18.3源码分析
    下载地址中提供了所有实现的源代码。代码实现分两部分:服务器程序(Media-
Server)和客户端程序(MediaClient);可以使用VC打开Daisy.dsw文件,同时浏览这两
个项目的实现。
    下面先来介绍一下这两个项目的一些公共的实现。比如SocketAPI的封装,设计了一
系列的类:CListenSoeket、CWorkerSocket、CMediaSocketServer、CMediasocketclient
等。这些类的继承关系如图18.4所示。
图18.4 Socket封装类的继承关系
    CListenSocket类主要用于在服务器端监听客户端计算机的网络连接(参见
CListenSocket.cpp的实现);CWorkerSocket类封装了各种Socket操作,主要用于实际的
数据传送和接收,并且使用TCP协议(参见CWorkerSocket.clap的实现);
类从CWorkerSocket中派生,主要用于服务器端的数据发送(参见
CMediaSocketServer.cpp的实现);CMediaSocketClient类也从CWorkerSocket中派生,主
要用于客户端的数据接收(参见CMediaSocketClient.cpp的实现)。
提示
还有一个跟Socket相关的类CLocalMachine,主要用于获取本地计算机的名字,以
及IP地址(参见CLocalMachine.cpp的实现)。

[1] [2] [3] [4] [5]  下一页

posted on 2008-12-10 13:21  刀锋战士  阅读(496)  评论(0编辑  收藏  举报