Java媒体框架(JMF),我的很欣赏.... (转)
Java媒体框架(JMF),我的很欣赏....
Java媒体框架(JMF)使你可以编写出功能强大的多媒体程序,却不用关心底层复杂的实现细节。JMF API的使用相对比较简单,可是可以知足几乎全部多媒体编程的需求。在这篇文章中,我将向你介绍如何用不多的代码就编写出多媒体程序。
Java多媒体框架(JMF)中包含了许多用于处理多媒体的API。它是一个至关复杂的系统,彻底了解这个系统可能须要花上几周的时间,可是这篇文章将主要介绍JMF的几个核心接口和类,而后经过一个简单的例子向你展现如何利用该接口进行编程。
JMF目前的最新版本是2.1,Sun经过它向Java中引入处理多媒体的能力。下面是JMF所支持的功能的一个概述:
● 能够在Java Applet和应用程序中播放各类媒体文件,例如AU、AVI、MIDI、MPEG、QuickTime和WAV等文件。
● 能够播放从互联网上下载的媒体流。
● 能够利用麦克风和摄像机一类的设备截取音频和视频,并保存成多媒体文件。
● 处理多媒体文件,转换文件格式。
● 向互联网上传音频和视频数据流。
● 在互联网上广播音频和视频数据。
JMF的结构
为了更好地说明JMF的结构,让咱们用立体声音响作一个简单的比喻。当你CD机播放CD唱片的时候,CD唱片向系统提供音乐信号。这些数据是在录音棚中用麦克风和其余相似的设备记录下来的。CD播放机将音乐信号传送到系统的音箱上。在这个例子中,麦克风就是一个音频截取设备,CD唱片是数据源,而音箱是输出设备。
JMF的结构和立体声音响系统很是类似,在后面的文章中,你会遇到下面的这些术语:
● 数据源(Data source)
● 截取设备(Capture Device,包括视频和音频截取设备)
● 播放器(Player)
● 处理器(Processor)
● 数据格式(Format)
● 管理器(Manager)
下面让咱们来看一看这些术语到底表明什么意思。
1.数据源
就像CD中保存了歌曲同样,数据源中包含了媒体数据流。在JMF中,DataSource对象就是数据源,它能够是一个多媒体文件,也能够是从互联网上下载的数据流。对于DataSource对象,一旦你肯定了它的位置和类型,对象中就包含了多媒体的位置信息和可以播放该多媒体的软件信息。当建立了DataSource对象后,能够将它送入Player对象中,而Player对象不须要关心DataSource中的多媒体是如何得到的,以及格式是什么。
在某些状况下,你须要将多个数据源合并成一个数据源。例如当你在制做一段录像时,你须要将音频数据源和视频数据源合并在一块儿。JMF支持数据源合并,在后面的例子中咱们将提到这一点。
2.截取设备
截取设备指的是能够截取到音频或视频数据的硬件,如麦克风、摄像机等。截取到的数据能够被送入Player对象中进行处理。
3.播放器
在JMF中对应播放器的接口是Player。Player对象将音频/视频数据流做为输入,而后将数据流输出到音箱或屏幕上,就像CD播放机读取CD唱片中的歌曲,而后将信号送到音箱上同样。Player对象有多种状态,JMF中定义了JMF的六种状态,在正常状况下Player对象须要经历每一个状态,而后才能播放多媒体。下面是对这些状态的说明。
● Unrealized:在这种状态下,Player对象已经被实例化,可是并不知道它须要播放的多媒体的任何信息。
● Realizing:当调用realize()方法时,Player对象的状态从Unrealized转变为Realizing。在这种状态下,Player对象正在肯定它须要占用哪些资源。
● Realized:在这种状态下Player对象已经肯定了它须要哪些资源,而且也知道须要播放的多媒体的类型。
● Prefetching:当调用prefectch()方法时,Player对象的状态从Realized变为Prefetching。在该状态下的Player对象正在为播放多媒体作一些准备工做,其中包括加载多媒体数据,得到须要独占的资源等。这个过程被称为预取(Prefetch)。
● Prefetched:当Player对象完成了预取操做后就到达了该状态。
● Started:当调用start()方法后,Player对象就进入了该状态并播放多媒体。
4.处理器
处理器对应的接口是Processor,它一种播放器。在JMF API中,Processor接口继承了Player接口。 Processor对象除了支持支持Player对象支持的全部功能,还能够控制对于输入的多媒体数据流进行何种处理以及经过数据源向其余的Player对象或Processor对象输出数据。
除了在播放器中提到了六种状态外,Processor 对象还包括两种新的状态,这两种状态是在Unrealized状态以后,可是在Realizing状态以前。
● Configuring:当调用configure()方法后,Processor对象进入该状态。在该状态下,Processor对象链接到数据源并获取输入数据的格式信息。
● Configured:当完成数据源链接,得到输入数据格式的信息后,Processor对象就处于Configured状态。
5.数据格式
Format对象中保存了多媒体的格式信息。该对象中自己没有记录多媒体编码的相关信息,可是它保存了编码的名称。Format的子类包括AudioFormat和VideoFormat类,ViedeoFomat又有六个子类:H261Format、H263Format、IndexedColorFormat、JPEGFormat、RGBFormat和YUVFormat类。
6.管理器
JMF提供了下面四种管理器:
● Manager:Manager至关于两个类之间的接口。例如当你须要播放一个DataSource对象,你能够经过使用Manager对象建立一个Player对象来播放它。使用Manager对象能够建立Player、Processor、DataSource和DataSink对象。
● PackageManager:该管理器中保存了JMF类注册信息。
● CaptureDeviceManager:该管理器中保存了截取设备的注册信息。
● PlugInManager:该管理器中保存了JMF插件的注册信息。 建立一个Player对象
在JMF编程中,最多见的工做就是建立一个Player对象。你能够经过Manager类的createPlayer()方法建立Player对象。Manager对象使用多媒体的URL或MediaLocator对象来建立Player对象。当你得到了一个Player对象后,你能够经过调用getVisualComponent()方法获得Player对象的图像部件(Visual Component,在图像部件上能够播放多媒体的图像)。而后将图像部件加入到应用程序或Applet的界面上。Player对象还包含一个控制面板,在上面能够控制媒体的播放、中止和暂停等。
Player类中的不少方法只有在Player对象处于Realized的状态下才会被调用。为了保证Player对象已经到达了该状态,你须要使用Manager的createRealizePlayer()方法来得到Player对象。可是对于start()方法来讲,你能够在Player对象到达Prefetched状态以前调用它,它能够自动将Player的状态转换到Started状态。
截取多媒体数据
多媒体数据的截取是JMF程序中另外一个很是重要的功能。你能够按照下面的步骤截取数据:
● 经过查询CaptureDevieceManager得到你但愿使用的截取设备。
● 得到设备对应的CaptureDeviceInfo对象。
● 从CaptureDeviecInfo对象中得到MediaLocator对象,而后用它建立一个DataSource对象。
● 使用DataSource对象建立Player对象或Processor对象。
● 调用start()方法,开始截取多媒体数据。
你可使用CaptureDeviceManager对象得到系统中可用的视频和音频截取设备。经过调用getDeviceList()方法你能够得到设备的列表。每一个设备都对应一个CaptrueDeviceInfo对象。也能够经过调用CaptureDevieceManager对象的getDevice()方法来得到特定的CaptureDeviceInfo对象。在使用设备截取多媒体数据前,还须要从CaptureDeviceInfo对象中得到设备对应的MediaLocator对象。而后你能够直接使用MediaLocator来构造Player或Processor的实例,也能够用MediaLocator构造一个DataSource对象,而后将DataSource对象送入Player或Processor对象中。最后调用start()方法来截取多媒体数据。
一个JMF例子
当你使用JMF进行编程之前,你须要安装JMF。同时在硬件上也有一些要求。因为本文的代码是在Windows 2000下编写和测试,所以文章中提到的操做系统须要的软件都是与Windows有关的。虽然Java是跨平台的,可是JMF是个例外――并非全部的平台上都实现了JMF。
硬件和软件要求
硬件方面你须要与SoundBlaster兼容的声卡,芯片最好使用奔腾III以上的芯片。内存最好不小于64MB。同时你须要安装下面的软件:
● Windows95/98,Windows NT 4.0, Windows2000或 WindowsXP。
● JDK1.1.6或以上的Windows版本。
● JMF类和动态库
在Windows下安装JMF2.1
当下载了JMF2.1之后,运行jmf-2_1_1b-windows-i586.exe。该程序会将JMF2.1安装到你指定的目录下。当安装成功后,你须要确认一下安装程序正确设定了CLASSPATH和PATH环境变量。在CLASSPATH中须要包含jmf.jar和sound.jar;在PATH中须要包含JMF动态库的路径。
JMFRegistry
若是你但愿使用视频和音频截取的设备,你须要确认安装了这些设备的驱动程序。除此以外,你还须要运行JMFRegistry应用程序。JMFRegistry能够向JMF注册新的数据源、媒体处理器、插件、视频和音频截取设备,而后你才可以在你的程序中使用它们。你只须要运行一次JMFRegistry就能注册系统中全部的视频和音频截取设备。
当你运行了JMFRegistry后,会弹出图一所示的窗口:
图一 经过JMFRegistry注册视频和音频截取设备
选择“Capture Devices”标签,而后按下“Detect Capture Devices”按钮,程序将自动检测出系统中的视频和音频截取设备。在左边的类表框中会列出全部检测到的设备的名称。在图一中咱们看到JMFRegsitery发现了JavaSound audio capture、vfw:Logitech USB Video Camera:0和vfw:Microsoft WDM Image Capture (Win32):1。单击某个设备能够看到该设备支持的视频或音频格式。若是JMFRegistry没法检测到设备,有多是没有正常安装设备的驱动程序。
例子程序
因为JMF2.1比较复杂,我不可能在在例子中包含JMF2.1支持的全部功能。所以我选择了下面几个在JMF中比较经常使用的功能:播放多媒体、注册音频和视频截取设备、截取视频和音频。
1.播放多媒体
在JMF.java中有一个play()方法。该方法能够播放用户选择的多媒体文件。当播放多媒体文件时,你须要一个Player对象。在例子中,dualPlayer就是Player接口的实现对象。
Player dualPlayer;
在Play()方法中,经过使用FileDialog得到媒体文件的路径和文件名,并保存在filename中。
try {
FileDialog fd =
new FileDialog(this, "Select File", FileDialog.LOAD);
fd.show();
String filename = fd.getDirectory() + fd.getFile();
...
}
catch (Exception e) {
System.out.println(e.toString());
}
而后你须要经过媒体管理器Manager间接建立一个Player对象。你可使用Manager类的createPlayer()方法或者createProcessor()方法来得到一个Player对象或Processor对象。在play()方法中,我使用的是createPlayer()方法。
dualPlayer = Manager.createPlayer
(new MediaLocator("file:///" + filename));
有时你须要使用一个Player对象来控制多个其余的Player和Controller对象,咱们把这个Player对象称为主对象,并把这些对象组成一个组。经过调用主对象中的start()、stop()、setMediaTime()等方法就能够激活组中全部成员的相应方法。主对象控制全部的状态变化和事件发布。而后使用addControllerListerner()方法来将一个ControllerListener对象绑定到Player对象上,Controller对象将向该ControllerListener对象发送事件消息。
dualPlayer.addControllerListener(this);
最后须要调用start()方法来启动Player对象。start()方法将Player对象的状态设置为Started。若是Player没有被实体化(Realize)或预取(Prefetch),start()方法会自动执行这些操做。
dualPlayer.start();
因为JMF类实现了ControllerLister接口,所以须要实现该接口中的controllerUpdate()方法,该方法在Controller对象产生一个事件时被调用。
public synchronized void controllerUpdate(ControllerEvent event) {
if (event instanceof RealizeCompleteEvent) {
Component comp;
if ((comp = dualPlayer.getVisualComponent()) != null)
add ("Center", comp);
if ((comp = dualPlayer.getControlPanelComponent()) != null)
add("South", comp);
validate();
}
}
当JMF类产生了一个RealizeCompleteEvent事件后,controllerUpdate()方法在界面上增长两个Component对象,一个用于播放媒体,一个用于放置控制按钮,例如播放、中止等。
在运行程序的过程当中,程序会产生下面的输出。
Starting player ...javax.media.TransitionEvent
[source=com.sun.media.content.video.mpeg.Handler@71bb78,
previous=Unrealized,
current=Realizing,
target=Started]
Open log file: C:\test\Java\JMF\JMF\jmf.log
javax.media.DurationUpdateEvent
[source=com.sun.media.content.video.mpeg.Handler@71bb78,duration=
javax.media.Time@2a37a6
javax.media.RealizeCompleteEvent
[source=com.sun.media.content.video.mpeg.Handler@71bb78,
previous=Realizing,
current=Realized,
target=Started]
Adding visual component
Adding control panel
javax.media.TransitionEvent
[source=com.sun.media.content.video.mpeg.Handler@71bb78,
previous=Realized,
current=Prefetching,
target=Started]
javax.media.PrefetchCompleteEvent
[source=com.sun.media.content.video.mpeg.Handler@71bb78,
previous=Prefetching,
current=Prefetched,target=Started]
javax.media.StartEvent
[source=com.sun.media.content.video.mpeg.Handler@71bb78,
previous=Prefetched,
current=Started,
target=Started,
mediaTime=javax.media.Time@56a05e,timeBaseTime=
javax.media.Time@3a8602]
javax.media.EndOfMediaEvent
[source=com.sun.media.content.video.mpeg.Handler@71bb78,
previous=Started,
current=Prefetched,
target=Prefetched,
mediaTime=javax.media.Time@1d332b]
前面提到,当调用start()方法的时候,Player会切换到Started状态。从上面列出的信息中能够看到Player对象的状态从Unrealized变成了Started。当EndOfMedia事件被激活时(这时Player对象完成了媒体文件的播放),状态从Started变成了Prefetched。图二显示了程序正在播放多媒体文件时的状况。
图二 程序正在播放媒体文件
2.注册音频和视频截取设备
在例子中,注册音频和视频截取设备的方法只在程序的内部注册这些设备,在程序外则不起做用。该方法的做用是当用户的计算机上存在多和音频和视频截取设备时,告诉程序因该使用哪一个设备和这些设备支持的音频和视频格式。所以在进行截取处理以前须要得到设备的配置信息。在例子中,当在Configure菜单上按下Capture Device命令后,会弹出CaptureDeviceDialog对话框。若是在截取音频或视频前没有设定设备的配置,也会弹出该对话框。图三显示了该对话框。
图三 设备注册对话框 让咱们来看一下CaptureDeviceDialog类中的init()方法:在初始化了界面以后,经过调用CaptureDeviceManager类的getDeviceList()方法: devices = CaptureDeviceManager.getDeviceList ( null ); CaptureDeviceManager类使用查询机制和一个注册表来定位设备,而后将设备的信息放入CaptureDeviceInfo对象中返回。咱们还能够利用CaptureDeviceManager类来注册新的设备。经过调用getDeviceList()方法程序获取了一个支持指定格式的设备的列表。在例子中,我将格式参数设定为null,这意味着设备可使用任何格式。返回值被放入device变量中。若是getDeviceList()方法返回的是一个非空值,程序会将包含在其中的音频设备名称和视频设备名称分别放入两个下拉列表中中,可是到目前为止咱们还不知道哪些设备是音频设备,哪些是视频设备。 咱们能够经过CaptureDeviceInfo的getFormat()方法得到Format对象组数,在Format对象中保存了设备支持的媒体格式。Format类间接被AudioFormat和VideoFormat类所继承。所以咱们能够利用设备支持的格式类型来区分设备的类型: if (devices!=null && devices.size()>0) { int deviceCount = devices.size(); audioDevices = new Vector(); videoDevices = new Vector(); Format[] formats; for ( int i = 0; i < deviceCount; i++ ) { cdi = (CaptureDeviceInfo) devices.elementAt ( i ); formats = cdi.getFormats(); for ( int j=0; j<formats.length; j++ ) { if ( formats[j] instanceof AudioFormat ) { audioDevices.addElement(cdi); break; } else if (formats[j] instanceof VideoFormat ) { videoDevices.addElement(cdi); break; } } } . . . } 上面的程序运行后,audioDevices()中将包含全部的音频设备,videoDevices()中将保存全部的视频设备。其中cdi是CaptureDeviceInfo对象。而后将设备名称填入下拉列表中: // 将音频设备显示在下拉列表中 for (int i=0; i<audioDevices.size(); i++) { cdi = (CaptureDeviceInfo) audioDevices.elementAt(i); audioDeviceCombo.addItem(cdi.getName()); } // 将视频设备显示在下拉列表中 for (int i=0; i<videoDevices.size(); i++) { cdi = (CaptureDeviceInfo) videoDevices.elementAt(i); videoDeviceCombo.addItem(cdi.getName()); } 而后程序显示出当前选中的设备支持的格式: displayAudioFormats(); displayVideoFormats(); 下一步须要获取用户选中的音频设备和视频设备以及它们支持的格式,相关的方法是JMF类中的getAudioDevice()、getVideoDevice()、getAudioFormat()和getVideoFormat()方法。而后将获取的对象分别保存到audioCDI,videoCDI,audioFormat和videoFormat中: audioCDI = cdDialog.getAudioDevice(); if (audioCDI!=null) { audioDeviceName = audioCDI.getName(); System.out.println("Audio Device Name: " + audioDeviceName); } videoCDI = cdDialog.getVideoDevice(); if (videoCDI!=null) { videoDeviceName = videoCDI.getName(); System.out.println("Video Device Name: " + videoDeviceName); } // 得到选中的多媒体格式 videoFormat = cdDialog.getVideoFormat(); audioFormat = cdDialog.getAudioFormat(); 3.截取视频和音频 使用capture()方法能够截取音频和视频数据。可是在使用该方法前须要肯定是否已经选中了视频和音频截取设备: if (audioCDI==null && videoCDI==null) registerDevices(); 和play()方法相似,能够经过使用Manger类中的静态方法createPlayer()建立一个Player对象,该对象能够播放一个DataSource对象中的数据流。 Player createPlayer(MediaLocator sourceLocator) 在例子中,我首先经过调用audioCDI和videoCDI的getLocator()方法来得到MediaLocator对象,而后利用Manager类的createPlayer()方法建立Player对象。最后将一个ControllerListener对象绑定到视频Player对象上并开始播放。 videoPlayer = Manager.createPlayer(videoCDI.getLocator()); audioPlayer = Manager.createPlayer(audioCDI.getLocator()); videoPlayer.addControllerListener(this); videoPlayer.start(); audioPlayer.start(); 使用这种方法致使最后得到了两个Player对象。咱们也可使用Manager类中的createDataSource()方法从视频和音频CaptureDeviceInfo对象(audioCID和videoCDI)中得到视频和音频数据源(DataSource对象),而后调用createMergingDataSource()方法将两个数据源合并成一个数据源(ds): DataSource[] dataSources = new DataSource[2]; dataSources[0] = Manager.createDataSource(audioCDI.getLocator()); dataSources[1] = Manager.createDataSource(videoCDI.getLocator()); DataSource ds = Manager.createMergingDataSource(dataSources); 而后可使用ds做为createPlayer()方法的参数来得到一个Player对象dualPlayer。调用addControllerListener()就能够进行播放了。 dualPlayer = Manager.createPlayer(ds); dualPlayer.addControllerListener(this); dualPlayer.start(); 小结 Java多媒体框架是一个很好的多媒体编程工具。在这篇文章中我只是简单介绍了JMF的一些基本功能。若是有兴趣的话能够仔细阅读一下Sun公司的Java网站上提供的JMStudio的例子。在JMStudio中不只实现了简单的播放和视频/音频截取功能,还实现了从互联网下载和向互联网上传多媒体数据流的功能。并且它还包含了JMFRegistry的源代码,将相应的代码移植到你的应用程序中后,你就不须要在运行程序前运行JMFRegistry来向JMF注册设备了。 做者简介:冯睿,2000年毕业于美国Northern Illinois大学电气工程系,获硕士学位。随后在New Monics软件公司工做了一年,其间参加了Java虚拟机的开发和优化工做。目前在国内一家GIS公司担任项目经理,主要从事应急指挥系统的交通GIS系统的开发
欢迎关注本站公众号,获取更多信息