📄 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 + -