import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Vector;
import javax.media.CaptureDeviceInfo;
import javax.media.CaptureDeviceManager;
import javax.media.DataSink;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.Player;
import javax.media.Processor;
import javax.media.ProcessorModel;
import javax.media.control.FormatControl;
import javax.media.format.VideoFormat;
import javax.media.protocol.DataSource;
import javax.media.protocol.FileTypeDescriptor;
import javax.media.protocol.SourceCloneable;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
// 这是我自己改写的一个示例,实现了简单的录制和再次录制功能!
public class RecordVideo extends JFrame {
private static final long serialVersionUID = -2570548473293856129L;
private CaptureDeviceInfo captureDevice;
private MediaLocator mediaLocator;
private Component visualComponent;
private Processor processor;
private Player player;
private DataSource source;
private DataSource cloneableSource;
private DataSource clonedDataSource;
private DataSink dataSink;
private JPanel videoPanel = new JPanel(new BorderLayout());
private JPanel controlPanel = new JPanel();
private JPanel contentPane;
private JButton btnStart;
private JButton btnStop;
private JFileChooser fileChooser = new JFileChooser();
public RecordVideo() {
this.setTitle("视频采集软件");
contentPane = (JPanel) this.getContentPane();
btnStart = new JButton("开始采集");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start();
}
});
btnStop = new JButton("停止采集");
btnStop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stop();
}
});
btnStop.setEnabled(false);
controlPanel.add(btnStart);
controlPanel.add(btnStop);
contentPane.add("South", controlPanel);
this.captureDevice = this.getCaptureDeviceInfo();
try {
mediaLocator = captureDevice.getLocator();
source = Manager.createDataSource(mediaLocator);
// 在调用 createCloneableDataSource(source) 方法以后,上句代码中的 source 引用不再能被使用。
cloneableSource = Manager.createCloneableDataSource(source);
player = Manager.createRealizedPlayer(cloneableSource);
player.start();
} catch (Exception e) {
this.processException(e);
}
visualComponent = player.getVisualComponent();
if (visualComponent != null) {
videoPanel.add(visualComponent);
}
contentPane.add("North", videoPanel);
// 打包使整个界面显得紧凑!
this.pack(); // 注释掉这句代码的话,窗口就缩成一个小布丁儿了!
this.setResizable(false);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
private void start() {
// ①第一个参数
clonedDataSource = ((SourceCloneable)cloneableSource).createClone();
// ②第二个参数
FormatControl formatControl = (FormatControl) player.getControl("javax.media.control.FormatControl");
Format defaultFormat = formatControl.getFormat();
// defaultFormat的值为: MJPG, 640x480, FrameRate=15.0, Length=921600 0 extra bytes
// ③第三个参数
FileTypeDescriptor outputFileType = new FileTypeDescriptor(FileTypeDescriptor.QUICKTIME); //.QUICKTIME
ProcessorModel processorModel = new ProcessorModel(clonedDataSource,
new Format[] { defaultFormat }, outputFileType); // DataSource 可能为 mixed, 故用数组
try {
processor = Manager.createRealizedProcessor(processorModel);
} catch (Exception e) {
this.processException(e);
}
String locatorString = this.getLocatorString(); //locatorString 表示存储录像数据的文件的路径
if (locatorString == null) {
return;
}
MediaLocator dest = new MediaLocator(locatorString);
DataSource outputDataSource = processor.getDataOutput();
try {
// 本地文件传输
dataSink = Manager.createDataSink(outputDataSource, dest);
dataSink.open();
dataSink.start();
} catch (Exception e) {
this.processException(e);
}
processor.start();
btnStart.setEnabled(false);
btnStop.setEnabled(true);
}
private void stop() {
processor.close();
processor.deallocate(); // 必须在processor 关闭并调用这个方法以后,录像文件才能保存下来
dataSink.close();
processor = null; // 清空 processor,回收无用的内存!
btnStop.setEnabled(false);
btnStart.setEnabled(true);
}
private void processException(Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, e.toString(), "错误", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
private String getLocatorString() {
if (JFileChooser.APPROVE_OPTION != fileChooser.showSaveDialog(this)) {
return null;
}
File file = fileChooser.getSelectedFile();
if (file == null) {
return null;
}
System.out.println(file); // C:/Documents and Settings/Administrator/桌面/123
String locatorString = file.getAbsolutePath();
System.out.println(locatorString); // C:/Documents and Settings/Administrator/桌面/123
if (!locatorString.endsWith(".QUICKTIME")) { // 没有后缀的话就程式化地加上 .QUICKTIME 的后缀!
locatorString += ".QUICKTIME";
}
locatorString = "file://" + locatorString;
System.out.println(locatorString); // file://C:/Documents and Settings/Administrator/桌面/123.QUICKTIME
return locatorString;
}
@SuppressWarnings("unchecked")
private CaptureDeviceInfo getCaptureDeviceInfo() {
// 以下是原来的代码,害惨我了,老是找不到我的摄像头,原来是因为我的摄像头输出不属于 RGB 格式!
// Format videoFormat = new VideoFormat(VideoFormat.RGB);
Format videoFormat = new VideoFormat(VideoFormat.MJPG); // 在改成 MJPG 后顺利找到我的视频设备
Vector<CaptureDeviceInfo> deviceList = CaptureDeviceManager.getDeviceList(videoFormat);
if (deviceList.size() < 1) {
JOptionPane.showMessageDialog(this, "未检测到视频输入设备!", "错误", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
String[] deviceNames = new String[deviceList.size()];
for (int i = 0; i < deviceList.size(); i++) {
deviceNames[i] = deviceList.get(i).getName();
}
String deviceName = (String) JOptionPane.showInputDialog(this,
"请选择视频输入设备", "请选择", JOptionPane.QUESTION_MESSAGE, null, deviceNames, deviceNames[0]);
if (deviceName == null) {
System.exit(0);
}
CaptureDeviceInfo captureDevice = null;
for (int i = 0; i < deviceList.size(); i++) {
captureDevice = deviceList.get(i);
if (deviceName.equals(captureDevice.getName())) {
return captureDevice;
}
}
return null;
}
public static void main(String[] args) {
new RecordVideo();
}
}