手头做一个视频相关项目,但是客户发来的测试视频(avi格式) 现有组件不能解码。现有
视频解码组件方案有基于JMF和opencv Jni调用。远远不能满足目前市面上玲琅满目的各种视频编码
标准。
进行检索 找到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; } /** ** 使用用户定义的连接参数打开AVI视频文件。 *
* * @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); } /** ** 通过打开对话框打开指定AVI视频文件。 *
* * @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 } /** ** Initialize the Player object. *
* * @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; }}