Package util

Source Code of util.MovieUtils

package util;

//----- JDK Imports ------------------------------------------------------------
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import javax.swing.JDialog;
import javax.swing.JPanel;

//----- Quicktime Imports ------------------------------------------------------
import quicktime.QTException;
import quicktime.io.IOConstants;
import quicktime.io.OpenMovieFile;
import quicktime.io.QTFile;
import quicktime.qd.Pict;
import quicktime.qd.QDColor;
import quicktime.qd.QDConstants;
import quicktime.qd.QDDimension;
import quicktime.qd.QDFont;
import quicktime.qd.QDGraphics;
import quicktime.qd.QDRect;
import quicktime.std.StdQTConstants;
import quicktime.std.StdQTException;
import quicktime.std.clocks.TimeRecord;
import quicktime.std.image.CSequence;
import quicktime.std.image.CodecComponent;
import quicktime.std.image.CompressedFrameInfo;
import quicktime.std.image.GraphicsImporter;
import quicktime.std.image.GraphicsMode;
import quicktime.std.image.ImageDescription;
import quicktime.std.image.Matrix;
import quicktime.std.image.QTImage;
import quicktime.std.movies.Movie;
import quicktime.std.movies.TimeInfo;
import quicktime.std.movies.Track;
import quicktime.std.movies.media.DataRef;
import quicktime.std.movies.media.Media;
import quicktime.std.movies.media.SoundMedia;
import quicktime.std.movies.media.TextMedia;
import quicktime.std.movies.media.TextMediaHandler;
import quicktime.std.movies.media.TimeCodeMedia;
import quicktime.std.movies.media.VideoMedia;
import quicktime.std.movies.media.VisualMediaHandler;
import quicktime.std.qtcomponents.TCTextOptions;
import quicktime.std.qtcomponents.TimeCodeDef;
import quicktime.std.qtcomponents.TimeCodeDescription;
import quicktime.std.qtcomponents.TimeCodeTime;
import quicktime.std.qtcomponents.TimeCoder;
import quicktime.util.EndianOrder;
import quicktime.util.QTHandle;
import quicktime.util.QTPointer;
import quicktime.util.QTUtils;
import quicktime.util.RawEncodedImage;

//----- SISC Imports -----------------------------------------------------------
import sisc.data.Pair;
import sisc.interpreter.SchemeException;

//----- Phoenix Imports --------------------------------------------------------
import controller.PhoenixController;

/**
* Video Phoenix
* Version 0.2.0
* Copyright (c) 2007 Lunderskov, Ian; Pan, Jiabei; Rebelsky, Samuel;
* Whisenhunt, Heather; Young, Ian; Zuleta Benavides, Luis.
* All rights reserved.
* 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.
* @author  Lunderskov, Ian; Pan, Jiabei; Rebelsky, Samuel; Whisenhunt, Heather;
*          Young, Ian; Zuleta Benavides, Luis
* @author  Glimmer Labs 2006-2007
* @version 0.2.0
*/
public class MovieUtils
{
  /*---------*------------------------------------------------------------------
   * Methods *
   *---------*/

  /**
   * Create a copy of a movie
   *
   * @param mov    Movie to copy
   * @return copy  Copy of mov
   */
  public static Movie cloneMovie(Movie mov)
  {
    Movie movie = null;
    try
    {
      movie = new Movie();
      mov.insertSegment(movie, 0, mov.getDuration(), 0);
      movie.setDefaultDataRef(new DataRef(new QTHandle()));
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return movie;
  } // cloneMovie(Movie)

  /**
   * Add an audio track to a movie
   *
   * @param source  Audio track to add
   * @param target  Movie to add audio track to
   * @return track  Track in target containing contents of source
   * @throws QTException
   */
  public static Track addAudioTrack(Track source, Movie target)
  throws QTException
  {
    Track targetTrack = target.newTrack(0f, 0f, 20);
    @SuppressWarnings("unused")
    SoundMedia media = new SoundMedia(targetTrack,
      source.getMedia().getTimeScale(), new DataRef(new QTHandle()));
    source.insertSegment(targetTrack, 0, source.getDuration(), 0);
    return targetTrack;
  } // addAudioTrack(Track, Movie)

  /**
   * Add a text track to a movie
   *
   * @param mov          Movie to add text track to
   * @param text         Text to be added
   * @param fontName     Font name
   * @param fontOptions  Font style (e.g. "bold", "italic", "underline", ...)
   * @param fontSize     Font size
   * @param fgColor      RGB values for text color (e.g. [0,0,0] for black)
   * @param bgColor      RGB values for box color (e.g. [0,0,0] for black)
   * @param x            X-coordinate on mov of upper left corner of text box
   * @param y            Y-coordinate on mov of upper left corner of text box
   * @param start        Time in mov to begin displaying text track
   * @param duration     Duration in seconds to display text track
   * @return result      Movie with text track
   */
  // Adapted from
  // Chris Adamson, QuickTime for Java: A Developer's Notebook
  // Published by O'Reilly, 2005
  // p. 190
  public static Movie addTextTrack(Movie mov, String text, String fontName,
    String fontOptions, int fontSize, int[] fgColor, int[] bgColor, int x,
    int y, float start, float duration)
  {
    // make a copy of the original movie
    Movie movie = MovieUtils.cloneMovie(mov);
    try
    {
      movie = new Movie();
      mov.insertSegment(movie, 0, mov.getDuration(), 0);
      movie.setDefaultDataRef(new DataRef(new QTHandle()));
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)

    // determine opaque / transparent background color; transparency is
    // indicated by a -1 in the first cell of bgColor
    int dfFlag = 0;
    if (bgColor[0] == -1)
    {
      dfFlag = StdQTConstants.dfAntiAlias | StdQTConstants.dfKeyedText;
    } // if (bgColor[0] == -1)
    else
    {
      dfFlag = StdQTConstants.dfAntiAlias;
    } // else

    // set up the Java font to estimate dimensions of the actual text
    // track using QDFont
    Font font = null;
    int style = 0;
    if (fontOptions.equalsIgnoreCase("plain"))
    {
      style = QDConstants.normal;
      font = new Font(fontName, Font.PLAIN, fontSize);
    } // if (fontOptions.equalsIgnoreCase("plain"))
    else if (fontOptions.equalsIgnoreCase("bold"))
    {
      style = QDConstants.bold;
      font = new Font(fontName, Font.BOLD, fontSize);
    } // else if (fontOptions.equalsIgnoreCase("bold"))
    else if (fontOptions.equalsIgnoreCase("italic"))
    {
      style = QDConstants.italic;
      font = new Font(fontName, Font.ITALIC, fontSize + 2);
    } // else if (fontOptions.equalsIgnoreCase("italic"))
    else if (fontOptions.equalsIgnoreCase("bolditalic"))
    {
      style = QDConstants.bold | QDConstants.italic;
      font = new Font(fontName, Font.PLAIN + Font.ITALIC, fontSize + 5);
    } // else if (fontOptions.equalsIgnoreCase("bolditalic"))
    else if (fontOptions.equalsIgnoreCase("underline"))
    {
      style = QDConstants.underlined;
      font = new Font(fontName, Font.ITALIC + Font.BOLD, fontSize);
    } // else if (fontOptions.equalsIgnoreCase("underline"))
    else if (fontOptions.equalsIgnoreCase("boldunderline"))
    {
      style = QDConstants.underlined | QDConstants.bold;
      font = new Font(fontName, Font.ITALIC + Font.BOLD, fontSize + 1);
    } // else if (fontOptions.equalsIgnoreCase("boldunderline"))
    else if (fontOptions.equalsIgnoreCase("italicunderline"))
    {
      style = QDConstants.underlined | QDConstants.italic;
      font = new Font(fontName, Font.ITALIC + Font.BOLD, fontSize + 1);
    } // else if (fontOptions.equalsIgnoreCase("italicunderline"))
    else if (fontOptions.equalsIgnoreCase("bolditalicunderline"))
    {
      style = QDConstants.underlined| QDConstants.italic | QDConstants.bold;
      font = new Font(fontName, Font.ITALIC + Font.BOLD, fontSize + 1);
    } // else if (fontOptions.equalsIgnoreCase("bolditalicunderline"))
    JDialog tempF = new JDialog();
    JPanel temp = new JPanel();
    temp.setFont(font);
    tempF.add(temp);

    // must be visible, otherwise g2 is null
    tempF.setVisible(true);
    temp.setVisible(true);
    Graphics2D g2 = (Graphics2D)temp.getGraphics();
    g2.setFont(font);
    g2.drawString(text, 0, 0);
    tempF.setVisible(false);
    FontRenderContext context = g2.getFontRenderContext();
    Rectangle2D rect = font.getStringBounds(text, context);
    int TEXT_TRACK_WIDTH = (int)rect.getWidth();
    int TEXT_TRACK_HEIGHT = (int)rect.getHeight();
    temp = null;
    tempF = null;
    QDRect textBox = new QDRect(0, 0, TEXT_TRACK_WIDTH, TEXT_TRACK_HEIGHT);

    // add text track
    Track textTrack;
    try
    {
      textTrack = movie.addTrack(TEXT_TRACK_WIDTH, TEXT_TRACK_HEIGHT, 0);
      Media textMedia = new TextMedia(textTrack, movie.getTimeScale());
      TextMediaHandler handler = (TextMediaHandler)textMedia.getHandler();
      textMedia.beginEdits();
      byte[] msgBytes = text.getBytes();
      QTPointer msgPoint = new QTPointer(msgBytes);
      handler.addTextSample(msgPoint, QDFont.getFNum(fontName), fontSize, style,
        new QDColor(fgColor[0] / 255f, fgColor[1] / 255f, fgColor[2] / 255f),
        new QDColor(bgColor[0] / 255f, bgColor[1] / 255f, bgColor[2] / 255f),
        QDConstants.teJustLeft, textBox, dfFlag, QDFont.getFNum(fontName), 0, 0,
        QDColor.white, Math.round(duration * movie.getTimeScale()));
      textMedia.endEdits();
      textTrack.insertMedia(Math.round(start * movie.getTimeScale()), 0,
        textMedia.getDuration(), 1);

      // position the text track to (x, y)
      Matrix mat = new Matrix();
      mat.rect(new QDRect(0, 0, TEXT_TRACK_WIDTH, TEXT_TRACK_HEIGHT),
        new QDRect(x, y, TEXT_TRACK_WIDTH, TEXT_TRACK_HEIGHT));
      textTrack.setMatrix(mat);
    } // try
    catch (Exception e)
    {
      e.printStackTrace();
    } // catch (Exception)
    return movie;
  } // addTextTrack(Movie, String, String, String, int, int[], int[], int, ...

  /**
   * Add a video track to a movie
   *
   * @param source  Video track to add
   * @param target  Movie to add video track to
   * @return track  Track in target containing contents of source
   * @throws QTException
   */
  public static Track addVideoTrack(Track source, Movie target)
  throws QTException
  {
    Track targetTrack = target.newTrack(source.getSize().getWidthF(),
      source.getSize().getHeightF(), 1.0f);
    @SuppressWarnings("unused")
    VideoMedia media = new VideoMedia(targetTrack,
      source.getMedia().getTimeScale(), new DataRef(new QTHandle()));
    int duration = source.getDuration();
    source.insertSegment(targetTrack, 0, duration, 0);
    return targetTrack;
  } // addVideoTrack(Track, Movie)

  /**
   * Add a time code track to a movie
   *
   * @param mov          Movie to add time code track to
   * @param fontName     Font name
   * @param fontOptions  Font style (e.g. "bold", "bolditalic", "italic", ...)
   * @param fontSize     Font size
   * @param fgColor      RGB values for text color (e.g. [0,0,0] for black)
   * @param bgColor      RGB values for box color (e.g. [0,0,0] for black)
   * @param x            X-coordinate on mov of upper left corner of text box
   * @param y            Y-coordinate on mov of upper left corner of text box
   * @param width        Width of text box in pixels
   * @param height       Height of text box in pixels
   * @return result      Movie with time code track
   * @throws QTException
   */
  // Adapted from
  // Chris Adamson, QuickTime for Java: A Developer's Notebook
  // Published by O'Reilly, 2005
  // p. 197
  public static Movie addTimeCode(Movie mov, String fontName,
    String fontOptions, int fontSize, int[] fgColor, int[] bgColor, int x,
    int y, int width, int height)
  throws QTException
  {
    // make a new copy of mov
    Movie movie = cloneMovie(mov);
    int timescale = movie.getTimeScale();

    TimeCodeDef tcDef = new TimeCodeDef();
    tcDef.setTimeScale(movie.getTimeScale());
    int fps = MovieUtils.countFrames(movie, 0, 1);

    tcDef.setFrameDuration(movie.getTimeScale() / fps);
    tcDef.setFramesPerSecond(fps);
    tcDef.setFlags(StdQTConstants.tcDropFrame);

    // first record at 0 hrs, 0 min, 0 sec, 0 frames
    TimeCodeTime tcTime = new TimeCodeTime (0, 0, 0, 0);

    // create time code track and media
    Track tcTrack = movie.addTrack (width, height, 0);
    TimeCodeMedia tcMedia = new TimeCodeMedia (tcTrack, timescale);
    TimeCoder timeCoder = tcMedia.getTimeCodeHandler();

    int style = 0;
    if (fontOptions.equalsIgnoreCase("plain"))
    {
      style = QDConstants.normal;
    } // if (fontOptions.equalsIgnoreCase("plain"))
    else if (fontOptions.equalsIgnoreCase("bold"))
    {
      style = QDConstants.bold;
    } // else if (fontOptions.equalsIgnoreCase("bold"))
    else if (fontOptions.equalsIgnoreCase("italic"))
    {
      style = QDConstants.italic;
    } // else if (fontOptions.equalsIgnoreCase("italic"))
    else if (fontOptions.equalsIgnoreCase("bolditalic"))
    {
      style = QDConstants.bold | QDConstants.italic;
    } // else if (fontOptions.equalsIgnoreCase("bolditalic"))
    else if (fontOptions.equalsIgnoreCase("underline"))
    {
      style = QDConstants.underlined;
    } // else if (fontOptions.equalsIgnoreCase("underline"))
    else if (fontOptions.equalsIgnoreCase("boldunderline"))
    {
      style = QDConstants.underlined | QDConstants.bold;
    } // else if (fontOptions.equalsIgnoreCase("boldunderline"))
    else if (fontOptions.equalsIgnoreCase("italicunderline"))
    {
      style = QDConstants.underlined | QDConstants.italic;
    } // else if (fontOptions.equalsIgnoreCase("italicunderline"))
    else if (fontOptions.equalsIgnoreCase("bolditalicunderline"))
    {
      style = QDConstants.underlined| QDConstants.italic | QDConstants.bold;
    } // else if (fontOptions.equalsIgnoreCase("bolditalicunderline"))

    // turn on time code display, set colors
    timeCoder.setFlags(timeCoder.getFlags() | StdQTConstants.tcdfShowTimeCode,
      StdQTConstants.tcdfShowTimeCode);
    TCTextOptions tcTextOptions = timeCoder.getDisplayOptions();
    tcTextOptions.setTXSize(fontSize);
    tcTextOptions.setTXFace(style);
    tcTextOptions.setTXFont(QDFont.getFNum(fontName));
    tcTextOptions.setForeColor(new QDColor(fgColor[0] / 255f, fgColor[1] / 255f,
      fgColor[2] / 255f));

    // if transparency is indicated, set background color to a special value
    if (bgColor[0] == -1)
    {
      tcTextOptions.setBackColor(new QDColor(0.1f, 0.7f, 0.43f));
    } // if (bgColor[0] == -1)
    else
    {
      tcTextOptions.setBackColor(new QDColor(bgColor[0] / 255f,
        bgColor[1] / 255f, bgColor[2] / 255f));
    } // else
    timeCoder.setDisplayOptions(tcTextOptions);

    // set up a sample as a 4-byte array in a QTHandle
    int frameNumber = timeCoder.toFrameNumber(tcTime, tcDef);
    int frameNums[] = new int[1];

    // BOOK ERRATA: this is buggy on Windows for time codes other
    // than 00:00:00;00.  You need to adjust for endianness, as
    // seen in the revised (uncommented) line.
    // frameNums[0] = frameNumber;
    frameNums[0] = EndianOrder.flipNativeToBigEndian32(frameNumber);
    QTHandle frameNumHandle = new QTHandle (4, false);
    frameNumHandle.copyFromArray(0, frameNums, 0, 1);

    // create a time code description (sample to be added)
    TimeCodeDescription tcDesc = new TimeCodeDescription();
    tcDesc.setTimeCodeDef (tcDef);

    // add the sample to the TimeCodeMedia
    tcMedia.beginEdits();
    tcMedia.addSample(frameNumHandle, 0, frameNumHandle.getSize(),
      movie.getDuration(), tcDesc, 1, 0);
    tcMedia.endEdits();

    // insert media into track
    tcTrack.insertMedia(0, 0, tcMedia.getDuration(), 1);

    // set a transparent-background GrahpicsMode
    QDRect moveFrom = new QDRect(0, 0, width, height);
    QDRect moveTo = new QDRect(x, y, width, height);
    Matrix matrix = new Matrix();
    matrix.rect(moveFrom, moveTo);
    tcTrack.setMatrix (matrix);

    // if transparency is indicated, make the special value the transparent
    // color
    if (bgColor[0] == -1)
    {
      timeCoder.setGraphicsMode(new GraphicsMode(QDConstants.transparent,
        new QDColor(0.1f, 0.7f, 0.43f)));
    } // if (bgColor[0] == -1)
    return movie;
  } // addTimeCode(Movie, String, String, int, int[], int[], int, int, int, int)

  /**
   * Append two movies
   *
   * @param mov1     Movie to append
   * @param mov2     Movie to append
   * @return result  mov2 appended to end of mov1
   */
  public static Movie movieAppend(Movie mov1, Movie mov2)
  {
    Movie composite = null;
    Movie movie1 = null;
    Movie movie2 = null;
    try
    {
      QDRect box1 = mov1.getBox();
      QDRect box2 = mov2.getBox();
      int height1 = box1.getHeight();
      int height2 = box2.getHeight();
      int width1 = box1.getWidth();
      int width2 = box2.getWidth();

      // variables for the matrix translations
      float a = 0, b = 0, c = 0, d = 0;
      movie1 = new Movie();
      movie2 = new Movie();
      composite = new Movie();

      // insert the movies to new movie files, in order to resize without
      // affecting originals
      mov1.insertSegment(movie1, 0, mov1.getDuration(), 0);
      mov2.insertSegment(movie2, 0, mov2.getDuration(), 0);
      Track track1 = movie1.getIndTrackType(1,
        StdQTConstants.videoMediaType,
        StdQTConstants.movieTrackMediaType);
      Track track2 = movie2.getIndTrackType(1,
        StdQTConstants.videoMediaType,
        StdQTConstants.movieTrackMediaType);
      Matrix matrix1 = track1.getMatrix();
      Matrix matrix2 = track2.getMatrix();

      // translate tracks based on size
      if (box1.equals(box2))
      {
        // do nothing - no resizing needed
      } // if (box1.equals(box2))
      else if ((height1 >= height2) && (width1 >= width2))
      {
        c = (width1 - width2) / 2;
        d = (height1 - height2) / 2;
      } // else if ((height1 >= height2) && (width1 >= width2))
      else if ((height2 >= height1) && (width2 >= width1))
      {
        a = (width2 - width1) / 2;
        b = (height2 - height1) / 2;
      } // else if ((height2 >= height1) && (width2 >= width1))
      else if ((height1 >= height2) && (width2 >= width1))
      {
        a = (width2 - width1) / 2;
        d = (height1 - height2) / 2;
      } // else if ((height1 >= height2) && (width2 >= width1))
      else if ((height2 >= height1) && (width1 >= width2))
      {
        b = (height2 - height1) / 2;
        c = (width1 - width2) / 2;
      } // else if ((height2 >= height1) && (width1 >= width2))

      // modify matrices based on changes made in the if-clause above
      matrix1.translate(a, b);
      matrix2.translate(c, d);

      // set tracks to changed matrices
      track1.setMatrix(matrix1);
      track2.setMatrix(matrix2);

      // add the two movies with translated video tracks to the returned
      // composite
      movie2.insertSegment(composite, 0, movie2.getDuration(), 0);
      movie1.insertSegment(composite, 0, movie1.getDuration(), 0);
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return composite;
  } // movieAppend(Movie, Movie)

  /**
   * Extract a segment of movie
   *
   * @param mov       Movie to extract segment from
   * @param start     Time in mov to start extracting
   * @param end       Time in mov to stop extracting
   * @return segment  Segment of mov from start to end
   */
  public static Movie extractSegment(Movie mov, int start, int end)
  {
    return MovieUtils.extractSegment(mov, (float) start, (float) end);
  } // extractSegment(Movie, int, int)

  /**
   * Extract a segment of a movie
   *
   * @param mov       Movie to extract segment from
   * @param start     Time in mov to start extracting
   * @param end       Time in mov to stop extracting
   * @return segment  Segment of mov from start to end
   */
  public static Movie extractSegment(Movie mov, float start, float end)
  {
    Movie section = null;
    try
    {
      section = new Movie();
      int begin = Math.round(start * mov.getTimeScale());
      int finish = Math.round(end * mov.getTimeScale());
      mov.insertSegment(section, begin, finish - begin, 0);
      section.setTimeScale(mov.getTimeScale());
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return section;
  } // extractSegment(Movie, float, float)

  /**
   * Insert a movie into another movie
   *
   * @param src      Movie to insert into dest
   * @param dest     Movie to insert src into
   * @param time     Time in dest to insert src
   * @return result  dest with src inserted at time
   */
  public static Movie insertSegment(Movie src, Movie dest, float time)
  {
    Movie union = null;
    try
    {
      // create a new movie
      union = new Movie();

      // convert time from seconds to a time in dest's time scale
      int there = Math.round(time * dest.getTimeScale());

      // copy dest into new movie up until the time of insertion
      dest.insertSegment(union, 0, there, 0);

      // copy src, in its entirety, into new movie
      src.insertSegment(union, 0, src.getDuration(), union.getDuration());

      // copy rest of dest into new movie
      dest.insertSegment(union, there, dest.getDuration(), union.getDuration());
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return union;
  } // insertSegment(Movie, Movie, float)

  /**
   * Open a file chooser to save a movie
   * Types of saved movies:
   *   Movie - a QuickTime reference movie that contains only references to
   *     media in their original locations
   *   Movie, self-contained - a QuickTime movie with its own copy of all media
   *   Movie to hinted movie - a self-contained QuickTime movie but allowing
   *     user to adjust hinting settings for use in a streaming server
   *   Movie to QT movie - a self-contained QuickTime movie but allowing user to
   *     choose different compressors and settings to re-encode audio and video
   *
   * @param mov  Movie to save
   */
  public static void saveMovie(Movie mov)
  {
    try
    {
      QTFile file = new QTFile(new File("mymovie.mov"));
      int flags = StdQTConstants.createMovieFileDeleteCurFile
      | StdQTConstants.createMovieFileDontCreateResFile
      | StdQTConstants.showUserSettingsDialog;
      mov.setProgressProc();
      mov.convertToFile(file, StdQTConstants.kQTFileTypeMovie,
        StdQTConstants.kMoviePlayer, IOConstants.smSystemScript, flags);
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
  } // saveMovie(Movie)

  /**
   * Resize a movie
   *
   * @param mov      Movie to be resized
   * @param aspect   Whether aspect ratio should be held
   * @param width    Width in pixels to resize mov to
   * @param height   Height in pixels to resize mov to (ignored if aspect is
   *                   true)
   * @return result  Resized mov
   */
  public static Movie resize(Movie mov, boolean aspect, int width, int height)
  {
    Movie movie = null;
    try
    {
      // create a new copy of the movie
      movie = cloneMovie(mov);
      Track video = movie.getIndTrackType(1,
        StdQTConstants.videoMediaType,
        StdQTConstants.movieTrackMediaType);
      QDDimension dim_size = video.getSize();

      // aspect ratio before resizing.
      float originalaspect = (dim_size.getWidthF() / dim_size.getHeightF());

      //  aspect with new dimensions
      float changedaspect = ((float) width / (float) height);

      // scaling factor to adjust aspect ratios
      float scale = changedaspect * (1 / originalaspect);

      // get track matrix to preserve other scaling changes
      Matrix stretch = video.getMatrix();
      QDRect oldsize = new QDRect(0, 0, dim_size.getWidthF(),
        dim_size.getHeightF());
      QDRect newsize;

      // determine whether the aspect ratio needs to be checked/maintained
      if (aspect)
      {
        if (originalaspect != changedaspect)
        {
          height = (int) (height * scale);
        } // if (originalaspect != changedaspect)
      } // if (aspect)
      newsize = new QDRect(0, 0, width, height);
      stretch.map(oldsize, newsize);
      video.setMatrix(stretch);
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return movie;
  } // resize(Movie, boolean, int, int)

  /**
   * Replace every pixel of a key color in a movie with a corresponding pixel
   * from another movie
   *
   * @param movFore  Foreground movie
   * @param movBack  Background movie
   * @param red      Red value of key color
   * @param green    Green value of key color
   * @param blue     Blue value of key color
   * @return result  movFore with pixels of the key color replaced with movBack
   */
  // Adapted from
  // Chris Adamson, QuickTime for Java: A Developer's Notebook
  // Published by O'Reilly, 2005
  // pp. 166~171
  public static Movie chromaKey(Movie movFore, Movie movBack, int red,
    int green, int blue)
  {
    Movie result = null;
    QDColor key = new QDColor((float)red / 255, (float)green / 255,
      (float)blue / 255);
    try
    {
      result = new Movie();

      // add front track
      Track trackFore = movFore.getIndTrackType(1,
        StdQTConstants.visualMediaCharacteristic,
        StdQTConstants.movieTrackCharacteristic);
      trackFore = MovieUtils.addVideoTrack(trackFore, result);

      // add back track
      Track trackBack = movBack.getIndTrackType(1,
        StdQTConstants.visualMediaCharacteristic,
        StdQTConstants.movieTrackCharacteristic);
      trackBack = MovieUtils.addVideoTrack(trackBack, result);

      // make sure both tracks have their actual size
      Matrix matrix1 = new Matrix();
      QDRect from1 = new QDRect(0, 0, trackFore.getSize().getWidth(),
        trackFore.getSize().getHeight());
      QDRect to1 = new QDRect(0, 0,
        movFore.getDisplayBoundsRgn().getBounds().getWidth(),
        movFore.getDisplayBoundsRgn().getBounds().getHeight());
      matrix1.map(from1, to1);
      Matrix matrix2 = new Matrix();
      QDRect from2 = new QDRect(0, 0, trackBack.getSize().getWidth(),
        trackBack.getSize().getHeight());
      QDRect to2 = new QDRect(0, 0,
        movBack.getDisplayBoundsRgn().getBounds().getWidth(),
        movBack.getDisplayBoundsRgn().getBounds().getHeight());
      matrix2.map(from2, to2);

      GraphicsMode graph = new GraphicsMode(QDConstants.transparent, key);

      // make pixels with key color transparent, so back movie shows through
      VisualMediaHandler handlerFore =
        (VisualMediaHandler)trackFore.getMedia().getHandler();
      handlerFore.setGraphicsMode(graph);
      trackBack.setMatrix(matrix2);
      trackFore.setMatrix(matrix1);
      trackFore.setLayer(-1);
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return result;
  } // chromaKey(Movie, Movie, int, int, int)

  /**
   * Get a Pict from a movie
   *
   * @param mov   Movie to get Pict from
   * @param time  Time in seconds in mov to get Pict
   * @return pic  Frame in mov at time
   */
  public static Pict getMovieFrame(Movie mov, float time)
  {
    try
    {
      float oldRate = mov.getRate();
      mov.stop();
      time = time * (float) mov.getTimeScale();
      Pict pic = mov.getPict((int) time);
      mov.setRate(oldRate);
      return pic;
    } // try
    catch (Exception e)
    {
      e.printStackTrace();
      return null;
    } // catch (Exception)
  } // getMovieFrame(Movie, float)

  /**
   * Get the number of interesting frames in a section of a movie
   *
   * @param mov      Movie to count frames in
   * @param start    Time in mov to start counting frames
   * @param end      Time in mov to stop counting frames
   * @return frames  Number of frames in mov from start to end
   * @throws QTException
   */
  public static int countFrames(Movie movie, float start, float end)
  throws QTException
  {
    Movie mov = MovieUtils.cloneMovie(movie);
    int timeScale = mov.getTimeScale();
    int begin = (int)Math.floor(start * timeScale);
    int finish = (int)Math.floor(end * timeScale);
    int count = 0;

    // get the video track from mov
    Track visualTrack = mov.getIndTrackType(1,
      StdQTConstants.visualMediaCharacteristic,
      StdQTConstants.movieTrackCharacteristic);
    mov.setTime(new TimeRecord(timeScale, (int) begin));
    Boolean go = true;
    int lastTime = 0;
    do
    {
      TimeInfo ti = visualTrack.getNextInterestingTime(
        StdQTConstants.nextTimeMediaSample, mov.getTime(), 1);

      // if looped to earlier frame or finished selected section
      if ((lastTime > ti.time) || (ti.time >= finish))
      {
        go = false;
      } // if ((lastTime > ti.time) || (ti.time >= finish))
      else
      {
        lastTime = ti.time;
        mov.setTime(new TimeRecord(mov.getTimeScale(), ti.time));
        ++count;
      } // else
    } // do
    while (go);
    return count;
  } // countFrames(Movie, float, float)

  /**
   * Get all frames in a selection of a movie
   *
   * @param mov      Movie to get frames from
   * @param start    Time in mov to start getting frames
   * @param end      Time in mov to stop getting frames
   * @return frames  All frames in mov from start to end
   * @throws SchemeException
   */
  public static Pict[] getAllFrames(Movie mov, float start, float end)
  throws SchemeException
  {
    Pict[] frames;

    Vector<Pict> pics;
    frames = new Pict[0];
    pics = new Vector<Pict>();
    try
    {
      Pict frame;
      int timeScale = mov.getTimeScale();
      int begin = (int)Math.floor(start * timeScale);
      int finish = (int)Math.floor(end * timeScale);

      // get the video track from mov
      Track visualTrack = mov.getIndTrackType(1,
        StdQTConstants.visualMediaCharacteristic,
        StdQTConstants.movieTrackCharacteristic);
      mov.setTime(new TimeRecord(timeScale, (int)begin));
      Boolean go = true;
      int lastTime = 0;
      do
      {
        if (Thread.interrupted())
        {
          throw new SchemeException(new Pair(), null, null);
        } // if (Thread.interrupted())
        TimeInfo ti = visualTrack.getNextInterestingTime(
          StdQTConstants.nextTimeMediaSample, mov.getTime(), 1);

        // if looped to earlier frame or finished selected section
        if ((lastTime > ti.time) || (ti.time >= finish))
        {
          go = false;
        } // if ((lastTime > ti.time) || (ti.time >= finish))
        else
        {
          // set time to next available frame and get Pict from that time
          mov.setTime(new TimeRecord(mov.getTimeScale(), ti.time));
          frame = mov.getPict(mov.getTime());
          pics.add(frame);
        } // else
      } // do
      while (go);
    } // try

    // catch two exceptions separately in order to pass SchemeException
    // to interpreter
    catch (StdQTException stdqte)
    {
      stdqte.printStackTrace();
    } // catch (StdQTException)
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return pics.toArray(frames);
  } // getAllFrames(Movie, float, float)

  /**
   * Compile an array of Picts into a movie with only a visual track using SVQ3
   * codec
   *
   * @param pics       Pics to build into movie
   * @param frameRate  Frame rate of movie
   * @param timeScale  QuickTime time scale of movie
   * @return mov       Movie with only a visual track containing contents of
   *                   pics in order
   */
  // Adapted from
  // Chris Adamson, QuickTime for Java: A Developer's Notebook
  // Published by O'Reilly, 2005
  // p. 175
  public static Movie compileFrames(Pict[] pics, int frameRate, int timeScale)
  {
    // codec to be used for compression
    int CODEC_TYPE = QTUtils.toOSType("SVQ3");

    // width and height of movie will be width and height of first pict in pics
    float WIDTH = pics[0].getPictFrame().getWidthF();
    float HEIGHT = pics[0].getPictFrame().getHeightF();

    // video tracks don't need volume
    int VOLUME = 0;

    // make every frame a key (self-contained) frame
    int KEY_FRAME_RATE = frameRate;
    Movie newmovie = null;
    java.util.Random rand = new java.util.Random();
    try
    {
      int random = rand.nextInt();

      // create a new empty movie and a track
      String tempFile = "Phoenix" + java.lang.Integer.toString(random) + ".mov";
      QTFile movFile = new QTFile(new File(tempFile));
      newmovie = Movie.createMovieFile(movFile, StdQTConstants.kMoviePlayer,
        StdQTConstants.createMovieFileDeleteCurFile
        | StdQTConstants.createMovieFileDontCreateResFile);
      Track videoTrack = newmovie.addTrack(WIDTH, HEIGHT, VOLUME);

      // create media for new track
      VideoMedia videoMedia = new VideoMedia(videoTrack, timeScale);

      // get a GraphicsImporter
      GraphicsImporter gi = new GraphicsImporter(
        StdQTConstants.kQTFileTypePicture);

      // create an offscreen QDGraphics / GWorld the size of the picts
      // importer will draw into this and pass to CSequence
      QDGraphics gw = new QDGraphics(new QDRect(0, 0, WIDTH, HEIGHT));

      // set importer's GWorld
      gi.setGWorld(gw, null);
      QDRect gRect = new QDRect(0, 0, WIDTH, HEIGHT);

      // add images to media
      videoMedia.beginEdits();
      int frames = pics.length;
      int rawImageSize = QTImage.getMaxCompressionSize(gw, gRect,
        gw.getPixMap().getPixelSize(), StdQTConstants.codecLosslessQuality,
        CODEC_TYPE, CodecComponent.bestFidelityCodec);
      QTHandle imageHandle = new QTHandle(rawImageSize, true);
      imageHandle.lock();
      RawEncodedImage compressed = RawEncodedImage.fromQTHandle(imageHandle);
      CSequence seq = new CSequence(gw, gRect, gw.getPixMap().getPixelSize(),
        CODEC_TYPE, CodecComponent.bestFidelityCodec,
        StdQTConstants.codecLosslessQuality,
        StdQTConstants.codecLosslessQuality,
        KEY_FRAME_RATE, null, StdQTConstants.codecFlagUpdatePrevious);
      ImageDescription imgDesc = seq.getDescription();

      // attempt to loop through all frames, scaling to fit the first frame
      for (int x = 0; x < frames; x++)
      {
        QDRect srcRect = new QDRect(0, 0, pics[x].getPictFrame().getWidthF(),
          pics[x].getPictFrame().getHeightF());

        // add 512 byte header so the grapics importer will think that it's
        // dealing with a pict file

        byte[] newPictBytes = new byte[pics[x].getSize() + 512];
        pics[x].copyToArray(0, newPictBytes, 512, newPictBytes.length - 512);
        pics[x] = new Pict(newPictBytes);

        // export the pict
        DataRef ref = new DataRef(pics[x],
          StdQTConstants.kDataRefQTFileTypeTag, "PICT");
        gi.setDataReference(ref);

        // create matrix to represent scaling of each pict
        Matrix drawMatrix = new Matrix();
        drawMatrix.rect(srcRect, gRect);
        gi.setMatrix(drawMatrix);
        gi.draw();

        // compress frame
        CompressedFrameInfo cfInfo = seq.compressFrame(gw, gRect,
          StdQTConstants.codecFlagUpdatePrevious, compressed);

        // check to see if frame is a key frame
        boolean syncSample = (cfInfo.getSimilarity() == 0);
        int flags = syncSample ? 0 : StdQTConstants.mediaSampleNotSync;

        // add compressed frame to video media
        videoMedia.addSample(imageHandle, 0, cfInfo.getDataSize(),
          timeScale / frameRate, imgDesc, 1, flags);
      } // for

      // done adding samples to the media
      videoMedia.endEdits();

      // insert media into track
      videoTrack.insertMedia(0, 0, videoMedia.getDuration(), 1);

      // save changes made into file and add to movie
      OpenMovieFile omf = OpenMovieFile.asWrite(movFile);
      newmovie.addResource(omf, StdQTConstants.movieInDataForkResID,
        movFile.getName());
      // delete file created; prevent any thread problems by terminating
      // current thread
      try
      {
        Thread.sleep(1000);
      } // try
      catch (InterruptedException ie)
      {
        // do nothing
      } // catch (InterruptedException)
      movFile.deleteOnExit();
      omf.getFile().deleteOnExit();
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return newmovie;
  } // compileFrames(Pict[], int, int)

  /**
   * Save a movie in a .pict file
   *
   * @param pic        Pict to save
   * @param path       Path where pict will be saved
   * @return succeeds  Whether the save operation succeeded
   */
  public static boolean saveFrame(Pict pic, String path)
  {
    boolean saved = false;
    try
    {
      QTFile file = new QTFile(path);
      pic.writeToFile(file);
      saved = true;
    } // try
    catch (QTException qte)
    {
      PhoenixController.getSchemeController().printStyledText(
        "\n IOError: Could not save the file, check your pathname and that the "
        + "file type is supported by Quicktime", "scheme_error");
    } // catch (QTException)
    catch (IOException ioe)
    {
      PhoenixController.getSchemeController().printStyledText(
        "\n IOError: Could not save the file, check your pathname and that the"
        + " file type is supported by Quicktime", "scheme_error");
    } // catch (IOException)
    return saved;
  } // saveFrame(Pict, String)

  /**
   * Get the first audio track of a movie
   *
   * @param mov     Movie to get audio track from
   * @return audio  Audio track in mov
   */
  public static Track getAudioTrack(Movie mov)
  {
    Track audio = null;
    try
    {
      Movie movie = cloneMovie(mov);
      audio = movie.getIndTrackType(1, StdQTConstants.soundMediaType,
        StdQTConstants.movieTrackMediaType);
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return audio;
  } // getAudioTrack(Movie)

  /**
   * Extract a segment of the first audio track in a movie
   *
   * @param mov     Movie to get audio track from
   * @param start   Time in mov to start getting audio track
   * @param end     Time in mov to stop getting audio track
   * @return audio  Segment of audio track in mov from start to end
   */
  public static Track extractAudioSegment(Movie mov, float start, float end)
  {
    Movie mseg = extractSegment(mov, start, end);
    return getAudioTrack(mseg);
  } // extractAudioSegment(Movie, float, float)

  /**
   * Add an audio track to a movie
   *
   * @param mov      Movie to add audio to
   * @param audio    Audio track to add to movie
   * @param start    Time in movie to start adding audio
   * @return result  mov with audio added at start
   */
  public static Movie addAudioTrack(Movie mov, Track audio, float start)
  {
    Movie newMov = null;
    try
    {
      // make a copy of mov
      newMov = cloneMovie(mov);
      Track newAudio;

      // use addEmptyTrack to make sure it has SoundMedia characteristics
      newAudio = newMov.addEmptyTrack(audio, new DataRef(new QTHandle()));
      audio.insertSegment(newAudio, 0, audio.getDuration(),
        (int)(start * newMov.getTimeScale()));
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return newMov;
  } // addAudioTrack(Movie, Track, float)

  /**
   * Add an audio track to a movie
   *
   * @param mov       Movie to add audio to
   * @param audio     Audio track to add to mov
   * @param start     Time in mov to start adding audio
   * @param duration  Length of audio to add to mov
   * @return result   mov with aduio added at start
   */
  public static Movie addAudioTrack(Movie mov, Track audio, float start,
    float duration)
  {
    Movie newMov = null;
    try
    {
      // make a copy of mov
      newMov = cloneMovie(mov);
      Track newAudio;
      newAudio = newMov.addEmptyTrack(audio, new DataRef(new QTHandle()));

      // use addEmptyTrack to make sure it has Sound Media characteristics
      audio.insertSegment(newAudio, 0, (int) (duration
          * audio.getMovie().getTimeScale()),
          (int) (start * newMov.getTimeScale()));
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return newMov;
  } // addAudioTrack(Movie, Track, float, float)

  /**
   * Remove the first audio track from a movie
   *
   * @param mov      Movie to remove an audio track from
   * @return result  mov without the first audio track
   */
  public static Movie removeAudioTrack(Movie mov)
  {
    Movie newMov = null;
    try
    {
      // make a copy of mov
      newMov = cloneMovie(mov);

      // remove first track with SoundMedia characteristics
      newMov.removeTrack(newMov.getIndTrackType(
        1, StdQTConstants.soundMediaType, StdQTConstants.movieTrackMediaType));
    } // try
    catch (QTException qte)
    {
      qte.printStackTrace();
    } // catch (QTException)
    return newMov;
  } // removeAudioTrack(Movie)
} // class MovieUtils
TOP

Related Classes of util.MovieUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.