Make sure jni library is loaded for non-AVObject derived static methods.
Checkpoint ongoing work on doc improvements
Checkpoint ongoing work on accessor cleanup
Auto-generate some of the AVIOContext flags
AVIOContext improvements
JJMediaWriter improvements
Attach all threads as daemons in jni
Change AVPacket interface for accessing data buffer
import java.io.FileNotFoundException;
import java.io.IOException;
import static java.lang.Double.min;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.time.Instant;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.List;
-import java.util.TimeZone;
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.geometry.Insets;
+import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyCodeCombination;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.text.Text;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
+import javafx.stage.Screen;
import javafx.stage.Stage;
/**
WritableImage image;
Text label;
Stage stage;
- SimpleDateFormat time;
+ DateTimeFormatter time;
long time0 = -1;
long stamp0;
height = vs.getHeight();
frameReader = new FXPixelReader(vs.getFrame().getPixelReader(width, height, 0));
- time = new SimpleDateFormat("HH:mm:ss.SSS");
- time.setTimeZone(TimeZone.getTimeZone("GMT"));
+ time = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
Platform.runLater(() -> {
label = new Text();
ap.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
- Scene scene = new Scene(ap, width, height);
+ Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
+ double swidth = bounds.getWidth() - 32;
+ double sheight = bounds.getHeight() - 32;
+ Scene scene;
+ if (width > swidth || height > sheight) {
+ double scalex = swidth / width;
+ double scaley = sheight / height;
+
+ swidth = scalex < scaley ? swidth : width * scaley;
+ sheight = scalex < scaley ? height * scalex : sheight;
+ scene = new Scene(ap, swidth, sheight);
+ } else {
+ scene = new Scene(ap, width, height);
+ }
stage = new Stage();
stage.setScene(scene);
ap.widthProperty().addListener(fitVideo);
ap.heightProperty().addListener(fitVideo);
+ fitVideo.invalidated(null);
+
+ scene.getAccelerators().put(new KeyCodeCombination(KeyCode.Q), () -> {
+ cancel = true;
+ stage.close();
+ });
});
}
* @param stamp
*/
void videoFrame(JJMediaReader.JJReaderVideo vs, long stamp) {
- String ts = time.format(new Date(stamp));
+ Instant x = Instant.ofEpochMilli(stamp + vs.getStartMS());
+ String ts = time.format(LocalTime.ofInstant(x, ZoneId.of("UTC")));
CountDownLatch latch = new CountDownLatch(1);
// Display the frame. Must run on javafx thread.
* @param x
*/
void videoError(IOException x) {
- Alert a = new Alert(Alert.AlertType.ERROR, x.getLocalizedMessage(), ButtonType.CLOSE);
- a.showAndWait();
- Platform.exit();
+ Platform.runLater(() -> {
+ Alert a = new Alert(Alert.AlertType.ERROR, x.getLocalizedMessage(), ButtonType.CLOSE);
+ a.showAndWait();
+ Platform.exit();
+ });
}
volatile boolean cancel = false;
}
} catch (AVIOException | FileNotFoundException ex) {
+ ex.printStackTrace();
videoError(ex);
}
}
/**
*/
-public class AVChannelLayout extends AVObject implements AVChannelLayoutBits {
+public class AVChannelLayout implements AVChannelLayoutBits {
+
+ static {
+ AVObject.init();
+ }
/**
* Call av_get_channel_layout(name)
*/
public static native long getDefaultLayout(int nchannels);
- private AVChannelLayout(long p) {
- super(p);
- }
}
public static native AVCodec findEncoder(String name);
- /*
- Accessors
+ /**
+ * Get AVCodec.name.
+ *
+ * @return
*/
public native String getName();
+ /**
+ * Get AVCodec.long_name.
+ *
+ * @return
+ */
public native String getLongName();
+ /**
+ * Get AVCodec.type.
+ *
+ * @return
+ * @see AVMediaType
+ */
public native int getType();
+ /**
+ * Get AVCodec.id.
+ *
+ * @return
+ * @see AVCodecID
+ */
public native int getID();
public native int getCapabilities();
/**
* AVCodecContext accessors.
* <p>
+ * The accessors here are currently implemented as direct struct accessors but
+ * most fields may also be accessed by the {@link AVOptions} accessor methods. The
+ * AVOptions methods are somewhat inconvenient but provide additional
+ * validation checks for setting options.
+ * <p>
+ * Note that there are many other options, these are documented in libavcodec/avcodec.h and the ffmpeg
+ * source in libavcodec/options_table.h. This can also be retrieved at runtime
+ * via
*/
public class AVCodecContext extends AVOptions implements AVCodecContextBits {
super(p);
}
+ /**
+ * Free the context.
+ * <p>
+ * Calls avcodec_free_context().
+ *
+ * @param p
+ */
private static native void release(long p);
- /*
- * Methods
- */
/**
+ * Allocate and initialise an AVCodecContext.
+ * <p>
+ * Calls avcodec_alloc_context3().
*
- * @param codec
+ * @param codec Codec used to initialise parameters, may be null.
* @return
*/
public static native AVCodecContext allocContext(AVCodec codec);
- // avcodec_open2
+ /**
+ * Open a context.
+ * <p>
+ * Calls avcodec_open2().
+ *
+ * @param codec Codec used. It must match the one used in allocContext().
+ * @param options Codec options.
+ * @throws AVIOException is thrown iv avcodec_open2 returns an error.
+ */
public native void open(AVCodec codec, AVDictionary options) throws AVIOException;
+ /**
+ * Flush internal buffers.
+ * <p>
+ * Used to reset codec state, for example after a seek.
+ * <p>
+ * Calls avcodec_flush_buffers().
+ */
public native void flushBuffers();
+ /**
+ * Align dimensions suitable for codec.
+ * <p>
+ * Calls avcodec_align_dimensions().
+ *
+ * @param input Input dimensions.
+ * @return Modified dimensions.
+ */
public native AVSize alignDimensions(AVSize input);
-
+
/**
- * Calls avcodec_send_packet()
+ * Submit raw packet to a decoder.
+ * <p>
+ * Packets are submitted to a decoder which generates output frames.
* <p>
* If the return value is EAGAIN or AVERROR_EOF then this function
* returns false, for all other error values it throws an exception.
+ * <p>
+ * Calls avcodec_send_packet().
*
* @param pkt
* @return true if the packet was accepted, false if a frame must be received first.
* @throws AVIOException
+ * @see #receiveFrame(au.notzed.jjmpeg.AVFrame)
*/
public native boolean sendPacket(AVPacket pkt) throws AVIOException;
/**
- * Calls avcodec_receive_frame().
+ * Retrieve a decoded frame from a decoder.
* <p>
- * If the return value is EAGAIN or AVERROR_EOF then this function
- * returns false, for all other error values it throws an exception.
+ * If the return value from avcodec_receive_frame() is EAGAIN or AVERROR_EOF
+ * then this function returns false, for all other error values it throws an exception.
+ * <p>
+ * Calls avcodec_receive_frame().
*
* @param frame
* @return Returns true if the frame is complete, false otherwise.
* @throws AVIOException
+ * @see #sendPacket(au.notzed.jjmpeg.AVPacket)
*/
public native boolean receiveFrame(AVFrame frame) throws AVIOException;
*/
public native boolean receivePacket(AVPacket pkt) throws AVIOException;
- /*
- * Accessors
+ /**
+ * Get encoding/decoding AVCodecContext.codec_type.
+ *
+ * @return
+ * @see AVMediaType
*/
public native int getCodecType();
+ /**
+ * Set encoding/decoding AVCodecContext.codec_type.
+ *
+ * @param val
+ * @see AVMediaType
+ */
public native void setCodecType(int val);
+ /**
+ * Get encoding/decoding AVCodecContext.codec_id.
+ *
+ * @return
+ * @see AVCodecID
+ */
public native int getCodecID();
+ /**
+ * Set encoding/decoding AVCodecContext.codec_id.
+ *
+ * @param val
+ * @see AVCodecID
+ */
public native void setCodecID(int val);
- public native int getCodecTag();
-
- public native void setCodecTag(int val);
-
+ /**
+ * Get encoding/decoding AVCodecContext.bit_rate.
+ * <p>
+ * AVOption int "b" or "ab"
+ *
+ * @return
+ */
public native long getBitRate();
- public native void setBitRate(long val);
-
- public native int getBitRateTolerance();
-
- public native void setBitRateTolerance(int val);
+ /**
+ * Set encoding/decoding AVCodecContext.bit_rate.
+ * <p>
+ * AVOption int "b" or "ab"
+ *
+ * @param bitrate
+ */
+ public native void setBitRate(long bitrate);
+ /**
+ * Get encoding AVCodecContext.global_quality.
+ * <p>
+ * AVOption int "global_quality".
+ *
+ * @return
+ */
public native int getGlobalQuality();
- public native void setGlobalQuality(int val);
-
- public native int getCompressionLevel();
-
- public native void setCompressionLevel(int val);
+ /**
+ * Set AVCodecContext.global_quality.
+ * <p>
+ * AVOption int "global_quality".
+ *
+ * @param quality
+ */
+ public native void setGlobalQuality(int quality);
/**
- * Get the contents of flags and flags2 together as a single 64-bit value.
+ * Get the encoding/decoding AVCodecContext.flags and AVCodecContext.flags2.
+ * <p>
+ * This combines .flags and .flags2 into a single 64-bit flag set. The
+ * AV_CODE_FLAG2_* constants have been shifted appropriately.
+ * <p>
+ * AVOption int "flag", also specific flags "unaligned", "mv4", and so on.
*
* @return
*/
public native long getFlags();
/**
- * Set or clear flags and flags2.
+ * Adjust the encoding/decoding AVCodecContext.flags and AVCodecContext.flags2.
+ * <p>
+ * This performs a combined get and set operation on the flags allowing any cobimation
+ * of flags to be set in a single call.
+ * <p>
+ * AVOption int "flag", also specific flags "unaligned", "mv4", and so on.
*
- * @param mask Mask of bits to change. Only flags with a bit set here will be altered.
+ * @param mask Mask of bits to change. Flags with a bit set here will be set to the values in flags.
* @param flags Value to set bits to.
+ * @see #getFlags() for an explanation of the flag values.
*/
public native void setFlags(long mask, long flags);
/**
- * Get the timebase.
+ * Get the encoding AVCodecContext.time_base.
* <p>
- * Note that the return value is a value type (copy) not a reference.
+ * AVOption rational (q) "time_base".
*
- * @return
+ * @return A copy of the time_base (adjustments will not be carried through).
*/
public native AVRational getTimeBase();
- public native void setTimeBase(AVRational val);
-
- public native int getTicksPerFrame();
-
- public native void setTicksPerFrame(int val);
+ /**
+ * Set the encoding AVCodecContext.time_base.
+ * <p>
+ * A time_base must always be set for encoding.
+ * <p>
+ * AVOption rational (q) "time_base".
+ *
+ * @param timebase
+ */
+ public native void setTimeBase(AVRational timebase);
+ /**
+ * Get the encoding/decoding AVCodecContext.delay.
+ * <p>
+ * AVOption int "delay".
+ *
+ * @return
+ */
public native int getDelay();
- /*
- Video only options
+ /**
+ * Get video encoding/decoding AVCodecContext.width.
+ * <p>
+ * AVOption image size "video_size".
+ *
+ * @return
*/
public native int getWidth();
- public native void setWidth(int val);
+ /**
+ * Set video encoding/decoding AVCodecContext.width.
+ * <p>
+ * AVOption image size "video_size".
+ *
+ * @param width
+ */
+ public native void setWidth(int width);
+ /**
+ * Get video encoding/decoding AVCodecContext.height.
+ * <p>
+ * AVOption image size "video_size".
+ *
+ * @return
+ */
public native int getHeight();
- public native void setHeight(int val);
+ /**
+ * Set video encoding/decoding AVCodecContext.width.
+ * <p>
+ * AVOption image size "video_size".
+ *
+ * @param height
+ */
+ public native void setHeight(int height);
+ /**
+ * Get video encoding/decoding AVCodecContext.sample_aspect_ratio.
+ * <p>
+ * AVOption rational "aspect" or "sar".
+ *
+ * @return
+ */
public native AVRational getAspectRatio();
- public native void setAspectRatio(AVRational r);
+ /**
+ * Set video encoding/decoding AVCodecContext.sample_aspect_ratio.
+ * <p>
+ * AVOption rational "aspect" or "sar".
+ *
+ * @param ratio
+ */
+ public native void setAspectRatio(AVRational ratio);
- // codedWidth, codedHeight
+ /**
+ * Get video encoding AVCodecContext.gop_size.
+ * <p>
+ * AVOption int "g".
+ *
+ * @return
+ */
public native int getGOPSize();
+ /**
+ * Set video encoding AVCodecContext.gop_size.
+ * <p>
+ * AVOption int "g".
+ *
+ * @param val
+ */
public native void setGOPSize(int val);
+ /**
+ * Get video encoding/decoding AVCodecContext.pix_fmt.
+ * <p>
+ * AVOption pixel format "pixel_format".
+ *
+ * @return
+ * @see AVPixelFormat
+ */
public native int getPixelFormat();
- public native void setPixelFormat(int val);
-
- //
- public native int getMaxBFrames();
-
- public native void setMaxBFrames(int val);
-
- public native float getBQuantFactor();
-
- public native void setBQuantFactor(float val);
-
- public native float getBQuantOffset();
-
- public native void setBQuantOffset(float val);
-
- public native boolean hasBFrames();
-
- public native float getIQuantFactor();
-
- public native void setIQuantFactor(float val);
-
- public native float getIQuantOffset();
-
- public native void setIQuantOffset(float val);
-// And a whole lot more ...
-//
-
- public native int getMBDecision();
-
- public native void setMBDecision(int val);
-// ...
+ /**
+ * Set video encoding/decoding AVCodecContext.pix_fmt.
+ * <p>
+ * AVOption pixel format "pixel_format".
+ *
+ * @param format
+ * @see AVPixelFormat
+ */
+ public native void setPixelFormat(int format);
- /*
- Audtio only options
+ /**
+ * Get audio encoding/decoding AVCodecContext.sample_rate.
+ * <p>
+ * AVOption int "ar".
+ *
+ * @return
*/
public native int getSampleRate();
- public native void setSampleRate(int val);
+ /**
+ * Set audio encoding/decoding AVCodecContext.sample_rate.
+ * <p>
+ * AVOption int "ar".
+ *
+ * @param hertz
+ */
+ public native void setSampleRate(int hertz);
+ /**
+ * Get audio encoding/decoding AVCodecContext.channels.
+ * <p>
+ * AVOption int "ac".
+ *
+ * @return
+ */
public native int getNumChannels();
- public native void setNumChannels(int val);
+ /**
+ * Set audio encoding/decoding AVCodecContext.channels.
+ * <p>
+ * AVOption int "ac".
+ *
+ * @param nchannels
+ */
+ public native void setNumChannels(int nchannels);
+ /**
+ * Get audio encoding/decoding AVCodecContext.sample_fmt.
+ *
+ * @return
+ * @see AVSampleFormat
+ */
public native int getSampleFormat();
- public native void setSampleFormat(int val);
+ /**
+ * Set audio encoding/decoding AVCodecContext.sample_fmt.
+ *
+ * @param format
+ * @see AVSampleFormat
+ */
+ public native void setSampleFormat(int format);
+ /**
+ * Get audio encoding/decoding AVCodecContext.frame_size.
+ * <p>
+ * AVOption int "frame_size".
+ *
+ * @return
+ */
public native int getFrameSize();
- public native void setFrameSize(int val);
-
+ /**
+ * Get audio encoding/decoding AVCodecContext.frame_number.
+ * <p>
+ * AVOption int "frame_number".
+ *
+ * @return
+ */
public native int getFrameNumber();
-// int block_align
-// int cutoff
+ /**
+ * Get audio encoding/decoding AVCodecContext.channel_layout.
+ * <p>
+ * AVOption channel layout "channel_layout".
+ *
+ * @return
+ * @see AVChannelLayout
+ */
public native long getChannelLayout();
- public native void setChannelLayout(long val);
+ /**
+ * Set audio encoding/decoding AVCodecContext.channel_layout.
+ * <p>
+ * AVOption channel layout "channel_layout".
+ *
+ * @param layout
+ * @see AVChannelLayout
+ */
+ public native void setChannelLayout(long layout);
+ /**
+ * Get audio decoding AVCodecContext.request_channel_layout.
+ * <p>
+ * AVOption channel layout "request_channel_layout".
+ *
+ * @return
+ * @see AVChannelLayout
+ */
public native long getRequestChannelLayout();
- public native void setRequestChannelLayout(long val);
-
-// enum AVAudioServiceType audio_service_type
-// enum AVSampleFormat request_sample_fmt;
-
- /*
- Encoding parameters
+ /**
+ * Set audio decoding AVCodecContext.request_channel_layout.
+ * <p>
+ * AVOption channel layout "request_channel_layout".
+ *
+ * @param layout
+ * @see AVChannelLayout
*/
-// float qcompress
-// float qblur
-// float qmin
-// float qmax
-// float max_qdiff
-// int rc_buffer_size
-// int rc_override_count
-// RCOverride *rc_override
-// int64_t rc_max_rate
-// int64_t rc_min_rate
-// ... more rc stuff
-// ...
-// int trellis
-// ...
-// int workaround_bugs
- public native int getStrictStdCompliance();
-
- public native void setStrictStdCompliance(int val);
-
- public native int getErrorConcealment();
-
- public native void setErrorConcealment(int val);
-// int debug
-
- public native int getErrorRecognition();
-
- public native void setErrorRecognition(int val);
-
- public native int getDCTAlgo();
-
- public native void setDCTAlgo(int val);
-
- public native int getIDCTAlgo();
-
- public native void setIDCTAlgo(int val);
+ public native void setRequestChannelLayout(long layout);
+ /**
+ * Get encoding/decoding AVCodecContext.thread_count.
+ * <p>
+ * AVOption int "threads".
+ *
+ * @return
+ */
public native int getThreadCount();
- public native void setThreadCount(int val);
-// ...
-
- public native int getProfile();
-
- public native void setProfile(int val);
-// ...
+ /**
+ * Set encoding/decoding AVCodecContext.thread_count.
+ * <p>
+ * AVOption int "threads".
+ *
+ * @param nthreads
+ */
+ public native void setThreadCount(int nthreads);
}
*/
public class AVCodecID implements AVCodecIDBits {
+ static {
+ AVObject.init();
+ }
+
/**
* Call avcodec_get_name(value)
*/
*
*/
public class AVError implements AVErrorBits {
-
+
+ static {
+ AVObject.init();
+ }
+
public static native String toString(int errno);
}
// hmm?
//public native void setOutputFormat(AVOutputFormat val);
-
public native AVIOContext getIOContext();
public native void setIOContext(AVIOContext val);
// returns false on end of file
public native boolean readFrame(AVPacket pkt) throws AVIOException;
- public native void seekFrame(int stream_index, long timestamp, int flags) throws AVIOException;
+ public native void seekFrame(int stream_index, long timestamp, int flags) throws AVIOException;
public native void seekFile(int stream_index, long min_ts, long ts, long max_ts, int flags) throws AVIOException;
// returns true if flushed
public native boolean writeFrame(AVPacket pktkt) throws AVIOException;
+ /**
+ * Write a frame to the output.
+ * <p>
+ * Calls av_interleaved_write_frame().
+ *
+ * @param pkt Packet containing data.
+ * @throws AVIOException on error.
+ */
public native void interleavedWriteFrame(AVPacket pkt) throws AVIOException;
public native void writeUncodedFrame(int stream_index, AVFrame frame) throws AVIOException;
/**
*
*/
-public class AVIOContext extends AVOptions {
+public class AVIOContext extends AVOptions implements AVIOContextBits {
+
+ /**
+ * Maintains references required for callbacks.
+ * <p>
+ * This is a {@link au.notzed.nativez.ReferenceZ} object.
+ */
+ Object opaque;
- // TODO: autogen?
- public static final int AVSEEK_SIZE = 0x10000;
- public static final int AVSEEK_FORCE = 0x20000;
//
public static final int AVSEEK_SET = 0;
public static final int AVSEEK_CUR = 1;
public static final int AVSEEK_END = 2;
- //
- public static final int ALLOC_WRITE = 1;
- public static final int ALLOC_STREAMED = 2;
- // flags for open
- public static final int AVIO_FLAG_READ = 1;
- public static final int AVIO_FLAG_WRITE = 2;
- public static final int AVIO_FLAG_READ_WRITE = 3;
- // flags for alloc seekable
- /**
- * Seeking works like for a local file.
- */
- public static final int AVIO_SEEKABLE_NORMAL = (1 << 0);
-
/**
- * Seeking by timestamp with avio_seek_time() is possible.
- */
- public static final int AVIO_SEEKABLE_TIME = (1 << 1);
- /**
- * pseudo-flag for alloc() which sets writable to avio_context_create.
+ * If set then alloc() will pass write_flag=1 to avio_alloc_context().
*/
public static final int AVIO_WRITABLE = (1 << 31);
/**
- * pseudo-flaga for alloc() which sets the direct indicator on the
- * created context.
+ * If set, then alloc() will set AVIOContext.direct=1.
*/
public static final int AVIO_DIRECT = (1 << 30);
private static native void release(long p);
- //?public native int getSeekable();
/**
- * Calls avio_open.
+ * Create an AVIOContext backed by libavformat i/o.
* <p>
- * TODO: should it call avio_open2?
+ * Calls avio_open2().
*
* @param url
* @param avio_flags AVIO_FLAGS_*.
+ * @param interrupt_cb If non-null, supplies interrupt-check function.
+ * @param options
+ * @return
+ * @throws au.notzed.jjmpeg.AVIOException
+ */
+ public static native AVIOContext open(String url, int avio_flags, AVIOInterrupt interrupt_cb, AVDictionary options) throws AVIOException;
+
+ /**
+ * Create an AVIOContext backed by libavformat i/o with no callback or options.
+ *
+ * @param url
+ * @param avio_flags
* @return
+ * @throws AVIOException
*/
- public static native AVIOContext open(String url, int avio_flags) throws AVIOException;
+ public static AVIOContext open(String url, int avio_flags) throws AVIOException {
+ return open(url, avio_flags, null, null);
+ }
/**
+ * Create an AVIOContext backed by Java I/O.
*
* @param buffer_size
* @param alloc_flags combination of AVIO_SEEKABLE_*, AVIO_WRITABLE, and AVIO_DIRECT.
- * @param handler
+ * @param handler Functions which implement i/o.
* @return
*/
public static native AVIOContext alloc(int buffer_size, int alloc_flags, AVIOHandler handler);
+ /**
+ * Callback hook for interrupt checking.
+ */
+ public interface AVIOInterrupt {
+
+ /**
+ * Called during blocking operations.
+ *
+ * @return true if the blocking i/o should abort.
+ */
+ public boolean isInterrupted();
+ }
+
+ /**
+ * Callback hooks which allow Java I/O functions to provide I/O for FFmpeg.
+ */
public interface AVIOHandler {
/**
*/
public int writePacket(ByteBuffer src);
+ /**
+ * Called to perform seek.
+ *
+ * @param offset
+ * @param whence AVSEEK_* value.
+ * @return
+ */
public long seek(long offset, int whence);
}
}
import java.io.IOException;
/**
- *
+ * General exception for FFmpeg errors.
+ * <p>
+ * This contains the error code which triggered the exception.
*/
public class AVIOException extends IOException {
*/
public class AVMediaType implements AVMediaTypeBits {
+ static {
+ AVObject.init();
+ }
+
/**
* Call av_get_media_type_string()
*/
AVUtil.setLogger(AVUtil.createAVLogger(Logger.getLogger("notzed.jjmpeg")));
}
-
+
+ /**
+ * Initialise AVObject.
+ * <p>
+ * This function does nothing itself but ensures the native library is
+ * loaded and can be used by objects which DO NOT derive from AVObject
+ * but need to use native calls.
+ */
+ public static void init() {
+ }
+
/**
* Handy iterator for various uses in the library.
*/
* Flag is used to discard packets which are required to maintain valid
* decoder state but are not required for output and should be dropped
* after decoding.
- *
+ * <p>
*/
public static final int AV_PKT_FLAG_DISCARD = 0x0004;
/**
/**
* Retrieve the current data buffer pointer and size.
* <p>
- * This value should not be saved between calls which might update the packet.
- * <p>
- * TODO: do i even need this?
+ * This value must not be saved between calls which might update the packet.
*
* @return
*/
public native ByteBuffer getData();
+ /**
+ * Resize then retrieve the current data buffer pointer and size.
+ * <p>
+ * This will call av_packet_grow() or av_packet_shrink() as necessary
+ * to set the data size, and will return a reference to it.
+ *
+ * @param size
+ * @return
+ */
+ public native ByteBuffer getData(long size);
+
/**
* Clear and free any data pointers and returns the packet to an initialised state.
* <p>
public native void setDTS(long val);
+ public native long getDuration();
+
+ public native void setDuration(long val);
+
public native int getSize();
public native int getStreamIndex();
*/
public class AVPixelFormat implements AVPixelFormatBits {
+ static {
+ AVObject.init();
+ }
+
/**
* Call av_get_pix_fmt(name)
*/
this.den = den;
}
+ static {
+ AVObject.init();
+ }
+
/**
* Calls av_d2q
*
* Compare two rationals.
*
* @param b Second rational
- *
+ *
* Taken from avutil/rational.c
*
* @return One of the following values:
*/
public class AVSampleFormat implements AVSampleFormatBits {
+ static {
+ AVObject.init();
+ }
+
/**
* Call av_get_sample_fmt(name)
*/
-
public static native int valueOf(String name);
-
+
/**
* Call av_get_sample_fmt_name(value)
*/
public static native String toString(int value);
}
-
*/
public class AVUtil {
+ static {
+ AVObject.init();
+ }
+
/**
* Call av_log_set_callback() with the provided logger.
*
return duration;
}
+ /**
+ * Retrieve stream start time in milliseconds.
+ *
+ * @return
+ */
+ public long getStartMS() {
+ return startms;
+ }
+
public boolean isOpened() {
return opened;
}
*/
package au.notzed.jjmpeg.io;
+import au.notzed.jjmpeg.AVChannelLayout;
import au.notzed.jjmpeg.AVCodec;
import au.notzed.jjmpeg.AVCodecContext;
import au.notzed.jjmpeg.AVFormatContext;
import au.notzed.jjmpeg.AVPacket;
import au.notzed.jjmpeg.AVRational;
import au.notzed.jjmpeg.AVStream;
-import au.notzed.jjmpeg.AVCodecID;
import au.notzed.jjmpeg.AVError;
import au.notzed.jjmpeg.AVIOContext;
import au.notzed.jjmpeg.AVPixelFormat;
import java.util.logging.Logger;
/**
- * High level av file writer interface
+ * High level av file writer interface.
* <p>
- * @author notzed
+ * This is still work in progress and subject to change.
*/
-public class JJMediaWriter {
+public class JJMediaWriter implements AutoCloseable {
List<JJWriterStream> streams = new ArrayList<>();
String filename;
AVFormatContext oc;
AVIOContext output; // need to keep ref around
+ // writeTrailer must only be called if writeHeader was successful.
+ boolean writeTrailer;
/**
* Create a new stream for writing to a file.
* and then call close().
*
* @param filename
- * <p>
- * @throws AVInvalidFormatException
*/
public JJMediaWriter(String filename) throws AVIOException {
this.filename = filename;
- /* allocate the output media context */
oc = AVFormatContext.allocContext(null, null, filename);
-
- // Caller now calls addVideoStream, etc.
}
/**
/**
* The stream must be opened after adding streams and before writing to them.
* <p>
- * @throws AVInvalidFormatException
- * @throws AVInvalidCodecException
* @throws AVIOException
*/
public void open() throws AVIOException {
/* now that all the parameters are set, we can open the audio and
video codecs and allocate the necessary encode buffers */
- for (JJWriterStream sd: streams) {
+ for (JJWriterStream sd : streams) {
sd.open();
}
/* open the output file, if needed */
//if (!(format->flags & AVFMT_NOFILE)) {
- output = AVIOContext.open(filename, AVIOContext.AVIO_FLAG_WRITE);
+ output = AVIOContext.open(filename, AVIOContext.AVIO_FLAG_WRITE, null, null);
oc.setIOContext(output);
/* write the stream header, if any */
oc.writeHeader(null);
- }
-
- /**
- * Add a stream using the default video codec for this stream.
- * The streamid is the index of the stream in the streams list.
- * <p>
- * @param width
- * @param height
- * @param frame_rate
- * @param bit_rate
- * <p>
- * @return
- */
- public JJWriterVideo addVideoStream(int width, int height, int frame_rate, long bit_rate) throws AVIOException {
- return addVideoStream(getFormat().getVideoCodec(), streams.size(), width, height, 1, frame_rate, bit_rate);
- }
-
- public JJWriterVideo addVideoStream(int width, int height, int frame_rate_num, int frame_rate_den, long bit_rate) throws AVIOException {
- return addVideoStream(getFormat().getVideoCodec(), streams.size(), width, height, frame_rate_num, frame_rate_den, bit_rate);
- }
-
- public JJWriterVideo addVideoStream(int codec_id, int streamid, int width, int height, int frame_rate_den, long bit_rate) throws AVIOException {
- return addVideoStream(codec_id, streamid, width, height, 1, frame_rate_den, bit_rate);
+ writeTrailer = true;
}
/**
* Add a video stream.
* <p>
- * @param codec_id
- * @param width
- * @param height
- * @param frame_rate
- * @param bit_rate
- * <p>
+ * Creates and adds a video stream. The video stream size, pixel format, and bitrate must still be initialised.
+ *
+ * @param codec If null then use the format default codec. If non-null must be a video codec.
+ * @param streamid StreamID to create, or -1 to use the next available slot.
* @return
- * @throws AVInvalidStreamException
+ * @throws AVIOException
*/
- public JJWriterVideo addVideoStream(int codec_id, int streamid, int width, int height, int frame_rate_num, int frame_rate_den, long bit_rate) throws AVIOException {
+ public JJWriterVideo addVideoStream(AVCodec codec, int streamid) throws AVIOException {
AVCodecContext c;
+ JJWriterVideo vs;
AVStream st;
- // TODO: all this crap should probably go into JJWriterVideo constructor
- AVCodec codec = AVCodec.findEncoder(codec_id);
+ if (codec == null) {
+ codec = AVCodec.findEncoder(getFormat().getVideoCodec());
+ if (codec == null)
+ throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND);
+ } else if (codec.getType() != AVMediaType.AVMEDIA_TYPE_VIDEO)
+ throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND);
c = AVCodecContext.allocContext(codec);
-
st = oc.newStream(codec);
- if (st == null) {
+ if (st == null)
throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND);
- }
- st.setID(streams.size());
- c.setCodecID(codec_id);
- c.setCodecType(AVMediaType.AVMEDIA_TYPE_VIDEO);
- /* put sample parameters */
- c.setBitRate(bit_rate);
+ st.setID(streamid == -1 ? streams.size() : streamid);
- /* resolution must be a multiple of two */
- c.setWidth(width);
- c.setHeight(height);
- /* time base: this is the fundamental unit of time (in seconds) in terms
- of which frame timestamps are represented. for fixed-fps content,
- timebase should be 1/framerate and timestamp increments should be
- identically 1. */
- st.setTimeBase(new AVRational(frame_rate_num, frame_rate_den));
- c.setTimeBase(new AVRational(frame_rate_num, frame_rate_den));
- c.setGOPSize(12);
- /* emit one intra frame every twelve frames at most */
-
- c.setPixelFormat(AVPixelFormat.AV_PIX_FMT_YUV420P);
- if (codec_id == AVCodecID.AV_CODEC_ID_MPEG2VIDEO) {
- /* just for testing, we also add B frames */
- c.setMaxBFrames(2);
- }
- if (codec_id == AVCodecID.AV_CODEC_ID_MPEG1VIDEO) {
- /* Needed to avoid using macroblocks in which some coeffs overflow.
- This does not happen with normal video, it just happens here as
- the motion of the chroma plane does not match the luma plane. */
- c.setMBDecision(2);
- }
- // FIXME: some formats want stream headers to be separate
- if ((oc.getOutputFormat().getFlags() & AVOutputFormat.AVFMT_GLOBALHEADER) != 0) {
- c.setFlags(AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER, AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER);
- }
+ c.setCodecID(codec.getID());
+ c.setCodecType(codec.getType());
- Logger.getLogger("jjmpeg.io")
- .fine(()
- -> String.format("add avideo stream %s %d [%dx%d %s aspect %s]\n",
- codec.getName(), c.getBitRate(),
- c.getWidth(), c.getHeight(), AVPixelFormat.toString(c.getPixelFormat()),
- c.getAspectRatio()));
+ if ((oc.getOutputFormat().getFlags() & AVOutputFormat.AVFMT_GLOBALHEADER) != 0)
+ c.setFlags(AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER, AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER);
- JJWriterVideo sd = new JJWriterVideo(st, c);
- streams.add(sd);
+ vs = new JJWriterVideo(st, c);
+ streams.add(vs);
- return sd;
+ return vs;
}
/**
- * Add a new audio stream.
+ * Add an initialise a video stream.
* <p>
- * @param codec_id
- * @param streamid
- * @param fmt
- * @param sample_rate
- * @param bit_rate
+ * The supplied parameters are used to initialise the corresponding fields of the AVCodecContext. Other fields initialised
+ * are:
+ * <ul>
+ * <li>AVCodecContext.TimeBase=1 / frame_rate
+ * <li>AVStream.TimeBase=1 / frame_rate
+ * </ul>
+ *
+ * @param codec If null then use the format default codec. If non-null must be a video codec.
+ * @param streamid StreamID to create, or -1 to use the next available slot.
+ * @param pixel_format AVPixelFormat.
+ * @param width Width of video stream.
+ * @param height Height of video stream.
+ * @param frame_rate Display frame rate. It's inverse is also set as the stream and codec timebase.
+ * @param bit_rate Bitrate of encoding.
* <p>
* @return
- * @throws AVInvalidStreamException
+ * @throws au.notzed.jjmpeg.AVIOException on error
*/
- public JJWriterAudio addAudioStream(int codec_id, int streamid, int fmt, int sample_rate, int channels, long bit_rate) throws AVIOException {
+ public JJWriterVideo addVideoStream(AVCodec codec, int streamid, int pixel_format, int width, int height, AVRational frame_rate, long bit_rate) throws AVIOException {
+ JJWriterVideo sd = addVideoStream(codec, streamid);
+
+ /* Initialise provided parameters */
+ AVCodecContext c = sd.c;
+ AVStream st = sd.stream;
+
+ c.setWidth(width);
+ c.setHeight(height);
+
+ st.setAverageFrameRate(frame_rate);
+ sd.setTimeBase(new AVRational(frame_rate.den, frame_rate.num));
+
+ c.setBitRate(bit_rate);
+ c.setPixelFormat(pixel_format);
+
+ return sd;
+ }
+
+ public JJWriterAudio addAudioStream(AVCodec codec, int streamid) throws AVIOException {
AVCodecContext c;
+ JJWriterAudio as;
AVStream st;
- AVCodec codec = AVCodec.findEncoder(codec_id);
+ if (codec == null) {
+ codec = AVCodec.findEncoder(getFormat().getAudioCodec());
+ if (codec == null)
+ throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND);
+ } else if (codec.getType() != AVMediaType.AVMEDIA_TYPE_AUDIO)
+ throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND);
c = AVCodecContext.allocContext(codec);
-
st = oc.newStream(codec);
- if (st == null) {
+ if (st == null)
throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND);
- }
-
- c.setCodecID(codec_id);
- c.setCodecType(AVMediaType.AVMEDIA_TYPE_AUDIO);
- c.setSampleFormat(fmt);
- c.setBitRate(bit_rate);
- c.setSampleRate(sample_rate);
+ st.setID(streamid == -1 ? streams.size() : streamid);
- c.setNumChannels(channels);
- c.setChannelLayout(4);
+ c.setCodecID(codec.getID());
+ c.setCodecType(codec.getType());
- if ((oc.getOutputFormat().getFlags() & AVOutputFormat.AVFMT_GLOBALHEADER) != 0) {
+ if ((oc.getOutputFormat().getFlags() & AVOutputFormat.AVFMT_GLOBALHEADER) != 0)
c.setFlags(AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER, AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER);
- }
- Logger.getLogger("jjmpeg.io")
- .fine(()
- -> String.format("add audio stream audio %s %d [x%d %dHz %s]\n",
- codec.getName(), c.getBitRate(),
- c.getNumChannels(), c.getSampleRate(), AVSampleFormat.toString(c.getSampleFormat())));
+ as = new JJWriterAudio(st, c);
+ streams.add(as);
- JJWriterAudio as = new JJWriterAudio(st, c);
+ return as;
+ }
- streams.add(as);
+ /**
+ * Add an initialise an audio stream.
+ * <p>
+ * @param codec If null then use the format default codec. If non-null must be a video codec.
+ * @param streamid StreamID to create, or -1 to use the next available slot.
+ * @param sample_format AVSampleFormat
+ * @param sample_rate Cycles per second.
+ * @param layout AVChannelLayout
+ * @param bit_rate Encoding bit rate.
+ * <p>
+ * @return
+ * @throws au.notzed.jjmpeg.AVIOException
+ * @see AVSampleFormat
+ * @see AVChannelLayout
+ * @see AVChannelLayout#getDefaultLayout(int)
+ */
+ public JJWriterAudio addAudioStream(AVCodec codec, int streamid, int sample_format, int sample_rate, long layout, long bit_rate) throws AVIOException {
+ JJWriterAudio as = addAudioStream(codec, streamid);
+ AVCodecContext c = as.c;
+
+ c.setSampleFormat(sample_format);
+ c.setBitRate(bit_rate);
+ c.setSampleRate(sample_rate);
+
+ c.setNumChannels(AVChannelLayout.getNumChannels(layout));
+ c.setChannelLayout(layout);
return as;
}
public void flush() throws AVIOException {
- for (JJWriterStream sd: streams) {
- sd.addFrame(null);
+ for (JJWriterStream sd : streams) {
+ sd.writeFrame(null);
}
}
public void close() throws AVIOException {
flush();
- /* write the trailer, if any. the trailer must be written
- * before you close the CodecContexts open when you wrote the
- * header; otherwise write_trailer may try to use memory that
- * was freed on av_codec_close() */
- oc.writeTrailer();
-
- /* close each codec */
- for (JJWriterStream sd: streams) {
- sd.close();
+ if (writeTrailer) {
+ /* write the trailer, if any. the trailer must be written
+ * before you close the CodecContexts open when you wrote the
+ * header; otherwise write_trailer may try to use memory that
+ * was freed on av_codec_close() */
+ oc.writeTrailer();
}
- /* close the output file */
- //output.close();
+ for (JJWriterStream sd : streams) {
+ sd.close();
+ }
/* free the stream */
oc.release();
return c;
}
+ public void setTimeBase(AVRational timeBase) {
+ stream.setTimeBase(timeBase);
+ c.setTimeBase(timeBase);
+ }
+
+ public void setBitRate(long bit_rate) {
+ c.setBitRate(bit_rate);
+ }
+
+ /**
+ * Open a write stream.
+ * This performs the step of set
+ *
+ * @throws AVIOException
+ */
public void open() throws AVIOException {
AVCodec codec = AVCodec.findEncoder(c.getCodecID());
opened = true;
}
- public void addFrame(AVFrame frame) throws AVIOException {
+ /**
+ * Write encoded packet.
+ * If the data is already in the correct format then use this to
+ * add a packet to the stream.
+ * <p>
+ * This sets the packet streamIndex and rescales the timestamp and calls
+ * {@link AVFormatContext#interleavedWriteFrame(au.notzed.jjmpeg.AVPacket)}
+ *
+ * @param packet
+ * @throws AVIOException
+ */
+ public void writePacket(AVPacket packet) throws AVIOException {
+ packet.rescaleTS(c.getTimeBase(), stream.getTimeBase());
+ packet.setStreamIndex(stream.getIndex());
+ oc.interleavedWriteFrame(packet);
+ }
+
+ /**
+ * Write unencoded frame.
+ * <p>
+ * The frame is passed through the
+ * {@link AVCodecContext#sendFrame(au.notzed.jjmpeg.AVFrame) /
+ * {@link AVCodecContext#receivePacket(au.notzed.jjmpeg.AVPacket)
+ * sequence and then written to the output.
+ *
+ * @param frame
+ * @throws AVIOException on error.
+ */
+ public void writeFrame(AVFrame frame) throws AVIOException {
if (c.sendFrame(frame)) {
while (c.receivePacket(packet)) {
- packet.rescaleTS(c.getTimeBase(), stream.getTimeBase());
- packet.setStreamIndex(stream.getIndex());
- oc.interleavedWriteFrame(packet);
+ writePacket(packet);
}
}
}
super.open();
frame = AVFrame.alloc(c.getPixelFormat(), c.getWidth(), c.getHeight());
+
+ Logger.getLogger("jjmpeg.io")
+ .fine(()
+ -> String.format("open video stream %s %d [%dx%d %s aspect %s]\n",
+ AVCodec.findEncoder(c.getCodecID()).getName(),
+ c.getBitRate(),
+ c.getWidth(), c.getHeight(), AVPixelFormat.toString(c.getPixelFormat()),
+ c.getAspectRatio()));
+ }
+
+ public void setWidth(int width) {
+ c.setWidth(width);
+ }
+
+ public void setHeight(int height) {
+ c.setHeight(height);
+ }
+
+ public void setPixelFormat(int format) {
+ c.setPixelFormat(format);
+ }
+
+ public void setAverateFrameRate(AVRational frameRate) {
+ stream.setAverageFrameRate(frameRate);
}
@Override
* <p>
* @param frame Video frame to write, or null to flush buffers.
* <p>
- * @throws AVEncodingError
- * @throws AVIOException
+ * @throws AVIOException on error.
*/
- public void addFrame(AVFrame frame) throws AVIOException {
- if (frame != null)
- frame.setPTS(nextPTS++);
- super.addFrame(frame);
+ @Override
+ public void writeFrame(AVFrame frame) throws AVIOException {
+ //if (frame != null)
+ // frame.setPTS(nextPTS++);
+ super.writeFrame(frame);
}
}
super(stream, c);
}
+ public void setSampleFormat(int sample_format) {
+ c.setSampleFormat(sample_format);
+ }
+
+ public void setSampleRate(int sample_rate) {
+ c.setSampleRate(sample_rate);
+ }
+
+ public void setNumChannels(int channels) {
+ c.setNumChannels(channels);
+ }
+
+ public void setChannelLayout(long layout) {
+ c.setChannelLayout(layout);
+ }
+
@Override
public void open() throws AVIOException {
super.open();
// ??? unchecked
frame = AVFrame.alloc(c.getSampleFormat(), c.getNumChannels(), 2048);
+
+ Logger.getLogger("jjmpeg.io")
+ .fine(()
+ -> String.format("open audio stream %s %d [x%d %dHz %s]\n",
+ AVCodec.findEncoder(c.getCodecID()).getName(),
+ c.getBitRate(),
+ c.getNumChannels(), c.getSampleRate(), AVSampleFormat.toString(c.getSampleFormat())));
}
@Override
my $def = $1;
#print $1."\n";
if ($lastc ne "") {
- #$lastc =~ s@\t+@\t@g;
+ $lastc =~ s@\"@\\\"@g;
print C "\tprintf(\"%s\", \"$lastc\");\n";
} elsif (m@///< (.*)@ || m@/\*\|<(.*)\*@) {
+ my $com = $1;
+ $com =~ s@\"@\\\"@g;
# handle single-line comments, perhaps these should override
- print C "\tprintf(\"\\t/**\\n\\t * $1\\n\\t */\\n\");\n";
+ print C "\tprintf(\"\\t/**\\n\\t * $com\\n\\t */\\n\");\n";
}
print C "\tprintf(\"\\tpublic final static $o{type} $def = $o{cfmt};\\n\", $o{ctype}$def$o{shift});\n";
au/notzed/jjmpeg/AVCodecIDBits.java \
au/notzed/jjmpeg/AVDiscardBits.java \
au/notzed/jjmpeg/AVErrorBits.java \
+ au/notzed/jjmpeg/AVIOContextBits.java \
au/notzed/jjmpeg/AVMediaTypeBits.java \
au/notzed/jjmpeg/AVOptionsBits.java \
au/notzed/jjmpeg/AVPixelFormatBits.java \
@install -d $(@D)
perl $(extract_defines) -c -interface AVCodecBits -f $(FFMPEG_HOME) -header libavcodec/avcodec.h -d AV_CODEC_CAP_ $@
+$(tmp)/AVIOContextBits-gen.c: $(extract_defines) $(dep)
+ @install -d $(@D)
+ perl $(extract_defines) -c -interface AVIOContextBits -f $(FFMPEG_HOME) -header libavformat/avio.h -d AVSEEK_ -d AVIO_FLAG_ -d AVIO_SEEKABLE_ $@
+
$(tmp)/AVOptionsBits-gen.c: $(extract_defines) $(dep)
@install -d $(@D)
perl $(extract_defines) -c -interface AVOptionsBits -f $(FFMPEG_HOME) -header libavutil/opt.h -d AV_OPT_ $@
GS_int(AVCodecContext, CodecType, codec_type)
GS_int(AVCodecContext, CodecID, codec_id)
-GS_int(AVCodecContext, CodecTag, codec_tag)
GS_long(AVCodecContext, BitRate, bit_rate)
-GS_int(AVCodecContext, BitRateTolerance, bit_rate_tolerance)
GS_int(AVCodecContext, GlobalQuality, global_quality)
-GS_int(AVCodecContext, CompressionLevel, compression_level)
-//GS_int(AVCodecContext, Flags1, flags)
-//GS_int(AVCodecContext, Flags2, flags2)
JNIEXPORT jlong JNICALL MAKE_JJNAME(AVCodecContext, getFlags)
(JNIEnv *env , jobject jo) {
o->time_base = AVRational_get(env, jtb);
}
-GS_int(AVCodecContext, TicksPerFrame, ticks_per_frame)
GET_prim(jint, AVCodecContext, Delay, delay)
/* Video options */
GS_int(AVCodecContext, PixelFormat, pix_fmt)
-GS_int(AVCodecContext, MaxBFrames, max_b_frames)
-GS_float(AVCodecContext, BQuantFactor, b_quant_factor)
-GS_float(AVCodecContext, BQuantOffset, b_quant_offset)
-
-GET_bool(AVCodecContext, hasBFrames, has_b_frames)
-
-GS_float(AVCodecContext, IQuantFactor, i_quant_factor)
-GS_float(AVCodecContext, IQuantOffset, i_quant_offset)
-
-GS_int(AVCodecContext, MBDecision, mb_decision)
-
/* Audio options */
GS_int(AVCodecContext, SampleRate, sample_rate)
GS_int(AVCodecContext, NumChannels, channels)
GS_int(AVCodecContext, SampleFormat, sample_fmt)
-GS_int(AVCodecContext, FrameSize, frame_size)
+GET_prim(jint, AVCodecContext, FrameSize, frame_size)
GET_prim(jint, AVCodecContext, FrameNumber, frame_number)
GS_long(AVCodecContext, ChannelLayout, channel_layout)
GS_long(AVCodecContext, RequestChannelLayout, request_channel_layout)
-GS_int(AVCodecContext, StrictStdCompliance, strict_std_compliance)
-
-GS_int(AVCodecContext, ErrorConcealment, error_concealment)
-GS_int(AVCodecContext, ErrorRecognition, err_recognition) // !!
-
-GS_int(AVCodecContext, DCTAlgo, dct_algo)
-GS_int(AVCodecContext, IDCTAlgo, idct_algo)
+/* other options */
GS_int(AVCodecContext, ThreadCount, thread_count)
-GS_int(AVCodecContext, Profile, profile)
/* ********************************************************************** */
/* Methods */
/* ********************************************************************** */
+/*
+ * All the callbacks use this structure to find the java instance
+ * which implement it.
+ *
+ * AVIOContext.open() has a further complication in that there is
+ * nowhere to store a pointer to this. To get around this if an
+ * interrupt callback is supplied it is referenced in an instance of a
+ * nativez.ReferenceZ object and set on the java AVIOContext.opaque
+ * field.
+ */
struct jj_io {
- // AVIOHandler global reference
+ // AVIOHandler or AVIOInterrupt depending on how it was created
jobject jhandler;
- // direct ByteBuffer for this data
- // I don't think this will work looking at avio*.c
- //jobject jbuffer;
- //uint8_t *buffer;
};
static int jj_read_packet(void *opaque, uint8_t *buf, int buf_size) {
- JNIEnv *env = nativez_AttachCurrentThread();
+ JNIEnv *env = nativez_AttachCurrentThreadAsDaemon();
if (env) {
struct jj_io *jjio = opaque;
- //size_t position = (buf - jjio->buffer);
jvalue args[1];
- //jjbufferSetLimit(env, jjio->jbuffer, (int)(position + buf_size));
- //jjbufferSetPosition(env, jjio->jbuffer, (int)(position));
- //args[0].l = jjio->jbuffer;
-
args[0].l = nativez_NewDirectBuffer(env, buf, buf_size);
return (*env)->CallIntMethodA(env, jjio->jhandler, AVIOHandler_readPacket_l, args);
}
static int jj_write_packet(void *opaque, uint8_t *buf, int buf_size) {
- JNIEnv *env = nativez_AttachCurrentThread();
+ JNIEnv *env = nativez_AttachCurrentThreadAsDaemon();
if (env) {
struct jj_io *jjio = opaque;
- //size_t position = (buf - jjio->buffer);
jvalue args[1];
- //jjbufferSetLimit(env, jjio->jbuffer, (int)(position + buf_size));
- //jjbufferSetPosition(env, jjio->jbuffer, (int)(position));
- //args[0].l = jjio->jbuffer;
-
args[0].l = nativez_NewDirectBuffer(env, buf, buf_size);
return (*env)->CallIntMethodA(env, jjio->jhandler, AVIOHandler_writePacket_l, args);
}
static int64_t jj_seek(void *opaque, int64_t offset, int whence) {
- JNIEnv *env = nativez_AttachCurrentThread();
+ JNIEnv *env = nativez_AttachCurrentThreadAsDaemon();
if (env) {
struct jj_io *jjio = opaque;
}
}
+static int jj_interrupt(void *opaque) {
+ JNIEnv *env = nativez_AttachCurrentThreadAsDaemon();
+
+ if (env) {
+ struct jj_io *jjio = opaque;
+
+ return (*env)->CallBooleanMethodA(env, jjio->jhandler, AVIOInterrupt_isInterrupted_, NULL);
+ } else {
+ return 1;
+ }
+}
+
JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVIOContext_release
(JNIEnv *env, jclass jc, jlong jic) {
AVIOContext *ic = (void *)(uintptr_t)jic;
struct jj_io *jjio = ic->opaque;
(*env)->DeleteGlobalRef(env, jjio->jhandler);
- //(*env)->DeleteGlobalRef(env, jjio->jbuffer);
- //DLCALL(av_free)(jjio->buffer);
- free(jjio);
-
DLCALL(av_free)(ic->buffer);
DLCALL(avio_context_free)(&ic);
+ free(jjio);
} else {
DLCALL(avio_close)(ic);
}
}
JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVIOContext_open
-(JNIEnv *env, jclass jc, jstring jurl, jint flags) {
- const char *url = nativez_GetString(env, jurl);
- AVIOContext *ic = NULL;
+(JNIEnv *env, jclass jc, jstring jurl, jint flags, jobject jcb, jobject joptions) {
+ AVIOContext *io = NULL;
int res;
+ struct jj_io *jjio = NULL;
+ AVIOInterruptCB cb = { 0, 0 };
+ jobject jio = NULL;
+ jobject jrefs = NULL;
+
+ jjio = malloc(sizeof(*jjio));
+ if (!jjio) {
+ nativez_ThrowOutOfMemoryError(env, "Allocating handler");
+ return NULL;
+ }
+
+ if (jcb) {
+ // FIXME: check returns
+ jrefs = ReferenceZ_create(env, &jcb, 1);
+ jjio->jhandler = (*env)->NewGlobalRef(env, jcb);
+ cb.callback = jj_interrupt;
+ cb.opaque = jjio;
+ }
- res = DLCALL(avio_open)(&ic, url, flags);
- if (res < 0)
+ AVDictionary *options = AVDictionary_get(env, joptions);
+ const char *url = nativez_GetString(env, jurl);
+
+ res = DLCALL(avio_open2)(&io, url, flags, &cb, &options);
+ AVDictionary_set(env, joptions, options, 1);
+
+ if (res < 0) {
jjthrowAVIOException(env, res, url);
+ goto fail;
+ }
+
+ jio = NativeZ_create(env, jc, io);
+ if (!jio)
+ goto fail;
+
+
+ (*env)->SetObjectField(env, jio, AVIOContext_opaque, jrefs);
+
+ return jio;
+ fail:
nativez_ReleaseString(env, jurl, url);
+ if (jjio->jhandler)
+ (*env)->DeleteGlobalRef(env, jjio->jhandler);
+ free(jjio);
- return NativeZ_create(env, jc, ic);
+ return NULL;
}
JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVIOContext_alloc
AVIOContext *io = NULL;
struct jj_io *jjio;
uint8_t *buffer;
+ jobject jio = NULL;
jjio = malloc(sizeof(*jjio));
- if (jjio) {
- buffer = DLCALL(av_malloc)(buffer_size);
- if (buffer) {
- int write_flag = (jflags & au_notzed_jjmpeg_AVIOContext_AVIO_WRITABLE) != 0;
+ if (!jjio)
+ goto fail_nojjio;
- jjio->jhandler = (*env)->NewGlobalRef(env, jhandler);
- //jjio->jbuffer = jjnewDirectBuffer(env, jjio->buffer, buffer_size);
+ buffer = DLCALL(av_malloc)(buffer_size);
+ if (!buffer)
+ goto fail_nobuffer;
+
+ jjio->jhandler = (*env)->NewGlobalRef(env, jhandler);
- io = DLCALL(avio_alloc_context)(buffer, buffer_size, write_flag, jjio,
- jj_read_packet,
- jj_write_packet,
- jj_seek);
- if (io) {
- io->seekable = jflags & 3;
- io->direct = (jflags & au_notzed_jjmpeg_AVIOContext_AVIO_DIRECT) != 0;
- } else {
- nativez_ThrowOutOfMemoryError(env, "Allocating io context");
- free(buffer);
- free(jjio);
- }
- } else {
- nativez_ThrowOutOfMemoryError(env, "Allocating buffer");
- free(jjio);
- }
- } else {
- nativez_ThrowOutOfMemoryError(env, "Allocating handler");
- }
-
- return NativeZ_create(env, jc, io);
+ io = DLCALL(avio_alloc_context)(buffer, buffer_size,
+ (jflags & au_notzed_jjmpeg_AVIOContext_AVIO_WRITABLE) != 0,
+ jjio,
+ jj_read_packet,
+ jj_write_packet,
+ jj_seek);
+ if (!io)
+ goto fail;
+
+ io->seekable = jflags & (AVIO_SEEKABLE_NORMAL | AVIO_SEEKABLE_TIME);
+ io->direct = (jflags & au_notzed_jjmpeg_AVIOContext_AVIO_DIRECT) != 0;
+ jio = NativeZ_create(env, jc, io);
+ if (!jio)
+ goto fail;
+
+ return jio;
+ fail:
+ (*env)->DeleteGlobalRef(env, jjio->jhandler);
+ free(buffer);
+ fail_nobuffer:
+ free(jjio);
+ fail_nojjio:
+ nativez_ThrowOutOfMemoryError(env, "Allocating io context");
+ return NULL;
}
avio_alloc_context
avio_context_free
- avio_open
+ avio_open2
avio_close
}
av_free
}
+java AVIOContext au/notzed/jjmpeg/AVIOContext {
+ opaque, Ljava/lang/Object;
+}
+
+java AVIOInterrupt au/notzed/jjmpeg/AVIOContext$AVIOInterrupt {
+ isInterrupted, ()Z
+}
+
java AVIOHandler au/notzed/jjmpeg/AVIOContext$AVIOHandler {
readPacket, (Ljava/nio/ByteBuffer;)I
writePacket, (Ljava/nio/ByteBuffer;)I
GS_long(AVPacket, PTS, pts)
GS_long(AVPacket, DTS, dts)
+GS_long(AVPacket, Duration, duration)
GET_prim(jint, AVPacket, Size, size)
GS_int(AVPacket, StreamIndex, stream_index)
GET_prim(jint, AVPacket, Flags, flags)
(JNIEnv *env, jclass jc) {
AVPacket *packet = DLCALL(av_packet_alloc)();
- //if (!packet) // outofmemoryerror?
-
- return NativeZ_create(env, jc, packet);
+ if (nativez_NonNull(env, "Allocating AVPacket", packet)) {
+ DLCALL(av_init_packet)(packet);
+ return NativeZ_create(env, jc, packet);
+ }
+ return NULL;
}
/* **************************************** */
-JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVPacket_getData
+JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVPacket_getData__
(JNIEnv *env, jobject jpacket) {
AVPacket *packet = NativeZ_getP(env, jpacket);
return NULL;
}
+JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVPacket_getData__J
+(JNIEnv *env, jobject jpacket, jlong size) {
+ AVPacket *packet = NativeZ_getP(env, jpacket);
+
+ if (packet->size < size) {
+ if (av_grow_packet(packet, size - packet->size) != 0) {
+ nativez_ThrowOutOfMemoryError(env, "Unable to grow packet");
+ return NULL;
+ }
+ } else {
+ av_shrink_packet(packet, size);
+ }
+
+ if (nativez_NonNull(env, "No data pointer", packet->data))
+ return nativez_NewDirectBuffer(env, packet->data, packet->size);
+
+ return NULL;
+}
+
JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVPacket_clear
(JNIEnv *env, jobject jpacket) {
AVPacket *packet = NativeZ_getP(env, jpacket);
AVPacket *packet = NativeZ_getP(env, jpacket);
AVPacket *dst = DLCALL(av_packet_clone)(packet);
- // if !dst ...
- return NativeZ_create(env, AVPacket_classid, dst);
+ if (nativez_NonNull(env, "Cloning AVPacket", dst))
+ return NativeZ_create(env, AVPacket_classid, dst);
+ return NULL;
}
JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVPacket_rescaleTS
av_packet_unref
av_packet_clone
av_packet_rescale_ts
+
+ av_init_packet
}
java AVPacket au/notzed/jjmpeg/AVPacket {
static jobject jlogger_ref;
static void log_callback(void*ptr, int level, const char*fmt, va_list vl) {
- JNIEnv *env = nativez_AttachCurrentThread();
+ JNIEnv *env = nativez_AttachCurrentThreadAsDaemon();
if (env) {
char log[1024];
/* ********************************************************************** */
-jstring jjnewIntArrayT(JNIEnv *env, const int *ap, int terminal) {
+jintArray jjnewIntArrayT(JNIEnv *env, const int *ap, int terminal) {
jintArray ret = NULL;
if (ap) {
ret = (*env)->NewIntArray(env, len);
(*env)->SetIntArrayRegion(env, ret, 0, len, (const jint *)ap);
}
+
return ret;
}
* @param ap Array pointer, may be NULL.
* @param terminal Terminating value.
*/
-jstring jjnewIntArrayT(JNIEnv *env, const int *ap, int terminal);
+jintArray jjnewIntArrayT(JNIEnv *env, const int *ap, int terminal);
void jjthrowAVIOException(JNIEnv *env, int error, const char *msg);