ざっぱ〜んのプログラムで、QuickTimeムービーを作成してみました。
ということで、そのQuickTimeムービー作成部分。
JMFを使うので、ここからJMFのライブラリをとってきて、jmf.jarをクラスパスに含める必要があります。
Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle
今回はムービーの再生などは行わないので、適当なPlatformを選んでOptional FilesのCross-platform Java版を使っておくと、面倒なJMFインストールの必要がありません。
JMFを使って動画ファイルを作成するサンプルはここです
Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle
けど、このソースはそのまま他の用途で使える形ではないので、汎用で使えるものを作ってみました。
下のImageToMovクラスを使うとこんな感じでコードが書けます。
public static void main(String[] arg){ ImageReader ir = new ImageToMov.ImageReader() { public BufferedImage getImage(int idx) { if(idx >= 4) return null; BufferedImage img = new BufferedImage(400, 300, BufferedImage.TYPE_INT_RGB); Graphics g = img.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, 400, 300); g.setColor(Color.GREEN); if(idx >= 1){ g.drawLine(0, 0, 400, 300); } if(idx >= 2){ g.drawLine(0, 300, 400, 0); } if(idx >= 3){ g.setColor(Color.BLUE); g.fillOval(150, 100, 100, 100); } g.dispose(); return img; } }; String filename = "C:\\Users\\naoki\\Desktop\\test.mov"; ImageToMov.createMovFromImages(filename, 400, 300, 1, ir); }
そうすると、こんな感じのmovファイルができます。
ImageToMovクラスのソースはこんな感じで
//ImageToMov.java public class ImageToMov{ /** このインタフェースをimplementsして画像取得処理を記述 */ public interface ImageReader{ /** * 画像を返す * @param idx 画像番号 * @return 終わってるときにはnullを返す */ BufferedImage getImage(int idx); } /** このメソッドを呼び出す */ public static void createMovFromImages(String filename, int width, int height, float frameRate, ImageReader ir) { ImageDataSource ids = new ImageDataSource(width, height, frameRate, ir); Processor p; try{ p = Manager.createProcessor(ids); }catch(Exception e){ System.out.println(e.getMessage()); return; } MyControllerListener cl = new MyControllerListener(); p.addControllerListener(cl); p.configure(); if(!cl.waitForState(p, p.Configured)){ System.out.println("processorの設定失敗"); return; } p.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.QUICKTIME)); TrackControl[] tcs = p.getTrackControls(); Format[] f = tcs[0].getSupportedFormats(); if(f == null || f.length <= 0){ System.out.println(tcs[0].getFormat() + "がサポートされてない"); return; } tcs[0].setFormat(f[0]); p.realize(); if(!cl.waitForState(p, p.Realized)){ System.out.println("realizeに失敗"); return; } MediaLocator oml = null; try{ oml = new MediaLocator(new File(filename).toURI().toURL()); }catch(MalformedURLException e){ } if (oml == null) { System.err.println("Cannot build media locator"); return; } DataSource ds = p.getDataOutput(); if(ds == null){ System.out.println("datasourceに失敗"); return; } DataSink sink = null; try{ sink = Manager.createDataSink(ds, oml); sink.open(); }catch(Exception e){ e.printStackTrace(); System.exit(-1); } MyDataSinkListener dsl = new MyDataSinkListener(); sink.addDataSinkListener(dsl); p.start(); try{ sink.start(); }catch(IOException e){ System.exit(0); } dsl.waitForFileDone(); sink.close(); p.removeControllerListener(cl); } private static class MyControllerListener implements ControllerListener{ Object waitSync = new Object(); boolean stateTransitionOK = true; public void controllerUpdate(ControllerEvent evt) { if (evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent || evt instanceof PrefetchCompleteEvent) { synchronized (waitSync) { stateTransitionOK = true; waitSync.notifyAll(); } } else if (evt instanceof ResourceUnavailableEvent) { synchronized (waitSync) { stateTransitionOK = false; waitSync.notifyAll(); } } else if (evt instanceof EndOfMediaEvent) { evt.getSourceController().stop(); evt.getSourceController().close(); } } private boolean waitForState(Processor p, int state) { synchronized (waitSync) { try { while (p.getState() < state && stateTransitionOK) waitSync.wait(); } catch (Exception e) {} } return stateTransitionOK; } } private static class MyDataSinkListener implements DataSinkListener{ Object waitFileSync = new Object(); boolean fileDone = false; boolean fileSuccess = true; public void dataSinkUpdate(DataSinkEvent evt) { if (evt instanceof EndOfStreamEvent) { synchronized (waitFileSync) { fileDone = true; waitFileSync.notifyAll(); } } else if (evt instanceof DataSinkErrorEvent) { synchronized (waitFileSync) { fileDone = true; fileSuccess = false; waitFileSync.notifyAll(); } } } private boolean waitForFileDone() { synchronized (waitFileSync) { try { while (!fileDone) waitFileSync.wait(); } catch (Exception e) {} } return fileSuccess; } } private static class ImageDataSource extends PullBufferDataSource{ ImageSourceStream[] streams; public ImageDataSource(int width, int height, float frameRate, ImageReader ir) { streams = new ImageSourceStream[]{ new ImageSourceStream(width, height, ir, frameRate) }; } @Override public PullBufferStream[] getStreams() { return streams; } @Override public String getContentType() { return ContentDescriptor.RAW; } @Override public void connect() throws IOException { } @Override public void disconnect() { } @Override public void start() throws IOException { } @Override public void stop() throws IOException { } @Override public Object getControl(String arg0) { return null; } @Override public Object[] getControls() { return new Object[0]; } @Override public Time getDuration() { return DURATION_UNKNOWN; } } private static class ImageSourceStream implements PullBufferStream{ VideoFormat format; boolean ended; ImageReader imagereader; int idx = 0; public ImageSourceStream(int width, int height, ImageReader ir, float frameRate) { format = new VideoFormat(VideoFormat.JPEG, new Dimension(width, height), Format.NOT_SPECIFIED, Format.byteArray, frameRate); imagereader = ir; } public boolean willReadBlock() { return false; } public void read(Buffer buf) throws IOException { //画像読み込み処理 //処理を考える必要があるのはここだけ。 BufferedImage img = imagereader.getImage(idx); idx++; if(img == null){ //終わってる buf.setEOM(true); buf.setOffset(0); buf.setLength(0); ended = true; return; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(img, "JPEG", baos); byte[] data = baos.toByteArray(); byte[] b = null; if(buf.getData() instanceof byte[]){ b = (byte[]) buf.getData(); } if(b == null || b.length < data.length){ b = new byte[data.length]; buf.setData(b); } for(int i = 0; i < data.length; ++i){ b[i] = data[i]; } buf.setData(data); buf.setOffset(0); buf.setLength(data.length); buf.setFormat(format); buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME); } public Format getFormat() { return format; } public ContentDescriptor getContentDescriptor() { return new ContentDescriptor(ContentDescriptor.RAW); } public long getContentLength() { return 0; } public boolean endOfStream() { return ended; } public Object[] getControls() { return new Object[0]; } public Object getControl(String arg0) { return null; } } }