熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

技術交流:QuickTime流媒體和Java(圖)

2013-11-23 19:13:51  來源: Java核心技術 

  這並不是即將問世的QuickTime for Java book一書的摘錄雖然我希望是的
  
  你看問題是在QTJ世界中大多數的我們都一直假定QTJ中的流媒體API已經損壞我並不是想為此事而掩蓋什麼好的我繼續來通過各種各樣的人通過不同的技術進行工作的掩蓋獲取去這樣做但是我不想再來一次此外流媒體沖突的情況似乎特別的糟糕沒有人能得到它的演示代碼工作方式――this post to the quicktimejava list 是令許多用戶對獲取蘋果公司的AudioBroadcaster 和DrawableBroadcaster 演示工作方式絕望的典型讓事情更糟糕演示之一依靠一個在QTJ 版本中作為退回到原始的GUI提供的已經被取消的GUI預覽組件類提供組件只對Movies MovieControllers 和GraphicsImporters 而不是流式的Presentations 視頻捕捉或者某些圖形美好得像來自多種資源合成制作所以官方給出的演示它首先看起來是不會工作和現在的關鍵類有沖突(如果在Java 中運行會拋出RuntimeExceptions異常)預測實際的流式內容和QTJ 看上去會非常糟糕
  
  令我欣喜甚至是有點吃驚的是有報道稱流媒體能夠在QTJ 中工作在本文章中我將介紹通過QTJ實現簡單的網絡廣播的基礎
  
  需求
  
  QuickTime的流媒體API在Java中由只可在Mac OS(Classic和OS X)中運行的包quicktimestreaming聲明在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中流媒體傳輸等同一個電影――一個電影可以有音軌和視軌一個元數據的聚集將它們全都聯系在一起此表示會將一些多種的音頻和視頻流的元數據聯系起來音頻和視頻你非常喜歡的流媒體種類也是值得關注的自從某些其他的媒體類型(spritesFlash內容)被QuickTime支持後並沒有操作好失去的包並不適合作為流媒體傳播
  
  你可能被建議去建立一個流媒體你會需要創建一個Presentation 並開始它但是現在呢?最普遍的這樣做的方法是創建一個會話描述協議(SDP)文件將其放入靜態工廠方法PresentationfromFile()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: xqt
  m=video RTP/AVP
  a=rtpmap: xqt
  
  這裡最大的不同就是音頻和視頻都使用了相同的動態負載映射這並不是針對一個真正的編碼器而是一般的xqt在這裡勝利的是你能在運行時間上挑選任一QuickTime的音頻和視頻編碼器而不是在SDP文件中強迫導致底側是這些可以不是由非QuickTime客戶端可分析的反之使用十分標准的和/或者舊的編碼器並且在SDP中指定他們使它更像其他的客戶端(Real JMF等)能夠操作你的系統
  
  這就是你的SDP文件現在不要加入一個Presentation
  
  創建Presentation
  
  我們的流媒體服務器程序調用LittleBroadcast這並沒有多少代碼只有不過在本文中我將一步一步的進行解釋一般的部分但提供其全部的清單在後面的Resources章節中有可用到一個targz文件連同SDP文件和一個Ant構建文件
  
  package commacinvalidnameqtjstreaming;import quicktime*;
  import quicktimestd*;
  import quicktimeutil*;
  import quicktimeqd*;
  import quicktimeio*;
  import quicktimestreaming*;
  import quicktimeapptime*;
  import javaio*;import javaawt*;
  import javaawtevent*;
  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) {
  Systemoutprintln (main);
  try {
  QTSessionopen();
  new LittleBroadcast();
  } catch (QTException qte) {
  qteprintStackTrace();
  }
  }
  
  在這個main中並沒有什麼獨特的地方我投入了所有的精力在構造函數上以防止為那些我需要的實例創建一個內部類如果你擴展本代碼你可能會發現這很有用
  
  public LittleBroadcast() throws QTException {
  Systemoutprintln (LittleBroadcast constructor);
  QTFile file = new QTFile (new File (littlesdp));
  try {
  MediaParams mediaParams = new MediaParams();
  mediaParamssetWidth (BROADCAST_WIDTH);
  mediaParamssetHeight (BROADCAST_HEIGHT);
  QDGraphics myGWorld =
  new QDGraphics (new QDRect (
  BROADCAST_WIDTH BROADCAST_HEIGHT));
  mediaParamssetGWorld (myGWorld);
  PresParams presParams =
  new PresParams( presenterTimeScale
  QTSConstantskQTSSendMediaFlag |
  QTSConstantskQTSAutoModeFlag |
  QTSConstantskQTSDontShowStatusFlag
  mediaParams );
  pres = PresentationfromFile(file presParams );
  
  構造函數的第一事是裝載名為littlesdp的SDP文件 但這並不是所有的都需要創建Presentation ――在調用PresentationfromFile()的時候需要服務器應用程序設置一些必要的參數首先你要創建一個MediaParams對象這樣你能設置視頻的高度和寬度您必須做的其它重要事是提供照相機一個圖形界面由QDGraphics創建MediaParams設置是的 名字是古怪的 因為QTJ 設計員想注重與AWT Graphics對象的相似性 但得到或設置這樣的對象的用途的所有方法是使用其本地API名字 GWorld 最後 你為所有的Presentation創建一個PresParams來設置參數 這采取一個有些任意的時標 一些算術上的行為標記彼此ORed 以及MediaParams 可能的行為標記 都被定義在QTSConstants 包括:
  
  ·KQTSAutoModeFlag: 都使用默認值 最重要地 這些使用默認值Sourcer Presentation的來源是從各種各樣的輸入裝置執行獲取的SequenceGrabber 它還可能播放一個在磁盤上或是任意目錄下的的QuickTime 文件; 稍後我將探討這些問題
  
  ·KQTDontShowStatusFlag: 不要創建一個會導致連接數和狀態信息總被顯示在客戶端的流媒體狀態處理程序
  
  ·KQTSSendMediaFlag:發送不接收數據
  
  ·KQTSReceiveMediaFlag接收不發送數據
  
  在SDP文件說明 參數 以及GWorld 設置下 創建Presentation和PresentationfromFile()
  
  // find audio stream      Stream audioStream = null;
  for (int i=; i<=presgetNumStreams(); i++) {
  Systemoutprintln (stream: + i + : +
  presgetIndStream(i));
  Stream aStream = presgetIndStream (i);
  if (preshasCharacteristic(aStream
  StdQTConstantsaudioMediaCharacteristic)) {
  audioStream = aStream;
  break;
  }
  }
  Systemoutprintln (audioStream = + audioStream);
  pressetVolumes (audioStream );
  Systemoutprintln (created presentation gworld == +
  presgetGWorld() + size == +
  mediaParamsgetWidth() + x +
  mediaParamsgetHeight() + streams == +
  presgetNumStreams());
  
  //*******這不是真正地必要的 但它將告訴你怎麼通過Presentation游覽來挑選各自的流媒體 PresentationgetIndStream 會由索引返回一個Stream(附注QuickTime 索引都是基於)它重復這些audioMediaCharacteristic 請求查找音頻流 (對於錄影 您就要請求visualMediaCharacteristic) 這個實例在audioStream上為左右聲道設置音量最大值為
  
  最後 println從Presentation和MediaParams轉存一些有意義的元數據
  
  配置Presentation
  
  SettingsDialog sd = new SettingsDialog (pres);
  Systemoutprintln (Did settings);
  prespreroll();
  broadcasting = false;
  
  這是設置presentation最後的步驟 SettingsDialog存在用戶以輸入裝置選擇的音頻和視頻 (二個流媒體SDP 文件被指定在Presentation中) 每個流媒體都可由一個壓縮格式來定制 (MPEG Sorenson Video H 等) 以及一個分包器(有時由壓縮格式定義; 可觀察它是否隨著壓縮格式的改變而自動改變) 這個GUI實例顯示在圖
  
 
  圖像 為一個Presentation SettingsDialog

  
  在此圖中 音頻默認為計算機連線輸入更改它為iSight 您需要點擊Source按鈕提出的可選設備列表顯示在圖
  
 
  圖 來源選擇對話框

  
  最後就是調用Presentationpreroll() 如同Moviepreroll() 提供Presentation一個機會預先分配資源以及准備好開始流媒體Presentation
  
  提供一個控制GUI
  
  // Make monitor window
  startStopButton = new Button (Start);
  configButton = new Button (Configure);
  startStopButtonaddActionListener (this);
  configButtonaddActionListener (this);
  Frame monitorFrame = new Frame (QTJ Streaming);
  monitorFramesetLayout (new BorderLayout());
  Panel buttonPanel = new Panel();
  buttonPaneladd (startStopButton);
  buttonPaneladd (configButton);
  monitorFrameadd (buttonPanel BorderLayoutSOUTH);
  monitorFramepack();
  monitorFramesetVisible(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在各通道上定義象素 您會有銀幕上的預覽何人有膽量如此做? 在quicktimejava list上查看
  
  詳細資料
  
  // add shutdown handler to make sure presentation
  // gets stopped
  Thread presentationStopper = new Thread() {
  public void run() {
  try {
  presstop();
  } catch (QTException qte) {}
  }
  };
  RuntimegetRuntime()addShutdownHook (presentationStopper);
  
  這個關閉異常分支確定Presentation在程序退出之前被終止 這是重要的原因 象SequenceGrabber Presentation愉快繼續運行在您的應用程序退出之後綁定一個端口嚴重的循環 使用您的獲取設備保留其它應用程序
  } catch ( QTException e ) {
  eprintStackTrace();
  Systemexit ();
  }
  }
  
  最後 構造函數捕捉並拋出所有的QTExceptions
  public void actionPerformed (ActionEvent ae) {
  Systemoutprintln (actionPerformed);
  try {
  if (aegetSource() == startStopButton) {
  if (broadcasting) {
  presstop();
  stopTasking();
  broadcasting = false;
  startStopButtonsetLabel (Start);
  Systemoutprintln (Stopped);
  } else {
  presstart();
  startTasking();
  broadcasting = true;
  startStopButtonsetLabel (Stop);
  Systemoutprintln (Started);
  }
  } else if (aegetSource() == configButton) {
  new SettingsDialog (pres);
  }
  } catch (QTException qte) {
  qteprintStackTrace();
  }
  }
  
  這是非常直接的處理起始/終止和設置按鈕如果點擊的按鈕是起始/終止 設置GUI 就會調用在Presentation上的 start() 或stop() 開始或停止正在執行的任務 (定期回調這個類的task()方法) 為下按鈕點擊目的設置broadcasting標志 並且更改按鈕標簽如果用戶點擊了配置 它生成為Presentation新的SettingsDialog
  
  public synchronized final void task() throws QTException {
  presidle(null);
  }}
  
  最後的這個方法實現了繼承自Tasking的task()方法並且被在操作開始按鈕調用startTasking()後定時的調用使用簡單的調用Presentationidle() 它提供了表達時間來從獲取設備取得當前數據對其編碼並傳輸流出去
  
  運行流媒體客戶端
  
  最簡單的使客戶機看廣播的方法是使用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
  • 上一篇文章:

  • 下一篇文章:
  • Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.