All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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