/*
Frame by frame jmf video read and write written by Timo Rantalainen.
jffmpeg decoders taken from http://jffmpeg.sourceforge.net/
h.264 encoder taken from http://sourceforge.net/projects/h264avcjavaenco
*/
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
N.B. the above text was copied from http://www.gnu.org/licenses/gpl.html
unmodified. I have not attached a copy of the GNU license to the source...
Copyright (C) 2011 Timo Rantalainen, tjrantal@gmail.com
*/
package analysis;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
import javax.media.format.*;
import javax.media.util.*;
import javax.swing.SwingUtilities; //SwingUtilities.root()
import javax.swing.JFrame;
import javax.swing.JSlider; //Slider
import javax.swing.JPanel; //GUI komennot swing
import javax.swing.DefaultBoundedRangeModel;
import java.net.URL;
import net.sourceforge.jffmpeg.*; //jffmpeg decoders
import com.sun.media.parser.video.*;
import com.sun.media.codec.video.cinepak.*;
import com.sun.media.*; //BasicSourceModule for getting a demuxer and SimpleGraphBuilder to get a codec
import mjpeg.*;
import ui.*;
import DrawImage.*;
import javax.media.Time; //For rewinding the video..
public class FrameByFrame implements ControllerListener
{
/*Private vars*/
private boolean goOn =false;
private MpngWriter writer;
private URL sourceVideo;
private String targetVideo;
private JavaVideoAnalysis mainProgram;
private Demultiplexer deMultiplexer;
private Track[] tracks;
private int videoTrack;
private Codec decoder;
private Buffer buffer;
private Buffer oBuf;
public Dimension videoSize;
/*Public vars*/
public float frameRate;
public Time duration;
public int frameCount;
public int[] frameData;
//public FrameByFrame(URL sourceVideo,File tempOutFile, JavaVideoAnalysis mainProgram){
public void close(){
decoder.close();
if (writer != null){
try{
writer.finalize_mjpeg();
}catch (Exception err){
System.out.println("Couldn't finalize");
System.exit(0);
}
writer = null;
deMultiplexer = null;
tracks = null;
buffer = null;
oBuf = null;
}
}
public FrameByFrame(URL sourceVideo,String targetVideo, JavaVideoAnalysis mainProgram){
this.sourceVideo = sourceVideo;
this.targetVideo = targetVideo;
String amTVstring = targetVideo.substring(0,targetVideo.length()-4);
amTVstring+="_avimux.avi";
System.out.println("Avimux target string "+amTVstring);
this.mainProgram = mainProgram;
Vector handlers;
/*Add video decoders*/
Format[] tempIF = new Format[2];
System.out.println("Add formats");
tempIF[0] = new YUVFormat();
tempIF[1] = new RGBFormat();
Codec[] codecsToAdd = new Codec[2];
System.out.println("Add Codecs");
codecsToAdd[0] = (Codec) new VideoEncoder(); //Takes in YUV
codecsToAdd[1] = (Codec) new VideoDecoder(); //jffmpeg decoders spits out RGB
/*Add codecs*/
System.out.println("Use plugInManager");
PlugInManager pim = new PlugInManager();
for (int c = 0; c<codecsToAdd.length;++c){
String name = codecsToAdd[c].getClass().getName();
System.out.println(name);
Format[] inFormats = codecsToAdd[c].getSupportedInputFormats();
System.out.println("il "+inFormats.length);
for (int i = 0;i<inFormats.length;++i){
System.out.println("\tif "+inFormats[i].toString());
}
Format[] outFormats = codecsToAdd[c].getSupportedOutputFormats(tempIF[c]);
System.out.println("ol "+outFormats.length);
for (int i = 0;i<outFormats.length;++i){
System.out.println("\tof "+outFormats[i].toString());
}
pim.addPlugIn(name,inFormats,outFormats,PlugInManager.CODEC);
System.out.println("Added plug-in");
}
System.out.println("Plugins added");
try{
pim.commit();
}catch(Exception err){System.out.println("Couldn't commit pim");}
System.out.println("Start opening "+sourceVideo.toString());
//Start opening source video
DataSource dSource = null;
try{
//dSource = Manager.createDataSource(new MediaLocator(sourceVideo));
System.out.println("Adding datasource");
dSource = Manager.createDataSource(sourceVideo);
System.out.println("Adding datasource");
}catch(Exception err){System.out.println("DataSource failed"); System.exit(0);}
BasicSourceModule bsm = null;
try{
bsm = BasicSourceModule.createModule(dSource);
}catch(Exception err){System.out.println("SetSource failed"); System.exit(0);}
deMultiplexer = bsm.getDemultiplexer();
tracks = null;
try{
tracks= deMultiplexer.getTracks();
}catch(Exception err){System.out.println("GetTracks failed"); System.exit(0);}
videoTrack = 0;
for (int i = 0; i< tracks.length; ++i){
if (tracks[i].getFormat() instanceof VideoFormat){
videoTrack = i;
}else{
tracks[i].setEnabled(false);
}
}
System.out.println("Encoding "+tracks[videoTrack].getFormat().getEncoding());
System.out.println("DeMultiplexer "+deMultiplexer.toString());
videoSize = null;
VideoFormat vf = (VideoFormat) tracks[videoTrack].getFormat();
videoSize =vf.getSize();
/*Check clip length*/
frameRate = vf.getFrameRate();
duration = deMultiplexer.getDuration();
frameCount = (int) (duration.getSeconds()*frameRate);
System.out.println("Duration "+(duration.getSeconds())+" s Frames: "+frameCount+" frameRate "+frameRate);
System.out.println("TrackFrames "+tracks[videoTrack].mapTimeToFrame(tracks[videoTrack].getDuration()));
System.out.println("Format "+tracks[videoTrack].getFormat().toString());
/*Create codecs for decoding, colorspace conversion and encoding*/
decoder = SimpleGraphBuilder.findCodec(tracks[videoTrack].getFormat(), null, null, null);
Vector dNames = PlugInManager.getPlugInList(tracks[videoTrack].getFormat(), null,PlugInManager.CODEC);
for (int i = 0; i<dNames.size();++i){
System.out.println("Matching plugins "+i+": "+dNames.get(i));
}
System.out.println("Decoder name "+decoder.getName());
/*init decoder*/
buffer = new Buffer();
oBuf = new Buffer();
decoder.setInputFormat(tracks[videoTrack].getFormat());
Format dof = decoder.setOutputFormat(new RGBFormat(videoSize,-1,(new int[0]).getClass(),25.0f,32, 0xff0000, 0x00ff00, 0x0000ff));
oBuf.setFormat(dof);
try{
decoder.open();
}catch (Exception err){System.out.println("Couldn't open codecs "+err.toString());}
//Read first frame to intialize other codecs
//for (int framesExtracted = 0; framesExtracted<10; ++framesExtracted){
writer = null;
/*Add JFRAME for image and slider...*/
mainProgram.videoFrame = new JFrame("Video");
mainProgram.videoFrame.addWindowListener(mainProgram);
mainProgram.videoFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
/*ADD DrawImage*/
mainProgram.drawImage = new DrawImage();
mainProgram.drawImage.setBackground(new Color(0, 0, 0));
mainProgram.drawImage.setPreferredSize(new Dimension(videoSize.width, videoSize.height));
mainProgram.drawImage.setOpaque(true);
mainProgram.drawImage.addMouseListener(mainProgram);
contentPane.add(mainProgram.drawImage);
/*Add Slider*/
mainProgram.slider = new JSlider(JSlider.HORIZONTAL,0, frameCount-1, 0);
mainProgram.slider.addChangeListener(mainProgram);
contentPane.add(mainProgram.slider);
contentPane.setOpaque(true); //content panes must be opaque
mainProgram.videoFrame.setContentPane(contentPane);
mainProgram.videoFrame.pack();
mainProgram.videoFrame.setLocation(100, 200);
mainProgram.videoFrame.setVisible(true);
/*Implement slider*/
/*
mainProgram.slider.setModel(new DefaultBoundedRangeModel(0,0,0,frameCount-1));
mainProgram.drawImage.setPreferredSize(videoSize);
mainProgram.setPreferredSize(new Dimension(videoSize.width, videoSize.height+300));
((JFrame) SwingUtilities.getRoot(mainProgram)).pack(); //Resize the window/
((JFrame) SwingUtilities.getRoot(mainProgram)).setSize(videoSize.width, videoSize.height+300);
*/
/*Saving mPNG*/
try{
//writer = new MpngWriter(amTVstring,videoSize);
//writer.writeHeader();
}catch(Exception err){System.out.println("Couldn't open writer"); System.exit(0);}
readFrame(0);
}
public int[] readFrame(int frameNo){
Time currentTime = null;
Time targetTime = tracks[videoTrack].mapFrameToTime(frameNo);
if (buffer != null){ //check whether seek is needed
currentTime = new Time(buffer.getTimeStamp());
if ((targetTime.getSeconds() - currentTime.getSeconds()) == 1.0/frameRate){
System.out.println("No seek required");
return readFrame();
}
}
try{
currentTime = deMultiplexer.setPosition(targetTime,javax.media.protocol.Positionable.RoundDown);
}catch(Exception err){System.out.println("Search failed"); System.exit(0);}
try{
tracks[videoTrack].readFrame(buffer);
decoder.process(buffer,oBuf);
currentTime = new Time(buffer.getTimeStamp());
while ((buffer.isDiscard() || buffer.getLength()==0 ||currentTime.getSeconds() < targetTime.getSeconds() )&& !buffer.isEOM()) { // && buffer.getSequenceNumber()<frameNo){
tracks[videoTrack].readFrame(buffer);
decoder.process(buffer,oBuf);
currentTime = new Time(buffer.getTimeStamp());
}
}catch(Exception err){System.out.println("ReadFrame failed"); System.exit(0);}
System.out.println("Time stamp after loop"+currentTime.getSeconds());
if (!buffer.isEOM()){
decoder.process(buffer,oBuf);
frameData = (int[]) oBuf.getData();
printImage(frameData,videoSize);
mainProgram.status.setText("Frame Seq#: "+tracks[videoTrack].mapTimeToFrame(currentTime));
int[] returnVal = new int[1];
returnVal[0] = tracks[videoTrack].mapTimeToFrame(currentTime);
return returnVal;
}else{
mainProgram.status.setText("End of file reached");
return null;
}
}
public int[] readFrame(){
Time currentTime = null;
try{
tracks[videoTrack].readFrame(buffer);
decoder.process(buffer,oBuf);
currentTime = new Time(buffer.getTimeStamp());
while ((buffer.isDiscard() || buffer.getLength()==0)&& !buffer.isEOM()) { // && buffer.getSequenceNumber()<frameNo){
tracks[videoTrack].readFrame(buffer);
decoder.process(buffer,oBuf);
currentTime = new Time(buffer.getTimeStamp()); }
}catch(Exception err){System.out.println("ReadFrame failed"); System.exit(0);}
System.out.println("Time stamp "+currentTime.getSeconds());
if (!buffer.isEOM()){
decoder.process(buffer,oBuf);
frameData = (int[]) oBuf.getData();
printImage(frameData,videoSize);
mainProgram.status.setText("Frame Seq#: "+tracks[videoTrack].mapTimeToFrame(currentTime));
int[] returnVal = new int[1];
returnVal[0] = tracks[videoTrack].mapTimeToFrame(currentTime);
return returnVal;
}else{
mainProgram.status.setText("End of file reached");
return null;
}
}
private void printImage(int[] data, Dimension size){
//BufferedImage buffImg = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
BufferedImage buffImg = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_BGR);
for (int j = 0; j<size.height;++j){
for (int i = 0; i<size.width;++i){
buffImg.setRGB(i,j,data[i+j*size.width]);
}
}
mainProgram.drawImage.drawImage(buffImg, mainProgram.width, mainProgram.height);
if (writer != null){
try{
writer.write_frame(buffImg);
}catch (Exception err){
System.out.println("Couldn't write frame");
System.exit(0);
}
}
}
public void controllerUpdate(ControllerEvent event){
//Configure event
if (event instanceof ConfigureCompleteEvent){
ConfigureCompleteEvent test = (ConfigureCompleteEvent) event;
if (test.getCurrentState() == Processor.Configured){
goOn = true;
System.out.println("Configure, moving On");
} else {System.out.println("Got notification, not configured");}
}
//Realize event
if (event instanceof RealizeCompleteEvent){
RealizeCompleteEvent test = (RealizeCompleteEvent) event;
if (test.getCurrentState() == Controller.Realized){
goOn = true;
System.out.println("Realized, moving on");
} else {System.out.println("Got notification, not Realized");}
}
//Realize event
if (event instanceof PrefetchCompleteEvent){
PrefetchCompleteEvent test = (PrefetchCompleteEvent) event;
if (test.getCurrentState() == Controller.Prefetched){
goOn = true;
System.out.println("Prefetched, moving on");
} else {System.out.println("Got notification, not Prefetched");}
}
}
}