Java媒體框架(JMF)使你能夠編寫出功能強大的多媒體程序
卻不用關心底層復雜的實現細節
JMF API的使用相對比較簡單
但是能夠滿足幾乎所有多媒體編程的需求
在這篇文章中
我將向你介紹如何用很少的代碼就編寫出多媒體程序
Java多媒體框架(JMF)中包含了許多用於處理多媒體的API
它是一個相當復雜的系統
完全了解這個系統可能需要花上幾周的時間
但是這篇文章將主要介紹JMF的幾個核心接口和類
然後通過一個簡單的例子向你展示如何利用該接口進行編程
JMF目前的最新版本是
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)
下面讓我們來看一看這些術語到底代表什麼意思
.數據源
就像CD中保存了歌曲一樣數據源中包含了媒體數據流在JMF中DataSource對象就是數據源它可以是一個多媒體文件也可以是從互聯網上下載的數據流對於DataSource對象一旦你確定了它的位置和類型對象中就包含了多媒體的位置信息和能夠播放該多媒體的軟件信息當創建了DataSource對象後可以將它送入Player對象中而Player對象不需要關心DataSource中的多媒體是如何獲得的以及格式是什麼
在某些情況下你需要將多個數據源合並成一個數據源例如當你在制作一段錄像時你需要將音頻數據源和視頻數據源合並在一起JMF支持數據源合並在後面的例子中我們將提到這一點
.截取設備
截取設備指的是可以截取到音頻或視頻數據的硬件如麥克風攝像機等截取到的數據可以被送入Player對象中進行處理
.播放器
在JMF中對應播放器的接口是PlayerPlayer對象將音頻/視頻數據流作為輸入然後將數據流輸出到音箱或屏幕上就像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對象就進入了該狀態並播放多媒體
.處理器
處理器對應的接口是Processor它一種播放器在JMF API中Processor接口繼承了Player接口 Processor對象除了支持支持Player對象支持的所有功能還可以控制對於輸入的多媒體數據流進行何種處理以及通過數據源向其他的Player對象或Processor對象輸出數據
除了在播放器中提到了六種狀態外Processor 對象還包括兩種新的狀態這兩種狀態是在Unrealized狀態之後但是在Realizing狀態之前
● Configuring當調用configure()方法後Processor對象進入該狀態在該狀態下Processor對象連接到數據源並獲取輸入數據的格式信息
● Configured當完成數據源連接獲得輸入數據格式的信息後Processor對象就處於Configured狀態
.數據格式
Format對象中保存了多媒體的格式信息該對象中本身沒有記錄多媒體編碼的相關信息但是它保存了編碼的名稱Format的子類包括AudioFormat和VideoFormat類ViedeoFomat又有六個子類HFormatHFormatIndexedColorFormatJPEGFormatRGBFormat和YUVFormat類
.管理器
JMF提供了下面四種管理器
● ManagerManager相當於兩個類之間的接口例如當你需要播放一個DataSource對象你可以通過使用Manager對象創建一個Player對象來播放它使用Manager對象可以創建PlayerProcessorDataSource和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 下編寫和測試因此文章中提到的操作系統需要的軟件都是與Windows有關的雖然Java是跨平台的但是JMF是個例外——並不是所有的平台上都實現了JMF
硬件和軟件要求
硬件方面你需要與SoundBlaster兼容的聲卡芯片最好使用奔騰III以上的芯片內存最好不小於MB同時你需要安裝下面的軟件
● Windows/Windows NT Windows或 WindowsXP
● JDK或以上的Windows版本
● JMF類和動態庫
在Windows下安裝JMF
當下載了JMF以後運行jmf__bwindowsiexe該程序會將JMF安裝到你指定的目錄下當安裝成功後你需要確認一下安裝程序正確設定了CLASSPATH和PATH環境變量在CLASSPATH中需要包含jmfjar和soundjar在PATH中需要包含JMF動態庫的路徑
JMFRegistry
如果你希望使用視頻和音頻截取的設備你需要確認安裝了這些設備的驅動程序除此之外你還需要運行JMFRegistry應用程序JMFRegistry可以向JMF注冊新的數據源媒體處理器插件視頻和音頻截取設備然後你才能夠在你的程序中使用它們你只需要運行一次JMFRegistry就能注冊系統中所有的視頻和音頻截取設備
當你運行了JMFRegistry後會彈出圖一所示的窗口
圖一 通過JMFRegistry注冊視頻和音頻截取設備
選擇Capture Devices標簽然後按下Detect Capture Devices按鈕程序將自動檢測出系統中的視頻和音頻截取設備在左邊的類表框中會列出所有檢測到的設備的名稱在圖一中我們看到JMFRegsitery發現了JavaSound audio capturevfw:Logitech USB Video Camera:和vfw:Microsoft WDM Image Capture (Win):單擊某個設備可以看到該設備支持的視頻或音頻格式如果JMFRegistry無法檢測到設備有可能是沒有正常安裝設備的驅動程序
例子程序
由於JMF比較復雜我不可能在在例子中包含JMF支持的所有功能因此我選擇了下面幾個在JMF中比較常用的功能:播放多媒體注冊音頻和視頻截取設備截取視頻和音頻
.播放多媒體
在JMFjava中有一個play()方法該方法可以播放用戶選擇的多媒體文件當播放多媒體文件時你需要一個Player對象在例子中dualPlayer就是Player接口的實現對象
Player dualPlayer;
在Play()方法中通過使用FileDialog獲得媒體文件的路徑和文件名並保存在filename中
try {
FileDialog fd =
new FileDialog(this "Select File" FileDialogLOAD);
fdshow();
String filename = fdgetDirectory() + fdgetFile();
}
catch (Exception e) {
Systemoutprintln(etoString());
}
然後你需要通過媒體管理器Manager間接創建一個Player對象你可以使用Manager類的createPlayer()方法或者createProcessor()方法來獲得一個Player對象或Processor對象在play()方法中我使用的是createPlayer()方法
dualPlayer = ManagercreatePlayer
(new MediaLocator("file:///" + filename));
有時你需要使用一個Player對象來控制多個其他的Player和Controller對象
我們把這個Player對象稱為主對象
並把這些對象組成一個組
通過調用主對象中的start()
stop()
setMediaTime()等方法就可以激活組中所有成員的相應方法
主對象控制所有的狀態變化和事件發布
然後使用addControllerListerner()方法來將一個ControllerListener對象綁定到Player對象上
Controller對象將向該ControllerListener對象發送事件消息
dualPlayeraddControllerListener(this);
最後需要調用start()方法來啟動Player對象
start()方法將Player對象的狀態設置為Started
如果Player沒有被實體化(Realize)或預取(Prefetch)
start()方法會自動執行這些操作
dualPlayerstart();
由於JMF類實現了ControllerLister接口
因此需要實現該接口中的controllerUpdate()方法
該方法在Controller對象產生一個事件時被調用
public synchronized void controllerUpdate(ControllerEvent event) {
if (event instanceof RealizeCompleteEvent) {
Component comp;
if ((comp = dualPlayergetVisualComponent()) != null)
add ("Center" comp);
if ((comp = dualPlayergetControlPanelComponent()) != null)
add("South" comp);
validate();
}
}
當JMF類產生了一個RealizeCompleteEvent事件後
controllerUpdate()方法在界面上增加兩個Component對象
一個用於播放媒體
一個用於放置控制按鈕
例如播放
停止等
在運行程序的過程中
程序會產生下面的輸出
Starting player diaTransitionEvent
[source=ntentvideompegHandler@bb
previous=Unrealized
current=Realizing
target=Started]
Open log file: C:\test\Java\JMF\JMF\jmflog
diaDurationUpdateEvent
[source=ntentvideompegHandler@bbduration=
diaTime@aa
diaRealizeCompleteEvent
[source=ntentvideompegHandler@bb
previous=Realizing
current=Realized
target=Started]
Adding visual component
Adding control panel
diaTransitionEvent
[source=ntentvideompegHandler@bb
previous=Realized
current=Prefetching
target=Started]
diaPrefetchCompleteEvent
[source=ntentvideompegHandler@bb
previous=Prefetching
current=Prefetchedtarget=Started]
diaStartEvent
[source=ntentvideompegHandler@bb
previous=Prefetched
current=Started
target=Started
mediaTime=diaTime@aetimeBaseTime=
diaTime@a]
diaEndOfMediaEvent
[source=ntentvideompegHandler@bb
previous=Started
current=Prefetched
target=Prefetched
mediaTime=diaTime@db]
前面提到
當調用start()方法的時候
Player會切換到Started狀態
從上面列出的信息中可以看到Player對象的狀態從Unrealized變成了Started
當EndOfMedia事件被激活時(這時Player對象完成了媒體文件的播放)
狀態從Started變成了Prefetched
圖二顯示了程序正在播放多媒體文件時的情況
圖二 程序正在播放媒體文件
.注冊音頻和視頻截取設備 在例子中
注冊音頻和視頻截取設備的方法只在程序的內部注冊這些設備
在程序外則不起作用
該方法的作用是當用戶的計算機上存在多和音頻和視頻截取設備時
告訴程序因該使用哪個設備和這些設備支持的音頻和視頻格式
因此在進行截取處理之前需要獲得設備的配置信息
在例子中
當在Configure菜單上按下Capture Device命令後
會彈出CaptureDeviceDialog對話框
如果在截取音頻或視頻前沒有設定設備的配置
也會彈出該對話框
圖三顯示了該對話框
圖三 設備注冊對話框讓我們來看一下CaptureDeviceDialog類中的init()方法
在初始化了界面之後
通過調用CaptureDeviceManager類的getDeviceList()方法
devices = CaptureDeviceManagergetDeviceList ( null );
CaptureDeviceManager類使用查詢機制和一個注冊表來定位設備
然後將設備的信息放入CaptureDeviceInfo對象中返回
我們還可以利用CaptureDeviceManager類來注冊新的設備
通過調用getDeviceList()方法程序獲取了一個支持指定格式的設備的列表
在例子中
我將格式參數設定為null
這意味著設備可以使用任何格式
返回值被放入device變量中
如果getDeviceList()方法返回的是一個非空值
程序會將包含在其中的音頻設備名稱和視頻設備名稱分別放入兩個下拉列表中中
但是到目前為止我們還不知道哪些設備是音頻設備
哪些是視頻設備
我們可以通過CaptureDeviceInfo的getFormat()方法獲得Format對象組數
在Format對象中保存了設備支持的媒體格式
Format類間接被AudioFormat和VideoFormat類所繼承
因此我們可以利用設備支持的格式類型來區分設備的類型
if (devices!=null &#;&#; devicessize()>) {
int deviceCount = devicessize();
audioDevices = new Vector();
videoDevices = new Vector();
Format[] formats;
for ( int i = ; i < deviceCount; i++ ) {
cdi = (CaptureDeviceInfo) deviceselementAt ( i );
formats = cdigetFormats();
for ( int j=; j<formatslength; j++ ) {
if ( formats[j] instanceof AudioFormat ) {
audioDevicesaddElement(cdi);
break;
}
else if (formats[j] instanceof VideoFormat ) {
videoDevicesaddElement(cdi);
break;
}
}
}
}
上面的程序運行後
audioDevices()中將包含所有的音頻設備
videoDevices()中將保存所有的視頻設備
其中cdi是CaptureDeviceInfo對象
然後將設備名稱填入下拉列表中
// 將音頻設備顯示在下拉列表中
for (int i=; i<audioDevicessize(); i++) {
cdi = (CaptureDeviceInfo) audioDeviceselementAt(i);
audioDeviceComboaddItem(cdigetName());
}
// 將視頻設備顯示在下拉列表中
for (int i=; i<videoDevicessize(); i++) {
cdi = (CaptureDeviceInfo) videoDeviceselementAt(i);
videoDeviceComboaddItem(cdigetName());
}
然後程序顯示出當前選中的設備支持的格式
displayAudioFormats();
displayVideoFormats();
下一步需要獲取用戶選中的音頻設備和視頻設備以及它們支持的格式
相關的方法是JMF類中的getAudioDevice()
getVideoDevice()
getAudioFormat()和getVideoFormat()方法
然後將獲取的對象分別保存到audioCDI
videoCDI
audioFormat和videoFormat中
audioCDI = cdDialoggetAudioDevice();
if (audioCDI!=null) {
audioDeviceName = audioCDIgetName();
Systemoutprintln("Audio Device Name: " + audioDeviceName);
}
videoCDI = cdDialoggetVideoDevice();
if (videoCDI!=null) {
videoDeviceName = videoCDIgetName();
Systemoutprintln("Video Device Name: " + videoDeviceName);
}
// 獲得選中的多媒體格式
videoFormat = cdDialoggetVideoFormat();
audioFormat = cdDialoggetAudioFormat();
.截取視頻和音頻 使用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 = ManagercreatePlayer(videoCDIgetLocator());
audioPlayer = ManagercreatePlayer(audioCDIgetLocator());
videoPlayeraddControllerListener(this);
videoPlayerstart();
audioPlayerstart();
使用這種方法導致最後獲得了兩個Player對象
我們也可以使用Manager類中的createDataSource()方法從視頻和音頻CaptureDeviceInfo對象(audioCID和videoCDI)中獲得視頻和音頻數據源(DataSource對象)
然後調用createMergingDataSource()方法將兩個數據源合並成一個數據源(ds)
DataSource[] dataSources = new DataSource[];
dataSources[] =
ManagercreateDataSource(audioCDIgetLocator());
dataSources[] =
ManagercreateDataSource(videoCDIgetLocator());
DataSource ds = ManagercreateMergingDataSource(dataSources);
然後可以使用ds作為createPlayer()方法的參數來獲得一個Player對象dualPlayer
調用addControllerListener()就可以進行播放了
dualPlayer = ManagercreatePlayer(ds);
dualPlayeraddControllerListener(this);
dualPlayerstart();
小結Java多媒體框架是一個很好的多媒體編程工具
在這篇文章中我只是簡單介紹了JMF的一些基本功能
如果有興趣的話可以仔細閱讀一下Sun公司的Java網站上提供的JMStudio的例子
在JMStudio中不僅實現了簡單的播放和視頻/音頻截取功能
還實現了從互聯網下載和向互聯網上傳多媒體數據流的功能
而且它還包含了JMFRegistry的源代碼
將相應的代碼移植到你的應用程序中後
你就不需要在運行程序前運行JMFRegistry來向JMF注冊設備了
作者簡介馮睿
年畢業於美國Northern Illinois大學電氣工程系
獲碩士學位
隨後在New Monics軟件公司工作了一年
其間參加了Java虛擬機的開發和優化工作
目前在國內一家GIS公司擔任項目經理
主要從事應急指揮系統的交通GIS系統的開發
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26663.html