亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術文章
文章詳情頁

JAVA 3D世界的動畫展示(Part2,使用QuickTime for Java)

瀏覽:79日期:2024-06-12 11:38:25
內容: JAVA 3D世界的動畫展示(Part2,使用QuickTime for Java)作者: Andrew Davison 翻譯 Caesh版權聲明:可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明英文原文地址:http://www.onjava.com/pub/a/onjava/2005/06/01/kgpjava_part2.html中文地址:http://www.matrix.org.cn/resource/article/43/43676_JAVA_3D.html關鍵詞: Java 3D QuickTime在這一系列的第一部分中,我描述了在JMF(JAVA媒體幀工作器 下同)的幫助下,怎樣將一部電影片斷插入JAVA 3D場景中。這個執行過程使用Model-View-Controller設計模式。。動畫屏幕是由JMFMovieScreen類表示的視覺元素。動畫模型部分由JMFSnapper類控制。java 3D行為類,TimeBehavior,是動畫中引起幀周期性恢復的控制類在這篇文章中,我將使用QTJ(QuickTime for Java 下同)再次解析動畫成份。QTJ提供一個覆蓋QuickTime API的依賴對象的java,使之可以展示,編輯和創建QuickTime動畫;捕獲視頻 音頻;展示2D和3D動畫。QuickTime可用于Mac和Windows系統。關于QTJ的安裝.文檔和實例細節可在developer.apple.com/quicktime/qtjava查詢。由設計模式作出的推論,只在動畫類JMFSnapper被QTSnapper取代時,QTJ取代JMF在應用中有微小的作用。圖1:兩幅QTJ情況下的3D動畫截屏,右邊圖片采取顯示屏背面視角 從圖1大致看出,QTJ-based 和JMF成像效果沒有明顯區別。然而,通過更仔細得比較可以看出有兩個變化:QTJ動畫有輕微的被像素化,播放的更慢。像素化(像素化是對內部像素對于觀看者易見的數字圖像的顯示。當一些用于普通的計算機顯示的低分辨率圖像被投射到一個大的顯示器上,每一個像素都會變得單獨可見,這種不常發生的現象就叫做像素化。)是由于原始動畫從MPEG轉換為QuickTime's MOV格式引起的,它可由更好的轉換方法矯正。速度問題更基礎:它與QTSnapper的潛在執行有關。這篇文章的重點為?。執行QTSnapper的兩種主要方法的討論.一種方法是將動畫里的每一幀都提出來顯示在屏幕上。另一種方法依靠當前的時間提出幀.第二種方法意味著可能將會遺漏部分幀,畫面顫抖,但遺漏 可以使播放更快。一些簡單的FPS(frame-per-second)度量器的介紹.我將用它們這兩種方法的相對速度,探測遺漏幀數1.. 此山非彼山與第一部分一樣,編碼將利用兩個大型的API,在這里沒有時間介紹利用的細節了。我將再次使用java 3D,但API媒體將由JMF轉變為QTJ。在我的O'Reilly book, Killer Game Programming in Java (KGPJ)中有大量關于java 3D的信息,還有圖1的原碼。我將不會解釋動畫屏幕和動畫更新行為,因為它們與第一部分相同。在QTJ技術中我將會使用QTSnapper從動畫中提取幀2.應用的兩種看法圖2:應用流程圖 此圖表與第一篇文章中的幾乎相同QuickTime動畫由QTSnapper類加載,動畫屏幕由QTMovieScreen創建.每40毫秒,TimeBehavior對象調用QTMovieScreen中的nextFrame()方法.然后調用QTSnapper中的getFrame()方法獲取動畫中的一個幀.依次循環.JMFSnapper與QTSnapper之間有一個很重要的不同.JMFSnapper返回一個幀,這個幀是動畫播放時的當前幀.而QTSnapper返回動畫中的幀根據遞增的索引.例如.當getFrame()方法在JMFSnapper被反復調用時,也許會重新得到幀1,3,6,9等等,它是由方法何時被調用與動畫播放速度決定的.當getFrame()方法在QTSnapper被調用,它將會返回幀1,2,3,4等等.圖3:UML類的應用圖表,僅列出公共方法. 此圖表除了動畫屏幕名和動畫類名(QTMovieScreen 和 QTSnapper)外,與第一篇文章的相同.事實上,只有Snapper類的內部執行被改變.JMF Movie3D應用程序和QTJ-based版本之間的改動需要Snapper被重寫. // global variableprivate QTSnapper snapper; // was JMFSnapper// in the constructor, load the movie in fnmsnapper = new QTSnapper(fnm);這兩處改動是由于須將JMFMovieScreen重命名為QTMovieScreen. 這個例子中的所有代碼,和文章的早期版本,可以在KGPJ website查詢到.3.一幀一幀的動畫理解QTSnapper的內在工作機理,可以幫助我們對QuickTime動畫構造有一個大致的認識.每一幅動畫可以理解為視頻軌跡和音頻軌跡在相同時間上的重疊.圖4是這種思想的圖示 圖4:QuickTime動畫的內部構造機理每個軌跡控制著其自身數據,例如它包含的媒體類型和媒體本身.媒體容器(media container)有它自己的數據結構,包括它的持續時間和播放率(每秒播放抽樣數).媒體是由一組抽樣(或幀)組成,第一個抽樣時間為0(與媒體時間有關).抽樣是被變址的,第一個抽樣在1位置(非0).圖5大致描述了QuickTime的軌跡和媒體結構 圖5: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(1, StdQTConstants.videoMediaType, StdQTConstants.movieTrackMediaType);if (videoTrack == null) { System.out.println('Sorry, not a video'); System.exit(0);}// 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 = 1; // get first sample in the trackmediaSample = vidMedia.getSample(0, vidMedia.sampleNumToMediaTime(sampIdx).time,1);// store width and height of image in the sampleImageDescription imgDesc = ImageDescription) mediaSample.description;width = imgDesc.getWidth();height = imgDesc.getHeight();sampIdx作為計數器將在抽樣中被重復調用(抽樣于位置1開始).動畫圖像的寬度和高度由第一個抽樣獲得,接下來所有的抽樣都是同樣的尺寸.測算 FPS由QTSnapper返回的 幀數/秒 將在稍后被用作類的不同使用方法的比較參數.構造器中必要的參數已被初始化. // frame rate globalsprivate long startTime;private long numFramesMade;// initialize them in the constructorstartTime = System.currentTimeMillis(); numFramesMade = 0;結束將應用程序結束時,QTSnapper類中的stopMovie()方法將被調用.它報告FPS,關閉QuickTime.// globalsprivate DecimalFormat frameDf = new DecimalFormat('0.#'); // 1 dpsynchronized public void stopMovie(){ if (isSessionOpen) { // report frame rate long duration = System.currentTimeMillis() - startTime; double frameRate = ((double) numFramesMade*1000.0)/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 = 1; try { /* Get the sample starting at the specified index time */ TimeInfo ti = vidMedia.sampleNumToMediaTime(sampIdx); mediaSample=vidMedia.getSample(0,ti.time,1); sampIdx++; writeToBufferedImage(mediaSample, img); // resize img, writing it to formatImg Graphics g = formatImg.getGraphics(); g.drawImage(img, 0, 0, FORMAT_SIZE, FORMAT_SIZE, null); // Overlay current time on image g.setColor(Color.RED); g.setFont( new Font('Helvetica', Font.BOLD, 12)); g.drawString(timeNow(), 5, 14); g.dispose(); numFramesMade++; // count frame } catch (Exception e) { System.out.println(e); formatImg = null; } return formatImg;} // end of getFrame()從QTJ的媒體類中調用getSample()方法可以容易的獲得抽樣.不幸的是,將抽樣轉化為BufferedImage仍然是個棘手的問題.豐富的細節和注釋,可以在編碼中研究.從抽樣種萃取一個“原始圖像,然后將其減壓寫成一個QuickTime版本的Graphics對象.Graphics對象中的無壓縮數據被拷貝成為另一個“原始圖像,然后成為一個像素數組.最后,這個數組被寫入空BufferedImage的DataBuffer.程序能工作嗎?能順利工作嗎? 是的,Movie3D顯示動畫,但是比較大的動畫播放的比較慢.這是由于getFrame()方法在幀補給上的緩慢,它可以通過FPS數量進行量化.對于圖1的動畫,在Windows 98系統FPS之大概在15-17幀/秒.然而,TimeBehavior對象要求每40毫秒更新,轉化為幀數大概在25FPS.getFrame()方法之所以慢是由于抽樣轉化為BufferedImage的時間消耗.由于當前調用的getFrame()方法在轉化幀時停頓,更多的請求將被延遲直到當前轉化完成.我將考慮兩種解決這一問題的方法:允許getFrame()方法在處理請求時遺漏幀,和在getFrame()方法中使用不同的轉化方法.我將輪流考慮這兩種方法,以幀遺漏開始.4. 遺漏幀的動畫新的Snapper類,QTSnapper1,仍然返回一個幀當getFrame()方法被調用時.與QTSnapper的不同在于它提供的類相應于當前動畫的執行時間.例如,getFrame()也許重新獲得幀1,2,5,8,14等等,依賴于方法的調用時間.因此,動畫以一個很好的速度播放,但是由于幀的遺漏可能導致畫面顫抖.對比的看,QTSnapper將會返回所有的幀數(1,2,3,4等等),但是由于調用getFrame()方法的延遲可能會導致動畫播放緩慢.然而,畫面將不會出現顫抖,由于沒有幀被遺漏.QTSnapper1種的關鍵部分是對于動畫的“當前執行時間理念.我的方法是當getFrame()方法被調用估計QTSnapper的當前執行時間,將它轉變為動畫執行時間,然后作為樣本給定值.QTSnapper1有與QTSnapper相同的公共方法,所以它只需作出微小的改變就可用于QTMovieScreen.只有在動畫播放時差別才變得明顯,在以很好的速度播放時會發出喳喳聲.詳細的測量,以圖1的“明顯幀率比較,31FPS對比QTSnapper的16FPS.打開動畫視頻媒體QTSnapper1訪問動畫視頻的過程與QTSnapper相同.一旦視頻可被利用,個別的媒體值將被儲存并稍后被getFrame()方法調用:// globalsprivate Media vidMedia;private int numSamples;private int timeScale; // media's time scaleprivate int duration; // duration of the media// in the constructor,// get the media used by the video trackvidMedia = videoTrack.getMedia();// store media details for laternumSamples = vidMedia.getSampleCount();timeScale = vidMedia.getTimeScale();duration = vidMedia.getDuration();獲取一幀getFrame()方法中的新要素是它怎樣計算被用于訪問詳悉抽樣的給定值.方法的其他部分,writeToBufferedImage()的調用和當前圖像的編碼與QTSnapper相同. // globalsprivate MediaSample mediaSample;private BufferedImage img, formatImg;private int prevSampNum;private int sampNum = 0;private int numCycles = 0;private int numSkips = 0;// inside getFrame(),// get the time in secs since start of QTSnapper1double currTime = ((double)(System.currentTimeMillis() - startTime))/1000.0;// use the video's time scaleint videoCurrTime = ((int)(currTime*timeScale)) % duration;try { // backup the previous sample number prevSampNum = sampNum; // calculate the new sample number sampNum = vidMedia.timeToSampleNum( videoCurrTime).sampleNum; // if no sample change, then don't generate // a new image if (sampNum == prevSampNum) return formatImg; if (sampNum < prevSampNum) numCycles++; // movie has just started over // record the number of frames skipped int skipSize = sampNum - (prevSampNum+1); if (skipSize> 0) // skipped frame(s) numSkips += skipSize; // get a single sample starting at the // sample number's time TimeInfo ti = vidMedia.sampleNumToMediaTime(sampNum); mediaSample = vidMedia.getSample(0,ti.time,1);getFrame()在很短的時間內計算當前時間,從QTSnapper1開始時測量:double currTime = ((double)(System.currentTimeMillis() - startTime))/1000.0;每一個QuickTime的媒體片斷都有它自己的時間刻度,ts,例如一個個體的時間是1/ts 秒.恒定的時間刻度必須由currTime方法增加以獲得當前動畫時間.int videoCurrTime = ((int)(currTime*timeScale)) % duration;以媒體持續時間為模校正刻度時間,允許動畫在當前時間已超過動畫結束時間重復.調用Media's timeToSampleNum()方法將抽樣序列數以刻度時間顯示:sampNum = vidMedia.timeToSampleNum( videoCurrTime).sampleNum;上次抽樣序列號存儲在prevSampNum,以便允許實現大量的檢測和計算.如果新的抽樣序列號與上次取樣的序列號相同,就不需要檢查將抽樣轉化為BufferedImage的過程;getFrame()可以返回現有的formatImg接口.如果新的抽樣序列號小于上次取樣的序列號,這就意味著動畫開始循環,動畫起始幀將被顯示.這就是被注冊的numCycles增加.如果新的抽樣序列號大于上次取樣的序列號+1,意味著被遺漏的幀序列被記錄上.結束stopMovie()打印出FPS,關閉QuickTime進程,與QTSnapper類中的stopMovie()方法相同.同時它也報告附加信息: long totalFrames = (numCycles * numSamples) + sampNum;// report percentage of skipped framesdouble skipPerCent = (double)(numSkips * 100) / totalFrames;System.out.println('Percentage frames skipped: '+ frameDf.format(skipPerCent) + '%');// 'apparent' FPS (AFPS)double appFrameRate = ((double) totalFrames * 1000.0) / duration;System.out.println('AFPS: ' + frameDf.format(appFrameRate)); // 1 dpappFrameRate方法描述“顯性幀頻,就是從QTSnapper1開始使用時的抽樣總量.感覺上的“顯性 是因為不是所有的抽樣都有被顯示出來的必要.程序能工作嗎?能順利工作嗎?QTSnapper被QTSnapper1取代后,緩慢的動畫(圖1所示)將會播放的更快.結束時間時,據報告的明顯幀頻是31FPS,實際上的幀頻大概在16FPS,遺漏頻大概占總數的50%.驚奇的是,大量遺漏頻的并沒有顯示在屏幕上.對與另外一些比較小的動畫,速度的增加幾乎是察覺不到的;遺漏頻率大概在5%-10%.不幸的是,仍然還有兩個問題:幀遺漏產生的雜亂像素和對遺漏幀的數量的控制.雜亂像素無論何時,只要QTSnapper1遺漏一個動畫幀,下一個幀將會包含一些雜亂像素.圖6是效果顯示圖.從一段早期視頻截得的錯誤像素使用值. 圖6 雜亂圖像截屏問題是我所有的視頻事例都是使用temporal compression,它是一種利用連續視頻幀之間類似處的一種壓縮方法.假如兩個連續視頻幀有相同的背景,就不用再次存儲背景.只有兩個幀之間的不同才會被存儲.這項技術,被用在幾乎所有的流行視頻格式,意味著從一個幀中抽取圖像依賴這個幀和此前的幾個幀.暫時解壓由在writeToBufferedImage()方法中的QuickTime DSequence對象處理.DSequence構造器詳細說明了QuickTime在解壓過程中應該使用的一種屏幕外圖像緩沖器.幀圖像被寫入緩沖器,在那里與早期的幀數據結合.結合后圖像被傳給轉化的下一過程.QTSnapper1順次解壓時工作的很好,沒有遺漏幀,但是如果產生遺漏會導致錯誤.例如,當QTSnapper1遺漏幀5和6,然后解壓幀7時會發生什莫?幀被寫入QuickTime圖像緩沖器,與此前幀的數據結合.然而,幀5和6的數據丟失,所以結合后的圖像會有錯誤.簡單的講,圖像中的雜亂像素是由動畫的暫時壓縮引起的.一個可選擇的辦法是使用空間壓縮技術,既獨立的壓縮每個幀.這就意味著解壓時幀里所有的信息都會從幀本身被釋放出來,不需要檢查早期的幀.QuickTime MOV支持名為Motion-JPEG(M-JPEG)的空間壓縮方案.我使用QuickTime 6 Pro 中的工具將圖1以M-JPEG A編碼形式存儲為MOV文件.當這個動畫用Movie3D播放時,沒有發生畫面顫抖.限制幀遺漏QTSnapper1的另一個問題時getFrame()沒有對可能遺漏幀的數量進行限制.在我的測試中,遺漏幀的數量被限制在3個以下.然而,如果getFrame()用于一個很大抽樣的轉化,那末它的緩慢增加將會導致更多幀的遺漏.動畫質量將會發生明顯的惡化.5. 試著使圖像更快在QTSnapper和QTSnapper1中使用的sample-to-BufferedImage轉換方法(writeToBufferedImage())是由Chris W. Johnson在實例中獲得的.有沒有一種更快的從抽樣中隨取圖像的方法呢?QTJ方面的權威書籍:QuickTime for Java: A Developer's Notebook,作者:Chris Adamson, O'Reilly,2005.1月出版.卷五,covering QuickDraw,中的ConvertToJavaImageBetter.java實例,展示了怎樣獲得一個PICT圖像的抽樣并將其轉化為Java圖像對象.這個例子也可在quicktime-java mailing list中找到.我使用Adamson碼作為另一個Snapper類的編碼基礎,稱之為QTSnapper2.它可以無遺漏的返回幀,與QTSnapper的方法一樣,但是使用PICT-to-Image轉化.在一部小動畫中,QTSnapper2與QTSnapper的表現沒有區別,但對于比較大的動畫例如例1,它的平局幀頻大概為9FPS,對比與QTSnapper的16FPS.換句話說,PICT-based轉化慢于Johnson技術--------------------------------------------------------------------------------------Andrew Davison 是個教育者,研究員,及作家。以前曾在墨爾本大學的計算機科學部門工作,現居住在泰國,并在Prince of Songkla大學任教。 Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd JAVA 3D世界的動畫展示(Part2,使用QuickTime for Java)作者: Andrew Davison 翻譯 Caesh版權聲明:可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲
標簽: Java
相關文章:
主站蜘蛛池模板: 婷婷综合色 | 久久青青国产 | 色老99久久九九爱精品69堂 | 国产大量女主播精品视频 | 99久久综合狠狠综合久久一区 | 国产日产高清欧美一区二区三区 | 黄色免费看片网站 | 欧美日韩另类在线观看视频 | 欧美日韩中文字幕在线 | 亚洲影视一区 | 成年做羞羞免费观看视频网站 | 蜜桃视频一区二区在线观看 | 看草逼| 青草免费免费观看视频在线 | 91网在线观看 | 久久精品99成人中文字幕880 | 真人女人一级毛片免费视频观看 | 亚洲午夜久久久精品影院视色 | 久久精品这里 | 91久久夜色精品国产网站 | 欧美伦禁片在线播放 | 国产亚洲欧美日韩在线观看一区二区 | 亚洲午夜电影一区二区三区 | 中文字幕在线影院 | 天天色天天综合 | 亚洲欧美一区二区久久香蕉 | 黄色片免费网址 | 曰本黄色录像 | 久久这里只有精品国产 | 久久精品亚洲精品一区 | 日韩精品一区二区三区 在线观看 | 狠狠ri| a级黄色免费看 | 免费国产一级特黄aa大片在线 | 国产美女精品视频免费观看 | 欧洲精品视频在线观看 | 老湿机一区午夜精品免费福利 | 国产精品果冻传媒在线 | 日韩欧美不卡一区二区三区 | 国内偷自视频区视频综合 | 国内精品久久久久 |