/* * File: SMovie.java * ----------------------- */ import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.util.ArrayList; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JProgressBar; import javax.swing.JTextField; import javax.swing.SwingUtilities; import smovielib.Clip; import smovielib.Effect; import smovielib.PixelMatrix; import smovielib.TimelineProgram; import smovielib.TimelineListener; import smovielib.Video; import acm.graphics.GCompound; import acm.graphics.GImage; import acm.graphics.GObject; import acm.graphics.GPoint; import acm.gui.IntField; import acm.io.*; public class SMovie extends TimelineProgram implements TimelineListener { private JComboBox effectSelector; private JFileChooser fileChooser; private JButton addButton; private JButton removeButton; private JButton addEffectButton, removeEffectButton, previewButton, exportButton; private JProgressBar progressBar; private ArrayList<VideoClip> clips; private ArrayList<EffectClip> effects; private boolean willShowPreview; public void run() { setUpInterface(); getTimeline().addListener(this); willShowPreview = false; clips = new ArrayList<VideoClip>(); effects = new ArrayList<EffectClip>(); } public void actionPerformed(ActionEvent ae) { if(ae.getSource().equals(addButton)) { addClipFromFile(); } else if(ae.getSource().equals(removeButton)) { removeSelectedClip(); } else if(ae.getSource().equals(previewButton)) { willShowPreview = true; } else if(ae.getSource().equals(addEffectButton)) { effects.add(new EffectClip((Effect) effectSelector.getSelectedItem(), effects.size() + 1)); getTimeline().addClip(effects.get(effects.size() - 1)); } else if(ae.getSource().equals(removeEffectButton)) { removeSelectedEffect(); } else if (ae.getSource().equals(exportButton)) { Thread th = new Thread(new Runnable() { public void run() { exportVideo(); } }); th.start(); } } public void pointSelected(int frame, int offset) { if(willShowPreview && !clips.isEmpty() && clips.get(clips.size() - 1).getFramePastTheEnd() - 1 > frame) { generatePreview(frame, offset); willShowPreview = false; } } public void clipMoved(Clip clip, long deltaFrames) { if(clip instanceof VideoClip) { VideoClip vClip = (VideoClip) clip; vClip.adjustBegin(deltaFrames); adjustClipsFromIndex(clips.indexOf(vClip)); } else if (clip instanceof EffectClip) { EffectClip eClip = (EffectClip) clip; eClip.adjustStartFrame(deltaFrames); } getTimeline().clipModified(clip); } public void clipResized(Clip clip, long deltaFrames) { if(clip instanceof VideoClip) { VideoClip vClip = (VideoClip) clip; vClip.adjustEnd(deltaFrames); adjustClipsFromIndex(clips.indexOf(vClip)); } else if (clip instanceof EffectClip) { EffectClip eClip = (EffectClip) clip; eClip.adjustNumFrames(deltaFrames); } getTimeline().clipModified(clip); } // ******** PRIVATE ********* / private void exportVideo() { try { int width = 640; int height = 480; int frames = 15; if(clips.size() == 1) { Video video = clips.get(0).getVideo(); width = video.getWidth(); height = video.getHeight(); frames = (int) Math.round(video.getFramesPerSecond()); } int totalFrames = (int) clips.get(clips.size() - 1).getFramePastTheEnd() - 1; progressBar.setMaximum(totalFrames); Video output = new Video("output.avi", width, height, frames); for(VideoClip clip : clips) { clip.prepareForExport(); while(clip.hasNextFrame()) { PixelMatrix frame = clip.getNextFrame(); applyEffectsToFrame(frame, clip); output.writeFrame(frame); final VideoClip cl = clip; SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue((int) cl.getGlobalCurrentFrame()); } }); } } output.close(); } catch (Exception e) { e.printStackTrace(); } } private void applyEffectsToFrame(PixelMatrix frame, VideoClip clip) { for(EffectClip effect : effects) { if(effect.withinClip(clip.getGlobalCurrentFrame())) { effect.getEffect().apply(frame, true); } } } private void generatePreview(int frame, int offset) { PixelMatrix pm = null; int i = 0; for(; i < clips.size(); i++) { if(clips.get(i).getFramePastTheEnd() - 1 > frame) { pm = clips.get(i).getMatrixForFrame(frame); break; } } if(offset-- > 0 && offset < effects.size() && effects.get(offset).withinClip(frame)) effects.get(offset).getEffect().apply(pm, true); getTimeline().showPreview(pm, clips.get(i).getLabel()); } private void adjustClipsFromIndex(int index) { for(int current = index, previous = current-1; current < clips.size(); previous = current, current++) { if(previous < 0) clips.get(current).setGlobalStart(1); else clips.get(current).setGlobalStart(clips.get(previous).getFramePastTheEnd()); getTimeline().clipModified(clips.get(current)); } } private void removeSelectedClip() { if(clips.isEmpty()) return; Clip clip = getTimeline().getSelectedClip(); if(clip instanceof VideoClip) { getTimeline().removeClip(clip); int index = clips.indexOf(clip); clips.remove(clip); adjustClipsFromIndex(index); } } private void removeSelectedEffect() { if(effects.isEmpty()) return; Clip clip = getTimeline().getSelectedClip(); if(clip instanceof EffectClip) { getTimeline().removeClip(clip); effects.remove(clip); } } private void addClipFromFile() { int retVal = fileChooser.showOpenDialog(this); if(retVal == JFileChooser.APPROVE_OPTION) { try { Video video = new Video(fileChooser.getSelectedFile().getName()); VideoClip clip = new VideoClip(video); if(clips.size() > 0) clip.setGlobalStart(clips.get(clips.size() - 1).getFramePastTheEnd()); clips.add(clip); getTimeline().addClip(clip); } catch (IOException e) { e.printStackTrace(); } } } private void setUpInterface() { fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(new File(".")); addButton = new JButton("Add Clip"); removeButton = new JButton("Remove Clip"); add(addButton, NORTH); add(removeButton, NORTH); effectSelector = new JComboBox(); effectSelector.addItem(new FlipHorizontalEffect()); effectSelector.addItem(new FlipVerticalEffect()); effectSelector.addItem(new NegativeEffect()); addEffectButton = new JButton("Add Effect"); removeEffectButton = new JButton("Remove Effect"); previewButton = new JButton("Preview"); exportButton = new JButton("Export"); progressBar = new JProgressBar(0,100); progressBar.setValue(0); add(effectSelector, SOUTH); add(addEffectButton, SOUTH); add(removeEffectButton, SOUTH); add(previewButton, SOUTH); add(exportButton, SOUTH); add(progressBar, SOUTH); addActionListeners(this); } }