在這一系列的第一部分中
我描述了在JMF(JAVA媒體幀工作器 下同)的幫助下
怎樣將一部電影片斷插入JAVA
D場景中
這個執行過程使用Model
View
Controller設計模式
動畫屏幕是由JMFMovieScreen類表示的視覺元素
動畫模型部分由JMFSnapper類控制
java
D行為類
TimeBehavior
是動畫中引起幀周期性恢復的控制類
在這篇文章中
我將使用QTJ(QuickTime for Java 下同)再次解析動畫成份
QTJ提供一個覆蓋QuickTime API的依賴對象的java
使之可以展示
編輯和創建QuickTime動畫
捕獲視頻 音頻
展示
D和
D動畫
QuickTime可用於Mac和Windows系統
關於QTJ的安裝
文檔和實例細節可在/quicktime/qtjava查詢
由設計模式作出的推論
只在動畫類JMFSnapper被QTSnapper取代時
QTJ取代JMF在應用中有微小的作用
圖
兩幅QTJ情況下的
D動畫截屏
右邊圖片采取顯示屏背面視角
從圖
大致看出
QTJ
based 和JMF成像效果沒有明顯區別
然而
通過更仔細得比較可以看出有兩個變化
QTJ動畫有輕微的被像素化
播放的更慢
像素化(像素化是對內部像素對於觀看者易見的數字圖像的顯示
當一些用於普通的計算機顯示的低分辨率圖像被投射到一個大的顯示器上
每一個像素都會變得單獨可見
這種不常發生的現象就叫做像素化
)是由於原始動畫從MPEG轉換為QuickTime
s MOV格式引起的
它可由更好的轉換方法矯正
速度問題更基礎
它與QTSnapper的潛在執行有關
這篇文章的重點為?
執行QTSnapper的兩種主要方法的討論
一種方法是將動畫裡的每一幀都提出來顯示在屏幕上
另一種方法依靠當前的時間提出幀
第二種方法意味著可能將會遺漏部分幀
畫面顫抖
但遺漏 可以使播放更快
一些簡單的FPS(frame
per
second)度量器的介紹
我將用它們這兩種方法的相對速度
探測遺漏幀數
此山非彼山 與第一部分一樣
編碼將利用兩個大型的API
在這裡沒有時間介紹利用的細節了
我將再次使用java
D
但API媒體將由JMF轉變為QTJ
在我的O
Reilly book
Killer Game Programming in Java (KGPJ)中有大量關於java
D的信息
還有圖
的原碼
我將不會解釋動畫屏幕和動畫更新行為
因為它們與第一部分相同
在QTJ技術中我將會使用QTSnapper從動畫中提取幀
應用的兩種看法 圖應用流程圖 此圖表與第一篇文章中的幾乎相同
QuickTime動畫由QTSnapper類加載
動畫屏幕由QTMovieScreen創建
每
毫秒
TimeBehavior對象調用QTMovieScreen中的nextFrame()方法
然後調用QTSnapper中的getFrame()方法獲取動畫中的一個幀
依次循環
JMFSnapper與QTSnapper之間有一個很重要的不同
JMFSnapper返回一個幀
這個幀是動畫播放時的當前幀
而QTSnapper返回動畫中的幀根據遞增的索引
例如
當getFrame()方法在JMFSnapper被反復調用時
也許會重新得到幀
等等
它是由方法何時被調用與動畫播放速度決定的
當getFrame()方法在QTSnapper被調用
它將會返回幀
等等
圖UML類的應用圖表僅列出公共方法 此圖表除了動畫屏幕名和動畫類名(QTMovieScreen 和 QTSnapper)外
與第一篇文章的相同
事實上
只有Snapper類的內部執行被改變
JMF Movie
D應用程序和QTJ
based版本之間的改動需要Snapper被重寫
// global variableprivate QTSnapper snapper; // was JMFSnapper// in the constructor
load the movie in fnmsnapper = new QTSnapper(fnm);
這兩處改動是由於須將JMFMovieScreen重命名為QTMovieScreen
這個例子中的所有代碼
和文章的早期版本
可以在KGPJ website查詢到
一幀一幀的動畫 理解QTSnapper的內在工作機理
可以幫助我們對QuickTime動畫構造有一個大致的認識
每一幅動畫可以理解為視頻軌跡和音頻軌跡在相同時間上的重疊
圖
是這種思想的圖示
圖QuickTime動畫的內部構造機理 每個軌跡控制著其自身數據
例如它包含的媒體類型和媒體本身
媒體容器(media container)有它自己的數據結構
包括它的持續時間和播放率(每秒播放抽樣數)
媒體是由一組抽樣(或幀)組成
第一個抽樣時間為
(與媒體時間有關)
抽樣是被變址的
第一個抽樣在
位置(非
)
圖
大致描述了QuickTime的軌跡和媒體結構
圖QuickTime軌跡和媒體的內在構造機理 想要得到更多信息
請查詢QuickTime指南的movie section
打開動畫視頻媒體
QTSnapper構造器打開動畫
// globalsprivate boolean isSessionOpen = false;private OpenMovieFile movieFile;private Movie movie;// in the constructor
// start a QuickTime sessionQTSession
open();isSessionOpen = true;// open the moviemovieFile = OpenMovieFile
asRead( new QTFile(fnm) );movie = Movie
fromFile(movieFile);
在QuickTime使用之前調用QTSession
open()方法將其初始化
在終止時相應的調用QTSession
close()方法
軌跡定位和媒體訪問
// more globalsprivate Track videoTrack;private Media vidMedia;// in the constructor
// extract the video track from the movievideoTrack = movie
getIndTrackType(
StdQTConstants
videoMediaType
StdQTConstants
movieTrackMediaType);if (videoTrack == null) { System
out
println(
Sorry
not a video
); System
exit(
);}// get the media used by the video trackvidMedia = videoTrack
getMedia();
一旦媒體打開
從中提取各種信息
// more globalsprivate MediaSample mediaSample;private int numSamples; // number of samplesprivate int sampIdx; // current sample indexprivate int width; // frame widthprivate int height; // frame height// in the constructornumSamples = vidMedia
getSampleCount();sampIdx =
; // get first sample in the trackmediaSample = vidMedia
getSample(
vidMedia
sampleNumToMediaTime(sampIdx)
time
);// store width and height of image in the sampleImageDescription imgDesc = ImageDescription) mediaSample
description;width = imgDesc
getWidth();height = imgDesc
getHeight();
sampIdx作為計數器將在抽樣中被重復調用(抽樣於位置
開始)
動畫圖像的寬度和高度由第一個抽樣獲得
接下來所有的抽樣都是同樣的尺寸
測算 FPS
由QTSnapper返回的 幀數/秒 將在稍後被用作類的不同使用方法的比較參數
構造器中必要的參數已被初始化
// frame rate globalsprivate long startTime;private long numFramesMade;// initialize them in the constructorstartTime = System
currentTimeMillis(); numFramesMade =
;
結束
將應用程序結束時
QTSnapper類中的stopMovie()方法將被調用
它報告FPS
關閉QuickTime
// globalsprivate DecimalFormat frameDf = new DecimalFormat(
#
); //
dpsynchronized public void stopMovie(){ if (isSessionOpen) { // report frame rate long duration = System
currentTimeMillis()
startTime; double frameRate = ((double) numFramesMade*
)/duration; System
out
println(
FPS:
+ frameDf
format(frameRate)); QTSession
close(); // close down QuickTime isSessionOpen = false; }}
由於stopMovie()和getFrame()是同步的
所以從動畫中提取幀和QuickTime關閉在時間上是不可能同時進行
緩存幀
getFrame()返回一次抽樣樣品
稱作BufferedImage對象
被選擇的幀利用索引指數存貯在sampIdx
// globalsprivate BufferedImage img
formatImg;synchronized public BufferedImage getFrame(){ if (!isSessionOpen) return null; if (sampIdx > numSamples) // start back with the first sample sampIdx =
; try { /* Get the sample starting at the specified index time */ TimeInfo ti = vidMedia
sampleNumToMediaTime(sampIdx); mediaSample=vidMedia
getSample(
ti
time
); sampIdx++; writeToBufferedImage(mediaSample
img); // resize img
writing it to formatImg Graphics g = formatImg
getGraphics(); g
drawImage(img
FORMAT_SIZE
FORMAT_SIZE
null); // Overlay current t
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26629.html