![]() | Source code below from: QuickTime for Java: A Developer's Notebook By Chris Adamson Published 14 January, 2005 Average rating
Powells
Alibris
|
/* Copyright (c) 2004, Chris Adamson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.oreilly.qtjnotebook.ch08; import quicktime.*; import quicktime.io.*; import quicktime.util.QTPointer; import quicktime.qd.*; import quicktime.std.*; import quicktime.std.movies.*; import quicktime.std.movies.media.*; import quicktime.std.image.*; import quicktime.util.*; import com.oreilly.qtjnotebook.ch01.QTSessionCheck; import java.io.*; import java.util.Random; import java.util.Properties; public class VideoSampleBuilder extends Object { public static final int VIDEO_TRACK_WIDTH = 320; public static final int VIDEO_TRACK_HEIGHT = 240; public static final int VIDEO_TRACK_VOLUME = 0; public static final int KEY_FRAME_RATE = 30; Properties userProps = new Properties(); QDRect startRect = null; QDRect endRect = null; public VideoSampleBuilder() throws QTException, IOException { /* try to load "videoSampleBuilder.properties" from current directory. this contains file.location and start.x/y/width/height and end.x/y/width/height params */ try { userProps.load (new FileInputStream (new File ("videosamplebuilder.properties"))); System.out.println ("Loaded \"videosamplebuilder.properties\""); } catch (Exception e) { System.out.println ("Couldn't load \"videosamplebuilder.properties"); } int CODEC_TYPE = QTUtils.toOSType ("SVQ3"); // int CODEC_TYPE = QTUtils.toOSType("mp4v"); // create a new empty movie QTFile movFile = new QTFile (new java.io.File("videotrack.mov")); Movie movie = Movie.createMovieFile(movFile, StdQTConstants.kMoviePlayer, StdQTConstants.createMovieFileDeleteCurFile | StdQTConstants.createMovieFileDontCreateResFile); System.out.println ("Created Movie"); // now create an empty video track int timeScale = 600; // 100 units per second Track videoTrack = movie.addTrack (VIDEO_TRACK_WIDTH, VIDEO_TRACK_HEIGHT, VIDEO_TRACK_VOLUME); System.out.println ("Added empty Track"); // now we need media for this track VideoMedia videoMedia = new VideoMedia(videoTrack, timeScale); // get image file from props or dialog QTFile imgFile = getImageFile(); if (imgFile == null) return; // get a GraphicsImporter GraphicsImporter importer = new GraphicsImporter (imgFile); System.out.println ("Got GraphicsImporter - Bounds are " + importer.getNaturalBounds()); // Create an offscreen QDGraphics / GWorld that's the // size of our frames. Importer will draw into this, // and we'll then hand it to the CSequence QDGraphics gw = new QDGraphics (new QDRect (0, 0, VIDEO_TRACK_WIDTH, VIDEO_TRACK_HEIGHT)); System.out.println ("Created GWorld, - Bounds are " + gw.getBounds()); // get start, end rects getRects (importer); System.out.println ("startRect = " + startRect); System.out.println ("endRect = " + endRect); // set importer's gworld importer.setGWorld (gw, null); System.out.println ("Reset importer's GWorld, now: " + importer.getGWorld()); // get to work videoMedia.beginEdits(); // figure out per-frame offsets QDRect gRect = new QDRect (0, 0, VIDEO_TRACK_WIDTH, VIDEO_TRACK_HEIGHT); int frames = 300; int startX = startRect.getX(); int startY = startRect.getY(); int endX = endRect.getX(); int endY = endRect.getY(); float xOffPerFrame = ((float)(endX - startX) / (float)frames); float yOffPerFrame = ((float)(endY - startY) / (float)frames); float widthOffPerFrame = ((float) (endRect.getWidth() - startRect.getWidth()) / (float) frames); float heightOffPerFrame = ((float) (endRect.getHeight() - startRect.getHeight()) / (float) frames); System.out.println ("xOffPerFrame=" + xOffPerFrame + ", yOffPerFrame=" + yOffPerFrame + ", widthOffPerFrame=" + widthOffPerFrame + ", heightOffPerFrame=" + heightOffPerFrame); // reserve an image with enough space to hold compressed image // this is needed by the last arg of CSequence.compressFrame int rawImageSize = QTImage.getMaxCompressionSize (gw, gRect, gw.getPixMap().getPixelSize(), StdQTConstants.codecNormalQuality, CODEC_TYPE, // CodecComponent.anyCodec); CodecComponent.bestFidelityCodec); QTHandle imageHandle = new QTHandle (rawImageSize, true); imageHandle.lock(); RawEncodedImage compressedImage = RawEncodedImage.fromQTHandle(imageHandle); // create a CSequence /* see behavior flags at http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESI/compresssequencebegin.htm#//apple_ref/c/func/CompressSequenceBegin */ CSequence seq = new CSequence (gw, gRect, gw.getPixMap().getPixelSize(), CODEC_TYPE, CodecComponent.bestFidelityCodec, StdQTConstants.codecNormalQuality, StdQTConstants.codecNormalQuality, KEY_FRAME_RATE, null, 0); // remember an ImageDescription from this sequence definition ImageDescription imgDesc = seq.getDescription(); // loop through the specified number of frames, drawing // scaled instances into our GWorld and compressing those // to the CSequence for (int i=1; i<frames; i++) { System.out.println ("i==" + i); // compute a rect for this frame int x = startX + (int) (xOffPerFrame * i); int y = startY + (int) (yOffPerFrame * i); int width = startRect.getWidth() + (int) (widthOffPerFrame * i); int height = startRect.getHeight() + (int) (heightOffPerFrame * i); QDRect fromRect = new QDRect (x, y, width, height); // create a Matrix to represent the move/scale from // the fromRect to the GWorld and make importer use it Matrix drawMatrix = new Matrix(); drawMatrix.rect (fromRect, gRect); System.out.println ("fromRect = " + fromRect); importer.setMatrix (drawMatrix); // have importer draw (scaled) into our GWorld importer.draw(); System.out.println ("Importer drew"); // compress a frame /* behavior flags at http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESI/compresssequenceframe.htm#//apple_ref/c/func/CompressSequenceFrame */ CompressedFrameInfo cfInfo = seq.compressFrame (gw, gRect, StdQTConstants.codecFlagUpdatePrevious, compressedImage); System.out.println ("similarity = " + cfInfo.getSimilarity()); // see http://developer.apple.com/qa/qtmcc/qtmcc20.html // for explanation of mediaSampleNotSync boolean syncSample = (cfInfo.getSimilarity() == 0); int flags = syncSample ? 0 : StdQTConstants.mediaSampleNotSync; // add compressed frame to the video media videoMedia.addSample (imageHandle, 0, cfInfo.getDataSize(), 20, // time per frame, in timescale imgDesc, 1, // one sample flags ); } // for // done editing videoMedia.endEdits(); // now insert this media into track videoTrack.insertMedia (0, // trackStart 0, // mediaTime videoMedia.getDuration(), // mediaDuration 1); // mediaRate System.out.println ("inserted media into video track"); // save up System.out.println ("Saving..."); OpenMovieFile omf = OpenMovieFile.asWrite (movFile); movie.addResource (omf, StdQTConstants.movieInDataForkResID, movFile.getName()); System.out.println ("Done"); } /** Gets imageFile from props file, or file-preview if that doesn't work. */ protected QTFile getImageFile () throws QTException { // is it in the props? QTFile imageFile = null; if (userProps.containsKey ("file")) { imageFile = new QTFile (userProps.getProperty("file")); if (! imageFile.exists()) imageFile = null; } // if not, or if that failed, then use a dialog if (imageFile == null) { int[] types = {}; imageFile = QTFile.standardGetFilePreview (types); } return imageFile; } /** Gets startRect, endRect from userProps, or selects randomly if that doesn't work */ protected void getRects (GraphicsImporter importer) throws QTException { Random rand = new Random(); int rightStop = importer.getNaturalBounds().getWidth() - VIDEO_TRACK_WIDTH; int bottomStop = importer.getNaturalBounds().getHeight() - VIDEO_TRACK_HEIGHT; // try to get startRect from userProps try { int startX = Integer.parseInt (userProps.getProperty("start.x")); int startY = Integer.parseInt (userProps.getProperty("start.y")); int startWidth = Integer.parseInt (userProps.getProperty("start.width")); int startHeight = Integer.parseInt (userProps.getProperty("start.height")); startRect = new QDRect (startX, startY, startWidth, startHeight); } catch (Exception e) { // make random start rect int startX = Math.abs (rand.nextInt() % rightStop); int startY = Math.abs (rand.nextInt() % bottomStop); startRect = new QDRect (startX, startY, VIDEO_TRACK_WIDTH, VIDEO_TRACK_HEIGHT); } // try to get endRect from userProps try { int endX = Integer.parseInt (userProps.getProperty("end.x")); int endY = Integer.parseInt (userProps.getProperty("end.y")); int endWidth = Integer.parseInt (userProps.getProperty("end.width")); int endHeight = Integer.parseInt (userProps.getProperty("end.height")); endRect = new QDRect (endX, endY, endWidth, endHeight); } catch (Exception e) { float zoom = (rand.nextFloat() - 0.5f); // -0.5 <= zoom <= 0.5 System.out.println ("zoom = " + zoom); int endX = Math.abs (rand.nextInt() % rightStop); int endY = Math.abs (rand.nextInt() % bottomStop); endRect = new QDRect (endX, endY, VIDEO_TRACK_WIDTH * zoom, VIDEO_TRACK_HEIGHT * zoom); } } public static void main (String[] arrrImAPirate) { try { QTSessionCheck.check(); new VideoSampleBuilder(); } catch (Exception e) { e.printStackTrace(); } System.exit(0); } }