视频拍照与播放——JMF多媒体库

来源:互联网 发布:淘宝详情psd模板 编辑:程序博客网 时间:2024/06/06 08:42

视频拍照与播放——JMF多媒体库

要进行视频的捕捉与播放,需要使用JMF。JMF实际上是Java的一个类包,它提供了先进的媒体处理能力,扩展了Java平台的功能。这些功能包括:媒体捕获、压缩、流转、回放,以及对各种主要媒体形式和编码的支持,如M-JPEG、H.263、MP3、RTP/RTSP (实时传送协议和实时流转协议)、Macromedias Flash、IBM的HotMedia和Beatniks的Rich Media Format(RMF)等。JMF还支持广受欢迎的媒体类型,如Quicktime、Microsofts AVI和MPEG-1等。此外,JMF中包括了一个开放的媒体架构,可使开发人员灵活采用各种媒体回放、捕获组件,或采用他们自己的定制的内插组件。

JMF的推出已经有几年的历史了,目前最新的版本为2.1.1e,在使用它开发之前需要下载JMF的安装程序。

Ø         官方网站:http://java.sun.com/javase/technologies/desktop/media/jmf/index.jsp。

Ø         下载页面:http://java.sun.com/javase/technologies/desktop/media/jmf/2.1.1/download.html。

Ø         下载地址:http://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/ USD/VerifyItem-Start/jmf-2_1_1e-windows-i586.exe?BundledLineItemUUID=McRIBe.mExYAAAEd6.IHtp.m&OrderID=X7RIBe.m1HIAAAEd1eIHtp.m&ProductID=X4zACUFBcakAAAEYzNI5AXis&FileName=/jmf-2_1_1e-windows-i586.exe。

Ø         下载文件:jmf-2_1_1e-windows-i586.exe。

安装该文件后,会在C:/Program Files/JMF2.1.1e/lib目录下生成几个JAR文件,包括customizer.jar、jmf.jar、mediaplayer.jar、multiplayer.jar、sound.jar,这些都是进行JMF开发所必须的包。首先我们将这些包添加到当前项目的classpath环境中。

接下来我们将首先讲解JMF的模型、Player创建方法、截取多媒体数据的过程。最后通过以下两个实例程序演练JMF。

Ø         视频拍照程序。

Ø         视频播放器。

17.4.1  JMF模型

在开发JMF 应用程序之前要完全理解JMF 的体系架构、接口和类。JMF 提供的模型可大致分为6类。

(1)数据源(DataSource)

在JMF中,DataSource对象就是数据源,它可以是一个多媒体文件,也可以是从互联网上下载的数据流。对于DataSource对象,一旦你确定了它的位置和类型,在对象中就包含了多媒体的位置信息和能够播放该多媒体的软件信息。当创建了DataSource对象后,可以将它送入Player对象中,而Player对象不需要关心DataSource中的多媒体是如何获得的,以及格式是什么。在某些情况下,你需要将多个数据源合并成一个数据源。例如当你在制作一段录像时,你需要将音频数据源和视频数据源合并在一起。

(2)截取设备(Capture Device,包括视频和音频截取设备)

截取设备指的是可以截取到音频或视频数据的硬件,如麦克风、摄像机等。截取到的数据可以被送入Player对象中进行处理。

(3)播放器(Player)

在JMF中对应播放器的接口是Player。Player对象将音频/视频数据流作为输入,然后将数据流输出到音箱或屏幕上,就像CD播放机读取CD唱片中的歌曲,然后将信号送到音箱上一样。

Player对象有多种状态,JMF中定义了JMF的6种状态,在正常情况下Player对象需要经历每个状态,然后才能播放多媒体。

Ø         Unrealized:在这种状态下,Player对象已经被实例化,但是并不知道它需要播放的多媒体的任何信息。

Ø         Realizing:当调用realize()方法时,Player对象的状态从Unrealized转变为Realizing。在这种状态下,Player对象正在确定它需要占用哪些资源。

Ø         Realized:在这种状态下Player对象已经确定了它需要哪些资源,并且也知道需要播放的多媒体的类型。

Ø         Prefetching:当调用prefectch()方法时,Player对象的状态从Realized变为Prefetching。在该状态下的Player对象正在为播放多媒体做一些准备工作,其中包括加载多媒体数据、获得需要独占的资源等,这个过程被称为预取(Prefetch)。

Ø         Prefetched:当Player对象完成了预取操作后就到达了该状态。

Ø         Started:当调用start()方法后,Player对象就进入了该状态并播放多媒体。

(4)处理器(Processor)

处理器对应的接口是Processor,它是一种播放器。在JMF API中,Processor接口继承了Player接口。Processor对象除了支持Player对象支持的所有功能,还可以控制对于输入的多媒体数据流进行某种处理,以及通过数据源向其他的Player对象或Processor对象输出数据。

除了在播放器中提到了6种状态外,Processor 对象还包括两种新的状态,这两种状态是在Unrealized状态之后,但是在Realizing状态之前。

Ø         Configuring:当调用configure()方法后,Processor对象进入该状态。在该状态下,Processor对象连接到数据源并获取输入数据的格式信息。

Ø         Configured:当完成数据源连接,获得输入数据格式的信息后,Processor对象就处于Configured状态。

(5)数据格式(Format)

Format对象中保存了多媒体的格式信息。该对象中本身没有记录多媒体编码的相关信息,但是它保存了编码的名称。Format的子类包括AudioFormat和VideoFormat类,ViedeoFomat又有6个子类:H261Format、H263Format、IndexedColorFormat、JPEGFormat、RGBFormat和YUVFormat类。

(6)管理器(Manager)

JMF提供了下面4种管理器。

Ø         Manager:Manager相当于两个类之间的接口。例如当你需要播放一个DataSource对象,你可以通过使用Manager对象创建一个Player对象来播放它。使用Manager对象可以创建Player、Processor、DataSource和DataSink对象。

Ø         PackageManager:该管理器中保存了JMF类注册信息。

Ø         CaptureDeviceManager:该管理器中保存了截取设备的注册信息。

Ø         PlugInManager:该管理器中保存了JMF插件的注册信息。

17.4.2  创建Player对象

在JMF编程中,最常见的工作就是创建一个Player对象。你可以通过Manager类的静态方法createPlayer()来创建Player对象。Manager对象使用多媒体的URL或MediaLocator对象来创建Player对象。例如下面的两个实例所示:

player = Manager.createRealizedPlayer(mediaLocator);

player = Manager.createPlayer(url);

当你获得了一个Player对象后,你可以通过调用getVisualComponent()方法得到Player对象的图像部件(Visual Component,在图像部件上可以播放多媒体的图像)。然后将图像部件加入到应用程序或Applet的界面上。Player对象还包含一个控制面板,在上面可以控制媒体的播放、停止和暂停等。

Player类中的很多方法只有在Player对象处于Realized的状态下才会被调用。为了保证Player对象已经到达了该状态,你需要使用Manager的createRealizePlayer()方法来获得Player对象。但是对于start()方法来说,你可以在Player对象到达Prefetched状态之前调用它,它可以自动将Player的状态转换到Started状态。

17.4.3  截取多媒体数据

多媒体数据的截取是JMF程序中另一个非常重要的功能。你可以按照下面的步骤截取数据。

Ø         通过查询CaptureDeviceManager获得你希望使用的截取设备,例如:

CaptureDeviceManager.getDevice("vfw:Microsoft WDM Image Capture (Win32):0");

Ø         获得设备对应的CaptureDeviceInfo对象。

Ø         从CaptureDeviceInfo对象中获得MediaLocator对象,例如:

mediaLocator = new MediaLocator("vfw://0");

Ø         使用MediaLocator对象创建Player对象或Processor对象,例如:

player = Manager.createRealizedPlayer(mediaLocator);

Ø         调用start()方法,开始截取多媒体数据:

player.start();

你可以使用CaptureDeviceManager对象获得系统中可用的视频和音频截取设备。通过调用getDeviceList()方法你可以获得设备的列表。每个设备都对应一个CaptureDeviceInfo对象。也可以通过调用CaptureDeviceManager对象的getDevice()方法来获得特定的CaptureDeviceInfo对象。

在使用设备截取多媒体数据前,还需要从CaptureDeviceInfo对象中获得设备对应的MediaLocator对象。然后你可以直接使用MediaLocator来构造Player或Processor的实例,也可以用MediaLocator构造一个DataSource对象,然后将DataSource对象送入Player或Processor对象中。最后调用start()方法来截取多媒体数据。

17.4.4  实例一:视频拍照程序

JMF的应用涉及到很多知识,为了更加直观地了解JMF,下面通过两个最常用的实例来展示JMF的应用。

首先开发一个视频拍照程序。请先安装视频设备和驱动程序,并调试可用。该程序包括以下两个类。

Ø         视频拍照主程序VideoCamera.java。

Ø         图片显示和保存内部类ImagePanel.java。

(1)视频拍照主程序VideoCamera.java

该类继承自Frame并实现了ActionListener接口。首先它包含了创建视频捕捉的两个变量:

private MediaLocator mediaLocator = null;       //视频设备

private Player player = null;                   //播放器

然后创建一个ImagePanel对象,该类用于显示视频拍照的照片并进行文件的保存。并添加两个按钮:

private Button ok, cancel;                 //拍照和重拍按钮

在该类的构造函数中,首先需要分别创建主窗体、按钮面板、图片显示面板和视频面板,视频面板的创建需要先加载视频设备,并创建Player对象,然后从Player中取得视频组件Component,将该组件添加到主窗体中即可实现视频的显示。

然后编写一个按钮事件处理函数,如果为“ok”按钮事件,则从player对象中取得一张图片,保存在Image对象中,调用ImagePanel显示该图像并保存。如果是“cancel”按钮事件,则清空ImagePanel的图像即可。

完整的程序如程序17-11所示。

程序17-11  视频拍照主程序VideoCamera.java

package com.test.video;

import java.awt.Button;

import java.awt.Component;

import java.awt.Frame;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Image;

import java.awt.Panel;

import java.awt.event.*;

import java.awt.image.BufferedImage;

import java.io.FileOutputStream;

import javax.media.Buffer;

import javax.media.CaptureDeviceManager;

import javax.media.Manager;

import javax.media.MediaLocator;

import javax.media.Player;

import javax.media.control.FrameGrabbingControl;

import javax.media.format.VideoFormat;

import javax.media.util.BufferToImage;

import com.sun.image.codec.jpeg.JPEGCodec;

import com.sun.image.codec.jpeg.JPEGEncodeParam;

import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class VideoCamera extends Frame implements ActionListener {

    private MediaLocator mediaLocator = null;

    private Player player = null;

    private ImagePanel imagePanel = null;

    private Button ok, cancel;

    public VideoCamera() {

        //主窗口

        super("视频捕捉");

        setSize(300, 500);

        addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent e) {

                player.close();

                System.exit(0);

            }

        });

        //按钮面板

        Panel panel = new Panel();

        ok = new Button("拍 照");

        cancel = new Button("重拍");

        ok.addActionListener(this);

        cancel.addActionListener(this);

        panel.add(ok);

        panel.add(cancel);

        add(panel, "Center");

        //图片面板

        imagePanel = new ImagePanel();

        add(imagePanel, "South");

        //视频面板

        CaptureDeviceManager.getDevice("vfw:Microsoft WDM Image Capture (Win32):0");

        mediaLocator = new MediaLocator("vfw://0");

        try {

            player = Manager.createRealizedPlayer(mediaLocator);

            player.start();

            Component comp;

            if ((comp = player.getVisualComponent()) != null)

                comp.setBounds(0, 0, 240, 180);

            add(comp, "North");

        } catch (Exception e) {

            e.printStackTrace();

        }

        setVisible(true);

    }

    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == ok) {

            FrameGrabbingControl fgc = (FrameGrabbingControl) player

                    .getControl("javax.media.control.FrameGrabbingControl");

            Buffer buffer = fgc.grabFrame();

            BufferToImage bufferToImage = new BufferToImage(

                    (VideoFormat) buffer.getFormat());

            Image image = bufferToImage.createImage(buffer);

            imagePanel.setImage(image);

            saveImage(image, "D:/app/"+System.currentTimeMillis()+".jpg");

        } else if (e.getSource() == cancel) {

            imagePanel.setImage(null);

        }

    }

    public static void main(String[] args) {

        new VideoCamera();

    }

}

 
(2)图片显示与保存内部类ImagePanel.java

在主程序中拍照后,需要调用ImagePanel来显示该图片,显示的函数为paint()。并调用saveImage()来保存图片,该函数与第一节中图片保存的过程类似,如程序17-12所示。

程序17-12  图片显示与保存内部类ImagePanel.java

class ImagePanel extends Panel {

    private static final long serialVersionUID = 1L;

    public Image myimg = null;

    public ImagePanel() {

        setLayout(null);

        setSize(240, 180);

    }

    public void setImage(Image img) {

        this.myimg = img;

        repaint();

    }

    //显示图片

    public void paint(Graphics g) {

        if (myimg != null) {

            g.drawImage(myimg, 0, 0, this);

        }

    }

   

    //保存图片

    public void saveImage(Image image, String path) {

        //图片缓存

        int width = image.getWidth(null);

        int height = image.getHeight(null);

        BufferedImage bi = new BufferedImage(width, height,

                BufferedImage.TYPE_INT_RGB);

        Graphics2D g2 = bi.createGraphics();

        g2.drawImage(image, null, null);

        //写入文件

        try {

            //打开文件

            FileOutputStream fos = new FileOutputStream(path);

            //JPG编码

            JPEGImageEncoder je = JPEGCodec.createJPEGEncoder(fos);

            JPEGEncodeParam jp = je.getDefaultJPEGEncodeParam(bi);

            jp.setQuality(1f, false);

            je.setJPEGEncodeParam(jp);

            je.encode(bi);

            fos.close();

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 
(3)运行视频拍照程序

完成以上代码后,即可运行主程序VideoCamera,就会显示如图17-12所示的窗口。


图17-12  视频程序主窗口

单击“拍照”按钮,即会在下方显示抓拍的图片,同时该图片会保存在D:/app目录下。

17.4.5  实例二:视频播放器

以上的视频拍照程序输入的Player来源是硬件的摄像设备,下面通过实例来演示从一个视频文件来产生Player并进行播放的过程。

类VideoPlayer继承自Frame,并实现了ControllerListener接口,该接口是JMF视频播放状态变化的事件接口。首先我们编写构造函数,来实现程序的主窗口。

然后在播放函数play()中,根据一个磁盘视频文件创建了一个Player对象,并为该对象分配资源,调用的是realize()方法;该事件会触发事件处理函数controllerUpdate()中的第一个分支,并进行播放预处理,一旦完成即会触发controllerUpdate()中的第二个分支,然后通过player对象取得如下的两个组件:

visual = player.getVisualComponent();                 //视频播放组件

control = player.getControlPanelComponent();          //视频进度控制组件

将这两个组件添加到主窗口中即可。完整的代码如程序17-13所示。

程序17-13  视频播放器主程序VideoPlayer.java

package com.test.video;

import java.awt.BorderLayout;

import java.awt.Component;

import java.awt.Dimension;

import java.awt.Frame;

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

import java.io.IOException;

import java.net.URL;

import javax.media.ControllerEvent;

import javax.media.ControllerListener;

import javax.media.EndOfMediaEvent;

import javax.media.Manager;

import javax.media.NoPlayerException;

import javax.media.Player;

import javax.media.PrefetchCompleteEvent;

import javax.media.RealizeCompleteEvent;

import javax.media.Time;

public class VideoPlayer extends Frame implements ControllerListener {

    private int videoWidth = 0;

    private int videoHeight = 0;

    private int controlHeight = 30;

    private int insetWidth = 10;

    private int insetHeight = 30;

    private Player player;

    private Component visual;               //播放组件

    private Component control = null;       //控制组件

    public VideoPlayer() {

        super("视频播放器");

        setSize(500, 400);

        setVisible(true);

        addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent we) {

                if (player != null) {

                    player.close();

                }

                System.exit(0);

            }

        });

    }

    public void play() {

        URL url = null;

        try {

            //准备一个要播放的视频文件的URL

            url = new URL("file:/I:/资料备份/影视/MTV/K歌之王(国)MTV.mpg");

            //通过调用Manager的createPlayer方法来创建一个Player的对象

            //这个对象是媒体播放的核心控制对象

            player = Manager.createPlayer(url);

        } catch (NoPlayerException e1) {

            e1.printStackTrace();

        } catch (IOException e1) {

            e1.printStackTrace();

        }

        //对Player对象注册监听器,能在相关事件发生的时候执行相关的动作

        player.addControllerListener(this);

        //让Player对象进行相关的资源分配

        player.realize();

    }

    //监听Player的相关事件

    public void controllerUpdate(ControllerEvent ce) {

        if (ce instanceof RealizeCompleteEvent) {

            //Player实例化完成后进行Player播放前预处理

            player.prefetch();

        } else if (ce instanceof PrefetchCompleteEvent) {

            if (visual != null)

                return;

            //取得Player中的播放视频的组件,并得到视频窗口的大小

            //然后把视频窗口的组件添加到Frame窗口中,

            if ((visual = player.getVisualComponent()) != null) {

                Dimension size = visual.getPreferredSize();

                videoWidth = size.width;

                videoHeight = size.height;

                add(visual);

            } else {

                videoWidth = 320;

            }

            //取得Player中的视频播放控制条组件,并把该组件添加到Frame窗口中

            if ((control = player.getControlPanelComponent()) != null) {

                controlHeight = control.getPreferredSize().height;

                add(control, BorderLayout.SOUTH);

            }

            //设定Frame窗口的大小,使得满足视频文件的默认大小

            setSize(videoWidth + insetWidth, videoHeight + controlHeight

                    + insetHeight);

            validate();

            //启动视频播放组件开始播放

            player.start();

        } else if (ce instanceof EndOfMediaEvent) {

            //当播放视频完成后,把时间进度条恢复到开始,并再次重新开始播放

            player.setMediaTime(new Time(0));

            player.start();

        }

    }

    public static void main(String[] args) {

        VideoPlayer vp = new VideoPlayer();

        vp.play();

    }

}

在main()主函数中创建该类的实例vp,即会打开视频的主窗口,如图17-13所示。


图17-13  视频播放主窗口

以上我们通过两个实例演示了硬件设备的图片捕捉、电影的播放,JMF还可以进行视频的捕捉录制、视频剪辑等。JMF的功能十分强大,本文只是抛砖引玉,你可以继续深入的研究JMF,来进行丰富的多媒体功能开发。

原创粉丝点击