基于JMF录制和播放视频源码

               

仅仅播放摄像头拍摄内容(修改版)

package org.bruce.myown_product;import java.awt.Component;import java.awt.Dimension;import java.awt.GridLayout;import java.awt.Panel;import javax.media.CaptureDeviceInfo;import javax.media.CaptureDeviceManager;import javax.media.Manager;import javax.media.MediaLocator;import javax.media.Player;import javax.media.protocol.DataSource;import javax.swing.JFrame;import javax.swing.JPanel;public class PlayCapturedVideo extends JPanel { private static final long serialVersionUID = -8040938476462674297L;  // 本地摄像头的注册信息(通过JMF.exe获得) String url = "vfw:Microsoft WDM Image Capture (Win32):0"; // 获取的摄像头信息 private CaptureDeviceInfo captureDeviceInfo = null; // 从获取的摄像头信息中提取的摄像头地址 private MediaLocator mediaLocator = null; private DataSource datasource = null; public static Player player = null;  // 构造函数! public PlayCapturedVideo() {  Panel editor = new Panel();  editor.setPreferredSize(new Dimension());  editor.setLayout(null);  editor.setLayout(new GridLayout(1, 3));  // CaptureDeviceInfo: 捕获设备信息  captureDeviceInfo = CaptureDeviceManager.getDevice(url);    // MediaLocator: 媒体定位器。根据捕获设备信息(是一份设备的列表) 获取 媒体定位器  mediaLocator = captureDeviceInfo.getLocator();  try {   datasource = Manager.createDataSource(mediaLocator); // 根据媒体定位器创建一个数据源   player = Manager.createRealizedPlayer(datasource);   player.start();   // 调用 player.start() 以后就可以用那个一个可视的组件来呈现摄像头拍摄的景象了!   Component comp = null;   if ((comp = player.getVisualComponent()) != null) {    this.add(comp); // 这个 this 继承了 Applet(相当于一个顶级容器)   }  } catch (Exception b) {   b.printStackTrace();  } } public static void main(String[] args) {  JFrame jf = new JFrame("用于呈现摄像头拍摄景象");  PlayCapturedVideo pcv = new PlayCapturedVideo();  jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  jf.add(pcv);  jf.pack();  jf.setResizable(false);  jf.setLocation(200, 200);  jf.setVisible(true); }}

 

播放+录制代码(修改版)

package org.bruce.myown_product;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(); }}

 

播放+录制+照相代码(简化版)

package org.bruce.most_valuable;import java.applet.Applet;import java.awt.BorderLayout;import java.awt.Component;import java.awt.Dimension;import java.awt.FileDialog;import java.awt.Frame;import java.awt.Graphics;import java.awt.GridLayout;import java.awt.Image;import java.awt.Panel;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.image.RenderedImage;import java.io.File;import java.io.IOException;import javax.imageio.ImageIO;import javax.media.Buffer;import javax.media.CaptureDeviceInfo;import javax.media.CaptureDeviceManager;import javax.media.ConfigureCompleteEvent;import javax.media.ControllerEvent;import javax.media.ControllerListener;import javax.media.DataSink;import javax.media.Manager;import javax.media.MediaLocator;import javax.media.Player;import javax.media.Processor;import javax.media.RealizeCompleteEvent;import javax.media.control.FrameGrabbingControl;import javax.media.format.VideoFormat;import javax.media.protocol.DataSource;import javax.media.protocol.FileTypeDescriptor;import javax.media.protocol.SourceCloneable;import javax.media.util.BufferToImage;import javax.swing.JButton;import javax.swing.JComponent;import javax.swing.JFrame;import javax.swing.JOptionPane;import javax.swing.JPanel;public class WebCamSwingSimplified extends Applet implements ActionListener { private static final long serialVersionUID = -8040938476462674297L; // 本地摄像头的注册信息(通过JMF.exe获得) String url = "vfw:Microsoft WDM Image Capture (Win32):0"; // 获取的摄像头信息 private CaptureDeviceInfo captureDeviceInfo = null; // 从获取的摄像头信息中提取的摄像头地址 private MediaLocator mediaLocator = null; // 原始的数据源 private DataSource dataSource = null; // 由原始数据源转变成的,可以被克隆的数据源 private DataSource cloneableDataSource = null; // 由可以克隆的数据源 cloneableDataSource 克隆出来的 clonedDataSource private DataSource clonedDataSource = null; // 用来播放的 player public static Player player = null; // 处理录制的视频的 Processor private Processor processor = null; // 保存录制数据的数据池(datasink: 数据接收装置; 数据接收器) private DataSink dataSink = null; // StateHelper 是处理 Player 或 Processor 的同步状态转换的一个很有用的类。 private StateHelper stateHelper = null; private ImagePanel imagePanel = null; // buffer 用于截取实时图像的缓冲区 private Buffer buffer = null; // 将 buffer 转换为图像的“中间变量” private BufferToImage buffer2image = null; // 拍下来的照片赋给img private Image img = null; // 保存图片的宽度和高度 private int imgWidth = 320; private int imgHeight = 240; // 拍照按钮 private JButton capture = null; // 保存照片按钮 private JButton save = null; // 录像按钮 private JButton recordVideo = null; // 停止录像按钮 private JButton quitRecord = null; /*  * 此 WebCamSwing 函数完成的功能: 创建一个对话框。 将摄像头的物理地址转化为电脑能够识别的 medialocator(媒体定位器) ,  * 并且通过 medialocator 产生一个数据源 dataSource , 通过这个 dataSource 数据源生成 player 用来播放视频。  * 然后将数据源进行处理,产生一个能够被克隆的 cloneableDataSource,  * 再这个 cloneableDataSource 进行克隆,产生一个 clonedDataSource 数据源, 用于后面的 processor 录制视频  */ public WebCamSwingSimplified() {  Panel editor = new Panel();  editor.setPreferredSize(new Dimension());  editor.setLayout(null);  editor.setLayout(new GridLayout(1, 3));  // ImagePanel 初始化  imagePanel = new ImagePanel();  // 各 Button 的初始化  capture = new JButton("拍照");  capture.addActionListener(this);  save = new JButton("保存照片");  save.addActionListener(this);  recordVideo = new JButton("开始录像");  recordVideo.addActionListener(this);  quitRecord = new JButton("停止录像");  quitRecord.addActionListener(this);    captureDeviceInfo = CaptureDeviceManager.getDevice(url); // CaptureDeviceInfo: 捕获设备信息  // 根据捕获设备信息(是一份设备的列表) 获取 媒体定位器  mediaLocator = captureDeviceInfo.getLocator(); // MediaLocator: 媒体定位器  try {   dataSource = Manager.createDataSource(mediaLocator); // 根据媒体定位器创建一个数据源   cloneableDataSource = Manager.createCloneableDataSource(dataSource);   clonedDataSource = ((SourceCloneable)cloneableDataSource).createClone();   player = Manager.createRealizedPlayer(cloneableDataSource);   player.start();   // 调用 player.start() 以后就可以用那个一个可视的组件来呈现摄像头拍摄的景象了!   Component comp = null;   if ((comp = player.getVisualComponent()) != null) {    this.add(comp); // 这个 this 继承了 Applet(相当于一个顶级容器)   }  } catch (Exception e) {   e.printStackTrace();  }    // 将4个按钮添加到主界面上  JPanel rightJPanel = new JPanel();  rightJPanel.setLayout(new BorderLayout());  JPanel buttonJPanel = new JPanel(new GridLayout(1, 4)); // 1行4列  buttonJPanel.add(capture);  buttonJPanel.add(save);  buttonJPanel.add(recordVideo);  buttonJPanel.add(quitRecord);  rightJPanel.add(buttonJPanel, BorderLayout.NORTH);  rightJPanel.add(imagePanel, BorderLayout.SOUTH); // 这个是干什么用的?是拍照时看效果用的  this.add(rightJPanel); } /*   * 点击拍照  * ActionEvent: 指示发生了组件定义的动作的语义事件。 当特定于组件的动作(比如  * 被按下)发生时, 由组件(比如 Button)生成此高级别事件。 事件被传递给每一个 ActionListener 对象, 这些对  * 象是使用组件的 addActionListener 方法注册的,用以接收这类事件。 注:要使用键盘在 Button 上触发  * ActionEvent,请使用空格键。  */ public void actionPerformed(ActionEvent e) {  // 捕获 ActionEvent,将其转化为 JComponent 类,应该转化为是被单击的 “按钮” 的意思  // 这种写法比用 setActionCommand("capture") 要节省代码(当然是在按钮比较少的 时候)!  JComponent c = (JComponent) e.getSource();  // ① 拍照  if (c == capture) {   // 取得对 Player 的控制(反射机制的典型应用)   FrameGrabbingControl fgc = (FrameGrabbingControl)player      .getControl("javax.media.control.FrameGrabbingControl");   buffer = fgc.grabFrame();   // System.out.println(buffer.getFormat());   buffer2image = new BufferToImage((VideoFormat) buffer.getFormat());   img = buffer2image.createImage(buffer);   // 在 imagePanel 上显示拍下来的样照,由用户决定是否保存下来   imagePanel.setImage(img);  }  // 如果还没有拍照就保存的话,弹出对话框进行提醒!  else if (c == save && img == null) {   JOptionPane.showMessageDialog(null, "请先拍照再保存");  }  // ②保存照片(弹出提示窗口让用户输入保存图片的路径)  else if (c == save && img != null) {   Frame savePhotoFrame = new Frame("保存");   FileDialog savePhotoFileDialog = new FileDialog(savePhotoFrame, "保存图像文件", FileDialog.SAVE);   savePhotoFileDialog.setFile("*.GIF"); // 用于设置保存图片的格式   savePhotoFileDialog.setVisible(true);   String savepath = savePhotoFileDialog.getDirectory();   String savename = savePhotoFileDialog.getFile();   // ImageIO.write是将图片保存到上面已经写好的路径里,并且用上面的savename命名   try {    ImageIO.write((RenderedImage)img, "GIF", new File(savepath + savename));   } catch (IOException e1) {    e1.printStackTrace();   }   img = null; // 将 img 清空(回收所占的内存资源)  }  // ③ 如果单击了“开始录像”按钮,但是processor不为空,那么就弹出提示表明正在录像  else if (c == recordVideo && processor != null) {   JOptionPane.showMessageDialog(null, "正在录像!");  }  // ④ 如果单击了“开始录像”按钮,且processor为空,那么就开始录像  else if (c == recordVideo && processor == null) {   Frame saveVideoFrame = new Frame("保存视频文件");   FileDialog saveVideoFileDialog = new FileDialog(saveVideoFrame, "保存视频文件", FileDialog.SAVE);   saveVideoFileDialog.setFile("*.QUICKTIME");   saveVideoFileDialog.setVisible(true);   String videoFileSavePath = saveVideoFileDialog.getDirectory();   String videoFileSaveName = saveVideoFileDialog.getFile();   // 如果输入了保存名字(没有点取消)的话,才可运行   if (videoFileSaveName != null) {    videoFileSavePath = videoFileSavePath.replace("//", "/");    try {     // player 用 cloneableDataSource 数据源,processor 用 clonedDataSource 的数据源     processor = Manager.createProcessor(clonedDataSource);     stateHelper = new StateHelper(processor);    } catch (Exception e1) {     e1.printStackTrace();     System.exit(-1);    }    // configure the processor, 让 processor 进入 configured 状态    if (!stateHelper.configure(10000)) {     System.out.println("configure wrong!");     System.exit(-1);    }    // 设置存储录像文件的格式为 .QUICKTIME    processor.setContentDescriptor(new FileTypeDescriptor(FileTypeDescriptor.QUICKTIME));    // realize the processor,让 processor 进入 realized 状态    if (!stateHelper.realize(10000)) {     System.out.println("realize wrong!");     System.exit(-1);    }    // 取得 processor 的输出,并且启动 processor    // DataSource 被送进 processor 处理,输出地时候还是以 DataSource 的类型输出!    DataSource outSource = processor.getDataOutput();        // 替换前:file:///C:/Documents and Settings/Administrator/桌面/12312.QUICKTIME    // 替换后:file:///C:/Documents and Settings/Administrator/桌面/12312.QUICKTIME    String destinationString = "file:///" + videoFileSavePath + videoFileSaveName;        // MediaLocator 类的两个构造方法:    //  ①MediaLocator(java.lang.String locatorString)     //  ②MediaLocator(java.net.URL url)     MediaLocator destination = new MediaLocator(destinationString);    processor.start();    try {     dataSink = Manager.createDataSink(outSource, destination);     dataSink.open();     dataSink.start();    } catch (Exception e1) {     e1.printStackTrace();     System.exit(-1);    }   }  }  // 停止录像(如果还没开始录像就单击这个按钮的话,弹出提示框提示)!  else if (c == quitRecord && processor == null) {   JOptionPane.showMessageDialog(null, "请先单击录像才能停止!");  }  // ⑤停止录像  else if (c == quitRecord && processor != null) {   // 如果要是能够连续录像,关键在于两点:    // 1、重新设置 clonedDataSource ,我认为在 clonedDataSource 被调用后 ,clonedDataSource被改变了   // 2、清空 processor   processor.close();   processor.deallocate();   dataSink.close();   clonedDataSource = ((SourceCloneable) cloneableDataSource).createClone();   processor = null;  } } public class StateHelper implements ControllerListener {  Player xplayer = null;  boolean configured = false;  boolean realized = false;  // boolean prefetched = false;  // boolean eom = false;// End of media.  boolean failed = false;  boolean closed = false;  public StateHelper(Player p) {   xplayer = p;   p.addControllerListener(this);  }  public boolean configure(int timeOutMillis) {   // RealizeCompleteEvent 发生了的话使 ce 事件与之比较,若相等,那么 realized 为 true。   /*    * 监听 ConfigureCompleteEvent 和 ConfigureCompleteEvent 事件的发生。    * 如 ConfigureCompleteEvent 事件发生,那么就会赋给 configured 为 ture, 使得 public    * boolean configure 方法中的 while (!configured && !failed){} 这个循环退出。    */   long startTime = System.currentTimeMillis();   synchronized (this) {    if (xplayer instanceof Processor) {     ((Processor) xplayer).configure();    } else {     return false;    }    while (!configured && !failed) {     try {      wait(timeOutMillis);     } catch (InterruptedException ex) {      ex.printStackTrace();     }     if (System.currentTimeMillis() - startTime > timeOutMillis) {      break;     }    }   }   return configured;  }  public boolean realize(int timeOutMillis) {   long startTime = System.currentTimeMillis();   synchronized (this) {    xplayer.realize();    while (!realized && !failed) {     try {      wait(timeOutMillis);     } catch (InterruptedException ie) {      ie.printStackTrace();     }     if (System.currentTimeMillis() - startTime > timeOutMillis) {      break;     }    }   }   return realized;  }  public synchronized void controllerUpdate(ControllerEvent ce) {   if (ce instanceof RealizeCompleteEvent) {    realized = true;   } else if (ce instanceof ConfigureCompleteEvent) {    configured = true;   } else {    return;   }   notifyAll();  } } // 显而易见,ImagePanel 是用于呈现 Image 的 Panel!实践检验我错了! // 把 Panel 改成 JPanel 妈的竟然显示不出来了! class ImagePanel extends Panel {  private static final long serialVersionUID = -5371206459688057671L;  private Image ImagePanelImg = null;  public ImagePanel() {   setLayout(null);   setSize(imgWidth, imgHeight);  }  public void setImage(Image img) {   this.ImagePanelImg = img;   this.setVisible(true);   repaint();  }  public void update(Graphics graphics) {   if (ImagePanelImg != null) {    graphics.drawImage(ImagePanelImg, 0, 0, this);   }  } } public static void main(String[] args) {  JFrame jf = new JFrame("拍照程序");  WebCamSwingSimplified cf = new WebCamSwingSimplified();  jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  jf.add("Center", cf);  jf.pack();  jf.setResizable(false);  jf.setVisible(true); }}

 

           
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值