Class QuickTimeWriter

java.lang.Object
org.monte.media.quicktime.QuickTimeWriter
All Implemented Interfaces:
Closeable, AutoCloseable, MovieWriter, Multiplexer

public class QuickTimeWriter extends Object implements MovieWriter
Supports writing of time-based video and audio data into a QuickTime movie file (.MOV) without the need of native code.

QuickTimeWriter works with tracks and samples. After creating a QuickTimeWriter one or more video and audio tracks can be added to it. Then samples can be written into the track(s). A sample is a single element in a sequence of time-ordered data. For video data a sample typically consists of a single video frame, for uncompressed stereo audio data a sample contains one PCM impulse per channel. Samples of compressed media data may encompass larger time units.

Tracks support edit lists. An edit list specifies when to play which portion of the media data at what speed. An empty edit can be used to insert an empty time span, for example to offset a track from the start of the movie. Edits can also be used to play the same portion of media data multiple times without having it to store it more than once in the track.
Moreover edit lists are useful for lossless cutting of media data at non-sync frames. For example, MP3 layer III audio data can not be cut at arbitrary frames, because audio data can be 'borrowed' from previous frames. An edit list can be used to select the desired portion of the audio data, while the track stores the media starting from the nearest sync frame.

Samples are stored in a QuickTime file in the same sequence as they are written. In order to getCodec optimal movie playback, the samples from different tracks should be interleaved from time to time. An interleave should occur about twice per second. Furthermore, to overcome any latencies in sound playback, at least one second of sound data needs to be placed at the beginning of the movie. So that the sound and video data is offset from each other in the file by one second.

For convenience, this class has built-in encoders for video frames in the following formats: RAW, ANIMATION, JPEG and PNG. Media data in other formats, including all audio data, must be encoded before it can be written with QuickTimeWriter. Alternatively, you can plug in your own codec.

Example: Writing 10 seconds of a movie with 640x480 pixel, 30 fps, PNG-encoded video and 16-bit stereo, 44100 Hz, PCM-encoded audio.

 QuickTimeWriter w = new QuickTimeWriter(new File("mymovie.mov"));
 w.addAudioTrack(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED), 44100, 2, 16, 2, 44100, true)); // audio in track 0
 w.addVideoTrack(QuickTimeWriter.VIDEO_PNG, 30, 640, 480);  // video in track 1

 // calculate total movie sampleDuration in media time units for each track
 long atmax = w.getMediaTimeScale(0) * 10;
 long vtmax = w.getMediaTimeScale(1) * 10;

 // sampleDuration of a single sample
 long asduration = 1;
 long vsduration = 1;

 // half a second in media time units (we interleave twice per second)
 long atstep = w.getMediaTimeScale(0) / 2;
 long vtstep = w.getMediaTimeScale(1) / 2;

 // the time when the next interleave occurs in media time units
 long atnext = w.getMediaTimeScale(0); // offset audio by 1 second
 long vtnext = 0;

 // the current time in media time units
 long atime = 0;
 long vtime = 0;

 // create buffers
 int asamplesize = 2 * 2; // 16-bit stereo * 2 channels
 byte[] audio=new byte[atstep * asamplesize];
 BufferedImage img=new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB);

 // main loop
 while (atime < atmax || vtime < vtmax) {
      atnext = Math.min(atmax, atnext + atstep); // advance audio to next interleave time
      while (atime < atnext) { // catch up with audio time
          int sampleDuration = (int) Math.min(audio.length / asamplesize, atmax - atime);
          ...fill in audio data for time "atime" and sampleDuration "sampleDuration" here...
          w.writeSamples(0, sampleDuration, audio, 0, sampleDuration * asamplesize, asduration);
          atime += sampleDuration;
      }
      vtnext = Math.min(vtmax, vtnext + vtstep); // advance video to next interleave time
      while (vtime < vtnext) { // catch up with video time
          int sampleDuration = (int) Math.min(1, vtmax - vtime);
          ...fill in image data for time "vtime" and sampleDuration "sampleDuration" here...
          w.write(1, img, vsduration);
          vtime += sampleDuration;
      }
 }
 w.close();
 

For information about the QuickTime file format see the "QuickTime File Format Specification", Apple Inc. 2010-08-03. (qtff) http://developer.apple.com/library/mac/documentation/QuickTime/QTFF/qtff.pdf

Author:
Werner Randelshofer
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final Format
     
    static final Format
     
    static final Format
     
    static final Format
     
    static final Format
     
  • Constructor Summary

    Constructors
    Constructor
    Description
    Creates a new QuickTime writer.
    Creates a new QuickTime writer.
  • Method Summary

    Modifier and Type
    Method
    Description
    int
    Adds an audio track, and configures it using an AudioFormat object from the javax.sound API.
    int
    Adds a track.
    int
    addVideoTrack(Format format, int width, int height, int depth, int syncInterval)
    Adds a video track.
    int
    addVideoTrack(Format format, long timeScale, int width, int height)
    Adds a video track.
    void
    Closes the writer.
    void
    Finishes writing the contents of the QuickTime output stream without closing the underlying stream.
    getCodec(int track)
    Returns the codec of the specified track.
    getDuration(int track)
    Returns the sampleDuration of the track in seconds.
    Returns the file format.
    getFormat(int track)
    Returns the media format of the specified track.
    long
    getMediaTimeScale(int track)
    Returns the time scale of the media in a track.
    long
    Returns the time scale of the movie.
    int
    Returns the number of tracks.
    long
    getTrackDuration(int track)
    Returns the track duration in the movie's time scale.
    boolean
    Returns true if the limit for media samples has been reached.
    boolean
    isEmpty(int track)
    Returns true if the specified track has no samples.
    boolean
    Returns true because QuickTime supports variable frame rates.
    void
    setCodec(int track, Codec codec)
    Sets the codec for the specified track.
    void
    setCompressionQuality(int track, float newValue)
    Sets the compression quality of a track.
    void
    setEditList(int track, AbstractQuickTimeStream.Edit[] editList)
    Sets the edit list for the specified track.
    void
    setMovieTimeScale(long timeScale)
    Sets the time scale for this movie, that is, the number of time units that pass per second in its time coordinate system.
    void
    setSyncInterval(int track, int i)
    Sets the sync interval for the specified video track.
    void
    setVideoColorTable(int track, ColorModel icm)
    Sets the color table for videos with indexed color models.
    void
    toWebOptimizedMovie(File outputFile, boolean compressHeader)
    Writes a version of the movie which is optimized for the web into the specified output file.
    void
    write(int track, byte[] data, int off, int len, long duration, boolean isSync)
    Deprecated.
    void
    write(int track, int sampleCount, byte[] data, int off, int len, long sampleDuration, boolean isSync)
    Deprecated.
    void
    write(int track, BufferedImage image, long duration)
    Encodes an image as a video frame and writes it into a video track.
    void
    write(int track, Buffer buf)
    Writes a sample.
    void
    writeSample(int track, File file, long duration, boolean isSync)
    Writes an already encoded sample from a file into a track.
    void
    writeSamples(int track, int sampleCount, byte[] data, int off, int len, long sampleDuration)
    Writes multiple sync samples from a byte array into a track.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • QUICKTIME

      public static final Format QUICKTIME
    • VIDEO_RAW

      public static final Format VIDEO_RAW
    • VIDEO_ANIMATION

      public static final Format VIDEO_ANIMATION
    • VIDEO_JPEG

      public static final Format VIDEO_JPEG
    • VIDEO_PNG

      public static final Format VIDEO_PNG
  • Constructor Details

    • QuickTimeWriter

      public QuickTimeWriter(File file) throws IOException
      Creates a new QuickTime writer.
      Parameters:
      file - the output file
      Throws:
      IOException
    • QuickTimeWriter

      public QuickTimeWriter(ImageOutputStream out) throws IOException
      Creates a new QuickTime writer.
      Parameters:
      out - the output stream.
      Throws:
      IOException
  • Method Details

    • getFileFormat

      public Format getFileFormat() throws IOException
      Description copied from interface: MovieWriter
      Returns the file format.
      Specified by:
      getFileFormat in interface MovieWriter
      Throws:
      IOException
    • getFormat

      public Format getFormat(int track)
      Description copied from interface: MovieWriter
      Returns the media format of the specified track. This is a refined version of the format that was requested when the track was added. See MovieWriter.addTrack(org.monte.media.av.Format).
      Specified by:
      getFormat in interface MovieWriter
      Parameters:
      track - Track number.
      Returns:
      The media format of the track.
    • addTrack

      public int addTrack(Format fmt) throws IOException
      Adds a track.
      Specified by:
      addTrack in interface MovieWriter
      Specified by:
      addTrack in interface Multiplexer
      Parameters:
      fmt - The format of the track.
      Returns:
      The track number.
      Throws:
      IOException
    • addVideoTrack

      public int addVideoTrack(Format format, long timeScale, int width, int height) throws IOException
      Adds a video track.
      Parameters:
      format - The QuickTime video format.
      timeScale - The media time scale. This is typically the frame rate. If the frame rate is not an integer fraction of a second, specify a multiple of the frame rate and specify a correspondingly multiplied sampleDuration when writing frames. For example, for a rate of 23.976 fps specify a time scale of 23976 and multiply the sampleDuration of a video frame by 1000.
      width - The width of a video image. Must be larger than 0.
      height - The height of a video image. Must be larger than 0.
      Returns:
      Returns the track index.
      Throws:
      IllegalArgumentException - if the width or the height is smaller than 1.
      IOException
    • addVideoTrack

      public int addVideoTrack(Format format, int width, int height, int depth, int syncInterval) throws IOException
      Adds a video track.
      Parameters:
      format - The QuickTime video format.
      width - The width of a video image. Must be larger than 0.
      height - The height of a video image. Must be larger than 0.
      Returns:
      Returns the track index.
      Throws:
      IllegalArgumentException - if the width or the height is smaller than 1.
      IOException
    • addAudioTrack

      public int addAudioTrack(AudioFormat format) throws IOException
      Adds an audio track, and configures it using an AudioFormat object from the javax.sound API.

      Use this method for writing audio data from an AudioInputStream into a QuickTime Movie file.

      Parameters:
      format - The javax.sound audio format.
      Returns:
      Returns the track index.
      Throws:
      IOException
    • getTrackCount

      public int getTrackCount()
      Description copied from interface: MovieWriter
      Returns the number of tracks.
      Specified by:
      getTrackCount in interface MovieWriter
    • getDuration

      public Rational getDuration(int track)
      Returns the sampleDuration of the track in seconds.
      Specified by:
      getDuration in interface MovieWriter
    • getCodec

      public Codec getCodec(int track)
      Returns the codec of the specified track.
    • setCodec

      public void setCodec(int track, Codec codec)
      Sets the codec for the specified track.
    • write

      public void write(int track, Buffer buf) throws IOException
      Writes a sample. Does nothing if the discard-flag in the buffer is set to true.
      Specified by:
      write in interface MovieWriter
      Specified by:
      write in interface Multiplexer
      Parameters:
      track - The track number.
      buf - The buffer containing the sample data.
      Throws:
      IOException - if the write fails
    • close

      public void close() throws IOException
      Description copied from interface: MovieWriter
      Closes the writer.
      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable
      Specified by:
      close in interface MovieWriter
      Specified by:
      close in interface Multiplexer
      Throws:
      IOException - if close fails
    • write

      public void write(int track, BufferedImage image, long duration) throws IOException
      Encodes an image as a video frame and writes it into a video track.
      Parameters:
      track - The track index.
      image - The image of the video frame.
      duration - The sampleDuration of the video frame in media time scale units.
      Throws:
      IOException - if writing the sample data failed.
    • write

      @Deprecated public void write(int track, byte[] data, int off, int len, long duration, boolean isSync) throws IOException
      Deprecated.
      Writes a sample from a byte array into a track.

      This method encodes the sample if the format of the track does not match the format of the media in this track.

      Parameters:
      track - The track index.
      data - The sample data.
      off - The start offset in the data.
      len - The number of bytes to write.
      duration - The duration of the sample in media time scale units.
      isSync - Whether the sample is a sync sample (keyframe).
      Throws:
      IllegalArgumentException - if the sampleDuration is less than 1.
      IOException - if writing the sample data failed.
    • write

      @Deprecated public void write(int track, int sampleCount, byte[] data, int off, int len, long sampleDuration, boolean isSync) throws IOException
      Deprecated.
      Writes multiple already encoded samples from a byte array into a track.

      This method does not inspect the contents of the data. The contents has to match the format and dimensions of the media in this track.

      Parameters:
      track - The track index.
      sampleCount - The number of samples.
      data - The encoded sample data.
      off - The start offset in the data.
      len - The number of bytes to write. Must be dividable by sampleCount.
      sampleDuration - The sampleDuration of a sample. All samples must have the same sampleDuration.
      isSync - Whether the samples are sync samples. All samples must either be sync samples or non-sync samples.
      Throws:
      IllegalArgumentException - if the sampleDuration is less than 1.
      IOException - if writing the sample data failed.
    • isVFRSupported

      public boolean isVFRSupported()
      Returns true because QuickTime supports variable frame rates.
    • isDataLimitReached

      public boolean isDataLimitReached()
      Returns true if the limit for media samples has been reached. If this limit is reached, no more samples should be added to the movie.

      QuickTime files can be up to 64 TB long, but there are other values that may overflow before this size is reached. This method returns true when the files size exceeds 2^60 or when the media sampleDuration value of a track exceeds 2^61.

      Specified by:
      isDataLimitReached in interface MovieWriter
    • isEmpty

      public boolean isEmpty(int track)
      Description copied from interface: MovieWriter
      Returns true if the specified track has no samples.
      Specified by:
      isEmpty in interface MovieWriter
    • setVideoColorTable

      public void setVideoColorTable(int track, ColorModel icm)
      Sets the color table for videos with indexed color models.
      Parameters:
      track - The track number.
      icm - IndexColorModel. Specify null to use the standard Macintosh color table.
    • setMovieTimeScale

      public void setMovieTimeScale(long timeScale)
      Sets the time scale for this movie, that is, the number of time units that pass per second in its time coordinate system.

      The default value is 600.

      Parameters:
      timeScale -
    • finish

      public void finish() throws IOException
      Finishes writing the contents of the QuickTime output stream without closing the underlying stream. Use this method when applying multiple filters in succession to the same output stream.
      Throws:
      IllegalStateException - if the dimension of the video track has not been specified or determined yet.
      IOException - if an I/O exception has occurred
    • setSyncInterval

      public void setSyncInterval(int track, int i)
      Sets the sync interval for the specified video track.
      Parameters:
      track - The track number.
      i - Interval between sync samples (keyframes). 0 = automatic. 1 = write all samples as sync samples. n = sync every n-th sample.
    • writeSample

      public void writeSample(int track, File file, long duration, boolean isSync) throws IOException
      Writes an already encoded sample from a file into a track.

      This method does not inspect the contents of the samples. The contents has to match the format and dimensions of the media in this track.

      Parameters:
      track - The track index.
      file - The file which holds the encoded data sample.
      duration - The duration of the sample in media time scale units.
      isSync - whether the sample is a sync sample (key frame).
      Throws:
      IOException - if writing the sample data failed.
    • writeSamples

      public void writeSamples(int track, int sampleCount, byte[] data, int off, int len, long sampleDuration) throws IOException
      Writes multiple sync samples from a byte array into a track.

      This method does not inspect the contents of the samples. The contents has to match the format and dimensions of the media in this track.

      Parameters:
      track - The track index.
      sampleCount - The number of samples.
      data - The encoded sample data.
      off - The start offset in the data.
      len - The number of bytes to write. Must be dividable by sampleCount.
      sampleDuration - The duration of a sample. All samples must have the same duration.
      Throws:
      IllegalArgumentException - if the duration is less than 1.
      IOException - if writing the sample data failed.
    • toWebOptimizedMovie

      public void toWebOptimizedMovie(File outputFile, boolean compressHeader) throws IOException
      Writes a version of the movie which is optimized for the web into the specified output file.

      This method finishes the movie and then copies its content into the specified file. The web-optimized file starts with the movie header.

      Parameters:
      outputFile - The output file
      compressHeader - Whether the movie header shall be compressed.
      Throws:
      IOException
    • getMediaTimeScale

      public long getMediaTimeScale(int track)
      Returns the time scale of the media in a track.
      Parameters:
      track - Track index.
      Returns:
      time scale
      See Also:
    • setCompressionQuality

      public void setCompressionQuality(int track, float newValue)
      Sets the compression quality of a track.

      A value of 0 stands for "high compression is important" a value of 1 for "high image quality is important".

      Changing this value affects the encoding of video frames which are subsequently written into the track. Frames which have already been written are not changed.

      This value has no effect on videos encoded with lossless encoders such as the PNG format.

      The default value is 0.97.

      Parameters:
      newValue -
    • getMovieTimeScale

      public long getMovieTimeScale()
      Returns the time scale of the movie.
      Returns:
      time scale
      See Also:
    • getTrackDuration

      public long getTrackDuration(int track)
      Returns the track duration in the movie's time scale.

      If the track has an edit-list, the track duration is the sum of all edit durations.

      If the track does not have an edit-list, then this method returns the media duration of the track in the movie's time scale.

      Parameters:
      track - Track index.
      Returns:
      track duration
    • setEditList

      public void setEditList(int track, AbstractQuickTimeStream.Edit[] editList)
      Sets the edit list for the specified track.

      In the absence of an edit list, the presentation of the track starts immediately. An empty edit is used to offset the start time of a track.

      Throws:
      IllegalArgumentException - If the edit list ends with an empty edit.