手头做一个视频相关项目,但是客户发来的测试视频(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;
}
}