关键程序如下:
//创建音频视频数据源
DataSourcedsVideo=null;
DataSourcedsAudio=null;
DataSourceds=null;
Processorprocessor=null;
//所有设备的列表
Vector
//捕获设备信息
CaptureDeviceInfocdi;
//视频采集设备对应的媒体定位器
MediaLocatorml;
//建立数据源
ds=Manager.createMergingDataSource(newDataSource[]{
dsAudio,dsVideo});
//获取视频设备列表
deviceList=CaptureDeviceManager.getDeviceList(format);//得到类型为
format的设备的清单,存放在表devices中
//选取第一个设备
cdi=(CaptureDeviceInfo)deviceList.elementAt(0);//设备定位
ml=cdi.getLocator();//返回一媒体定位器从而为设备创建数据源
//得到音频的信息
intsamplingRate=8000;
intsamplingSize=16
intchannels=1;
af=newAudioFormat(AudioFormat.LINEAR,8000,16,1);
//得到视频文件的信息
Stringencoding="YUV";
Dimensionsize=newDimension(176,144);
vf=newVideoFormat(encoding,size,Format.NOT_SPECIFIED,
null,15f);
//得到捕获数据源
datasource=this.getCaptureDS(vf,af);
//开始捕获
processor=javax.media.Manager.createProcessor(datasource);
processor.start();
音视频的处理和传输
我们采集到的数据源是不适合直接传输的,要先经过处理,使其适合为RTP传输的格式并产生新的数据源再进行传输,处理的过程为:用前面获得的数据源构造处理器对象,以便得到采集到的数据;
processor=javax.media.Manager.createProcessor(datasource);
然后将数据源处理为适合RTP传输的格式;
processor.setContentDescriptor(new
ContentDescriptor(ContentDescriptor.RAW_RTP););
然后调用处理器对象的getTrackControl()方法,得到通道的TrackControl对象;为媒体流中的每一个磁道得到一个控制器;
TrackControl[]tracks=processor.getTrackControls();
接下来调用TrackControl对象的setFormat()方法设置每个轨道的编码格式;
Formatformat=tracks[i].getFormat();
Formatsupported=tracks[i].getSupportedFormats();
tracks[i].setFormat(supported[0]);
最后,调用处理器对象的getDataOutput()方法产生数据源,以便数据传输时
使用;
DataSourcedataOutput=processor.getDataOutput();
下来就要进行RTP的传输了,RTP会话是由RTPManager完成的,每个轨道都需要有一个管理器,管理器对象采用RTPManager.newInstance()方法来获得。
建立会话时,需创建SessionAddress对象描述发送端与接收端的地址信息,再加入RTP管理器。会话建立后,由管理器创建流将数据通过网络发送;
先创建会话管理器RTPMangger对象,并调用该类的静态方法newInstance()
实例化该对象,设置该会话的本地地址及端口和目的地址及端口;
RTPManagerrtpMgrs=RTPManager.newInstance();
intport=portBase+2*i;//每增加一个磁道,端口号加2
InetAddressipAddr=InetAddress.getByName(ipAddress);//得到发送目的地的IP地址
SessionAddresslocalAddr=new
SessionAddress(InetAddress.getLocalHost(),port);//得到本机的会话地址
//这里传输端使用和接收目的端相同的端口号(实际上也可以不同)
SessionAddressdestAddr=newSessionAddress(ipAddr,port);//得到目的机器
(接收端)的会话地址
调用createSendStream(DataSoucredatasource,intstreamIndex)方法创建输出
流对象;
SendStreamsendStream=rtpMgrs[i].createSendStream(dataOutput,i);
调用输出流对象的start()方法,启动并控制数据流传输,,在SendStream上
注册一个SendStreamListener;通过监听ControllerEvent事件控制会话过程;
sendStream.start();
音视频的接收和播放
接收媒体流也同样也需要建立RTP会话,其过程与发送流时建立会话的过程类似,不同的是每个RTP管理器需要加上监听器来监听相应地址与端口的情况,包括会话请求、数据流到达等。系统实现SessionListener和ReceiveStreamListener两个接口,前者用于监听RTP会话请求,后者用于监听是否有数据流到达。当创建一个新的数据流后,SessionManager会发出一个NewReceiveStreamEvent。注册了ReceiveStreamListener接收数据流监听器,就会收到这些事件。就可以为每一个新的接收数据流创建Player。从接收数据流中获取DataSource,并作为参数传递给Manager来创建Player。在发送端和接收端播放多媒体信息的过程是相同的,由SessionManager到DataSource到Player,直接将播放的数据源交由播放器即可;创建并初始化RTPSession,创建一个SessionManager(会话管理器),并调用addReceiveStreamListener方法为RTPManager对象添加RTP时间监听器,监听NewReceiveStreamEvent事件,然后调用RTPManager对象的initialize方法初始化RTP会话,设置本地和目的地的地址和端口;
RTPManagermgrs=newRTPManager[sessions.length];
mgrs=(RTPManager)RTPManager.newInstance();
mgrs.addSessionListener(this);
mgrs.addReceiveStreamListener(this);
mgrs.initialize(new
SessionAddress(InetAddress.getLocalHost(),session.port););
mgrs.addTarget(newSessionAddress(InetAddress.getByName(session.addr);,
session.port););
在ReceiveStreamListenerupdate方法中,监视NewReceiveStreamEvent事件,
它会指示新的数据流被检测到。
当一个NewReceiveStreamEvent事件被检测到时,以NewReceiveStreamEvent
事件调用getReceiveStream方法,以取得ReceiveStream数据流对象,然后通过调用输入数据流对象的getDataSource获取RTP数据源对象;
ReceiveStreamEventevt;
ReceiveStreamstream=evt.getReceiveStream();
stream=((NewReceiveStreamEvent)evt).getReceiveStream();
DataSourceds=stream.getDataSource();
把数据源对象作为参数传递到Manager的createPlayer方法中来创建Player。
直接将播放的数据源交由播放器即可,播放器的界面分为播放界面和控制界面两部分,分别用getVisualComponent和getControlPanelComponent方法来得到。
Playerp=javax.media.Manager.createPlayer(ds);
p.addControllerListener(this);
p.realize();
Componentvc,cc;
vc=player.getVisualComponent();
cc=player.getControlPanelComponent();
多播的设计
通过SUN公司提供IP多播类,可以实现多播技术分发媒体流。在具体实现时可通过MulticastSocket类创建一个所获得的指定端口号的多播套接字,然后调用joinGroup方法加入到该多播组,接收端程序加入多播组后,便可以从该多播地址和端口号处接收媒体流。
InetAddressgroup=InetAddress.getByName(MuiltAddr);
MulticastSocketsocket=newMulticastSocket(port);
socket.joinGroup(group);
实现界面
系统采用JAVAGUI编程,运用SWING组件来编写,通过良好的布局,能够显示连接上的用户,在右下方可以进行文本聊天,左上角功能键可以实现面试的基本功能,通过连接服务器取得连接后可以进行视频的连接,连接后的视频在最显眼的地方显示。由于用到SWING组件,具有良好的跨平台性,可以在不同的操作系统上保持同样的界面,做到一次开发,处处使用。其关键代码实现如下:
JFramejFrame=newJFrame("客户端");
Dimensiondimension=Toolkit.getDefaultToolkit().getScreenSize();
jFrame.setBounds(((int)dimension.getWidth()-200)/2,((int)dimension.getHeight()-300)/2,200,150);
jFrame.setResizable(false);
jFrame.setLayout(null);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabellabel1=newJLabel("用户界面");
label1.setBounds(10,10,100,30);
jFrame.add(label1);
JLabellabel2=newJLabel("即时发言");
label2.setBounds(10,40,100,30);
jFrame.add(label2);
finalJTextFieldtext1=newJTextField();
text1.setBounds(50,15,130,20);
jFrame.add(text1);
finalJPasswordFieldtext2=newJPasswordField();
text2.setBounds(50,45,130,20);
jFrame.add(text2);
JButtonbutton=newJButton("Login");
button.setBounds(10,75,170,40);
button.addActionListener(newActionListener(){
界面如图4.7所示。
小结
本章结合前面的设计,对整个系统进行了软件实现,实现了视频面试的基本功能。本系统通过自定义信令能简化系统结构,提高总体效率,有效降低了开发成本,但不能和基于H.323等标准协议的视频面试系统实现互通。通过非阻塞通信技术的运用,在文本传输方面,不再需要为每一个用户接收数据去专门开启一个线程,节省了资源,提高了效率,有效的解决了用户增多文本传输速度慢的问题,防止了系统因此无响应。通过选择适当的性能指标,在功能上已基本满足视频面试的要求,但在视频质量和系统的性能上还是存在一定差距,但也能够满足一般场合的应用。系统采取JAVA开发平台,不仅具有良好的可扩展性,同时可以跨平台使用,为Windows、Unix、Linux等平台的用户提供了便利。本系统结构简单,功能实用,成本低廉,对中小用户具有一定的实用价值。
第六章 总结
随着因特网的普及和多媒体通信技术的广泛运用,越来越多的人们开始享受互联网带给我们的便利。现代通信网络已经不能单纯的满足于传输语音及传真等非话业务,流媒体越来越多的运用到了通信领域中,如多媒体信息数据、视频服务、高清晰度电视图像等。网络视频面试系统结合网络技术,多媒体通信技术和计算机技术,通过网络来实现视频面试,既能实现语音、视频和图像的交流,还能实现绘图及文字的交流,使参与者有亲临现场的感觉。给招聘双方一种直接、全面的交流方式,而且还能节约时间、降低成本,因此具有广阔的发展前景。
近两年来,视频面试系统的发展已经走向纯软件方向,用软件的方式来实现网络视频面试系统,具有成本低,容易实现等优点,而基于JAVA语言的视频面试系统随着技术的成熟得到了越来越广泛的应用。
通过以上各章介绍了视频面系统及其组成,了解了常用的H.323协议,就视频面试系统的关键技术进行了详细的介绍说明,如TCP/IP协议,RTP/RTCP协议,多媒体技术、视频编码技术、语音技术、网络通信方式等,通过对比比较进行了对视频面试系统进行了全方面的设计,最后结合JAVA语言和其多媒体框架JMF进行了实现。
本系统基本实现了面试的基本功能,但有些问题有待进一步研究。如传输文本等将留待以后进一步解决。