手头做一个视频相关项目,但是客户发来的测试视频(avi格式) 现有组件不能解码。现有

视频解码组件方案有基于JMF和opencv Jni调用。远远不能满足目前市面上玲琅满目的各种视频编码

标准。

    进行检索 找到xuggler官方主页:http://www.xuggle.com/xuggler  对5.4版本进行简单封装,实现现有组件接口。需要slf4j包支持。实现了从现有组件只能支持摄像头和特定编码AVI文件到多种编码格式视频解码的支持。

    经过测试至少支持 flv mov avi mpg wmv mp4 mkv 这些格式的视频解码。

实现代码如下:  

package edu.zjut.framecollector;

import java.awt.image.BufferedImage;
import java.io.File;

import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.swing.JFileChooser;

import com.xuggle.xuggler.Global;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.Utils;

/**
 * @author 田旭园 E-mail: tianxuyuan@yahoo.com.cn
 * @version 1.0 创建时间:2012-8-14 下午07:11:02
 * @说明 AVI视频采集 解码 可对多种格式音频视频进行解码编码 http://blog.xuggle.com/
 * @说明 JMF和opencv对各种视频编码格式的 不给力 学习AVIFrameCollector.java,进行对xuggler简单封装使用
 * @说明 不需要有安装JMF 但是需要导入xuggle-xuggler-5.4.jar 和 slf4j库
 */
public class AVIFrameCollectorOnXuggle extends FrameCollector implements
		ControllerListener {

	private int step = 1;
	IContainer container = null;
	IStreamCoder videoCoder = null;
	IPacket packet;
	long firstTimestampInStream = Global.NO_PTS;
	long systemClockStartTime = 0;
	int videoStreamId = -1;
	String filename;
	IVideoResampler resampler = null;

	@Override
	public void close() {
		if (videoCoder != null) {
			videoCoder.close();
			videoCoder = null;
		}
		if (container != null) {
			container.close();
			container = null;
		}

	}

	@Override
	public BufferedImage getCurrentFrame() {
		BufferedImage javaImage = null;

		while (container.readNextPacket(packet) >= 0) {
			/*
			 * Now we have a packet, let's see if it belongs to our video stream
			 */
			if (packet.getStreamIndex() == videoStreamId) {
				/*
				 * We allocate a new picture to get the data out of Xuggler
				 */
				IVideoPicture picture = IVideoPicture.make(videoCoder
						.getPixelType(), videoCoder.getWidth(), videoCoder
						.getHeight());

				int offset = 0;
				while (offset < packet.getSize()) {
					/*
					 * Now, we decode the video, checking for any errors.
					 */
					int bytesDecoded = videoCoder.decodeVideo(picture, packet,
							offset);
					if (bytesDecoded < 0)
						throw new RuntimeException(
								"got error decoding video in: " + filename);
					offset += bytesDecoded;

					/*
					 * Some decoders will consume data in a packet, but will not
					 * be able to construct a full video picture yet. Therefore
					 * you should always check if you got a complete picture
					 * from the decoder
					 */
					if (picture.isComplete()) {
						IVideoPicture newPic = picture;
						/*
						 * If the resampler is not null, that means we didn't
						 * get the video in BGR24 format and need to convert it
						 * into BGR24 format.
						 */
						if (resampler != null) {
							// we must resample
							newPic = IVideoPicture.make(resampler
									.getOutputPixelFormat(),
									picture.getWidth(), picture.getHeight());
							if (resampler.resample(newPic, picture) < 0)
								throw new RuntimeException(
										"could not resample video from: "
												+ filename);
						}
						if (newPic.getPixelType() != IPixelFormat.Type.BGR24)
							throw new RuntimeException("could not decode video"
									+ " as BGR 24 bit data in: " + filename);

						/**
						 * We could just display the images as quickly as we
						 * decode them, but it turns out we can decode a lot
						 * faster than you think.
						 * 
						 * So instead, the following code does a poor-man's
						 * version of trying to match up the frame-rate
						 * requested for each IVideoPicture with the system
						 * clock time on your computer.
						 * 
						 * Remember that all Xuggler IAudioSamples and
						 * IVideoPicture objects always give timestamps in
						 * Microseconds, relative to the first decoded item. If
						 * instead you used the packet timestamps, they can be
						 * in different units depending on your IContainer, and
						 * IStream and things can get hairy quickly.
						 */
						if (firstTimestampInStream == Global.NO_PTS) {
							// This is our first time through
							firstTimestampInStream = picture.getTimeStamp();
							// get the starting clock time so we can hold up
							// frames
							// until the right time.
							systemClockStartTime = System.currentTimeMillis();
						} else {
							long systemClockCurrentTime = System
									.currentTimeMillis();
							long millisecondsClockTimeSinceStartofVideo = systemClockCurrentTime
									- systemClockStartTime;
							// compute how long for this frame since the first
							// frame in the
							// stream.
							// remember that IVideoPicture and IAudioSamples
							// timestamps are
							// always in MICROSECONDS,
							// so we divide by 1000 to get milliseconds.
							long millisecondsStreamTimeSinceStartOfVideo = (picture
									.getTimeStamp() - firstTimestampInStream) / 1000;
							final long millisecondsTolerance = 50; // and we
							// give
							// ourselfs
							// 50 ms of
							// tolerance
							final long millisecondsToSleep = (millisecondsStreamTimeSinceStartOfVideo - (millisecondsClockTimeSinceStartofVideo + millisecondsTolerance));
							if (millisecondsToSleep > 0) {
								try {
									Thread.sleep(millisecondsToSleep);
								} catch (InterruptedException e) {
									// we might get this when the user closes
									// the dialog box, so
									// just return from the method.
									return null;
								}
							}
						}

						// And finally, convert the BGR24 to an Java buffered
						// image
						javaImage = Utils.videoPictureToImage(newPic);

						return javaImage;
					}
				}
			} else {
				/*
				 * This packet isn't part of our video stream, so we just
				 * silently drop it.
				 */
				do {
				} while (false);
			}

		}
		return javaImage;

	}

	@Override
	public FrameCollectorMode getMode() {
		return FrameCollectorMode.AVI_FILE;
	}

	/**
	 * <p>
	 * 使用用户定义的连接参数打开AVI视频文件。
	 * </p>
	 * 
	 * @param fileURL
	 *            AVI视频文件的地址
	 * @param strStep
	 *            指定avi文件的播放速度,即每次跳帧的步进,调用时请注意给定的字符串要可以转换为整型。 1为正常速度,2为两倍速度,....
	 * @return true,如果成功打开,否则返回false
	 * @since 1.0
	 */
	@Override
	public boolean open(String fileURL, String strStep) {

		// fileURL="file:/F:/组件和项目/图像质量诊断工程/vedio/视频文件/亮度1.avi";

		// 打开AVI视频文件
		if (fileURL == null) {
			return open();
		}
		if ((fileURL.substring(0, 6)).equals("file:/")) {
			fileURL = fileURL.substring(6);
			// System.out.println("   dfsfjasjf    "+fileURL);
		}
		try {
			step = Integer.parseInt(strStep);
		} catch (NumberFormatException ex) {
			step = 1;
		}
		return setupPlayer(fileURL);
	}

	/**
	 * <p>
	 * 通过打开对话框打开指定AVI视频文件。
	 * </p>
	 * 
	 * @return true,如果成功打开,否则返回false
	 * @since 1.0
	 */
	@Override
	public boolean open() {
		// 从文件对话框中选择AVI文件

		JFileChooser chooser = new JFileChooser();
		chooser.setAcceptAllFileFilterUsed(false);

		int result = chooser.showOpenDialog(null);
		if (result == JFileChooser.CANCEL_OPTION) {
			return false;
		}

		File file = chooser.getSelectedFile();
		String fileURL = null;
		try {
			fileURL = file.getAbsolutePath();
			System.out.println(fileURL);
		} catch (Exception e) {
			return false;
		}

		// 打开AVI视频文件
		return setupPlayer(fileURL);
	}

	@Override
	public void controllerUpdate(ControllerEvent arg0) {
		// TODO Auto-generated method stub

	}

	/**
	 * <p>
	 * Initialize the Player object.
	 * </p>
	 * 
	 * @param fileURL
	 *            The selected file's URL
	 * @return true if set up the player successfully, false otherwise
	 */
	private boolean setupPlayer(String filename) {
		this.filename = filename;
		System.out.println(filename + "          ==============");
		// Let's make sure that we can actually convert video pixel formats.
		if (!IVideoResampler
				.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION))
			throw new RuntimeException("you must install the GPL version"
					+ " of Xuggler (with IVideoResampler support) for "
					+ "this demo to work");

		// Create a Xuggler container object
		container = IContainer.make();

		// Open up the container
		if (container.open(filename, IContainer.Type.READ, null) < 0)
			throw new IllegalArgumentException("could not open file: "
					+ filename);

		// query how many streams the call to open found
		int numStreams = container.getNumStreams();

		// and iterate through the streams to find the first video stream

		for (int i = 0; i < numStreams; i++) {
			// Find the stream object
			IStream stream = container.getStream(i);
			// Get the pre-configured decoder that can decode this stream;
			IStreamCoder coder = stream.getStreamCoder();

			if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
				videoStreamId = i;
				videoCoder = coder;
				break;
			}
		}
		if (videoStreamId == -1)
			throw new RuntimeException(
					"could not find video stream in container: " + filename);

		/*
		 * Now we have found the video stream in this file. Let's open up our
		 * decoder so it can do work.
		 */
		if (videoCoder.open() < 0)
			throw new RuntimeException(
					"could not open video decoder for container: " + filename);

		if (videoCoder.getPixelType() != IPixelFormat.Type.BGR24) {
			// if this stream is not in BGR24, we're going to need to
			// convert it. The VideoResampler does that for us.
			resampler = IVideoResampler.make(videoCoder.getWidth(), videoCoder
					.getHeight(), IPixelFormat.Type.BGR24, videoCoder
					.getWidth(), videoCoder.getHeight(), videoCoder
					.getPixelType());
			if (resampler == null)
				throw new RuntimeException("could not create color space "
						+ "resampler for: " + filename);
		}

		/*
		 * Now, we start walking through the container looking at each packet.
		 */
		packet = IPacket.make();

		return true;
	}

}