zallery/src/zall/ZalleryTranscoder.java
2012-10-31 19:34:46 +00:00

364 lines
11 KiB
Java

package zall;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.MediaToolAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.AudioSamplesEvent;
import com.xuggle.mediatool.event.IAddStreamEvent;
import com.xuggle.mediatool.event.IAudioSamplesEvent;
import com.xuggle.mediatool.event.IVideoPictureEvent;
import com.xuggle.mediatool.event.VideoPictureEvent;
import com.xuggle.xuggler.IAudioResampler;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;
import zall.bean.Media.Size;
import zall.bean.Video;
import zutil.StringUtil;
import zutil.db.DBConnection;
import zutil.log.LogUtil;
public class ZalleryTranscoder extends HttpServlet{
private static final Logger logger = LogUtil.getLogger();
private static final long serialVersionUID = 1L;
// Media Queue
private static Queue<Video> transcodingQueue;
private static TranscoderThread worker;
public void init( ServletConfig config ){
try{
transcodingQueue = new LinkedList<Video>();
worker = new TranscoderThread();
worker.start();
// get untranscoded videos
DBConnection db = null;
try {
db = Zallery.getDB();
List<Video> incomplete = Video.loadUntransoded( db );
synchronized (transcodingQueue) {
transcodingQueue.addAll( incomplete );
transcodingQueue.notify();
}
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}finally{
if( db != null ) db.close();
}
}
catch(Exception e){
logger.log(Level.SEVERE, "Unable to initialize ZalleryTranscoder!", e);
}
}
public void destroy( ){
worker.abort();
}
public static Video getProcessingVideo(){
return worker.currentVideo;
}
public static Queue<Video> getQueue(){
return transcodingQueue;
}
/**
* Add a video to the transcoding queue
*
* @param video is the video to transcode
*/
public static void addVideo(Video video) {
if( transcodingQueue == null ){
logger.severe("ZalleryTranscoder not initialized!");
return;
}
if( !transcodingQueue.contains(video) && worker.currentVideo != video ){
synchronized (transcodingQueue){
transcodingQueue.add( video );
transcodingQueue.notify();
}
}
}
/**
* Is the class that is doing the actual work of transcoding videos
*
* @author Ziver
*/
private static class TranscoderThread extends Thread{
private boolean stop;
protected long startTime;
protected Video currentVideo;
public void run(){
logger.info("ZalleryTranscoder thread started.");
try {
while( true ){
// Get video to transcode
while( !transcodingQueue.isEmpty() ){
currentVideo = transcodingQueue.poll();
startTime = System.currentTimeMillis();
logger.info("Starting transcoding video(id:"+currentVideo.getId()+")");
File originalFile = currentVideo.getFile( Size.ORIGINAL );
File mediumFile = currentVideo.getFile( Size.MEDIUM );
File smallFile = currentVideo.getFile( Size.SMALL );
///////////// Start Transcoding
// create a media reader
IMediaReader reader = ToolFactory.makeReader( originalFile.getPath() );
reader.addListener(new FrameGrabListener(reader, smallFile, 0.2));
reader.addListener(new ProgressListener(reader));
VideoTranscoderListener converter = new VideoTranscoderListener(640, 360);
reader.addListener(converter);
// create a media writer
IMediaWriter writer = ToolFactory.makeWriter(mediumFile.getPath(), reader);
converter.addListener(writer);
// create a media viewer with stats enabled for debugging
// add a viewer to the reader, to see the decoded media
//IMediaViewer mediaViewer = ToolFactory.makeViewer(true);
//reader.addListener(mediaViewer);
//writer.addListener(mediaViewer);
// read and decode packets from the source file and
// and dispatch decoded audio and video to the writer
while (reader.readPacket() == null);
// Incomplete transcoding
if( reader.isOpen() ){
logger.severe("Transcoding incomplete, removing incomplete files!");
reader.close();
mediumFile.delete();
smallFile.delete();
if( stop )
return;
}
else{
logger.info("Done transcoding video(id:"+currentVideo.getId()+") time: "+StringUtil.formatTimeToString(System.currentTimeMillis()-startTime));
currentVideo.setTranscoded(true);
try{
DBConnection db = Zallery.getDB();
currentVideo.save(db);
db.close();
}catch(Exception e){
logger.log(Level.SEVERE, "Unable to save video bean!", e);
}
}
currentVideo = null;
}
// Wait for new video to transcode
synchronized (transcodingQueue) {
transcodingQueue.wait();
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Transcoding thread has crashed!", e);
}
}
public void abort(){
stop = true;
}
}
}
class MyVideoListener extends MediaToolAdapter {
private Integer width;
private Integer height;
public MyVideoListener(Integer aWidth, Integer aHeight) {
this.width = aWidth;
this.height = aHeight;
}
@Override
public void onAddStream(IAddStreamEvent event) {
int streamIndex = event.getStreamIndex();
IStreamCoder streamCoder = event.getSource().getContainer().getStream(streamIndex).getStreamCoder();
if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
} else if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
streamCoder.setWidth(width);
streamCoder.setHeight(height);
}
super.onAddStream(event);
}
}
class VideoTranscoderListener extends MediaToolAdapter{
private int width;
private int height;
public VideoTranscoderListener(int width, int height) {
this.width = width;
this.height = height;
}
private IVideoResampler videoResampler = null;
private IAudioResampler audioResampler = null;
@Override
public void onAddStream(IAddStreamEvent event) {
int streamIndex = event.getStreamIndex();
IStreamCoder streamCoder = event.getSource().getContainer().getStream(streamIndex).getStreamCoder();
if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
streamCoder.setSampleRate( 44100 );
//streamCoder.setCodec( ICodec.ID.CODEC_ID_AAC );
//streamCoder.setBitRate( 128 );
}
else if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
streamCoder.setWidth( width );
streamCoder.setHeight( height );
//streamCoder.setCodec(ICodec.findEncodingCodec( ICodec.ID.CODEC_ID_H264 ));
//streamCoder.setBitRate( 800000 );
/*
int retval = Configuration.configure("/usr/local/xuggler/share/ffmpeg/libx264-superfast.ffpreset", streamCoder);
if (retval<0)
throw new RuntimeException("cound not cofigure coder from preset file");
*/
}
super.onAddStream(event);
}
@Override
public void onVideoPicture(IVideoPictureEvent event) {
IVideoPicture pic = event.getPicture();
if (videoResampler == null) {
videoResampler = IVideoResampler.make(width, height, pic.getPixelType(), pic.getWidth(), pic.getHeight(), pic.getPixelType());
}
IVideoPicture out = IVideoPicture.make(pic.getPixelType(), width, height);
videoResampler.resample(out, pic);
IVideoPictureEvent asc = new VideoPictureEvent(event.getSource(), out, event.getStreamIndex());
super.onVideoPicture(asc);
out.delete();
}
@Override
public void onAudioSamples(IAudioSamplesEvent event) {
IAudioSamples samples = event.getAudioSamples();
if (audioResampler == null) {
audioResampler = IAudioResampler.make(2, samples.getChannels(), 44100, samples.getSampleRate());
}
if (event.getAudioSamples().getNumSamples() > 0) {
IAudioSamples out = IAudioSamples.make(samples.getNumSamples(), samples.getChannels());
audioResampler.resample(out, samples, samples.getNumSamples());
AudioSamplesEvent asc = new AudioSamplesEvent(event.getSource(), out, event.getStreamIndex());
super.onAudioSamples(asc);
out.delete();
}
}
}
class ProgressListener extends MediaToolAdapter {
private long currentLength;
private long totalLength;
private int progress = -1;
public ProgressListener(IMediaReader reader) {
if( !reader.isOpen() )
reader.open(); // read container
this.totalLength = reader.getContainer().getDuration();
}
@Override
public void onVideoPicture(IVideoPictureEvent event) {
currentLength = event.getTimeStamp();
if( (int)(100*getProgress()) != progress ){
progress = (int)(100*getProgress());
System.out.print("\n"+(int)(100*getProgress())+"% ");
}
else System.out.print(".");
}
public long getProcessedLength(){
return currentLength;
}
public long getTotalLength(){
return totalLength;
}
public double getProgress(){
if(totalLength > 0)
return ((double)currentLength/totalLength);
return 0;
}
}
class FrameGrabListener extends MediaToolAdapter {
private long totalLength;
private long grabAt;
private File outputFile;
public FrameGrabListener(IMediaReader reader, File outputFile, double at) {
if( !reader.isOpen() )
reader.open(); // read container
this.totalLength = reader.getContainer().getDuration();
this.outputFile = outputFile;
setAtProgress(at);
}
@Override
public void onVideoPicture(IVideoPictureEvent event) {
try{
long currentLength = event.getTimeStamp();
if( grabAt > 0 && currentLength > grabAt ){
System.out.println("\nScreanshoot!!!");
ConverterFactory.Type mConverterType = ConverterFactory.findRegisteredConverter(
ConverterFactory.XUGGLER_BGR_24);
IConverter mVideoConverter = ConverterFactory.createConverter(
mConverterType.getDescriptor(),
event.getPicture());
ImageIO.write(
mVideoConverter.toImage(event.getPicture()),
"png",
outputFile);
grabAt = -1;
}
}catch(IOException e){
e.printStackTrace();
}
}
public void setAtProgress(double procent){
grabAt = (long)(totalLength * procent);
}
}