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系統的開發
歡迎關注本站公眾號,獲取更多信息