這並不是即將問世的QuickTime for Java book一書的摘錄
雖然我希望是的
你看
問題是
在QTJ世界中大多數的我們都一直假定QTJ中的流媒體API已經損壞
我並不是想為此事而掩蓋什麼
好的
我繼續來通過各種各樣的人通過不同的技術進行工作的掩蓋獲取去這樣做
但是我不想再來一次
此外
流媒體沖突的情況似乎特別的糟糕
沒有人能得到它的演示代碼工作方式――this post to the quicktime
java list 是令許多用戶對獲取蘋果公司的AudioBroadcaster 和DrawableBroadcaster 演示工作方式絕望的典型
讓事情更糟糕
演示之一依靠一個在QTJ
版本中作為退回到原始的GUI提供的已經被取消的GUI預覽組件類
提供組件只對Movies
MovieControllers 和GraphicsImporters
而不是流式的Presentations
視頻捕捉
或者某些圖形美好得像來自多種資源
合成
制作
所以
官方給出的演示它首先看起來是不會工作
和現在的關鍵類有沖突(如果在Java
中運行會拋出RuntimeExceptions異常)
預測實際的流式內容和QTJ
看上去會非常糟糕
令我欣喜
甚至是有點吃驚的是
有報道稱流媒體能夠在QTJ
中工作
在本文章中
我將介紹通過QTJ實現簡單的網絡廣播的基礎
需求 QuickTime的流媒體API
在Java中由只可在Mac OS(Classic和OS X)中運行的包quicktime
streaming聲明
在QTJ中存在Windows版本的類
但是它們卻不能工作
但是
你可以使用Windows版本的QuickTime作為流媒體的客戶端
如果在Java中運行並不是關鍵的
你可獲取Darwin Streaming Server
一個開源項目可在Windows
Server 和
Server上運行如同在Solaris
和 Red Hat Linux
上一樣
使用QuickTime流媒體最簡單的事情就是我在此說明的生動內容
你需要至少一個音頻輸入設備
如一個內置的麥克風或者一個耳機
當然
擁有一個QuickTime支持的攝像機
如一個iSight
將會更讓人印象深刻
Streaming 是什麼不是什麼 給出了術語
流
的含義並不容易明確術語
Streaming
的正確含義
例如
QuickTime長期支持一種
快速啟動
的特征――如果QuickTime明確擁有足夠的開始播放的資源並且不會用完目前下載速率下的資源一段錄像能開始播放――那是一些用戶將Streaming 的一種形式弄錯了
自然的
這有它自己的優勢
容易創建並且確保了所有的包都到達了客戶端
但是真正的 Streaming
換句話說
Streaming 符合Internet工程工作小組(IETF)的標准
這是一個完全不同的問題
直到QuickTime
才被支持
並且直到QTJ
才支持Java
Streaming 的形式允許服務器控制傳輸
但很難在實時中保證最優化運行
客戶端未下載潛在的大文件
這樣的方法是獨特的便利的直接廣播
事實上
QuickTime的流媒體使用兩種
實時
的流媒體傳輸協議
實時傳輸協議(RTP)來傳輸媒體數據包
實時流協議(RTSP)用於控制信息
RTP使用潛在的有損UDP連接
所以所有的人都有意的忍耐再傳輸期間的包的丟失
這就意味著客戶端需要友好的操作未獲取所有數據的視頻幀或者音頻例子
更好的方法是通過基於TCP/IP的連接
它可以使用不確定的重試(也會因此需要一個不確定的時間)來獲取丟失的包
Presentation和SDP文件 在QuickTime中
流媒體傳輸等同一個電影――一個電影可以有音軌和視軌
一個元數據的聚集將它們全都聯系在一起
此表示會將一些多種的音頻和視頻流的元數據聯系起來
音頻和視頻你非常喜歡的流媒體種類也是值得關注的
自從某些其他的媒體類型(sprites
Flash內容)被QuickTime支持後並沒有操作好失去的包
並不適合作為流媒體傳播
你可能被建議去建立一個流媒體
你會需要創建一個Presentation 並開始它
但是現在呢?最普遍的這樣做的方法是創建一個會話描述協議(SDP)文件
將其放入靜態工廠方法Presentation
fromFile()
SDP文件以一種適當的簡單的文本格式
由RFC
和several updates所定義
我發現這些都是早期的理論而不是實際操作
但是稍後讓我們擔心的是執行詳細信息
這是一個被一些Apple的流媒體使用的例子
在Tim Monroe的QuickTime Toolkit Volume Two中
v=
c=IN IP
/
/
m=audio
RTP/AVP
m=video
RTP/AVP
a=rtpmap:
H
以下是每一行的解釋
·v=
這是SDP的版本號
在這裡版本號是
表示在SDP中沒有次要的號碼
·c=IN IP
/
/
這是提供在描述中使用的連接的信息
IN IP
表示是一個IPv
的網絡地址
是地址(注意這是一個多點傳輸地址
所有許多的客戶端能連接到廣播)
是存在時間
是臨近使用的多點傳輸地址的數量
·m=audio
RTP/AVP
m=這一行定義了用於廣播的流媒體
在這裡明顯的是audio
發送經由到RTP到端口
在簡單的QCELP音頻中定義了有效負載類型
這些在RFC
中定義了
·m=video
RTP/AVP
這一媒體行定義了一個video媒體流
由RTP傳輸到端口
有效負載類型為
所以使用
表示在原始的RFC中視頻格式沒有給出負載類型
在SDP中替換它會被映射到一個眾所周知的常量中
·a=rtpmap:
H
這個完成鍵入在前一行指出的動態負載
使用此類型
你會使用一個在
和
之間的值(本例中是
)
然後用一個字符串命名此負載類型(H
)
這當然好
但是當在我的例程序中使用它的時候
我只獲得了一個視頻流卻沒有聲音
所以
我使用了一個很不同的SDP
最初在QTJ的DrawableBroadcaster演示中出現
是的
他們不贊同這麼做
m=audio
RTP/AVP
c=IN IP
a=rtpmap:
x
qt
m=video
RTP/AVP
a=rtpmap:
x
qt
這裡最大的不同就是音頻和視頻都使用了相同的動態負載映射
這並不是針對一個真正的編碼器
而是一般的x
qt
在這裡勝利的是你能在運行時間上挑選任一QuickTime的音頻和視頻編碼器
而不是在SDP文件中強迫導致
底側是這些可以不是由非QuickTime客戶端可分析的
反之使用十分標准的和/或者舊的編碼器並且在SDP中指定他們使它更像其他的客戶端(Real
JMF等)能夠操作你的系統
這就是你的SDP文件
現在不要加入一個Presentation
創建Presentation 我們的流媒體服務器程序調用LittleBroadcast
這並沒有多少代碼
只有不過
行
在本文中
我將一步一步的進行
解釋一般的部分
但提供其全部的清單
在後面的Resources章節中有可用到一個
tar
gz文件
連同SDP文件和一個Ant構建文件
package com
mac
invalidname
qtjstreaming;import quicktime
*;
import quicktime
std
*;
import quicktime
util
*;
import quicktime
qd
*;
import quicktime
io
*;
import quicktime
streaming
*;
import quicktime
app
time
*;
import java
io
*;import java
awt
*;
import java
awt
event
*;
public class LittleBroadcast extends Tasking implements ActionListener {
這是一長串典型的引入QuickTime
包括了使用其QDGraphics來提供一個攝像機畫面以外的圖形界面的qd
讀取SDP文件的io
用於流媒體API的streaming
以及獲得給予Presentation運行時間的有效任務的time
最後一點
注意該類擴展直Tasking――提供周期性調用的task()
本應用程序中
它用於不斷的調用Presentation的idle()方法
並使其循環工作
你在本書中學習到也就是Movies所需要的
但是此任務幾乎一直都自動的為你所操作
使用Presentation並不好運
(或者為此事而捕獲
但有些離開本主題了
)
boolean broadcasting = false; public static final int BROADCAST_WIDTH =
; public static final int BROADCAST_HEIGHT =
; Button startStopButton; Button configButton; Presentation pres; int presenterTimeScale =
;
這些是服務器的實例變量
是一個用於指定當開始/停止按鈕按下的時候做什麼的標記
下面是一對廣播視頻大小的常量
緊跟著是服務器GUI的按鈕
最後是一個Presentation對象
以及它的時間尺度
(媒體的保持時間系統
一個
的時間尺度表示一秒種裡有
個單位
也是QuickTime中默認的
)
public static void main (String[] args) {
System
out
println (
main
);
try {
QTSession
open();
new LittleBroadcast();
} catch (QTException qte) {
qte
printStackTrace();
}
}
在這個main中並沒有什麼獨特的地方
我投入了所有的精力在構造函數上以防止為那些我需要的實例創建一個內部類
如果你擴展本代碼
你可能會發現這很有用
public LittleBroadcast() throws QTException {
System
out
println (
LittleBroadcast constructor
);
QTFile file = new QTFile (new File (
little
sdp
));
try {
MediaParams mediaParams = new MediaParams();
mediaParams
setWidth (BROADCAST_WIDTH);
mediaParams
setHeight (BROADCAST_HEIGHT);
QDGraphics myGWorld =
new QDGraphics (new QDRect (
BROADCAST_WIDTH
BROADCAST_HEIGHT));
mediaParams
setGWorld (myGWorld);
PresParams presParams =
new PresParams( presenterTimeScale
QTSConstants
kQTSSendMediaFlag |
QTSConstants
kQTSAutoModeFlag |
QTSConstants
kQTSDontShowStatusFlag
mediaParams );
pres = Presentation
fromFile(file
presParams );
構造函數的第一事是裝載名為little
sdp的SDP文件
但這並不是所有的都需要創建Presentation ――在調用Presentation
fromFile()的時候需要服務器應用程序設置一些必要的參數
首先
你要創建一個MediaParams對象
這樣你能設置視頻的高度和寬度
您必須做的其它重要事是提供照相機一個圖形界面
由QDGraphics創建MediaParams設置
是的
名字是古怪的
因為QTJ 設計員想注重與AWT Graphics對象的相似性
但得到或設置這樣的對象的用途的所有方法是使用其本地API名字
GWorld
最後
你為所有的Presentation創建一個PresParams來設置參數
這采取一個有些任意的時標
一些算術上的行為標記彼此OR
ed
以及MediaParams
可能的行為標記
都被定義在QTSConstants
包括:
·KQTSAutoModeFlag: 都使用默認值
最重要地
這些使用默認值Sourcer
Presentation的來源
是從各種各樣的輸入裝置執行獲取的SequenceGrabber
它還可能播放一個在磁盤上或是任意目錄下的的QuickTime 文件; 稍後我將探討這些問題
·KQTDontShowStatusFlag: 不要創建一個會導致連接數和狀態信息總被顯示在客戶端的流媒體狀態處理程序
·KQTSSendMediaFlag:發送
不接收數據
·KQTSReceiveMediaFlag
接收
不發送數據
在SDP文件說明
參數
以及GWorld 設置下
創建Presentation和Presentation
fromFile()
// find audio stream Stream audioStream = null;
for (int i=
; i<=pres
getNumStreams(); i++) {
System
out
println (
stream:
+ i +
:
+
pres
getIndStream(i));
Stream aStream = pres
getIndStream (i);
if (pres
hasCharacteristic(aStream
StdQTConstants
audioMediaCharacteristic)) {
audioStream = aStream;
break;
}
}
System
out
println (
audioStream =
+ audioStream);
pres
setVolumes (audioStream
);
System
out
println (
created presentation
gworld ==
+
pres
getGWorld() +
size ==
+
mediaParams
getWidth() +
x
+
mediaParams
getHeight() +
streams ==
+
pres
getNumStreams());
//*******這不是真正地必要的
但它將告訴你怎麼通過Presentation游覽來挑選各自的流媒體
Presentation
getIndStream 會由索引返回一個Stream(附注QuickTime 索引都是基於
)
它重復這些audioMediaCharacteristic 請求查找音頻流 (對於錄影
您就要請求visualMediaCharacteristic)
這個實例在audioStream上為左右聲道設置音量最大值為
最後
println從Presentation和MediaParams轉存一些有意義的元數據
配置Presentation SettingsDialog sd = new SettingsDialog (pres);
System
out
println (
Did settings
);
pres
preroll();
broadcasting = false;
這是設置presentation最後的步驟
SettingsDialog存在用戶以輸入裝置選擇的音頻和視頻 (二個流媒體SDP 文件被指定在Presentation中)
每個流媒體都可由一個壓縮格式來定制 (MPEG
Sorenson Video
H
等) 以及一個分包器(有時由壓縮格式定義; 可觀察它是否隨著壓縮格式的改變而自動改變)
這個GUI實例顯示在圖
圖像 為一個Presentation SettingsDialog 在此圖中
音頻默認為計算機連線輸入
更改它為iSight
您需要點擊Source按鈕
提出的可選設備列表顯示在圖
上
圖 來源選擇對話框 最後就是調用Presentation
preroll()
如同Movie
preroll()
提供Presentation一個機會預先分配資源以及准備好開始流媒體Presentation
提供一個控制GUI // Make monitor window
startStopButton = new Button (
Start
);
configButton = new Button (
Configure
);
startStopButton
addActionListener (this);
configButton
addActionListener (this);
Frame monitorFrame = new Frame (
QTJ Streaming
);
monitorFrame
setLayout (new BorderLayout());
Panel buttonPanel = new Panel();
buttonPanel
add (startStopButton);
buttonPanel
add (configButton);
monitorFrame
add (buttonPanel
BorderLayout
SOUTH);
monitorFrame
pack();
monitorFrame
setVisible(true);
這個為控制和配置Presentation設置了很小的GUI
提供基本的一個起始/停止鍵和一個配置按鈕
按鈕作為一個ActionListener提交給this
意味著這個類將需要提供一個actionPerformed方法來處理按鈕點擊
控制GUI 的屏幕截圖顯示在圖
圖 監控/控制窗體 在這點上你也許會問一個有趣的問題:
從什麼時候我們開始關心使用GUI提供server?
據推測
這是從Classic Mac OS開始的一個傳統
它沒有一個用命令行啟動和傳遞參數的程序
但此外
你通常會希望提供一個流媒體數據的預覽
並且如果您有一個預覽視窗
為什麼會也沒有一個配置的GUI?
無論如何
這是大概的討論
因為QTJ
不提供您能使用來預覽的一個AWT Component
在有些方面有希望的是
QTFactory將得到一個新的超負荷為采用一個Presentation並且返回一個顯示流媒體視頻的Component的makeQTComponent
它大概可能使用一些QuickDraw voodoo而完全放棄Java的東西
如果
在各task()中回調(參見下面) 您采取GWorld被及早創建
轉換它成Pict
並且作為一份唯一命名的文件
您會看見每一個都是不同的
意味GWorld每次都得到新數據
所以如果您替換掉GWorld
改為能給AWT Component在各通道上定義象素
您會有銀幕上的預覽
何人有膽量如此做? 在quicktime
java list上查看
詳細資料 // add shutdown handler to make sure presentation
// gets stopped
Thread presentationStopper = new Thread() {
public void run() {
try {
pres
stop();
} catch (QTException qte) {}
}
};
Runtime
getRuntime()
addShutdownHook (presentationStopper);
這個關閉異常分支確定Presentation在程序退出之前被終止
這是重要的原因
象SequenceGrabber
Presentation愉快繼續運行在您的應用程序退出之後
綁定一個端口
嚴重的循環
使用您的獲取設備保留其它應用程序
等
} catch ( QTException e ) {
e
printStackTrace();
System
exit (
);
}
}
最後
構造函數捕捉並拋出所有的QTExceptions
public void actionPerformed (ActionEvent ae) {
System
out
println (
actionPerformed
);
try {
if (ae
getSource() == startStopButton) {
if (broadcasting) {
pres
stop();
stopTasking();
broadcasting = false;
startStopButton
setLabel (
Start
);
System
out
println (
Stopped
);
} else {
pres
start();
startTasking();
broadcasting = true;
startStopButton
setLabel (
Stop
);
System
out
println (
Started
);
}
} else if (ae
getSource() == configButton) {
new SettingsDialog (pres);
}
} catch (QTException qte) {
qte
printStackTrace();
}
}
這是非常直接的處理起始/終止和設置按鈕
如果點擊的按鈕是起始/終止
設置GUI 就會調用在Presentation上的 start() 或stop()
開始或停止正在執行的任務 (定期回調這個類的task()方法)
為下按鈕點擊目的設置broadcasting標志
並且更改按鈕標簽
如果用戶點擊了配置
它生成為Presentation新的SettingsDialog
public synchronized final void task() throws QTException {
pres
idle(null);
}}
最後的這個方法實現了繼承自Tasking的task()方法並且被在操作開始按鈕調用startTasking()後定時的調用
使用簡單的調用Presentation
idle()
它提供了表達時間來從獲取設備取得當前數據
對其編碼並傳輸流出去
運行流媒體客戶端
最簡單的使客戶機看廣播的方法是使用QuickTime 播放器打開服務器使用並創建Presentation的同樣SDP 文件
這將調用SDP 輸入程序連接到流媒體並且開始分析這些內容
注意客戶機和服務器不能在同一台機器上
明顯地因為服務器為使用表示而占用端口
拒絕客戶機對這些端口的使用
圖
顯示在我的計算機上的流媒體的外觀(那是正在播放我的Macross和Escaflowne玩具)
圖 QuickTime流媒體客戶端 如果您使用QuickTime 播放器
您能使用其得到信息指令顯示兩種流媒體以及他們的格式
在表
您能看有二種媒體: 一條未壓縮的
kHz 音頻流
和一條H
的視頻流
圖 客戶端信息窗體 結語 對我來說
播放基於Java的QuickTime流媒體比想象的容易多了
最簡單的例子
從獲取設備播放
只需要少於
個代碼行
顯然
最困難的部份是了解SDP文件
它被證明是非常的過分講究並且它的說明文件包含大量應用程序級別程序員不會有的知識
同樣不幸的是
QTJ不再提供預覽組件
但也許在將來會提供
以及一小段的GWorld/QuickDraw 堆砌也許會在將來制造出這樣的組件
本文只包括怎麼為實時獲取數據設置廣播
其它可利用的Sourcers
譬如來自磁盤或任意目錄種的那些廣播QuickTime 文件
將會在以後的部分中討論
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26490.html