/* * Copyright (c) 2011 Michael Zucchi * * This file is part of jjmpeg, a java binding to ffmpeg's libraries. * * jjmpeg is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * jjmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with jjmpeg. If not, see <http://www.gnu.org/licenses/>. */ package au.notzed.jjmpeg.util; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToggleButton; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; import au.notzed.jjmpeg.AVFormatContext; import au.notzed.jjmpeg.PixelFormat; import au.notzed.jjmpeg.io.JJMediaReader; import au.notzed.jjmpeg.io.JJMediaReader.JJReaderVideo; /** * Video loading file chooser which shows a preview and opens at a decent size * and layout. * * @author notzed */ public class JJFileChooser extends JFileChooser { private static final long serialVersionUID = 1L; class VideoInfo extends JPanel implements PropertyChangeListener, AncestorListener { private static final long serialVersionUID = 1L; File file; JLabel icon; JLabel info; ImageIcon iicon; BufferedImage preview; public VideoInfo(JFileChooser fc) { super(new BorderLayout()); // setPreferredSize(new Dimension(256, 256)); fc.addPropertyChangeListener(this); setBorder(new EmptyBorder(0, 8, 0, 0)); preview = new BufferedImage(256, 256, BufferedImage.TYPE_3BYTE_BGR); iicon = new ImageIcon(preview); icon = new JLabel(iicon); // icon.setHorizontalAlignment(icon.CENTER); // icon.setVerticalAlignment(icon.CENTER); icon.setPreferredSize(new Dimension(256, 256)); icon.setMinimumSize(new Dimension(256, 256)); icon.setMaximumSize(new Dimension(256, 256)); icon.setHorizontalTextPosition(SwingConstants.CENTER); icon.setVerticalTextPosition(SwingConstants.CENTER); add(icon, BorderLayout.CENTER); info = new JLabel(); JScrollPane sp = new JScrollPane(info); // sp.setPreferredSize(new Dimension(256, 180)); // sp.setMaximumSize(new Dimension(256, -1)); info.setPreferredSize(new Dimension(256, 180)); info.setMaximumSize(new Dimension(256, -1)); info.setVerticalAlignment(SwingConstants.TOP); add(sp, BorderLayout.SOUTH); fc.addAncestorListener(this); } @Override public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (name.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { file = (File) evt.getNewValue(); update(); } } SwingWorker<?, ?> loadIcon; AsyncVideoLoader ail; void update() { if (ail != null) { ail.abort(); ail = null; } if (file == null) { // icon.setIcon(null); } else { ail = new AsyncVideoLoader(file, icon, info); ail.execute(); } } public void ancestorAdded(AncestorEvent event) { } public void ancestorRemoved(AncestorEvent event) { if (ail != null) { ail.abort(); } ail = null; } public void ancestorMoved(AncestorEvent event) { } class AsyncVideoLoader implements Runnable { File image; Thread worker; ImageReader reader; JLabel thumb; ImageInputStream iis; boolean cancelled; private final JLabel info; public AsyncVideoLoader(File image, JLabel thumb, JLabel info) { this.image = image; this.thumb = thumb; this.info = info; worker = new Thread(this); } public void execute() { worker.start(); } public void abort() { try { cancelled = true; if (worker.isAlive()) { // System.out.println("aborting thread"); worker.interrupt(); worker.join(); // System.out.println(" .. done"); } } catch (InterruptedException ex) { Logger.getLogger(AsyncVideoLoader.class.getName()).log( Level.SEVERE, null, ex); } } Runnable setIcon = new Runnable() { public void run() { if (thumb.getIcon() == null) { thumb.setIcon(new ImageIcon(preview)); } else { thumb.repaint(); } } }; @Override public void run() { JJMediaReader mr = null; JJMediaReader.JJReaderVideo vr = null; BufferedImage preview_tmp; try { info.setText(null); thumb.setText(null); Graphics2D gg = preview.createGraphics(); gg.setColor(Color.black); gg.fillRect(0, 0, preview.getWidth(), preview.getHeight()); mr = new JJMediaReader(image.getPath()); vr = mr.openFirstVideoStream(); if (vr == null) { thumb.setText("<empty>"); return; } int height = vr.getHeight(); int width = vr.getWidth(); PixelFormat fmt = vr.getPixelFormat(); info.setText(String .format("<html><b>Type:</b> %s<br><b>Size:</b> %dx%d<br><b>Format:</b> %s\n", vr.getCodec().getName(), width, height, fmt)); int delay = (int) (1000 * vr.getContext().getTimeBase() .q2d()); if (delay < 10) { System.out.printf( "suspect time delay = %d, resetting to 20ms\n", delay); delay = 40; } final int w, h; if (width > height) { h = 256 * height / width; w = 256; } else { w = 256 * width / height; h = 256; } vr.setOutputFormat(PixelFormat.PIX_FMT_BGR24, w, h); preview_tmp = vr.createImage(); while (!cancelled && (vr = (JJReaderVideo) mr.readFrame()) != null) { vr.getOutputFrame(preview_tmp); // Use java to centre it gg.setColor(Color.black); gg.fillRect(0, 0, preview.getWidth(), preview.getHeight()); int l = (preview.getWidth() - w) / 2; int t = (preview.getHeight() - h) / 2; gg.drawImage(preview_tmp, l, t, l + w, t + h, 0, 0, w, h, null); SwingUtilities.invokeAndWait(setIcon); Thread.sleep(delay); } } catch (InterruptedException x) { } catch (Exception x) { System.out.println("ex reading image" + x); info.setText("Unable to read video"); x.printStackTrace(); } finally { if (mr != null) { // System.out.println("closing video file"); mr.dispose(); } } } } } public JJFileChooser() { super(); setAccessory(new VideoInfo(this)); // HACK: change mode to detail mode to make it nicer to use try { JPanel rootPanel = (JPanel) getComponent(0); JPanel buttonPanel = (JPanel) rootPanel.getComponent(0); int len = buttonPanel.getComponentCount(); int seen = 0; for (int i = 0; i < len; i++) { Component c = buttonPanel.getComponent(i); if (c instanceof JToggleButton) { seen++; if (seen == 2) { // ((JToggleButton)c).setSelected(true); ((JToggleButton) c).doClick(); } } } } catch (Exception x) { System.out.println("i made a mistake: " + x.getMessage()); } setPreferredSize(new Dimension(500 + 256, 600)); AVFormatContext.registerAll(); } public static void main(String[] args) { if (true) { try { UIManager .setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); } catch (Exception ex) { } } SwingUtilities.invokeLater(new Runnable() { public void run() { JJFileChooser vfc = new JJFileChooser(); vfc.showOpenDialog(null); } }); } }