ru.sbtqa.monte.moviemaker.MovieMakerMain Maven / Gradle / Ivy
/* @(#)MovieMakerMain.java
* Copyright © 2010 Werner Randelshofer, Switzerland.
* You may only use this software in accordance with the license terms.
*/
package ru.sbtqa.monte.moviemaker;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import static java.lang.Math.*;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.prefs.Preferences;
import javax.imageio.ImageIO;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.ProgressMonitor;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileSystemView;
import ru.sbtqa.monte.media.Buffer;
import ru.sbtqa.monte.media.Format;
import static ru.sbtqa.monte.media.VideoFormatKeys.*;
import ru.sbtqa.monte.media.gui.BackgroundTask;
import ru.sbtqa.monte.media.gui.datatransfer.FileTextFieldTransferHandler;
import ru.sbtqa.monte.media.math.Rational;
import ru.sbtqa.monte.media.mp3.MP3AudioInputStream;
import ru.sbtqa.monte.media.quicktime.QuickTimeWriter;
/**
* A demo for the {@link QuickTimeWriter} class.
*
* @author Werner Randelshofer
* @version $Id: MovieMakerMain.java 364 2016-11-09 19:54:25Z werner $
*/
public class MovieMakerMain extends javax.swing.JFrame {
private final static long serialVersionUID = 1L;
private JFileChooser imageFolderChooser;
private JFileChooser soundFileChooser;
private JFileChooser movieFileChooser;
private Preferences prefs;
enum WhatToDo {
REPEAT_SHORTER_TRACK,
STRETCH_AND_SQUASH_VIDEO_TRACK,
CUT_LONGER_TRACK,
DONT_CARE
}
private WhatToDo whatToDo = WhatToDo.STRETCH_AND_SQUASH_VIDEO_TRACK;
/**
* Creates new form MovieMakerMain
*/
public MovieMakerMain() {
initComponents();
String version = getClass().getPackage().getImplementationVersion();
if (version != null) {
setTitle(getTitle() + " " + version);
}
((JComponent) getContentPane()).setBorder(new EmptyBorder(12, 18, 18, 18));
imageFolderField.setTransferHandler(new FileTextFieldTransferHandler(JFileChooser.DIRECTORIES_ONLY));
soundFileField.setTransferHandler(new FileTextFieldTransferHandler());
JComponent[] smallComponents = {compressionBox,
compressionLabel,
fpsField,
fpsLabel,
heightField,
heightLabel,
widthField,
widthLabel,
passThroughCheckBox,
noPreparationRadio,
fastStartCompressedRadio,
fastStartRadio};
for (JComponent c : smallComponents) {
c.putClientProperty("JComponent.sizeVariant", "small");
}
// Get Preferences
prefs = Preferences.userNodeForPackage(MovieMakerMain.class);
imageFolderField.setText(prefs.get("movie.imageFolder", ""));
soundFileField.setText(prefs.get("movie.soundFile", ""));
widthField.setText("" + prefs.getInt("movie.width", 320));
heightField.setText("" + prefs.getInt("movie.height", 240));
passThroughCheckBox.setSelected(prefs.getBoolean("movie.passThrough", false));
String fps = "" + prefs.getDouble("movie.fps", 30);
if (fps.endsWith(".0")) {
fps = fps.substring(0, fps.length() - 2);
}
fpsField.setText(fps);
compressionBox.setSelectedIndex(Math.max(0, Math.min(compressionBox.getItemCount() - 1, prefs.getInt("movie.compression", 1))));
//
String streaming = prefs.get("movie.streaming", "fastStartCompressed");
for (Enumeration i = streamingGroup.getElements(); i.hasMoreElements();) {
AbstractButton btn = i.nextElement();
if (btn.getActionCommand().equals(streaming)) {
btn.setSelected(true);
break;
}
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// //GEN-BEGIN:initComponents
private void initComponents() {
streamingGroup = new javax.swing.ButtonGroup();
aboutLabel = new javax.swing.JLabel();
imageFolderHelpLabel = new javax.swing.JLabel();
imageFolderField = new javax.swing.JTextField();
chooseImageFolderButton = new javax.swing.JButton();
soundFileHelpLable = new javax.swing.JLabel();
soundFileField = new javax.swing.JTextField();
chooseSoundFileButton = new javax.swing.JButton();
createMovieButton = new javax.swing.JButton();
widthLabel = new javax.swing.JLabel();
widthField = new javax.swing.JTextField();
heightLabel = new javax.swing.JLabel();
heightField = new javax.swing.JTextField();
compressionLabel = new javax.swing.JLabel();
compressionBox = new javax.swing.JComboBox();
fpsLabel = new javax.swing.JLabel();
fpsField = new javax.swing.JTextField();
passThroughCheckBox = new javax.swing.JCheckBox();
streamingLabel = new javax.swing.JLabel();
noPreparationRadio = new javax.swing.JRadioButton();
fastStartRadio = new javax.swing.JRadioButton();
fastStartCompressedRadio = new javax.swing.JRadioButton();
FormListener formListener = new FormListener();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("QuickTime Movie Maker");
aboutLabel.setText("This is a demo of the Monte Media library.
Copyright © 2010-2012 Werner Randelshofer. All rights reserved.
This software can be licensed under Creative Commons Atribution 3.0.");
imageFolderHelpLabel.setText("Drag a folder with image files into the field below:");
chooseImageFolderButton.setText("Choose...");
chooseImageFolderButton.addActionListener(formListener);
soundFileHelpLable.setText("Drag a sound file into the field below (.au, .aiff, .wav, .mp3):");
chooseSoundFileButton.setText("Choose...");
chooseSoundFileButton.addActionListener(formListener);
createMovieButton.setText("Create QuickTime Movie...");
createMovieButton.addActionListener(formListener);
widthLabel.setFont(new java.awt.Font("Lucida Grande", 0, 11));
widthLabel.setText("Width:");
widthField.setColumns(4);
widthField.setFont(new java.awt.Font("Lucida Grande", 0, 11));
widthField.setText("320");
heightLabel.setFont(new java.awt.Font("Lucida Grande", 0, 11));
heightLabel.setText("Height:");
heightField.setColumns(4);
heightField.setFont(new java.awt.Font("Lucida Grande", 0, 11));
heightField.setText("240");
compressionLabel.setFont(new java.awt.Font("Lucida Grande", 0, 11));
compressionLabel.setText("Compression:");
compressionBox.setFont(new java.awt.Font("Lucida Grande", 0, 11));
compressionBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "None", "Animation", "JPEG", "PNG" }));
fpsLabel.setFont(new java.awt.Font("Lucida Grande", 0, 11));
fpsLabel.setText("FPS:");
fpsField.setColumns(4);
fpsField.setFont(new java.awt.Font("Lucida Grande", 0, 11));
fpsField.setText("30");
passThroughCheckBox.setFont(new java.awt.Font("Lucida Grande", 0, 11));
passThroughCheckBox.setText("Pass through");
passThroughCheckBox.setToolTipText("Check this box if the folder contains already encoded video frames in the desired size.");
streamingLabel.setText("Prepare for Internet Streaming");
streamingGroup.add(noPreparationRadio);
noPreparationRadio.setFont(new java.awt.Font("Lucida Grande", 0, 11));
noPreparationRadio.setSelected(true);
noPreparationRadio.setText("No preparation");
noPreparationRadio.setActionCommand("none");
noPreparationRadio.addActionListener(formListener);
streamingGroup.add(fastStartRadio);
fastStartRadio.setFont(new java.awt.Font("Lucida Grande", 0, 11));
fastStartRadio.setText("Fast Start");
fastStartRadio.setActionCommand("fastStart");
fastStartRadio.addActionListener(formListener);
streamingGroup.add(fastStartCompressedRadio);
fastStartCompressedRadio.setFont(new java.awt.Font("Lucida Grande", 0, 11));
fastStartCompressedRadio.setText("Fast Start - Compressed Header");
fastStartCompressedRadio.setActionCommand("fastStartCompressed");
fastStartCompressedRadio.addActionListener(formListener);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(61, 61, 61)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(widthLabel)
.addComponent(fpsLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(fpsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(compressionLabel)
.addGap(1, 1, 1)
.addComponent(compressionBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(passThroughCheckBox))
.addGroup(layout.createSequentialGroup()
.addComponent(widthField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(heightLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(heightField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(41, 41, 41))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(aboutLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 484, Short.MAX_VALUE)
.addComponent(imageFolderHelpLabel)
.addComponent(soundFileHelpLable)
.addGroup(layout.createSequentialGroup()
.addComponent(soundFileField, javax.swing.GroupLayout.DEFAULT_SIZE, 372, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chooseSoundFileButton))
.addComponent(createMovieButton, javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(imageFolderField, javax.swing.GroupLayout.DEFAULT_SIZE, 372, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chooseImageFolderButton))))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(streamingLabel)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(fastStartRadio)
.addComponent(noPreparationRadio)
.addComponent(fastStartCompressedRadio))))))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(aboutLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(imageFolderHelpLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(imageFolderField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(chooseImageFolderButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(widthLabel)
.addComponent(widthField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(heightLabel)
.addComponent(heightField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(compressionBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(fpsLabel)
.addComponent(fpsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(compressionLabel)
.addComponent(passThroughCheckBox))
.addGap(18, 18, 18)
.addComponent(soundFileHelpLable)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(soundFileField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(chooseSoundFileButton))
.addGap(18, 18, 18)
.addComponent(streamingLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(noPreparationRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(fastStartRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(fastStartCompressedRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 18, Short.MAX_VALUE)
.addComponent(createMovieButton)
.addContainerGap())
);
pack();
}
// Code for dispatching events from components to event handlers.
private class FormListener implements java.awt.event.ActionListener {
FormListener() {}
public void actionPerformed(java.awt.event.ActionEvent evt) {
if (evt.getSource() == chooseImageFolderButton) {
MovieMakerMain.this.chooseImageFolder(evt);
}
else if (evt.getSource() == chooseSoundFileButton) {
MovieMakerMain.this.chooseSoundFile(evt);
}
else if (evt.getSource() == createMovieButton) {
MovieMakerMain.this.createMovie(evt);
}
else if (evt.getSource() == noPreparationRadio) {
MovieMakerMain.this.streamingRadioPerformed(evt);
}
else if (evt.getSource() == fastStartRadio) {
MovieMakerMain.this.streamingRadioPerformed(evt);
}
else if (evt.getSource() == fastStartCompressedRadio) {
MovieMakerMain.this.streamingRadioPerformed(evt);
}
}
}// //GEN-END:initComponents
private void chooseImageFolder(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseImageFolder
if (imageFolderChooser == null) {
imageFolderChooser = new JFileChooser();
imageFolderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (imageFolderField.getText().length() > 0) {
imageFolderChooser.setSelectedFile(new File(imageFolderField.getText()));
} else if (soundFileField.getText().length() > 0) {
imageFolderChooser.setCurrentDirectory(new File(soundFileField.getText()).getParentFile());
}
}
if (JFileChooser.APPROVE_OPTION == imageFolderChooser.showOpenDialog(this)) {
imageFolderField.setText(imageFolderChooser.getSelectedFile().getPath());
}
}//GEN-LAST:event_chooseImageFolder
private void chooseSoundFile(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseSoundFile
if (soundFileChooser == null) {
soundFileChooser = new JFileChooser();
if (soundFileField.getText().length() > 0) {
soundFileChooser.setSelectedFile(new File(soundFileField.getText()));
} else if (imageFolderField.getText().length() > 0) {
soundFileChooser.setCurrentDirectory(new File(imageFolderField.getText()));
}
}
if (JFileChooser.APPROVE_OPTION == soundFileChooser.showOpenDialog(this)) {
soundFileField.setText(soundFileChooser.getSelectedFile().getPath());
}
}//GEN-LAST:event_chooseSoundFile
private void createMovie(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createMovie
// ---------------------------------
// Check input
// ---------------------------------
final File soundFile = soundFileField.getText().trim().length() == 0 ? null : new File(soundFileField.getText().trim());
final File imageFolder = imageFolderField.getText().trim().length() == 0 ? null : new File(imageFolderField.getText().trim());
final String streaming = prefs.get("movie.streaming", "fastStartCompressed");
if (soundFile == null && imageFolder == null) {
JOptionPane.showMessageDialog(this, "You need to specify a folder with
image files and/or a sound file.");
return;
}
final int width, height;
final double fps;
try {
width = Integer.parseInt(widthField.getText());
height = Integer.parseInt(heightField.getText());
fps = Double.parseDouble(fpsField.getText());
} catch (Throwable t) {
JOptionPane.showMessageDialog(this, "Width, Height and FPS must be numeric.");
return;
}
if (fps != fps) {
JOptionPane.showMessageDialog(this, "FPS must be a real number greater than zero.");
return;
}
if (width <= 0 || height <= 0 || fps <= 0.0) {
JOptionPane.showMessageDialog(this, "Width, Height and FPS must be greater than zero.");
return;
}
final Format videoFormat;
switch (compressionBox.getSelectedIndex()) {
case 0:
videoFormat = QuickTimeWriter.VIDEO_RAW;
break;
case 1:
videoFormat = QuickTimeWriter.VIDEO_ANIMATION;
break;
case 2:
videoFormat = QuickTimeWriter.VIDEO_JPEG;
break;
case 3:
default:
videoFormat = QuickTimeWriter.VIDEO_PNG;
break;
}
// ---------------------------------
// Update Preferences
// ---------------------------------
prefs.put("movie.imageFolder", imageFolderField.getText());
prefs.put("movie.soundFile", soundFileField.getText());
prefs.putInt("movie.width", width);
prefs.putInt("movie.height", height);
prefs.putDouble("movie.fps", fps);
prefs.putInt("movie.compression", compressionBox.getSelectedIndex());
prefs.putBoolean("movie.passThrough", passThroughCheckBox.isSelected());
// ---------------------------------
// Choose an output file
// ---------------------------------
if (movieFileChooser == null) {
movieFileChooser = new JFileChooser();
if (prefs.get("movie.outputFile", null) != null) {
movieFileChooser.setSelectedFile(new File(prefs.get("movie.outputFile", null)));
} else if (imageFolderField.getText().length() > 0) {
movieFileChooser.setCurrentDirectory(new File(imageFolderField.getText()).getParentFile());
} else if (soundFileField.getText().length() > 0) {
movieFileChooser.setCurrentDirectory(new File(soundFileField.getText()).getParentFile());
}
}
if (JFileChooser.APPROVE_OPTION != movieFileChooser.showSaveDialog(this)) {
return;
}
final File movieFile = movieFileChooser.getSelectedFile().getPath().toLowerCase().endsWith(".mov")//
? movieFileChooser.getSelectedFile()
: new File(movieFileChooser.getSelectedFile().getPath() + ".mov");
prefs.put("movie.outputFile", movieFile.getPath());
createMovieButton.setEnabled(false);
final boolean passThrough = passThroughCheckBox.isSelected();
// ---------------------------------
// Create the QuickTime movie
// ---------------------------------
BackgroundTask w = new BackgroundTask() {
@Override
protected void construct() throws IOException {
// Read image files
File[] imgFiles = null;
if (imageFolder != null) {
imgFiles = imageFolder.listFiles(new FileFilter() {
FileSystemView fsv = FileSystemView.getFileSystemView();
@Override
public boolean accept(File f) {
return f.isFile() && !fsv.isHiddenFile(f) && !f.getName().equals("Thumbs.db");
}
});
if (imgFiles != null) {
Arrays.sort(imgFiles);
}
}
// FIXME Check on first image, if we can actually do pass through
if (passThrough) {
}
// Delete movie file if it already exists.
if (movieFile.exists()) {
movieFile.delete();
}
if (imgFiles != null && soundFile != null && imgFiles.length > 0) {
writeVideoAndAudio(movieFile, imgFiles, soundFile, width, height, fps, videoFormat, passThrough, streaming);
} else if (imgFiles != null && imgFiles.length > 0) {
writeVideoOnlyVFR(movieFile, imgFiles, width, height, fps, videoFormat, passThrough, streaming);
} else if (soundFile != null) {
writeAudioOnly(movieFile, soundFile, streaming);
}
}
@Override
protected void failed(Throwable t) {
t.printStackTrace();
JOptionPane.showMessageDialog(MovieMakerMain.this, "Creating the QuickTime Movie failed.
" + (t.getMessage() == null ? t.toString() : t.getMessage()), "Sorry", JOptionPane.ERROR_MESSAGE);
}
@Override
protected void finished() {
createMovieButton.setEnabled(true);
}
};
w.start();
}//GEN-LAST:event_createMovie
private void streamingRadioPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_streamingRadioPerformed
prefs.put("movie.streaming", evt.getActionCommand());
}//GEN-LAST:event_streamingRadioPerformed
/**
* variable frame rate.
*/
private void writeVideoOnlyVFR(File movieFile, File[] imgFiles, int width, int height, double fps, Format videoFormat, boolean passThrough, String streaming) throws IOException {
File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp");
ProgressMonitor p = new ProgressMonitor(MovieMakerMain.this, "Creating " + movieFile.getName(), "Creating Output File...", 0, imgFiles.length);
Graphics2D g = null;
BufferedImage img = null;
BufferedImage prevImg = null;
int[] data = null;
int[] prevData = null;
QuickTimeWriter qtOut = null;
try {
int timeScale = (int) (fps * 100.0);
int duration = 100;
qtOut = new QuickTimeWriter(tmpFile);
int vt = qtOut.addVideoTrack(videoFormat, timeScale, width, height);
qtOut.setSyncInterval(0, 30);
if (!passThrough) {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
data = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
prevImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
prevData = ((DataBufferInt) prevImg.getRaster().getDataBuffer()).getData();
g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
}
int prevImgDuration = 0;
Buffer buf = new Buffer();
for (int i = 0; i < imgFiles.length && !p.isCanceled(); i++) {
File f = imgFiles[i];
p.setNote("Processing " + f.getName());
p.setProgress(i);
if (passThrough) {
qtOut.writeSample(vt, f, duration, true);
} else {
BufferedImage fImg = ImageIO.read(f);
g.drawImage(fImg, 0, 0, width, height, null);
if (i != 0 && Arrays.equals(data, prevData)) {
prevImgDuration += duration;
} else {
if (prevImgDuration != 0) {
qtOut.write(vt, prevImg, prevImgDuration);
}
prevImgDuration = duration;
System.arraycopy(data, 0, prevData, 0, data.length);
}
}
}
if (prevImgDuration != 0) {
qtOut.write(vt, prevImg, prevImgDuration);
}
if (streaming.equals("fastStart")) {
qtOut.toWebOptimizedMovie(movieFile, false);
tmpFile.delete();
} else if (streaming.equals("fastStartCompressed")) {
qtOut.toWebOptimizedMovie(movieFile, true);
tmpFile.delete();
}
qtOut.close();
qtOut = null;
} finally {
p.close();
if (g != null) {
g.dispose();
}
if (img != null) {
img.flush();
}
if (qtOut != null) {
qtOut.close();
}
}
}
/**
* fixed framerate.
*/
private void writeVideoOnlyFFR(File movieFile, File[] imgFiles, int width, int height, double fps, Format videoFormat, boolean passThrough, String streaming) throws IOException {
File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp");
ProgressMonitor p = new ProgressMonitor(MovieMakerMain.this, "Creating " + movieFile.getName(), "Creating Output File...", 0, imgFiles.length);
Graphics2D g = null;
BufferedImage imgBuffer = null;
QuickTimeWriter qtOut = null;
try {
Rational duration = new Rational(1000, (int) (fps * 1000));
qtOut = new QuickTimeWriter(tmpFile);
int vt = qtOut.addTrack(videoFormat.append(WidthKey, width, HeightKey, height));
//qtOut.setSyncInterval(0,0);
if (!passThrough) {
imgBuffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
g = imgBuffer.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
}
Buffer buf = new Buffer();
for (int i = 0; i < imgFiles.length && !p.isCanceled(); i++) {
File f = imgFiles[i];
p.setNote("Processing " + f.getName());
p.setProgress(i);
if (passThrough) {
qtOut.writeSample(vt, f, duration.floor(qtOut.getMediaTimeScale(vt)).getNumerator(), true);
} else {
BufferedImage fImg = ImageIO.read(f);
if (fImg == null) {
continue;
}
g.drawImage(fImg, 0, 0, width, height, null);
buf.data = imgBuffer;
buf.sampleDuration = duration;
qtOut.write(vt, buf);
}
}
if (streaming.equals("fastStart")) {
qtOut.toWebOptimizedMovie(movieFile, false);
tmpFile.delete();
} else if (streaming.equals("fastStartCompressed")) {
qtOut.toWebOptimizedMovie(movieFile, true);
tmpFile.delete();
}
qtOut.close();
qtOut = null;
} finally {
p.close();
if (g != null) {
g.dispose();
}
if (imgBuffer != null) {
imgBuffer.flush();
}
if (qtOut != null) {
qtOut.close();
}
}
}
private void writeAudioOnly(File movieFile, File audioFile, String streaming) throws IOException {
File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp");
int length = (int) Math.min(Integer.MAX_VALUE, audioFile.length()); // file length is used for a rough progress estimate. This will only work for uncompressed audio.
ProgressMonitor p = new ProgressMonitor(MovieMakerMain.this, "Creating " + movieFile.getName(), "Initializing...", 0, length);
AudioInputStream audioIn = null;
QuickTimeWriter qtOut = null;
try {
qtOut = new QuickTimeWriter(tmpFile);
if (audioFile.getName().toLowerCase().endsWith(".mp3")) {
audioIn = new MP3AudioInputStream(audioFile);
} else {
audioIn = AudioSystem.getAudioInputStream(audioFile);
}
AudioFormat audioFormat = audioIn.getFormat();
//System.out.println("MovieMakerMain " + audioFormat);
int at = qtOut.addAudioTrack(audioFormat);
boolean isVBR = audioFormat.getProperty("vbr") != null && ((Boolean) audioFormat.getProperty("vbr")).booleanValue();
int asSize = audioFormat.getFrameSize();
int nbOfFramesInBuffer = isVBR ? 1 : Math.max(1, 1024 / asSize);
int asDuration = (int) (audioFormat.getSampleRate() / audioFormat.getFrameRate());
//System.out.println(" frameDuration=" + asDuration);
long count = 0;
byte[] audioBuffer = new byte[asSize * nbOfFramesInBuffer];
for (int bytesRead = audioIn.read(audioBuffer); bytesRead
!= -1; bytesRead = audioIn.read(audioBuffer)) {
if (bytesRead != 0) {
int framesRead = bytesRead / asSize;
qtOut.writeSamples(at, framesRead, audioBuffer, 0, bytesRead, asDuration);
count += bytesRead;
p.setProgress((int) count);
}
if (isVBR) {
audioFormat = audioIn.getFormat();
if (audioFormat == null) {
break;
}
asSize = audioFormat.getFrameSize();
asDuration = (int) (audioFormat.getSampleRate() / audioFormat.getFrameRate());
if (audioBuffer.length < asSize) {
audioBuffer = new byte[asSize];
}
}
}
audioIn.close();
audioIn = null;
if (streaming.equals("fastStart")) {
qtOut.toWebOptimizedMovie(movieFile, false);
tmpFile.delete();
} else if (streaming.equals("fastStartCompressed")) {
qtOut.toWebOptimizedMovie(movieFile, true);
tmpFile.delete();
}
qtOut.close();
qtOut = null;
} catch (UnsupportedAudioFileException e) {
IOException ioe = new IOException(e.getMessage());
ioe.initCause(e);
throw ioe;
} finally {
p.close();
if (audioIn != null) {
audioIn.close();
}
if (qtOut != null) {
qtOut.close();
}
}
}
private void writeVideoAndAudio(File movieFile, File[] imgFiles, File audioFile, int width, int height, double fps, Format videoFormat, boolean passThrough, String streaming) throws IOException {
File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp");
ProgressMonitor p = new ProgressMonitor(MovieMakerMain.this, "Creating " + movieFile.getName(), "Creating Output File...", 0, imgFiles.length);
AudioInputStream audioIn = null;
QuickTimeWriter qtOut = null;
BufferedImage imgBuffer = null;
Graphics2D g = null;
try {
// Determine audio format
if (audioFile.getName().toLowerCase().endsWith(".mp3")) {
audioIn = new MP3AudioInputStream(audioFile);
} else {
audioIn = AudioSystem.getAudioInputStream(audioFile);
}
AudioFormat audioFormat = audioIn.getFormat();
boolean isVBR = audioFormat.getProperty("vbr") != null && ((Boolean) audioFormat.getProperty("vbr")).booleanValue();
// Determine sampleDuration of a single sample
int asDuration = (int) (audioFormat.getSampleRate() / audioFormat.getFrameRate());
int vsDuration = 100;
// Create writer
qtOut = new QuickTimeWriter(tmpFile);
int at = qtOut.addAudioTrack(audioFormat); // audio in track 0
int vt = qtOut.addVideoTrack(videoFormat, (int) (fps * vsDuration), width, height); // video in track 1
qtOut.setCompressionQuality(vt, 0.95f);
// Create audio buffer
int asSize;
byte[] audioBuffer;
if (isVBR) {
// => variable bit rate: create audio buffer for a single frame
asSize = audioFormat.getFrameSize();
audioBuffer = new byte[asSize];
} else {
// => fixed bit rate: create audio buffer for half a second
asSize = audioFormat.getChannels() * audioFormat.getSampleSizeInBits() / 8;
audioBuffer = new byte[(int) (qtOut.getMediaTimeScale(0) / 2 * asSize)];
}
// Create video buffer
if (!passThrough) {
imgBuffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
g = imgBuffer.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
} // Main loop
int movieTime = 0;
int imgIndex = 0;
boolean isAudioDone = false;
Buffer buf = new Buffer();
while ((imgIndex < imgFiles.length || !isAudioDone) && !p.isCanceled()) {
// Advance movie time by half a second (we interleave twice per second)
movieTime += qtOut.getMovieTimeScale() / 2;
// Advance audio to movie time + 1 second (audio must be ahead of video by 1 second)
while (!isAudioDone && qtOut.getTrackDuration(0) < movieTime + qtOut.getMovieTimeScale()) {
int len = audioIn.read(audioBuffer);
if (len == -1) {
isAudioDone = true;
} else {
qtOut.writeSamples(at, len / asSize, audioBuffer, 0, len, asDuration);
}
if (isVBR) {
// => variable bit rate: format can change at any time
audioFormat = audioIn.getFormat();
if (audioFormat == null) {
break;
}
asSize = audioFormat.getFrameSize();
asDuration = (int) (audioFormat.getSampleRate() / audioFormat.getFrameRate());
if (audioBuffer.length < asSize) {
audioBuffer = new byte[asSize];
}
}
}
// Advance video to movie time
while (imgIndex < imgFiles.length && qtOut.getTrackDuration(1) < movieTime) {
// catch up with video time
p.setProgress(imgIndex);
p.setNote("Processing " + imgFiles[imgIndex].getName());
if (passThrough) {
qtOut.writeSample(vt, imgFiles[imgIndex], vsDuration, true);
} else {
BufferedImage fImg = ImageIO.read(imgFiles[imgIndex]);
if (fImg == null) {
continue;
}
g.drawImage(fImg, 0, 0, width, height, null);
fImg.flush();
qtOut.write(vt, imgBuffer, vsDuration);
}
++imgIndex;
}
}
switch (whatToDo) {
case CUT_LONGER_TRACK: {
long d0 = qtOut.getTrackDuration(at);
long d1 = qtOut.getTrackDuration(vt);
int longerTrack = -1;
int shorterDuration = -1;
if (d0 != 0 && d1 != 0) {
if (d0 > d1) {
longerTrack = 0;
shorterDuration = (int) d1;
} else if (d1 > d0) {
longerTrack = 1;
shorterDuration = (int) d0;
}
}
if (longerTrack != -1) {
LinkedList l = new LinkedList();
l.add(new QuickTimeWriter.Edit(shorterDuration, 0, 1.0)); // sampleDuration, media time, media rate
qtOut.setEditList(longerTrack, l.toArray(new QuickTimeWriter.Edit[l.size()]));
}
}
break;
case DONT_CARE:
break;
case REPEAT_SHORTER_TRACK: {
long d0 = qtOut.getTrackDuration(at);
long d1 = qtOut.getTrackDuration(vt);
int shorterTrack = -1;
int longerTrack = -1;
int shorterDuration = -1;
int longerDuration = -1;
if (d0 != 0 && d1 != 0) {
if (d0 > d1) {
shorterTrack = 1;
longerTrack = 0;
shorterDuration = (int) d1;
longerDuration = (int) d0;
} else if (d1 > d0) {
longerTrack = 1;
shorterTrack = 1;
shorterDuration = (int) d0;
longerDuration = (int) d1;
}
}
if (longerTrack != -1) {
LinkedList l = new LinkedList();
for (; longerDuration > 0; longerDuration -= shorterDuration) {
l.add(new QuickTimeWriter.Edit(min(shorterDuration, longerDuration), 0, 1.0)); // sampleDuration, media time, media rate
}
qtOut.setEditList(shorterTrack, l.toArray(new QuickTimeWriter.Edit[l.size()]));
}
}
break;
case STRETCH_AND_SQUASH_VIDEO_TRACK: {
long d0 = qtOut.getTrackDuration(at);
long d1 = qtOut.getTrackDuration(vt);
if (d0 != d1 && d0 != 0 && d1 != 0) {
LinkedList l = new LinkedList();
l.add(new QuickTimeWriter.Edit((int) d0, 0, d1 / (float) d0)); // sampleDuration, media time, media rate
qtOut.setEditList(1, l.toArray(new QuickTimeWriter.Edit[l.size()]));
}
}
break;
}
if (streaming.equals("fastStart")) {
qtOut.toWebOptimizedMovie(movieFile, false);
tmpFile.delete();
} else if (streaming.equals("fastStartCompressed")) {
qtOut.toWebOptimizedMovie(movieFile, true);
tmpFile.delete();
}
qtOut.close();
qtOut = null;
} catch (UnsupportedAudioFileException e) {
IOException ioe = new IOException(e.getMessage());
ioe.initCause(e);
throw ioe;
} finally {
p.close();
if (qtOut != null) {
qtOut.close();
}
if (audioIn != null) {
audioIn.close();
}
if (g != null) {
g.dispose();
}
if (imgBuffer != null) {
imgBuffer.flush();
}
}
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
//ignore
}
MovieMakerMain m = new MovieMakerMain();
m.setVisible(true);
m.pack();
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel aboutLabel;
private javax.swing.JButton chooseImageFolderButton;
private javax.swing.JButton chooseSoundFileButton;
private javax.swing.JComboBox compressionBox;
private javax.swing.JLabel compressionLabel;
private javax.swing.JButton createMovieButton;
private javax.swing.JRadioButton fastStartCompressedRadio;
private javax.swing.JRadioButton fastStartRadio;
private javax.swing.JTextField fpsField;
private javax.swing.JLabel fpsLabel;
private javax.swing.JTextField heightField;
private javax.swing.JLabel heightLabel;
private javax.swing.JTextField imageFolderField;
private javax.swing.JLabel imageFolderHelpLabel;
private javax.swing.JRadioButton noPreparationRadio;
private javax.swing.JCheckBox passThroughCheckBox;
private javax.swing.JTextField soundFileField;
private javax.swing.JLabel soundFileHelpLable;
private javax.swing.ButtonGroup streamingGroup;
private javax.swing.JLabel streamingLabel;
private javax.swing.JTextField widthField;
private javax.swing.JLabel widthLabel;
// End of variables declaration//GEN-END:variables
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy