?? livestream.java
字號:
package com.sun.media.protocol.screen;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.media.*;
import javax.media.format.*;
import javax.media.protocol.*;
import java.io.IOException;
import java.util.StringTokenizer;
// 實時數據流類:實時采集屏幕數據
public class LiveStream implements PushBufferStream, Runnable {
protected int maxDataLength; // 圖像最大長度
protected int [] data; // 存放圖像數據的數組
protected RGBFormat rgbFormat; // 圖像采集格式
protected boolean started; // 是否開始采集的標志
protected Thread thread; // 圖像采集子線程
protected float frameRate = 1f; // 圖像采集的幀率
protected BufferTransferHandler transferHandler; // 緩沖區傳輸句柄
protected Control [] controls = new Control[0]; // 控制器數組
protected int x, y, width, height; // 采集區域的開始(左上點)坐標和尺寸
protected Robot robot = null; // 獲取屏幕數據的類對象
int seqNo = 0; // 圖像序列號
// 構造函數
public LiveStream(MediaLocator locator) {
try {
parseLocator(locator); // 解析媒體定位器
}
catch (Exception e) {
System.err.println(e);
}
try {
robot = new Robot(); // 在主屏幕坐標系下構造一個Robot對象
}
catch (AWTException awe) {
throw new RuntimeException("");
}
Dimension size = new Dimension(width, height); // 得到采集圖像的尺寸
maxDataLength = size.width * size.height * 3; // 圖像數據最大長度
rgbFormat = new RGBFormat(size, maxDataLength,Format.intArray,
frameRate,32,0xFF0000, 0xFF00, 0xFF,
1, size.width,VideoFormat.FALSE,Format.NOT_SPECIFIED);
// 構造一個RGB格式
data = new int[maxDataLength]; // 初始化數據數組
thread = new Thread(this, "Screen Grabber"); // 初始化采集線程
}
// 解析媒體定位器,得到采集屏幕圖像的參數
protected void parseLocator(MediaLocator locator) {
String rem = locator.getRemainder(); // 得到媒體定位器的字符串描述,不包括前面的協議名
while (rem.startsWith("/") && rem.length() > 1) // 去掉字符串前面的"/"
rem = rem.substring(1);
StringTokenizer st = new StringTokenizer(rem, "/"); // 用“/”作為分割符分割rem字符串
if (st.hasMoreTokens()) { // 如果rem中“/”之后有字符串
String position = st.nextToken(); // 得到該字符串
StringTokenizer nums = new StringTokenizer(position, ","); // 用“,”作為分割符分割position字符串
String stX = nums.nextToken(); // 得到采集圖像左上點的橫坐標
x = Integer.parseInt(stX);
String stY = nums.nextToken(); // 得到采集圖像左上點的縱坐標
y = Integer.parseInt(stY);
String stW = nums.nextToken(); // 得到采集圖像的寬度
width = Integer.parseInt(stW);
String stH = nums.nextToken(); // 得到采集圖像的高度
height = Integer.parseInt(stH);
}
if (st.hasMoreTokens()) { // 如果rem中“/”之后有字符串
String stFPS = st.nextToken(); // 得到該字符串
frameRate = (Double.valueOf(stFPS)).floatValue(); // 轉換為圖像采集的幀率
}
}
// 通知開始或停止采集
void start(boolean started) {
synchronized ( this ) {
this.started = started;
if (started && !thread.isAlive()) { // 如果是開始采集
thread = new Thread(this);
thread.start(); // 開始采集線程
}
notifyAll(); // 通知等待過程
}
}
/***************************************************************************
* 下面幾個函數實現了 PushBufferStream 接口
***************************************************************************/
// 得到圖像采集格式
public Format getFormat() {
return rgbFormat;
}
// 從數據源讀數據到緩沖區
public void read(Buffer buffer) throws IOException {
synchronized (this) {
Object outdata = buffer.getData();
if (outdata == null || !(outdata.getClass() == Format.intArray) ||
((int[])outdata).length < maxDataLength) {
outdata = new int[maxDataLength];
buffer.setData(outdata); // 設置緩沖區中存放數據的單元
}
buffer.setFormat( rgbFormat ); // 設置采集格式
buffer.setTimeStamp( (long) (seqNo * (1000 / frameRate) * 1000000) ); // 設置時間戳(以納秒為單位),給出了該幀圖像的表現時間
BufferedImage bi = robot.createScreenCapture(new Rectangle(x, y, width, height));
// 讀給定的屏幕區域產生一幅圖像
bi.getRGB(0, 0, width, height,(int[])outdata, 0, width); // 得到RGB圖像數據,存在緩沖區的outdata單元中
buffer.setSequenceNumber( seqNo ); // 設置圖像序列號
buffer.setLength(maxDataLength); // 設置緩沖區中的數據長度
buffer.setFlags(Buffer.FLAG_KEY_FRAME); // 設置采集的圖像為關鍵幀
buffer.setHeader( null );
seqNo++; // 圖像序列號加1
}
}
// 設置緩沖區傳輸句柄,以便數據準備好時通知緩沖區
public void setTransferHandler(BufferTransferHandler transferHandler) {
synchronized (this) {
this.transferHandler = transferHandler;
notifyAll(); // 通知等待過程
}
}
/***************************************************************************
* 下面這幾個函數實現了 SourceStream 接口,因為 public interface PushBufferStream extends SourceStream
***************************************************************************/
// 得到數據源數據類型的描述
public ContentDescriptor getContentDescriptor() {
ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW); // 內容描述
return cd;
}
// 得到數據流的長度
public long getContentLength() {
return LENGTH_UNKNOWN;
}
// 判斷數據流是否結束
public boolean endOfStream() {
return false;
}
/***************************************************************************
* 下面幾個函數實現了 Controls 接口,因為 public interface SourceStream extends Controls
***************************************************************************/
// 得到所有控制器
public Object [] getControls() {
return controls;
}
// 得到特定類型的控制器
public Object getControl(String controlType) {
try {
Class cls = Class.forName(controlType);
Object cs[] = getControls();
for (int i = 0; i < cs.length; i++) {
if (cls.isInstance(cs[i]))
return cs[i];
}
return null;
}
catch (Exception e) {
return null;
}
}
/***************************************************************************
* 下面這個函數實現了 Runnable 接口
***************************************************************************/
// 采集線程的執行體
public void run() {
while (started) { // 不斷采集數據直到通知停止
synchronized (this) {
while (transferHandler == null && started) { // 等待傳輸句柄與數據流連接好并被告知開始采集
try {
wait(1000);
}
catch (InterruptedException ie) { }
} // while
}
if (started && transferHandler != null) {
transferHandler.transferData(this); // 向緩沖區中寫數據,形成“推”數據流
try {
Thread.currentThread().sleep( 10 );
}
catch (InterruptedException ise) { }
}
} // while (started)
} // run
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -