diff --git a/org.monte.demo.moviemaker/src/main/java/org.monte.demo.moviemaker/org/monte/demo/moviemaker/Main.java b/org.monte.demo.moviemaker/src/main/java/org.monte.demo.moviemaker/org/monte/demo/moviemaker/Main.java index ff3c2e4..b6a0ab2 100755 --- a/org.monte.demo.moviemaker/src/main/java/org.monte.demo.moviemaker/org/monte/demo/moviemaker/Main.java +++ b/org.monte.demo.moviemaker/src/main/java/org.monte.demo.moviemaker/org/monte/demo/moviemaker/Main.java @@ -8,6 +8,7 @@ import org.monte.media.av.Format; import org.monte.media.math.Rational; import org.monte.media.mp3.MP3AudioInputStream; +import org.monte.media.quicktime.QuickTimeOutputStream; import org.monte.media.quicktime.QuickTimeWriter; import org.monte.media.swing.BackgroundTask; import org.monte.media.swing.datatransfer.FileTextFieldTransferHandler; @@ -851,9 +852,9 @@ private void writeVideoAndAudio(File movieFile, File[] imgFiles, File audioFile, } } if (longerTrack != -1) { - LinkedList l = new LinkedList<>(); - l.add(new QuickTimeWriter.Edit(shorterDuration, 0, 1.0)); // sampleDuration, media time, media rate - qtOut.setEditList(longerTrack, l.toArray(new QuickTimeWriter.Edit[l.size()])); + LinkedList l = new LinkedList<>(); + l.add(new QuickTimeOutputStream.Edit(shorterDuration, 0, 1.0)); // sampleDuration, media time, media rate + qtOut.setEditList(longerTrack, l.toArray(new QuickTimeOutputStream.Edit[l.size()])); } } break; @@ -880,11 +881,11 @@ private void writeVideoAndAudio(File movieFile, File[] imgFiles, File audioFile, } } if (longerTrack != -1) { - LinkedList l = new LinkedList<>(); + LinkedList l = new LinkedList<>(); for (; longerDuration > 0; longerDuration -= shorterDuration) { - l.add(new QuickTimeWriter.Edit(min(shorterDuration, longerDuration), 0, 1.0)); // sampleDuration, media time, media rate + l.add(new QuickTimeOutputStream.Edit(min(shorterDuration, longerDuration), 0, 1.0)); // sampleDuration, media time, media rate } - qtOut.setEditList(shorterTrack, l.toArray(new QuickTimeWriter.Edit[l.size()])); + qtOut.setEditList(shorterTrack, l.toArray(new QuickTimeOutputStream.Edit[l.size()])); } } break; @@ -892,9 +893,9 @@ private void writeVideoAndAudio(File movieFile, File[] imgFiles, File audioFile, long d0 = qtOut.getTrackDuration(at); long d1 = qtOut.getTrackDuration(vt); if (d0 != d1 && d0 != 0 && d1 != 0) { - LinkedList l = new LinkedList<>(); - l.add(new QuickTimeWriter.Edit((int) d0, 0, d1 / (float) d0)); // sampleDuration, media time, media rate - qtOut.setEditList(1, l.toArray(new QuickTimeWriter.Edit[l.size()])); + LinkedList l = new LinkedList<>(); + l.add(new QuickTimeOutputStream.Edit((int) d0, 0, d1 / (float) d0)); // sampleDuration, media time, media rate + qtOut.setEditList(1, l.toArray(new QuickTimeOutputStream.Edit[l.size()])); } } diff --git a/org.monte.demo.moviereader/src/main/java/org.monte.demo.moviereader/org/monte/demo/moviereader/ReadImagesFromAMovieMain.java b/org.monte.demo.moviereader/src/main/java/org.monte.demo.moviereader/org/monte/demo/moviereader/ReadImagesFromAMovieMain.java index d581124..b5035bb 100755 --- a/org.monte.demo.moviereader/src/main/java/org.monte.demo.moviereader/org/monte/demo/moviereader/ReadImagesFromAMovieMain.java +++ b/org.monte.demo.moviereader/src/main/java/org.monte.demo.moviereader/org/monte/demo/moviereader/ReadImagesFromAMovieMain.java @@ -81,6 +81,7 @@ private void createFrame() { JPanel panel = new JPanel(new BorderLayout()); imageLabel = new JLabel(); slider = new JSlider(); + slider.setMinimum(0); panel.add(imageLabel, BorderLayout.CENTER); panel.add(slider, BorderLayout.SOUTH); frame.getContentPane().add(panel); diff --git a/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/anim/ANIMMultiplexer.java b/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/anim/ANIMMultiplexer.java index 75ed27e..ce39425 100755 --- a/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/anim/ANIMMultiplexer.java +++ b/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/anim/ANIMMultiplexer.java @@ -6,6 +6,7 @@ import org.monte.media.amigabitmap.AmigaBitmapImage; import org.monte.media.av.Buffer; +import org.monte.media.av.Format; import org.monte.media.av.Multiplexer; import org.monte.media.math.Rational; @@ -21,16 +22,22 @@ * * @author Werner Randelshofer */ -public class ANIMMultiplexer extends ANIMOutputStream implements Multiplexer { +public class ANIMMultiplexer implements Multiplexer { + private final ANIMOutputStream out; protected Rational inputTime; public ANIMMultiplexer(File file) throws IOException { - super(file); + this.out = new ANIMOutputStream(file); } public ANIMMultiplexer(ImageOutputStream out) throws IOException { - super(out); + this.out = new ANIMOutputStream(out); + } + + @Override + public int addTrack(Format fmt) throws IOException { + return 0; } @Override @@ -40,14 +47,14 @@ public void write(int trackIndex, Buffer buf) throws IOException { // Or maybe, just let them accumulate. In case the // frames are compressed, we can't do anything at this // stage anyway. - long jiffies = getJiffies(); + long jiffies = out.getJiffies(); if (inputTime == null) { inputTime = new Rational(0, 1); } inputTime = inputTime.add(buf.sampleDuration.multiply(buf.sampleCount)); - Rational outputTime = new Rational(getMovieTime(), jiffies); + Rational outputTime = new Rational(out.getMovieTime(), jiffies); Rational outputDuration = inputTime.subtract(outputTime); @@ -58,7 +65,28 @@ public void write(int trackIndex, Buffer buf) throws IOException { outputTime.add(new Rational(outputMediaDuration, jiffies)); // System.out.println("ANIMMultiplexer #" + frameCount + " jiffies:"+jiffies+" movieT:" + outputTime + " inputT:" + inputTime+" diff:"+(outputTime.subtract(inputTime))+ " sampleDuration:" + outputMediaDuration + " == " + outputDuration+" ~= "+buf.sampleDuration); - writeFrame((AmigaBitmapImage) buf.data, outputMediaDuration); + out.writeFrame((AmigaBitmapImage) buf.data, outputMediaDuration); } } + + @Override + public void close() throws IOException { + out.close(); + } + + /** + * Sets the Commodore Amiga Graphics Mode. The default value is 0. + *

+ * The graphics mode is an or-combination of the monitor ID and the mode ID. + *

+ * Example: + *

+     * setCAMG(PAL_MONITOR_ID|HAM_MODE);
+     * 
+ *

+ * Also sets the Jiffies for the Graphics Mode. + */ + public void setCAMG(int newValue) { + out.setCAMG(newValue); + } } diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/math/MathUtils.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/math/MathUtils.java deleted file mode 100755 index 48e9900..0000000 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/math/MathUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * @(#)MathUtils.java - * Copyright © 2023 Werner Randelshofer, Switzerland. MIT License. - */ -package org.monte.media.math; - -import static java.lang.Math.max; -import static java.lang.Math.min; - -/** - * {@code MathUtils}. - * - * @author Werner Randelshofer - */ -public class MathUtils { - - public static float clamp(float value, float minValue, float maxValue) { - return max(minValue, min(value, maxValue)); - - } - - public static double clamp(double value, double minValue, double maxValue) { - return max(minValue, min(value, maxValue)); - - } - - public static int clamp(int value, int minValue, int maxValue) { - return max(minValue, min(value, maxValue)); - - } - - public static long clamp(long value, long minValue, long maxValue) { - return max(minValue, min(value, maxValue)); - - } -} diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeDeserializer.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeDeserializer.java index abd521f..218217f 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeDeserializer.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeDeserializer.java @@ -19,8 +19,6 @@ import java.util.TreeSet; import java.util.zip.InflaterInputStream; -import static org.monte.media.math.MathUtils.clamp; - /** * {@code QuickTimeDeserializer}. This is an internal class of * QuickTimeInputStream. @@ -1019,8 +1017,12 @@ protected void parseVideoSampleDescription(QTFFImageInputStream in, long remaini int descriptionVersion = in.readUnsignedShort(); int revisionLevel = in.readUnsignedShort(); int vendor = in.readInt(); - d.videoTemporalQuality = clamp(in.readInt() / 1024f, 0.0f, 1.0f); - d.videoSpatialQuality = clamp(in.readInt() / 1024f, 0.0f, 1.0f); + float value1 = in.readInt() / 1024f; + + d.videoTemporalQuality = Math.clamp(value1, 0.0f, 1.0f); + float value = in.readInt() / 1024f; + + d.videoSpatialQuality = Math.clamp(value, 0.0f, 1.0f); d.videoWidth = in.readUnsignedShort(); d.videoHeight = in.readUnsignedShort(); d.videoHorizontalResolution = in.readFixed16D16(); diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeWriter.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeWriter.java index 1a04cfc..0a495a2 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeWriter.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/QuickTimeWriter.java @@ -675,4 +675,126 @@ public void setMovieTimeScale(long timeScale) { public void finish() throws IOException { out.finish(); } + + + /** + * Sets the sync interval for the specified video track. + * + * @param track The track number. + * @param i Interval between sync samples (keyframes). 0 = automatic. 1 = + * write all samples as sync samples. n = sync every n-th sample. + */ + public void setSyncInterval(int track, int i) { + out.setSyncInterval(track, i); + } + + /** + * 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. + * + * @param track The track index. + * @param file The file which holds the encoded data sample. + * @param duration The duration of the sample in media time scale units. + * @param isSync whether the sample is a sync sample (key frame). + * @throws IOException if writing the sample data failed. + */ + public void writeSample(int track, File file, long duration, boolean isSync) throws IOException { + out.writeSample(track, file, duration, isSync); + } + + /** + * 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. + * + * @param track The track index. + * @param sampleCount The number of samples. + * @param data The encoded sample data. + * @param off The start offset in the data. + * @param len The number of bytes to write. Must be dividable by + * sampleCount. + * @param sampleDuration The duration of a sample. All samples must have the + * same duration. + * @throws IllegalArgumentException if the duration is less than 1. + * @throws IOException if writing the sample data failed. + */ + public void writeSamples(int track, int sampleCount, byte[] data, int off, int len, long sampleDuration) throws IOException { + out.writeSamples(track, sampleCount, data, off, len, sampleDuration); + } + + /** + * 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. + * + * @param outputFile The output file + * @param compressHeader Whether the movie header shall be compressed. + */ + public void toWebOptimizedMovie(File outputFile, boolean compressHeader) throws IOException { + out.toWebOptimizedMovie(outputFile, compressHeader); + } + + /** + * Returns the time scale of the media in a track. + * + * @param track Track index. + * @return time scale + * @see #setMovieTimeScale(long) + */ + public long getMediaTimeScale(int track) { + return out.getMediaTimeScale(track); + } + + /** + * 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. + * + * @param newValue + */ + public void setCompressionQuality(int track, float newValue) { + out.setCompressionQuality(track, newValue); + } + + /** + * Returns the time scale of the movie. + * + * @return time scale + * @see #setMovieTimeScale(long) + */ + public long getMovieTimeScale() { + return out.getMovieTimeScale(); + } + + /** + * 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. + * + * @param track Track index. + * @return track duration + */ + public long getTrackDuration(int track) { + return out.getTrackDuration(track); + } + + + /** + * 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. + */ + public void setEditList(int track, AbstractQuickTimeStream.Edit[] editList) { + out.setEditList(track, editList); + } } diff --git a/pom.xml b/pom.xml index 9d3dd34..edb146b 100644 --- a/pom.xml +++ b/pom.xml @@ -80,8 +80,8 @@ - 17 - 17 + 21 + 21 UTF-8 UTF-8 ${git.commit.time}