?? rtptransmit.java
字號:
package RTPTransmit;import java.io.*;import java.awt.Dimension;import java.net.InetAddress;import javax.media.*;import javax.media.protocol.*;import javax.media.protocol.DataSource;import javax.media.format.*;import javax.media.control.TrackControl;import javax.media.rtp.*;// 用RTP協(xié)議傳輸數(shù)據(jù)的類public class RTPTransmit { private MediaLocator locator; // 媒體定位,可以是一個本機文件,也可以是一個網(wǎng)絡文件或采集設備得到的數(shù)據(jù)源 private String ipAddress; // 發(fā)送目的地(接收端)的IP地址 private int portBase; // 傳輸端口號 private Processor processor = null; // 處理器 private RTPManager rtpMgrs[]; // RTP管理器 private DataSource dataOutput = null; // 輸出的數(shù)據(jù)源 // 構造函數(shù) public RTPTransmit(MediaLocator locator, String ipAddress,String pb,Format format) { this.locator = locator; this.ipAddress = ipAddress; Integer integer = Integer.valueOf(pb); if (integer != null) this.portBase = integer.intValue(); } // 開始傳輸 // 如果一切正常,就返回 null,否則返回出錯原因 public synchronized String start() { String result; result = createProcessor(); // 產生一個處理器 if (result != null) return result; result = createTransmitter(); // 產生RTP會話,將處理器輸出的數(shù)據(jù)傳給指定的IP地址的指定的端口號 if (result != null) { processor.close(); processor = null; return result; } processor.start(); // 讓處理器開始傳輸 return null; } // 為指定的媒體定位器產生一個處理器 private String createProcessor() { if (locator == null) return "Locator is null"; DataSource ds; try { ds = javax.media.Manager.createDataSource(locator); // 為定義的MediaLocator定位并實例化一個適當?shù)臄?shù)據(jù)源。 } catch (Exception e) { return "Couldn't create DataSource"; } try { processor = javax.media.Manager.createProcessor(ds); // 通過數(shù)據(jù)源來產生一個處理器 } catch (NoProcessorException npe) { return "Couldn't create processor"; } catch (IOException ioe) { return "IOException creating processor"; } boolean result = waitForState(processor, Processor.Configured); // 等待處理器配置好 if (result == false) return "Couldn't configure processor"; TrackControl [] tracks = processor.getTrackControls(); // 為媒體流中的每一個磁道得到一個控制器 if (tracks == null || tracks.length < 1) // 確保至少有一個可用的磁道 return "Couldn't find tracks in processor"; ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); processor.setContentDescriptor(cd); // 設置輸出的內容描述為RAW_RTP // 從而限定每個磁道支持的格式僅為合法的RTP格式,即它影響后面的 Track.getSupportedFormats() Format supported[]; Format chosen = null; boolean atLeastOneTrack = false; for (int i = 0; i < tracks.length; i++) { // 對每一個磁道,選擇一種RTP支持的傳輸格式 Format format = tracks[i].getFormat(); if (tracks[i].isEnabled()) { // 如果該磁道可用 supported = tracks[i].getSupportedFormats(); if (supported.length > 0) { if (supported[0] instanceof VideoFormat) { chosen = checkForVideoSizes(tracks[i].getFormat(),supported[0]); // 檢查視頻格式的尺寸,以確保正常工作 } else chosen = supported[0]; // 前面已經設置了輸出內容描述為RIP,這里支持的格式都可以與RTP配合工作 // 這里選擇第一種支持的格式 tracks[i].setFormat(chosen); System.err.println("Track " + i + " is set to transmit as:"); System.err.println(" " + chosen); atLeastOneTrack = true; } else tracks[i].setEnabled(false); } else tracks[i].setEnabled(false); } if (!atLeastOneTrack) return "Couldn't set any of the tracks to a valid RTP format"; result = waitForState(processor, Controller.Realized); // 等待處理器實現(xiàn) if (result == false) return "Couldn't realize processor"; dataOutput = processor.getDataOutput(); // 從處理器得到輸出的數(shù)據(jù)源 return null; } // 為處理器的每一個媒體磁道產生一個RTP會話 private String createTransmitter() { PushBufferDataSource pbds = (PushBufferDataSource)dataOutput; // 將數(shù)據(jù)源轉化為“Push”(推)數(shù)據(jù)源 PushBufferStream pbss[] = pbds.getStreams(); // 得到“Push”數(shù)據(jù)流 rtpMgrs = new RTPManager[pbss.length]; // 為每個磁道產生一個RTP會話管理器 for (int i = 0; i < pbss.length; i++) { try { rtpMgrs[i] = RTPManager.newInstance(); int port = portBase + 2 * i; // 每增加一個磁道,端口號加2 InetAddress ipAddr = InetAddress.getByName(ipAddress); // 得到發(fā)送目的地的IP地址 SessionAddress localAddr = new SessionAddress( InetAddress.getLocalHost(),port); // 得到本機的會話地址 // 這里傳輸端使用和接收目的端相同的端口號(實際上也可以不同) SessionAddress destAddr = new SessionAddress( ipAddr, port); // 得到目的機器(接收端)的會話地址 rtpMgrs[i].initialize( localAddr); // 將本機會話地址傳給RTP管理器 rtpMgrs[i].addTarget( destAddr); // 加入目的會話地址 System.err.println( "Created RTP session: " + ipAddress + " " + port); SendStream sendStream = rtpMgrs[i].createSendStream(dataOutput, i); // 產生數(shù)據(jù)源的RTP傳輸流 sendStream.start(); // 開始RTP數(shù)據(jù)流發(fā)送 } catch (Exception e) { return e.getMessage(); } } return null; } // 由于JPEG和H.263編碼標準,只支持一些特定的圖像大小,所以這里進行必要的檢查,以確保其可以正確編碼 Format checkForVideoSizes(Format original, Format supported) { int width, height; Dimension size = ((VideoFormat)original).getSize(); // 得到視頻圖像的尺寸 Format jpegFmt = new Format(VideoFormat.JPEG_RTP); Format h263Fmt = new Format(VideoFormat.H263_RTP); if (supported.matches(jpegFmt)) { // 對JPEG格式,視頻圖像的寬和高必須是8像素的整數(shù)倍 width = size.width % 8 == 0 ? size.width : ((int)(size.width / 8) * 8); height = size.height % 8 == 0 ? size.height : ((int)(size.height / 8) * 8); } else if (supported.matches(h263Fmt)) { // H.263格式僅支持三種特定的圖像尺寸 if (size.width <= 128) { width = 128; height = 96; } else if (size.width <= 176) { width = 176; height = 144; } else { width = 352; height = 288; } } else { // 對其他格式不予處理 return supported; } return (new VideoFormat(null,new Dimension(width, height),Format.NOT_SPECIFIED, null,Format.NOT_SPECIFIED)).intersects(supported); // 返回經過處理后的視頻格式 } // 停止傳輸 public void stop() { synchronized (this) { if (processor != null) { processor.stop(); processor.close(); // 停止處理器 processor = null; // 關閉處理器 for (int i = 0; i < rtpMgrs.length; i++) { // 刪除所有RTP管理器 rtpMgrs[i].removeTargets( "Session ended."); rtpMgrs[i].dispose(); } } } } // 以下兩個變量為對處理器狀態(tài)改變的處理服務 private Integer stateLock = new Integer(0); // 狀態(tài)鎖變量 private boolean failed = false; // 是否失敗的狀態(tài)標志 // 得到狀態(tài)鎖 Integer getStateLock() { return stateLock; } // 設置失敗標志 void setFailed() { failed = true; } // 等待處理器達到相應的狀態(tài) private synchronized boolean waitForState(Processor p, int state) { p.addControllerListener(new StateListener()); // 為處理器加上狀態(tài)監(jiān)聽 failed = false; if (state == Processor.Configured) { // 配置處理器 p.configure(); } else if (state == Processor.Realized) { // 實現(xiàn)處理器 p.realize(); } // 一直等待,直到成功達到所需狀態(tài),或失敗 while (p.getState() < state && !failed) { synchronized (getStateLock()) { try { getStateLock().wait(); } catch (InterruptedException ie) { return false; } } } if (failed) return false; else return true; } // 內部類:處理器的狀態(tài)監(jiān)聽器 class StateListener implements ControllerListener { public void controllerUpdate(ControllerEvent ce) { // 如果在處理器配置或實現(xiàn)過程中出現(xiàn)錯誤,它將關閉 if (ce instanceof ControllerClosedEvent) // 控制器關閉 setFailed(); // 對于所有的控制器事件,通知在waitForState方法中等待的線程 if (ce instanceof ControllerEvent) { synchronized (getStateLock()) { getStateLock().notifyAll(); } } } }}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -