mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
modernizing a bit of MovieMaker before attempting an update
This commit is contained in:
@@ -114,23 +114,12 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
fpsField.setText(fps);
|
||||
compressionBox.setSelectedIndex(Math.max(0, Math.min(compressionBox.getItemCount() - 1, prefs.getInt("movie.compression", 0))));
|
||||
|
||||
originalSizeCheckBox.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
boolean enabled = !originalSizeCheckBox.isSelected();
|
||||
widthField.setEnabled(enabled);
|
||||
heightField.setEnabled(enabled);
|
||||
}
|
||||
originalSizeCheckBox.addActionListener(e -> {
|
||||
boolean enabled = !originalSizeCheckBox.isSelected();
|
||||
widthField.setEnabled(enabled);
|
||||
heightField.setEnabled(enabled);
|
||||
});
|
||||
|
||||
// String streaming = prefs.get("movie.streaming", "fastStartCompressed");
|
||||
// for (Enumeration<AbstractButton> i = streamingGroup.getElements(); i.hasMoreElements();) {
|
||||
// AbstractButton btn = i.nextElement();
|
||||
// if (btn.getActionCommand().equals(streaming)) {
|
||||
// btn.setSelected(true);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// scoot everybody around
|
||||
pack();
|
||||
// center the frame on screen
|
||||
@@ -148,7 +137,7 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
root.registerKeyboardAction(disposer, stroke,
|
||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
|
||||
int modifiers = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
int modifiers = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
|
||||
stroke = KeyStroke.getKeyStroke('W', modifiers);
|
||||
root.registerKeyboardAction(disposer, stroke,
|
||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
@@ -156,19 +145,19 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
|
||||
|
||||
private void initComponents(final boolean standalone) {
|
||||
imageFolderHelpLabel = new JLabel();
|
||||
JLabel imageFolderHelpLabel = new JLabel();
|
||||
imageFolderField = new JTextField();
|
||||
chooseImageFolderButton = new JButton();
|
||||
soundFileHelpLabel = new JLabel();
|
||||
JButton chooseImageFolderButton = new JButton();
|
||||
JLabel soundFileHelpLabel = new JLabel();
|
||||
soundFileField = new JTextField();
|
||||
chooseSoundFileButton = new JButton();
|
||||
JButton chooseSoundFileButton = new JButton();
|
||||
createMovieButton = new JButton();
|
||||
widthLabel = new JLabel();
|
||||
widthField = new JTextField();
|
||||
heightLabel = new JLabel();
|
||||
heightField = new JTextField();
|
||||
compressionLabel = new JLabel();
|
||||
compressionBox = new JComboBox<String>();
|
||||
compressionBox = new JComboBox<>();
|
||||
fpsLabel = new JLabel();
|
||||
fpsField = new JTextField();
|
||||
originalSizeCheckBox = new JCheckBox();
|
||||
@@ -179,95 +168,67 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
setVisible(false);
|
||||
}
|
||||
});
|
||||
registerWindowCloseKeys(getRootPane(), new ActionListener() {
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
if (standalone) {
|
||||
System.exit(0);
|
||||
} else {
|
||||
setVisible(false);
|
||||
}
|
||||
registerWindowCloseKeys(getRootPane(), actionEvent -> {
|
||||
if (standalone) {
|
||||
System.exit(0);
|
||||
} else {
|
||||
setVisible(false);
|
||||
}
|
||||
});
|
||||
setTitle(Language.text("movie_maker.title"));
|
||||
|
||||
aboutLabel = new JLabel(Language.text("movie_maker.blurb"));
|
||||
JLabel aboutLabel = new JLabel(Language.text("movie_maker.blurb"));
|
||||
imageFolderHelpLabel.setText(Language.text("movie_maker.image_folder_help_label"));
|
||||
chooseImageFolderButton.setText(Language.text("movie_maker.choose_button"));
|
||||
//chooseImageFolderButton.addActionListener(formListener);
|
||||
chooseImageFolderButton.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Chooser.selectFolder(MovieMaker.this,
|
||||
Language.text("movie_maker.select_image_folder"),
|
||||
new File(imageFolderField.getText()),
|
||||
new Chooser.Callback() {
|
||||
void select(File file) {
|
||||
if (file != null) {
|
||||
imageFolderField.setText(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
});
|
||||
chooseImageFolderButton.addActionListener(e -> Chooser.selectFolder(MovieMaker.this,
|
||||
Language.text("movie_maker.select_image_folder"),
|
||||
new File(imageFolderField.getText()),
|
||||
new Chooser.Callback() {
|
||||
void select(File file) {
|
||||
if (file != null) {
|
||||
imageFolderField.setText(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
soundFileHelpLabel.setText(Language.text("movie_maker.sound_file_help_label"));
|
||||
chooseSoundFileButton.setText(Language.text("movie_maker.choose_button"));
|
||||
//chooseSoundFileButton.addActionListener(formListener);
|
||||
chooseSoundFileButton.addActionListener(new ActionListener() {
|
||||
chooseSoundFileButton.addActionListener(e -> Chooser.selectInput(MovieMaker.this,
|
||||
Language.text("movie_maker.select_sound_file"),
|
||||
new File(soundFileField.getText()),
|
||||
new Chooser.Callback() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Chooser.selectInput(MovieMaker.this,
|
||||
Language.text("movie_maker.select_sound_file"),
|
||||
new File(soundFileField.getText()),
|
||||
new Chooser.Callback() {
|
||||
|
||||
void select(File file) {
|
||||
if (file != null) {
|
||||
soundFileField.setText(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
});
|
||||
void select(File file) {
|
||||
if (file != null) {
|
||||
soundFileField.setText(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
createMovieButton.setText(Language.text("movie_maker.create_movie_button"));
|
||||
// createMovieButton.addActionListener(formListener);
|
||||
createMovieButton.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String lastPath = prefs.get("movie.outputFile", null);
|
||||
File lastFile = lastPath == null ? null : new File(lastPath);
|
||||
Chooser.selectOutput(MovieMaker.this,
|
||||
Language.text("movie_maker.save_dialog_prompt"),
|
||||
lastFile,
|
||||
new Chooser.Callback() {
|
||||
@Override
|
||||
void select(File file) {
|
||||
if (file != null) {
|
||||
String path = file.getAbsolutePath();
|
||||
if (!path.toLowerCase().endsWith(".mov")) {
|
||||
path += ".mov";
|
||||
}
|
||||
prefs.put("movie.outputFile", path);
|
||||
createMovie(new File(path));
|
||||
// final File target = new File(path);
|
||||
// //new Thread(new Runnable() {
|
||||
// EventQueue.invokeLater(new Runnable() {
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// createMovie(target);
|
||||
// }
|
||||
//
|
||||
// });
|
||||
createMovieButton.addActionListener(e -> {
|
||||
String lastPath = prefs.get("movie.outputFile", null);
|
||||
File lastFile = lastPath == null ? null : new File(lastPath);
|
||||
Chooser.selectOutput(MovieMaker.this,
|
||||
Language.text("movie_maker.save_dialog_prompt"),
|
||||
lastFile,
|
||||
new Chooser.Callback() {
|
||||
@Override
|
||||
void select(File file) {
|
||||
if (file != null) {
|
||||
String path = file.getAbsolutePath();
|
||||
if (!path.toLowerCase().endsWith(".mov")) {
|
||||
path += ".mov";
|
||||
}
|
||||
prefs.put("movie.outputFile", path);
|
||||
createMovie(new File(path));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Font font = new Font("Dialog", Font.PLAIN, 11);
|
||||
@@ -288,8 +249,8 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
compressionLabel.setText(Language.text("movie_maker.compression"));
|
||||
compressionBox.setFont(font);
|
||||
//compressionBox.setModel(new DefaultComboBoxModel(new String[] { "None", "Animation", "JPEG", "PNG" }));
|
||||
compressionBox.setModel(new DefaultComboBoxModel<String>(
|
||||
new String[] {
|
||||
compressionBox.setModel(new DefaultComboBoxModel<>(
|
||||
new String[]{
|
||||
Language.text("movie_maker.compression.animation"),
|
||||
Language.text("movie_maker.compression.jpeg"),
|
||||
Language.text("movie_maker.compression.png")
|
||||
@@ -306,27 +267,6 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
originalSizeCheckBox.setText(Language.text("movie_maker.orig_size_button"));
|
||||
originalSizeCheckBox.setToolTipText(Language.text("movie_maker.orig_size_tooltip"));
|
||||
|
||||
// streamingLabel.setText("Prepare for Internet Streaming");
|
||||
//
|
||||
// streamingGroup.add(noPreparationRadio);
|
||||
// noPreparationRadio.setFont(font);
|
||||
// noPreparationRadio.setSelected(true);
|
||||
// noPreparationRadio.setText("No preparation");
|
||||
// noPreparationRadio.setActionCommand("none");
|
||||
// noPreparationRadio.addActionListener(formListener);
|
||||
//
|
||||
// streamingGroup.add(fastStartRadio);
|
||||
// fastStartRadio.setFont(font);
|
||||
// fastStartRadio.setText("Fast Start");
|
||||
// fastStartRadio.setActionCommand("fastStart");
|
||||
// fastStartRadio.addActionListener(formListener);
|
||||
//
|
||||
// streamingGroup.add(fastStartCompressedRadio);
|
||||
// fastStartCompressedRadio.setFont(font);
|
||||
// fastStartCompressedRadio.setText("Fast Start - Compressed Header");
|
||||
// fastStartCompressedRadio.setActionCommand("fastStartCompressed");
|
||||
// fastStartCompressedRadio.addActionListener(formListener);
|
||||
|
||||
GroupLayout layout = new GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
@@ -409,47 +349,7 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
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) {
|
||||
// MovieMaker.this.chooseImageFolder(evt);
|
||||
// }
|
||||
// else if (evt.getSource() == chooseSoundFileButton) {
|
||||
// MovieMaker.this.chooseSoundFile(evt);
|
||||
// }
|
||||
// else if (evt.getSource() == createMovieButton) {
|
||||
// MovieMaker.this.createMovie(evt);
|
||||
// }
|
||||
//// else if (evt.getSource() == fastStartCompressedRadio) {
|
||||
//// MovieMaker.this.streamingRadioPerformed(evt);
|
||||
//// }
|
||||
//// else if (evt.getSource() == fastStartRadio) {
|
||||
//// MovieMaker.this.streamingRadioPerformed(evt);
|
||||
//// }
|
||||
//// else if (evt.getSource() == noPreparationRadio) {
|
||||
//// MovieMaker.this.streamingRadioPerformed(evt);
|
||||
//// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void chooseImageFolder(ActionEvent evt) {
|
||||
// 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());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void chooseSoundFile(ActionEvent evt) {
|
||||
// if (soundFileChooser == null) {
|
||||
// soundFileChooser = new JFileChooser();
|
||||
@@ -465,7 +365,6 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// this is super naughty, and shouldn't be out here. it's a hack to get the
|
||||
// ImageIcon width/height setting to work. there are better ways to do this
|
||||
// given a bit of time. you know, time? the infinite but non-renewable resource?
|
||||
@@ -479,8 +378,6 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
// ---------------------------------
|
||||
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");
|
||||
final String streaming = "fastStartCompressed";
|
||||
if (soundFile == null && imageFolder == null) {
|
||||
JOptionPane.showMessageDialog(this, Language.text("movie_maker.error.need_input"));
|
||||
return;
|
||||
@@ -539,10 +436,10 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
protected Throwable doInBackground() {
|
||||
try {
|
||||
// Read image files
|
||||
File[] imgFiles = null;
|
||||
File[] imgFiles;
|
||||
if (imageFolder != null) {
|
||||
imgFiles = imageFolder.listFiles(new FileFilter() {
|
||||
FileSystemView fsv = FileSystemView.getFileSystemView();
|
||||
final FileSystemView fsv = FileSystemView.getFileSystemView();
|
||||
|
||||
public boolean accept(File f) {
|
||||
return f.isFile() && !fsv.isHiddenFile(f) &&
|
||||
@@ -553,30 +450,30 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
return new RuntimeException(Language.text("movie_maker.error.no_images_found"));
|
||||
}
|
||||
Arrays.sort(imgFiles);
|
||||
}
|
||||
|
||||
// Get the width and height if we're preserving size.
|
||||
if (originalSize) {
|
||||
Dimension d = findSize(imgFiles);
|
||||
if (d == null) {
|
||||
// No images at all? No video then.
|
||||
throw new RuntimeException(Language.text("movie_maker.error.no_images_found"));
|
||||
// Get the width and height if we're preserving size.
|
||||
if (originalSize) {
|
||||
Dimension d = findSize(imgFiles);
|
||||
if (d == null) {
|
||||
// No images at all? No video then.
|
||||
throw new RuntimeException(Language.text("movie_maker.error.no_images_found"));
|
||||
}
|
||||
width = d.width;
|
||||
height = d.height;
|
||||
}
|
||||
width = d.width;
|
||||
height = d.height;
|
||||
}
|
||||
|
||||
// Delete movie file if it already exists.
|
||||
if (movieFile.exists()) {
|
||||
movieFile.delete();
|
||||
}
|
||||
// Delete movie file if it already exists.
|
||||
if (movieFile.exists()) {
|
||||
if (!movieFile.delete()) {
|
||||
return new RuntimeException("Could not replace " + movieFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (imageFolder != null && soundFile != null) {
|
||||
writeVideoAndAudio(movieFile, imgFiles, soundFile, width, height, fps, videoFormat, /*passThrough,*/ streaming);
|
||||
} else if (imageFolder != null) {
|
||||
writeVideoOnlyVFR(movieFile, imgFiles, width, height, fps, videoFormat, /*passThrough,*/ streaming);
|
||||
} else {
|
||||
writeAudioOnly(movieFile, soundFile, streaming);
|
||||
if (soundFile != null) {
|
||||
writeVideoAndAudio(movieFile, imgFiles, soundFile, width, height, fps, videoFormat);
|
||||
} else {
|
||||
writeVideoOnlyVFR(movieFile, imgFiles, width, height, fps, videoFormat);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -700,17 +597,17 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
|
||||
|
||||
/** variable frame rate. */
|
||||
private void writeVideoOnlyVFR(File movieFile, File[] imgFiles, int width, int height, double fps, QuickTimeWriter.VideoFormat videoFormat, /*boolean passThrough,*/ String streaming) throws IOException {
|
||||
File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp");
|
||||
private void writeVideoOnlyVFR(File movieFile, File[] imgFiles, int width, int height, double fps, QuickTimeWriter.VideoFormat videoFormat) throws IOException {
|
||||
File tmpFile = new File(movieFile.getPath() + ".tmp");
|
||||
ProgressMonitor p = new ProgressMonitor(MovieMaker.this,
|
||||
Language.interpolate("movie_maker.progress.creating_file_name", movieFile.getName()),
|
||||
Language.text("movie_maker.progress.creating_output_file"),
|
||||
0, imgFiles.length);
|
||||
Graphics2D g = null;
|
||||
BufferedImage img = null;
|
||||
BufferedImage prevImg = null;
|
||||
int[] data = null;
|
||||
int[] prevData = null;
|
||||
BufferedImage prevImg;
|
||||
int[] data;
|
||||
int[] prevData;
|
||||
QuickTimeWriter qtOut = null;
|
||||
try {
|
||||
int timeScale = (int) (fps * 100.0);
|
||||
@@ -721,14 +618,12 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
qtOut.setSyncInterval(0, 30);
|
||||
|
||||
//if (!passThrough) {
|
||||
if (true) {
|
||||
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);
|
||||
}
|
||||
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;
|
||||
for (int i = 0; i < imgFiles.length && !p.isCanceled(); i++) {
|
||||
File f = imgFiles[i];
|
||||
@@ -737,36 +632,31 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
p.setNote(Language.interpolate("movie_maker.progress.processing", f.getName()));
|
||||
p.setProgress(i);
|
||||
|
||||
//if (passThrough) {
|
||||
if (false) {
|
||||
qtOut.writeSample(0, f, duration);
|
||||
} else {
|
||||
//BufferedImage fImg = ImageIO.read(f);
|
||||
BufferedImage fImg = readImage(f);
|
||||
if (fImg == null) continue;
|
||||
//BufferedImage fImg = ImageIO.read(f);
|
||||
BufferedImage fImg = readImage(f);
|
||||
if (fImg == null) continue;
|
||||
|
||||
g.drawImage(fImg, 0, 0, width, height, null);
|
||||
if (i != 0 && Arrays.equals(data, prevData)) {
|
||||
prevImgDuration += duration;
|
||||
} else {
|
||||
if (prevImgDuration != 0) {
|
||||
qtOut.writeFrame(0, prevImg, prevImgDuration);
|
||||
}
|
||||
prevImgDuration = duration;
|
||||
System.arraycopy(data, 0, prevData, 0, data.length);
|
||||
g.drawImage(fImg, 0, 0, width, height, null);
|
||||
if (i != 0 && Arrays.equals(data, prevData)) {
|
||||
prevImgDuration += duration;
|
||||
} else {
|
||||
if (prevImgDuration != 0) {
|
||||
qtOut.writeFrame(0, prevImg, prevImgDuration);
|
||||
}
|
||||
prevImgDuration = duration;
|
||||
System.arraycopy(data, 0, prevData, 0, data.length);
|
||||
}
|
||||
}
|
||||
if (prevImgDuration != 0) {
|
||||
qtOut.writeFrame(0, prevImg, prevImgDuration);
|
||||
}
|
||||
if (streaming.equals("fastStart")) {
|
||||
qtOut.toWebOptimizedMovie(movieFile, false);
|
||||
tmpFile.delete();
|
||||
} else if (streaming.equals("fastStartCompressed")) {
|
||||
qtOut.toWebOptimizedMovie(movieFile, true);
|
||||
tmpFile.delete();
|
||||
}
|
||||
// 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 {
|
||||
@@ -784,85 +674,10 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
}
|
||||
|
||||
|
||||
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(MovieMaker.this,
|
||||
Language.interpolate("movie_maker.progress.creating_file_name", movieFile.getName()),
|
||||
Language.text("movie_maker.progress.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("QuickTimeMovieMakerMain " + audioFormat);
|
||||
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(0, 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, QuickTimeWriter.VideoFormat videoFormat,
|
||||
/*boolean passThrough,*/ String streaming) throws IOException {
|
||||
int width, int height, double fps, QuickTimeWriter.VideoFormat videoFormat) throws IOException {
|
||||
|
||||
File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp");
|
||||
File tmpFile = new File(movieFile.getPath() + ".tmp");
|
||||
ProgressMonitor p = new ProgressMonitor(MovieMaker.this,
|
||||
Language.interpolate("movie_maker.progress.creating_file_name", movieFile.getName()),
|
||||
Language.text("movie_maker.progress.creating_output_file"),
|
||||
@@ -880,7 +695,7 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
audioIn = AudioSystem.getAudioInputStream(audioFile);
|
||||
}
|
||||
AudioFormat audioFormat = audioIn.getFormat();
|
||||
boolean isVBR = audioFormat.getProperty("vbr") != null && ((Boolean) audioFormat.getProperty("vbr")).booleanValue();
|
||||
boolean isVBR = audioFormat.getProperty("vbr") != null && (Boolean) audioFormat.getProperty("vbr");
|
||||
|
||||
// Determine duration of a single sample
|
||||
int asDuration = (int) (audioFormat.getSampleRate() / audioFormat.getFrameRate());
|
||||
@@ -904,12 +719,9 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
}
|
||||
|
||||
// Create video buffer
|
||||
//if (!passThrough) {
|
||||
if (true) {
|
||||
imgBuffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
g = imgBuffer.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
} // Main loop
|
||||
imgBuffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
g = imgBuffer.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
int movieTime = 0;
|
||||
int imgIndex = 0;
|
||||
boolean isAudioDone = false;
|
||||
@@ -947,33 +759,25 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
if (f == null) continue;
|
||||
|
||||
p.setNote(Language.interpolate("movie_maker.progress.processing", f.getName()));
|
||||
//if (passThrough) {
|
||||
if (false) {
|
||||
qtOut.writeSample(1, f, vsDuration);
|
||||
} else {
|
||||
//BufferedImage fImg = ImageIO.read(imgFiles[imgIndex]);
|
||||
BufferedImage fImg = readImage(f);
|
||||
if (fImg == null) continue;
|
||||
BufferedImage fImg = readImage(f);
|
||||
if (fImg == null) continue;
|
||||
|
||||
g.drawImage(fImg, 0, 0, width, height, null);
|
||||
fImg.flush();
|
||||
qtOut.writeFrame(1, imgBuffer, vsDuration);
|
||||
}
|
||||
g.drawImage(fImg, 0, 0, width, height, null);
|
||||
fImg.flush();
|
||||
qtOut.writeFrame(1, imgBuffer, vsDuration);
|
||||
}
|
||||
}
|
||||
if (streaming.equals("fastStart")) {
|
||||
qtOut.toWebOptimizedMovie(movieFile, false);
|
||||
tmpFile.delete();
|
||||
} else if (streaming.equals("fastStartCompressed")) {
|
||||
qtOut.toWebOptimizedMovie(movieFile, true);
|
||||
tmpFile.delete();
|
||||
}
|
||||
// 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;
|
||||
throw new IOException(e.getMessage(), e);
|
||||
} finally {
|
||||
p.close();
|
||||
if (qtOut != null) {
|
||||
@@ -997,10 +801,9 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
* Code taken from PApplet, any changes here should lead to updates there.
|
||||
*/
|
||||
static private BufferedImage loadImageTGA(File file) throws IOException {
|
||||
InputStream is = new FileInputStream(file);
|
||||
|
||||
try {
|
||||
byte header[] = new byte[18];
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
byte[] header = new byte[18];
|
||||
int offset = 0;
|
||||
do {
|
||||
int count = is.read(header, offset, header.length - offset);
|
||||
@@ -1031,18 +834,18 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
final int ALPHA = 4;
|
||||
|
||||
if (((header[2] == 3) || (header[2] == 11)) && // B&W, plus RLE or not
|
||||
(header[16] == 8) && // 8 bits
|
||||
((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32 bit
|
||||
(header[16] == 8) && // 8 bits
|
||||
((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32 bit
|
||||
format = ALPHA;
|
||||
|
||||
} else if (((header[2] == 2) || (header[2] == 10)) && // RGB, RLE or not
|
||||
(header[16] == 24) && // 24 bits
|
||||
((header[17] == 0x20) || (header[17] == 0))) { // origin
|
||||
(header[16] == 24) && // 24 bits
|
||||
((header[17] == 0x20) || (header[17] == 0))) { // origin
|
||||
format = RGB;
|
||||
|
||||
} else if (((header[2] == 2) || (header[2] == 10)) &&
|
||||
(header[16] == 32) &&
|
||||
((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32
|
||||
(header[16] == 32) &&
|
||||
((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32
|
||||
format = ARGB;
|
||||
}
|
||||
|
||||
@@ -1063,58 +866,58 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
|
||||
if ((header[2] == 2) || (header[2] == 3)) { // not RLE encoded
|
||||
if (reversed) {
|
||||
int index = (h-1) * w;
|
||||
int index = (h - 1) * w;
|
||||
switch (format) {
|
||||
case ALPHA:
|
||||
for (int y = h-1; y >= 0; y--) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
pixels[index + x] = is.read();
|
||||
case ALPHA:
|
||||
for (int y = h - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
pixels[index + x] = is.read();
|
||||
}
|
||||
index -= w;
|
||||
}
|
||||
index -= w;
|
||||
}
|
||||
break;
|
||||
case RGB:
|
||||
for (int y = h-1; y >= 0; y--) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
pixels[index + x] =
|
||||
break;
|
||||
case RGB:
|
||||
for (int y = h - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
pixels[index + x] =
|
||||
is.read() | (is.read() << 8) | (is.read() << 16) |
|
||||
0xff000000;
|
||||
0xff000000;
|
||||
}
|
||||
index -= w;
|
||||
}
|
||||
index -= w;
|
||||
}
|
||||
break;
|
||||
case ARGB:
|
||||
for (int y = h-1; y >= 0; y--) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
pixels[index + x] =
|
||||
break;
|
||||
case ARGB:
|
||||
for (int y = h - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
pixels[index + x] =
|
||||
is.read() | (is.read() << 8) | (is.read() << 16) |
|
||||
(is.read() << 24);
|
||||
(is.read() << 24);
|
||||
}
|
||||
index -= w;
|
||||
}
|
||||
index -= w;
|
||||
}
|
||||
}
|
||||
} else { // not reversed
|
||||
int count = w * h;
|
||||
switch (format) {
|
||||
case ALPHA:
|
||||
for (int i = 0; i < count; i++) {
|
||||
pixels[i] = is.read();
|
||||
}
|
||||
break;
|
||||
case RGB:
|
||||
for (int i = 0; i < count; i++) {
|
||||
pixels[i] =
|
||||
case ALPHA:
|
||||
for (int i = 0; i < count; i++) {
|
||||
pixels[i] = is.read();
|
||||
}
|
||||
break;
|
||||
case RGB:
|
||||
for (int i = 0; i < count; i++) {
|
||||
pixels[i] =
|
||||
is.read() | (is.read() << 8) | (is.read() << 16) |
|
||||
0xff000000;
|
||||
}
|
||||
break;
|
||||
case ARGB:
|
||||
for (int i = 0; i < count; i++) {
|
||||
pixels[i] =
|
||||
0xff000000;
|
||||
}
|
||||
break;
|
||||
case ARGB:
|
||||
for (int i = 0; i < count; i++) {
|
||||
pixels[i] =
|
||||
is.read() | (is.read() << 8) | (is.read() << 16) |
|
||||
(is.read() << 24);
|
||||
}
|
||||
break;
|
||||
(is.read() << 24);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1128,17 +931,17 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
num -= 127; // (num & 0x7F) + 1
|
||||
int pixel = 0;
|
||||
switch (format) {
|
||||
case ALPHA:
|
||||
pixel = is.read();
|
||||
break;
|
||||
case RGB:
|
||||
pixel = 0xFF000000 |
|
||||
is.read() | (is.read() << 8) | (is.read() << 16);
|
||||
break;
|
||||
case ARGB:
|
||||
pixel = is.read() |
|
||||
(is.read() << 8) | (is.read() << 16) | (is.read() << 24);
|
||||
break;
|
||||
case ALPHA:
|
||||
pixel = is.read();
|
||||
break;
|
||||
case RGB:
|
||||
pixel = 0xFF000000 |
|
||||
is.read() | (is.read() << 8) | (is.read() << 16);
|
||||
break;
|
||||
case ARGB:
|
||||
pixel = is.read() |
|
||||
(is.read() << 8) | (is.read() << 16) | (is.read() << 24);
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < num; i++) {
|
||||
pixels[index++] = pixel;
|
||||
@@ -1147,34 +950,34 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
} else { // write up to 127 bytes as uncompressed
|
||||
num += 1;
|
||||
switch (format) {
|
||||
case ALPHA:
|
||||
for (int i = 0; i < num; i++) {
|
||||
pixels[index++] = is.read();
|
||||
}
|
||||
break;
|
||||
case RGB:
|
||||
for (int i = 0; i < num; i++) {
|
||||
pixels[index++] = 0xFF000000 |
|
||||
is.read() | (is.read() << 8) | (is.read() << 16);
|
||||
}
|
||||
break;
|
||||
case ARGB:
|
||||
for (int i = 0; i < num; i++) {
|
||||
pixels[index++] = is.read() |
|
||||
(is.read() << 8) | (is.read() << 16) | (is.read() << 24);
|
||||
}
|
||||
break;
|
||||
case ALPHA:
|
||||
for (int i = 0; i < num; i++) {
|
||||
pixels[index++] = is.read();
|
||||
}
|
||||
break;
|
||||
case RGB:
|
||||
for (int i = 0; i < num; i++) {
|
||||
pixels[index++] = 0xFF000000 |
|
||||
is.read() | (is.read() << 8) | (is.read() << 16);
|
||||
}
|
||||
break;
|
||||
case ARGB:
|
||||
for (int i = 0; i < num; i++) {
|
||||
pixels[index++] = is.read() |
|
||||
(is.read() << 8) | (is.read() << 16) | (is.read() << 24);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!reversed) {
|
||||
int[] temp = new int[w];
|
||||
for (int y = 0; y < h/2; y++) {
|
||||
int z = (h-1) - y;
|
||||
System.arraycopy(pixels, y*w, temp, 0, w);
|
||||
System.arraycopy(pixels, z*w, pixels, y*w, w);
|
||||
System.arraycopy(temp, 0, pixels, z*w, w);
|
||||
for (int y = 0; y < h / 2; y++) {
|
||||
int z = (h - 1) - y;
|
||||
System.arraycopy(pixels, y * w, temp, 0, w);
|
||||
System.arraycopy(pixels, z * w, pixels, y * w, w);
|
||||
System.arraycopy(temp, 0, pixels, z * w, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1186,49 +989,29 @@ public class MovieMaker extends JFrame implements Tool {
|
||||
wr.setDataElements(0, 0, w, h, pixels);
|
||||
return image;
|
||||
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
MovieMaker m = new MovieMaker();
|
||||
m.init(null);
|
||||
// m.init();
|
||||
m.setVisible(true);
|
||||
// m.pack();
|
||||
}
|
||||
static public void main(String[] args) {
|
||||
EventQueue.invokeLater(() -> {
|
||||
MovieMaker m = new MovieMaker();
|
||||
m.init(null);
|
||||
m.setVisible(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private JLabel aboutLabel;
|
||||
private JButton chooseImageFolderButton;
|
||||
private JButton chooseSoundFileButton;
|
||||
private JComboBox<String> compressionBox;
|
||||
private JLabel compressionLabel;
|
||||
// private JRadioButton fastStartCompressedRadio;
|
||||
// private JRadioButton fastStartRadio;
|
||||
private JTextField fpsField;
|
||||
private JLabel fpsLabel;
|
||||
private JTextField heightField;
|
||||
private JLabel heightLabel;
|
||||
private JTextField imageFolderField;
|
||||
private JLabel imageFolderHelpLabel;
|
||||
// private JRadioButton noPreparationRadio;
|
||||
private JCheckBox originalSizeCheckBox;
|
||||
private JTextField soundFileField;
|
||||
private JLabel soundFileHelpLabel;
|
||||
// private ButtonGroup streamingGroup;
|
||||
// private JLabel streamingLabel;
|
||||
private JTextField widthField;
|
||||
private JLabel widthLabel;
|
||||
// private JLabel copyrightLabel;
|
||||
private JButton createMovieButton;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,19 @@ _ listing: https://search.maven.org/artifact/org.jcodec/jcodec/0.2.5/jar
|
||||
_ jars: https://repo.maven.apache.org/maven2/org/jcodec/jcodec/0.2.5/
|
||||
_ demo: https://github.com/jcodec/jcodec/blob/master/samples/main/java/org/jcodec/samples/gen/SequenceEncoderDemo.java
|
||||
|
||||
the essentials build for macos64, just one file:
|
||||
https://evermeet.cx/ffmpeg/ffmpeg-4.4.7z
|
||||
https://evermeet.cx/ffmpeg/ffmpeg-4.4.zip (!)
|
||||
via https://evermeet.cx/ffmpeg/
|
||||
release essentials for windows64, multiple files:
|
||||
https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.7z
|
||||
https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip
|
||||
ffmpeg-4.4-essentials_build/bin/ffmpeg.exe is our man
|
||||
via https://www.gyan.dev/ffmpeg/builds/
|
||||
linux64
|
||||
https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
|
||||
via https://johnvansickle.com/ffmpeg/ also has arm and others
|
||||
|
||||
|
||||
decisions before final 4.0 release
|
||||
X Shutting off VAqua due to interface ugliness and Contribution Manager freezing
|
||||
|
||||
Reference in New Issue
Block a user