From 7aea9c7f509659c9fc0f4847d34b0d93017ff78f Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 11 Mar 2025 18:39:06 +0000 Subject: [PATCH 01/23] WIP - this commit isn't currently compiling. Wound up being a major ffmpeg bump, so need to replace the deprecated function calls in FFmpegFrameGrabber... --- build.gradle | 2 +- integration/boofcv-ffmpeg/build.gradle | 13 +- .../io/ffmpeg/FfmpegVideoImageSequence.java | 8 +- .../copiedstuff/FFmpegFrameGrabber.java | 1686 +++++++++-------- integration/boofcv-javacv/build.gradle | 14 +- 5 files changed, 868 insertions(+), 855 deletions(-) diff --git a/build.gradle b/build.gradle index 3b05ce94e6..0db7697cce 100644 --- a/build.gradle +++ b/build.gradle @@ -269,7 +269,7 @@ subprojects { } spotless { - ratchetFrom 'origin/SNAPSHOT' +// ratchetFrom 'origin/SNAPSHOT' format 'misc', { // define the files to apply `misc` to diff --git a/integration/boofcv-ffmpeg/build.gradle b/integration/boofcv-ffmpeg/build.gradle index 342ad16776..93f0d95558 100644 --- a/integration/boofcv-ffmpeg/build.gradle +++ b/integration/boofcv-ffmpeg/build.gradle @@ -1,15 +1,6 @@ - -// Hack around gradle including stuff it shouldn't -//configurations { -// all*.exclude group: 'org.bytedeco', module: 'javacpp-presets' -//} - dependencies { api project(':main:boofcv-ip') api project(':main:boofcv-io') - api group: 'org.bytedeco.javacpp-presets', name: "ffmpeg", version: '4.1-1.4.4' - native_arch.each { - implementation group: 'org.bytedeco.javacpp-presets', name: "ffmpeg", version: '4.1-1.4.4', classifier: "${it}" - } -} + implementation group: 'org.bytedeco', name: 'ffmpeg-platform', version: '7.1-1.5.11' +} \ No newline at end of file diff --git a/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java b/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java index 92eb0ca896..0de4ba61f2 100644 --- a/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java +++ b/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java @@ -27,11 +27,11 @@ import org.bytedeco.copiedstuff.Frame; import org.bytedeco.copiedstuff.FrameGrabber; import org.bytedeco.copiedstuff.Java2DFrameConverter; +import org.bytedeco.ffmpeg.global.avutil; import java.awt.image.BufferedImage; -import static org.bytedeco.javacpp.avutil.AV_LOG_ERROR; -import static org.bytedeco.javacpp.avutil.av_log_set_level; +import static org.bytedeco.ffmpeg.global.avutil.av_log_set_level; /** * Uses JavaCV, which uses FFMPEG, to read in a video. @@ -55,7 +55,9 @@ public class FfmpegVideoImageSequence> implements SimpleI public FfmpegVideoImageSequence(String filename, ImageType imageType ) { // Turn off that super annoying error message! - av_log_set_level(AV_LOG_ERROR); + + + av_log_set_level(avutil.AV_LOG_ERROR); this.filename = filename; this.imageType = imageType; diff --git a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java b/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java index 6700941e47..39ac02d4c2 100644 --- a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java +++ b/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java @@ -67,6 +67,17 @@ package org.bytedeco.copiedstuff; +import org.bytedeco.ffmpeg.avcodec.AVCodec; +import org.bytedeco.ffmpeg.avcodec.AVCodecContext; +import org.bytedeco.ffmpeg.avcodec.AVCodecParameters; +import org.bytedeco.ffmpeg.avcodec.AVPacket; +import org.bytedeco.ffmpeg.avformat.*; +import org.bytedeco.ffmpeg.avutil.AVDictionary; +import org.bytedeco.ffmpeg.avutil.AVDictionaryEntry; +import org.bytedeco.ffmpeg.avutil.AVFrame; +import org.bytedeco.ffmpeg.avutil.AVRational; +import org.bytedeco.ffmpeg.global.*; +import org.bytedeco.ffmpeg.swscale.SwsContext; import org.bytedeco.javacpp.*; import java.io.BufferedInputStream; @@ -78,843 +89,862 @@ import java.util.HashMap; import java.util.Map.Entry; -import static org.bytedeco.javacpp.avcodec.*; -import static org.bytedeco.javacpp.avdevice.avdevice_register_all; -import static org.bytedeco.javacpp.avformat.*; -import static org.bytedeco.javacpp.avutil.*; -import static org.bytedeco.javacpp.swscale.*; +import static org.bytedeco.ffmpeg.global.avcodec.*; +import static org.bytedeco.ffmpeg.global.avdevice.avdevice_register_all; +import static org.bytedeco.ffmpeg.global.avformat.*; +import static org.bytedeco.ffmpeg.global.avutil.*; +import static org.bytedeco.ffmpeg.global.swscale.*; /** - * * @author Samuel Audet */ -@SuppressWarnings({"UnsafeFinalization","MissingOverride","BadImport"}) +@SuppressWarnings({"UnsafeFinalization", "MissingOverride", "BadImport"}) public class FFmpegFrameGrabber extends FrameGrabber { - public static String[] getDeviceDescriptions() throws Exception { - tryLoad(); - throw new UnsupportedOperationException("Device enumeration not support by FFmpeg."); - } - - public static FFmpegFrameGrabber createDefault(File deviceFile) throws Exception { return new FFmpegFrameGrabber(deviceFile); } - public static FFmpegFrameGrabber createDefault(String devicePath) throws Exception { return new FFmpegFrameGrabber(devicePath); } - public static FFmpegFrameGrabber createDefault(int deviceNumber) throws Exception { throw new Exception(FFmpegFrameGrabber.class + " does not support device numbers."); } - - private static Exception loadingException = null; - public static void tryLoad() throws Exception { - if (loadingException != null) { - throw loadingException; - } else { - try { - Loader.load(org.bytedeco.javacpp.avutil.class); - Loader.load(org.bytedeco.javacpp.swresample.class); - Loader.load(org.bytedeco.javacpp.avcodec.class); - Loader.load(org.bytedeco.javacpp.avformat.class); - Loader.load(org.bytedeco.javacpp.swscale.class); - - // Register all formats and codecs - avcodec_register_all(); - av_register_all(); - avformat_network_init(); - - Loader.load(org.bytedeco.javacpp.avdevice.class); - avdevice_register_all(); - } catch (Throwable t) { - if (t instanceof Exception) { - throw loadingException = (Exception)t; - } else { - throw loadingException = new Exception("Failed to load " + FFmpegFrameGrabber.class, t); - } - } - } - } - - static { - try { - tryLoad(); - } catch (Exception ex) { } - } - - public FFmpegFrameGrabber(File file) { - this(file.getAbsolutePath()); - } - public FFmpegFrameGrabber(String filename) { - this.filename = filename; - } - public FFmpegFrameGrabber(InputStream inputStream) { - this.inputStream = inputStream; - } - public void release() throws Exception { - synchronized (org.bytedeco.javacpp.avcodec.class) { - releaseUnsafe(); - } - } - void releaseUnsafe() throws Exception { - if (pkt != null && pkt2 != null) { - if (pkt2.size() > 0) { - av_packet_unref(pkt); - } - pkt = pkt2 = null; - } - - // Free the RGB image - if (image_ptr != null) { - for (int i = 0; i < image_ptr.length; i++) { - av_free(image_ptr[i]); - } - image_ptr = null; - } - if (picture_rgb != null) { - av_frame_free(picture_rgb); - picture_rgb = null; - } - - // Free the native format picture frame - if (picture != null) { - av_frame_free(picture); - picture = null; - } - - // Close the video codec - if (video_c != null) { - avcodec_free_context(video_c); - video_c = null; - } - - // Free the audio samples frame - if (samples_frame != null) { - av_frame_free(samples_frame); - samples_frame = null; - } - - // Close the audio codec - if (audio_c != null) { - avcodec_free_context(audio_c); - audio_c = null; - } - - // Close the video file - if (inputStream == null && oc != null && !oc.isNull()) { - avformat_close_input(oc); - oc = null; - } - - if (img_convert_ctx != null) { - sws_freeContext(img_convert_ctx); - img_convert_ctx = null; - } - - got_frame = null; - frameGrabbed = false; - frame = null; - timestamp = 0; - frameNumber = 0; - - if (inputStream != null) { - try { - if (oc == null) { - // when called a second time - inputStream.close(); - } else { - inputStream.reset(); - } - } catch (IOException ex) { - throw new Exception("Error on InputStream.close(): ", ex); - } finally { - inputStreams.remove(oc); - if (avio != null) { - if (avio.buffer() != null) { - av_free(avio.buffer()); - avio.buffer(null); - } - av_free(avio); - avio = null; - } - if (oc != null) { - avformat_free_context(oc); - oc = null; - } - } - } - } - @Override protected void finalize() throws Throwable { - super.finalize(); - release(); - } - - static HashMap inputStreams = new HashMap(); - - static class ReadCallback extends Read_packet_Pointer_BytePointer_int { - @Override public int call(Pointer opaque, BytePointer buf, int buf_size) { - try { - byte[] b = new byte[buf_size]; - InputStream is = inputStreams.get(opaque); - int size = is.read(b, 0, buf_size); - if (size < 0) { - return 0; - } else { - buf.put(b, 0, size); - return size; - } - } - catch (Throwable t) { - System.err.println("Error on InputStream.read(): " + t); - return -1; - } - } - } - - static class SeekCallback extends Seek_Pointer_long_int { - @Override public long call(Pointer opaque, long offset, int whence) { - try { - InputStream is = inputStreams.get(opaque); - switch (whence) { - case 0: is.reset(); break; - case 1: break; - default: return -1; - } - is.skip(offset); - return 0; - } catch (Throwable t) { - System.err.println("Error on InputStream.reset() or skip(): " + t); - return -1; - } - } - } - - static ReadCallback readCallback; - static SeekCallback seekCallback; - - private InputStream inputStream; - private AVIOContext avio; - private String filename; - private AVFormatContext oc; - private AVStream video_st, audio_st; - private AVCodecContext video_c, audio_c; - private AVFrame picture, picture_rgb; - private BytePointer[] image_ptr; - private Buffer[] image_buf; - private AVFrame samples_frame; - private BytePointer[] samples_ptr; - private Buffer[] samples_buf; - private AVPacket pkt, pkt2; - private int sizeof_pkt; - private int[] got_frame; - private SwsContext img_convert_ctx; - private boolean frameGrabbed; - private Frame frame; - - @Override public double getGamma() { - // default to a gamma of 2.2 for cheap Webcams, DV cameras, etc. - if (gamma == 0.0) { - return 2.2; - } else { - return gamma; - } - } - - @Override public String getFormat() { - if (oc == null) { - return super.getFormat(); - } else { - return oc.iformat().name().getString(); - } - } - - @Override public int getImageWidth() { - return imageWidth > 0 || video_c == null ? super.getImageWidth() : video_c.width(); - } - - @Override public int getImageHeight() { - return imageHeight > 0 || video_c == null ? super.getImageHeight() : video_c.height(); - } - - @Override public int getAudioChannels() { - return audio_c == null ? super.getAudioChannels() : audio_c.channels(); - } - - @Override public int getPixelFormat() { - if (imageMode == ImageMode.COLOR || imageMode == ImageMode.GRAY) { - if (pixelFormat == AV_PIX_FMT_NONE) { - return imageMode == ImageMode.COLOR ? AV_PIX_FMT_BGR24 : AV_PIX_FMT_GRAY8; - } else { - return pixelFormat; - } - } else if (video_c != null) { // RAW - return video_c.pix_fmt(); - } else { - return super.getPixelFormat(); - } - } - - @Override public int getVideoCodec() { - return video_c == null ? super.getVideoCodec() : video_c.codec_id(); - } - - @Override public int getVideoBitrate() { - return video_c == null ? super.getVideoBitrate() : (int)video_c.bit_rate(); - } - - @Override public double getAspectRatio() { - if (video_st == null) { - return super.getAspectRatio(); - } else { - AVRational r = av_guess_sample_aspect_ratio(oc, video_st, picture); - double a = (double)r.num() / r.den(); - return a == 0.0 ? 1.0 : a; - } - } - - @Override public double getFrameRate() { - if (video_st == null) { - return super.getFrameRate(); - } else { - AVRational r = video_st.avg_frame_rate(); - if (r.num() == 0 && r.den() == 0) { - r = video_st.r_frame_rate(); - } - return (double)r.num() / r.den(); - } - } - - @Override public int getAudioCodec() { - return audio_c == null ? super.getAudioCodec() : audio_c.codec_id(); - } - - @Override public int getAudioBitrate() { - return audio_c == null ? super.getAudioBitrate() : (int)audio_c.bit_rate(); - } - - @Override public int getSampleFormat() { - return audio_c == null ? super.getSampleFormat() : audio_c.sample_fmt(); - } - - @Override public int getSampleRate() { - return audio_c == null ? super.getSampleRate() : audio_c.sample_rate(); - } - - @Override public String getMetadata(String key) { - if (oc == null) { - return super.getMetadata(key); - } - AVDictionaryEntry entry = av_dict_get(oc.metadata(), key, null, 0); - return entry == null || entry.value() == null ? null : entry.value().getString(); - } - - @Override public String getVideoMetadata(String key) { - if (video_st == null) { - return super.getVideoMetadata(key); - } - AVDictionaryEntry entry = av_dict_get(video_st.metadata(), key, null, 0); - return entry == null || entry.value() == null ? null : entry.value().getString(); - } - - @Override public String getAudioMetadata(String key) { - if (audio_st == null) { - return super.getAudioMetadata(key); - } - AVDictionaryEntry entry = av_dict_get(audio_st.metadata(), key, null, 0); - return entry == null || entry.value() == null ? null : entry.value().getString(); - } - - @Override public void setFrameNumber(int frameNumber) throws Exception { - // best guess, AVSEEK_FLAG_FRAME has not been implemented in FFmpeg... - setTimestamp(Math.round(1000000L * frameNumber / getFrameRate())); - } - - @Override public void setTimestamp(long timestamp) throws Exception { - int ret; - if (oc == null) { - super.setTimestamp(timestamp); - } else { - timestamp = timestamp * AV_TIME_BASE / 1000000L; - /* add the stream start time */ - if (oc.start_time() != AV_NOPTS_VALUE) { - timestamp += oc.start_time(); - } - if ((ret = avformat_seek_file(oc, -1, Long.MIN_VALUE, timestamp, Long.MAX_VALUE, AVSEEK_FLAG_BACKWARD)) < 0) { - throw new Exception("avformat_seek_file() error " + ret + ": Could not seek file to timestamp " + timestamp + "."); - } - if (video_c != null) { - avcodec_flush_buffers(video_c); - } - if (audio_c != null) { - avcodec_flush_buffers(audio_c); - } - if (pkt2.size() > 0) { - pkt2.size(0); - av_packet_unref(pkt); - } + static HashMap inputStreams = new HashMap(); + static ReadCallback readCallback; + static SeekCallback seekCallback; + private static Exception loadingException = null; + + static { + try { + tryLoad(); + } catch (Exception ex) { + } + } + + private InputStream inputStream; + private AVIOContext avio; + private String filename; + private AVFormatContext oc; + private AVStream video_st, audio_st; + private AVCodecContext video_c, audio_c; + private AVFrame picture, picture_rgb; + private BytePointer[] image_ptr; + private Buffer[] image_buf; + private AVFrame samples_frame; + private BytePointer[] samples_ptr; + private Buffer[] samples_buf; + private AVPacket pkt, pkt2; + private int sizeof_pkt; + private int[] got_frame; + private SwsContext img_convert_ctx; + private boolean frameGrabbed; + private Frame frame; + + public FFmpegFrameGrabber( File file ) { + this(file.getAbsolutePath()); + } + + public FFmpegFrameGrabber( String filename ) { + this.filename = filename; + } + + public FFmpegFrameGrabber( InputStream inputStream ) { + this.inputStream = inputStream; + } + + public static String[] getDeviceDescriptions() throws Exception { + tryLoad(); + throw new UnsupportedOperationException("Device enumeration not support by FFmpeg."); + } + + public static FFmpegFrameGrabber createDefault( File deviceFile ) throws Exception {return new FFmpegFrameGrabber(deviceFile);} + + public static FFmpegFrameGrabber createDefault( String devicePath ) throws Exception {return new FFmpegFrameGrabber(devicePath);} + + public static FFmpegFrameGrabber createDefault( int deviceNumber ) throws Exception {throw new Exception(FFmpegFrameGrabber.class + " does not support device numbers.");} + + public static void tryLoad() throws Exception { + if (loadingException != null) { + throw loadingException; + } else { + try { + //todo: check we got the right classes here. there are two... + Loader.load(avutil.class); + Loader.load(swresample.class); + Loader.load(avcodec.class); + Loader.load(avformat.class); + Loader.load(swscale.class); + + //todo: document that these are unnecessary: + // https://www.ffmpeg.org/doxygen/4.4/group__lavc__core.html#gaf139c95c2211e49496017459033eee1f + // https://github.com/leandromoreira/ffmpeg-libav-tutorial/issues/29 +// avcodec_register_all(); +// av_register_all(); + avformat_network_init(); + + Loader.load(avdevice.class); + avdevice_register_all(); + } catch (Throwable t) { + //todo: this isn't gonna ever get hit lol + if (t instanceof Exception) { + throw loadingException = (Exception)t; + } else { + throw loadingException = new Exception("Failed to load " + FFmpegFrameGrabber.class, t); + } + } + } + } + + public void release() throws Exception { + synchronized (avcodec.class) { + releaseUnsafe(); + } + } + + void releaseUnsafe() throws Exception { + if (pkt != null && pkt2 != null) { + if (pkt2.size() > 0) { + av_packet_unref(pkt); + } + pkt = pkt2 = null; + } + + // Free the RGB image + if (image_ptr != null) { + for (int i = 0; i < image_ptr.length; i++) { + av_free(image_ptr[i]); + } + image_ptr = null; + } + if (picture_rgb != null) { + av_frame_free(picture_rgb); + picture_rgb = null; + } + + // Free the native format picture frame + if (picture != null) { + av_frame_free(picture); + picture = null; + } + + // Close the video codec + if (video_c != null) { + avcodec_free_context(video_c); + video_c = null; + } + + // Free the audio samples frame + if (samples_frame != null) { + av_frame_free(samples_frame); + samples_frame = null; + } + + // Close the audio codec + if (audio_c != null) { + avcodec_free_context(audio_c); + audio_c = null; + } + + // Close the video file + if (inputStream == null && oc != null && !oc.isNull()) { + avformat_close_input(oc); + oc = null; + } + + if (img_convert_ctx != null) { + sws_freeContext(img_convert_ctx); + img_convert_ctx = null; + } + + got_frame = null; + frameGrabbed = false; + frame = null; + timestamp = 0; + frameNumber = 0; + + if (inputStream != null) { + try { + if (oc == null) { + // when called a second time + inputStream.close(); + } else { + inputStream.reset(); + } + } catch (IOException ex) { + throw new Exception("Error on InputStream.close(): ", ex); + } finally { + inputStreams.remove(oc); + if (avio != null) { + if (avio.buffer() != null) { + av_free(avio.buffer()); + avio.buffer(null); + } + av_free(avio); + avio = null; + } + if (oc != null) { + avformat_free_context(oc); + oc = null; + } + } + } + } + + @Override protected void finalize() throws Throwable { + super.finalize(); + release(); + } + + @Override public double getGamma() { + // default to a gamma of 2.2 for cheap Webcams, DV cameras, etc. + if (gamma == 0.0) { + return 2.2; + } else { + return gamma; + } + } + + @Override public String getFormat() { + if (oc == null) { + return super.getFormat(); + } else { + return oc.iformat().name().getString(); + } + } + + @Override public int getImageWidth() { + return imageWidth > 0 || video_c == null ? super.getImageWidth() : video_c.width(); + } + + @Override public int getImageHeight() { + return imageHeight > 0 || video_c == null ? super.getImageHeight() : video_c.height(); + } + + @Override public int getAudioChannels() { + return audio_c == null ? super.getAudioChannels() : audio_c.ch_layout().nb_channels(); + } + + @Override public int getPixelFormat() { + if (imageMode == ImageMode.COLOR || imageMode == ImageMode.GRAY) { + if (pixelFormat == AV_PIX_FMT_NONE) { + return imageMode == ImageMode.COLOR ? AV_PIX_FMT_BGR24 : AV_PIX_FMT_GRAY8; + } else { + return pixelFormat; + } + } else if (video_c != null) { // RAW + return video_c.pix_fmt(); + } else { + return super.getPixelFormat(); + } + } + + @Override public int getVideoCodec() { + return video_c == null ? super.getVideoCodec() : video_c.codec_id(); + } + + @Override public int getVideoBitrate() { + return video_c == null ? super.getVideoBitrate() : (int)video_c.bit_rate(); + } + + @Override public double getAspectRatio() { + if (video_st == null) { + return super.getAspectRatio(); + } else { + AVRational r = av_guess_sample_aspect_ratio(oc, video_st, picture); + double a = (double)r.num()/r.den(); + return a == 0.0 ? 1.0 : a; + } + } + + @Override public double getFrameRate() { + if (video_st == null) { + return super.getFrameRate(); + } else { + AVRational r = video_st.avg_frame_rate(); + if (r.num() == 0 && r.den() == 0) { + r = video_st.r_frame_rate(); + } + return (double)r.num()/r.den(); + } + } + + @Override public int getAudioCodec() { + return audio_c == null ? super.getAudioCodec() : audio_c.codec_id(); + } + + @Override public int getAudioBitrate() { + return audio_c == null ? super.getAudioBitrate() : (int)audio_c.bit_rate(); + } + + @Override public int getSampleFormat() { + return audio_c == null ? super.getSampleFormat() : audio_c.sample_fmt(); + } + + @Override public int getSampleRate() { + return audio_c == null ? super.getSampleRate() : audio_c.sample_rate(); + } + + @Override public String getMetadata( String key ) { + if (oc == null) { + return super.getMetadata(key); + } + AVDictionaryEntry entry = av_dict_get(oc.metadata(), key, null, 0); + return entry == null || entry.value() == null ? null : entry.value().getString(); + } + + @Override public String getVideoMetadata( String key ) { + if (video_st == null) { + return super.getVideoMetadata(key); + } + AVDictionaryEntry entry = av_dict_get(video_st.metadata(), key, null, 0); + return entry == null || entry.value() == null ? null : entry.value().getString(); + } + + @Override public String getAudioMetadata( String key ) { + if (audio_st == null) { + return super.getAudioMetadata(key); + } + AVDictionaryEntry entry = av_dict_get(audio_st.metadata(), key, null, 0); + return entry == null || entry.value() == null ? null : entry.value().getString(); + } + + @Override public void setFrameNumber( int frameNumber ) throws Exception { + // best guess, AVSEEK_FLAG_FRAME has not been implemented in FFmpeg... + setTimestamp(Math.round(1000000L*frameNumber/getFrameRate())); + } + + @Override public void setTimestamp( long timestamp ) throws Exception { + int ret; + if (oc == null) { + super.setTimestamp(timestamp); + } else { + timestamp = timestamp*AV_TIME_BASE/1000000L; + /* add the stream start time */ + if (oc.start_time() != AV_NOPTS_VALUE) { + timestamp += oc.start_time(); + } + if ((ret = avformat_seek_file(oc, -1, Long.MIN_VALUE, timestamp, Long.MAX_VALUE, AVSEEK_FLAG_BACKWARD)) < 0) { + throw new Exception("avformat_seek_file() error " + ret + ": Could not seek file to timestamp " + timestamp + "."); + } + if (video_c != null) { + avcodec_flush_buffers(video_c); + } + if (audio_c != null) { + avcodec_flush_buffers(audio_c); + } + if (pkt2.size() > 0) { + pkt2.size(0); + av_packet_unref(pkt); + } /* comparing to timestamp +/- 1 avoids rouding issues for framerates which are no proper divisors of 1000000, e.g. where av_frame_get_best_effort_timestamp in grabFrame sets this.timestamp to ...666 and the given timestamp has been rounded to ...667 (or vice versa) */ - while (this.timestamp > timestamp + 1 && grabFrame(false, true, false, false) != null) { - // flush frames if seeking backwards - } - while (this.timestamp < timestamp - 1 && grabFrame(false, true, false, false) != null) { - // decode up to the desired frame - } - if (video_c != null) { - frameGrabbed = true; - } - } - } - - @Override public int getLengthInFrames() { - // best guess... - return (int)(getLengthInTime() * getFrameRate() / 1000000L); - } - @Override public long getLengthInTime() { - return oc.duration() * 1000000L / AV_TIME_BASE; - } - - public AVFormatContext getFormatContext() { - - return oc; - } - - public void start() throws Exception { - synchronized (org.bytedeco.javacpp.avcodec.class) { - startUnsafe(); - } - } - void startUnsafe() throws Exception { - int ret; - img_convert_ctx = null; - oc = new AVFormatContext(null); - video_c = null; - audio_c = null; - pkt = new AVPacket(); - pkt2 = new AVPacket(); - sizeof_pkt = pkt.sizeof(); - got_frame = new int[1]; - frameGrabbed = false; - frame = new Frame(); - timestamp = 0; - frameNumber = 0; - - pkt2.size(0); - - // Open video file - AVInputFormat f = null; - if (format != null && format.length() > 0) { - if ((f = av_find_input_format(format)) == null) { - throw new Exception("av_find_input_format() error: Could not find input format \"" + format + "\"."); - } - } - AVDictionary options = new AVDictionary(null); - if (frameRate > 0) { - AVRational r = av_d2q(frameRate, 1001000); - av_dict_set(options, "framerate", r.num() + "/" + r.den(), 0); - } - if (pixelFormat >= 0) { - av_dict_set(options, "pixel_format", av_get_pix_fmt_name(pixelFormat).getString(), 0); - } else if (imageMode != ImageMode.RAW) { - av_dict_set(options, "pixel_format", imageMode == ImageMode.COLOR ? "bgr24" : "gray8", 0); - } - if (imageWidth > 0 && imageHeight > 0) { - av_dict_set(options, "video_size", imageWidth + "x" + imageHeight, 0); - } - if (sampleRate > 0) { - av_dict_set(options, "sample_rate", "" + sampleRate, 0); - } - if (audioChannels > 0) { - av_dict_set(options, "channels", "" + audioChannels, 0); - } - for (Entry e : this.options.entrySet()) { - av_dict_set(options, e.getKey(), e.getValue(), 0); - } - if (inputStream != null) { - if (readCallback == null) { - readCallback = new ReadCallback(); - } - if (seekCallback == null) { - seekCallback = new SeekCallback(); - } - if (!inputStream.markSupported()) { - inputStream = new BufferedInputStream(inputStream, 1024 * 1024); - } - inputStream.mark(1024 * 1024); - oc = avformat_alloc_context(); - avio = avio_alloc_context(new BytePointer(av_malloc(4096)), 4096, 0, oc, readCallback, null, seekCallback); - oc.pb(avio); - - filename = inputStream.toString(); - inputStreams.put(oc, inputStream); - } - if ((ret = avformat_open_input(oc, filename, f, options)) < 0) { - av_dict_set(options, "pixel_format", null, 0); - if ((ret = avformat_open_input(oc, filename, f, options)) < 0) { - throw new Exception("avformat_open_input() error " + ret + ": Could not open input \"" + filename + "\". (Has setFormat() been called?)"); - } - } - av_dict_free(options); - - // Retrieve stream information - if ((ret = avformat_find_stream_info(oc, (PointerPointer)null)) < 0) { - throw new Exception("avformat_find_stream_info() error " + ret + ": Could not find stream information."); - } - - // Dump information about file onto standard error - av_dump_format(oc, 0, filename, 0); - - // Find the first video and audio stream, unless the user specified otherwise - video_st = audio_st = null; - AVCodecParameters video_par = null, audio_par = null; - int nb_streams = oc.nb_streams(); - for (int i = 0; i < nb_streams; i++) { - AVStream st = oc.streams(i); - // Get a pointer to the codec context for the video or audio stream - AVCodecParameters par = st.codecpar(); - if (video_st == null && par.codec_type() == AVMEDIA_TYPE_VIDEO && (videoStream < 0 || videoStream == i)) { - video_st = st; - video_par = par; - } else if (audio_st == null && par.codec_type() == AVMEDIA_TYPE_AUDIO && (audioStream < 0 || audioStream == i)) { - audio_st = st; - audio_par = par; - } - } - if (video_st == null && audio_st == null) { - throw new Exception("Did not find a video or audio stream inside \"" + filename - + "\" for videoStream == " + videoStream + " and audioStream == " + audioStream + "."); - } - - if (video_st != null) { - // Find the decoder for the video stream - AVCodec codec = avcodec_find_decoder(video_par.codec_id()); - if (codec == null) { - throw new Exception("avcodec_find_decoder() error: Unsupported video format or codec not found: " + video_par.codec_id() + "."); - } - - /* Allocate a codec context for the decoder */ - if ((video_c = avcodec_alloc_context3(codec)) == null) { - throw new Exception("avcodec_alloc_context3() error: Could not allocate video decoding context."); - } - - /* copy the stream parameters from the muxer */ - if ((ret = avcodec_parameters_to_context(video_c, video_st.codecpar())) < 0) { - release(); - throw new Exception("avcodec_parameters_to_context() error: Could not copy the video stream parameters."); - } - - options = new AVDictionary(null); - for (Entry e : videoOptions.entrySet()) { - av_dict_set(options, e.getKey(), e.getValue(), 0); - } - // Open video codec - if ((ret = avcodec_open2(video_c, codec, options)) < 0) { - throw new Exception("avcodec_open2() error " + ret + ": Could not open video codec."); - } - av_dict_free(options); - - // Hack to correct wrong frame rates that seem to be generated by some codecs - if (video_c.time_base().num() > 1000 && video_c.time_base().den() == 1) { - video_c.time_base().den(1000); - } - - // Allocate video frame and an AVFrame structure for the RGB image - if ((picture = av_frame_alloc()) == null) { - throw new Exception("av_frame_alloc() error: Could not allocate raw picture frame."); - } - if ((picture_rgb = av_frame_alloc()) == null) { - throw new Exception("av_frame_alloc() error: Could not allocate RGB picture frame."); - } - - int width = imageWidth > 0 ? imageWidth : video_c.width(); - int height = imageHeight > 0 ? imageHeight : video_c.height(); - - switch (imageMode) { - case COLOR: - case GRAY: - int fmt = getPixelFormat(); - - // Determine required buffer size and allocate buffer - int size = av_image_get_buffer_size(fmt, width, height, 1); - image_ptr = new BytePointer[] { new BytePointer(av_malloc(size)).capacity(size) }; - image_buf = new Buffer[] { image_ptr[0].asBuffer() }; - - // Assign appropriate parts of buffer to image planes in picture_rgb - // Note that picture_rgb is an AVFrame, but AVFrame is a superset of AVPicture - av_image_fill_arrays(new PointerPointer(picture_rgb), picture_rgb.linesize(), image_ptr[0], fmt, width, height, 1); - picture_rgb.format(fmt); - picture_rgb.width(width); - picture_rgb.height(height); - break; - - case RAW: - image_ptr = new BytePointer[] { null }; - image_buf = new Buffer[] { null }; - break; - - default: - assert false; - } - } - - if (audio_st != null) { - // Find the decoder for the audio stream - AVCodec codec = avcodec_find_decoder(audio_par.codec_id()); - if (codec == null) { - throw new Exception("avcodec_find_decoder() error: Unsupported audio format or codec not found: " + audio_par.codec_id() + "."); - } - - /* Allocate a codec context for the decoder */ - if ((audio_c = avcodec_alloc_context3(codec)) == null) { - throw new Exception("avcodec_alloc_context3() error: Could not allocate audio decoding context."); - } - - /* copy the stream parameters from the muxer */ - if ((ret = avcodec_parameters_to_context(audio_c, audio_st.codecpar())) < 0) { - release(); - throw new Exception("avcodec_parameters_to_context() error: Could not copy the audio stream parameters."); - } - - options = new AVDictionary(null); - for (Entry e : audioOptions.entrySet()) { - av_dict_set(options, e.getKey(), e.getValue(), 0); - } - // Open audio codec - if ((ret = avcodec_open2(audio_c, codec, options)) < 0) { - throw new Exception("avcodec_open2() error " + ret + ": Could not open audio codec."); - } - av_dict_free(options); - - // Allocate audio samples frame - if ((samples_frame = av_frame_alloc()) == null) { - throw new Exception("av_frame_alloc() error: Could not allocate audio frame."); - } - } - } - - public void stop() throws Exception { - release(); - } - - @Override - public void trigger() throws Exception { - if (oc == null || oc.isNull()) { - throw new Exception("Could not trigger: No AVFormatContext. (Has start() been called?)"); - } - if (pkt2.size() > 0) { - pkt2.size(0); - av_packet_unref(pkt); - } - for (int i = 0; i < numBuffers+1; i++) { - if (av_read_frame(oc, pkt) < 0) { - return; - } - av_packet_unref(pkt); - } - } - - private void processImage() throws Exception { - frame.imageWidth = imageWidth > 0 ? imageWidth : video_c.width(); - frame.imageHeight = imageHeight > 0 ? imageHeight : video_c.height(); - frame.imageDepth = Frame.DEPTH_UBYTE; - switch (imageMode) { - case COLOR: - case GRAY: - // Deinterlace Picture - if (deinterlace) { - throw new Exception("Cannot deinterlace: Functionality moved to FFmpegFrameFilter."); - } - - // Convert the image into BGR or GRAY format that OpenCV uses - img_convert_ctx = sws_getCachedContext(img_convert_ctx, - video_c.width(), video_c.height(), video_c.pix_fmt(), - frame.imageWidth, frame.imageHeight, getPixelFormat(), SWS_BILINEAR, - null, null, (DoublePointer)null); - if (img_convert_ctx == null) { - throw new Exception("sws_getCachedContext() error: Cannot initialize the conversion context."); - } - - // Convert the image from its native format to RGB or GRAY - sws_scale(img_convert_ctx, new PointerPointer(picture), picture.linesize(), 0, - video_c.height(), new PointerPointer(picture_rgb), picture_rgb.linesize()); - frame.imageStride = picture_rgb.linesize(0); - frame.image = image_buf; - break; - - case RAW: - frame.imageStride = picture.linesize(0); - BytePointer ptr = picture.data(0); - if (ptr != null && !ptr.equals(image_ptr[0])) { - image_ptr[0] = ptr.capacity(frame.imageHeight * frame.imageStride); - image_buf[0] = ptr.asBuffer(); - } - frame.image = image_buf; - break; - - default: - assert false; - } - frame.image[0].limit(frame.imageHeight * frame.imageStride); - frame.imageChannels = frame.imageStride / frame.imageWidth; - } - - @Override - public Frame grab() throws Exception { - return grabFrame(true, true, true, false); - } - public Frame grabImage() throws Exception { - return grabFrame(false, true, true, false); - } - public Frame grabSamples() throws Exception { - return grabFrame(true, false, true, false); - } - public Frame grabKeyFrame() throws Exception { - return grabFrame(false, true, true, true); - } - public Frame grabFrame(boolean doAudio, boolean doVideo, boolean processImage, boolean keyFrames) throws Exception { - if (oc == null || oc.isNull()) { - throw new Exception("Could not grab: No AVFormatContext. (Has start() been called?)"); - } else if ((!doVideo || video_st == null) && (!doAudio || audio_st == null)) { - return null; - } - frame.keyFrame = false; - frame.imageWidth = 0; - frame.imageHeight = 0; - frame.imageDepth = 0; - frame.imageChannels = 0; - frame.imageStride = 0; - frame.image = null; - frame.sampleRate = 0; - frame.audioChannels = 0; - frame.samples = null; - frame.opaque = null; - if (doVideo && frameGrabbed) { - frameGrabbed = false; - if (processImage) { - processImage(); - } - frame.keyFrame = picture.key_frame() != 0; - frame.image = image_buf; - frame.opaque = picture; - return frame; - } - boolean done = false; - while (!done) { - if (pkt2.size() <= 0) { - if (av_read_frame(oc, pkt) < 0) { - if (doVideo && video_st != null) { - // The video codec may have buffered some frames - pkt.stream_index(video_st.index()); - pkt.flags(AV_PKT_FLAG_KEY); - pkt.data(null); - pkt.size(0); - } else { - return null; - } - } - } - - // Is this a packet from the video stream? - if (doVideo && video_st != null && pkt.stream_index() == video_st.index() - && (!keyFrames || pkt.flags() == AV_PKT_FLAG_KEY)) { - // Decode video frame - int len = avcodec_decode_video2(video_c, picture, got_frame, pkt); - - // Did we get a video frame? - if (len >= 0 && got_frame[0] != 0 - && (!keyFrames || picture.pict_type() == AV_PICTURE_TYPE_I)) { - long pts = av_frame_get_best_effort_timestamp(picture); - AVRational time_base = video_st.time_base(); - timestamp = 1000000L * pts * time_base.num() / time_base.den(); - // best guess, AVCodecContext.frame_number = number of decoded frames... - frameNumber = (int)(timestamp * getFrameRate() / 1000000L); - if (processImage) { - processImage(); - } - done = true; - frame.keyFrame = picture.key_frame() != 0; - frame.image = image_buf; - frame.opaque = picture; - } else if (pkt.data() == null && pkt.size() == 0) { - return null; - } - } else if (doAudio && audio_st != null && pkt.stream_index() == audio_st.index()) { - if (pkt2.size() <= 0) { - // HashMap is unacceptably slow on Android - // pkt2.put(pkt); - BytePointer.memcpy(pkt2, pkt, sizeof_pkt); - } - av_frame_unref(samples_frame); - // Decode audio frame - int len = avcodec_decode_audio4(audio_c, samples_frame, got_frame, pkt2); - if (len <= 0) { - // On error, trash the whole packet - pkt2.size(0); - } else { - pkt2.data(pkt2.data().position(len)); - pkt2.size(pkt2.size() - len); - if (got_frame[0] != 0) { - long pts = av_frame_get_best_effort_timestamp(samples_frame); - AVRational time_base = audio_st.time_base(); - timestamp = 1000000L * pts * time_base.num() / time_base.den(); - /* if a frame has been decoded, output it */ - done = true; - int sample_format = samples_frame.format(); - int planes = av_sample_fmt_is_planar(sample_format) != 0 ? (int)samples_frame.channels() : 1; - int data_size = av_samples_get_buffer_size((IntPointer)null, audio_c.channels(), - samples_frame.nb_samples(), audio_c.sample_fmt(), 1) / planes; - if (samples_buf == null || samples_buf.length != planes) { - samples_ptr = new BytePointer[planes]; - samples_buf = new Buffer[planes]; - } - frame.keyFrame = samples_frame.key_frame() != 0; - frame.sampleRate = audio_c.sample_rate(); - frame.audioChannels = audio_c.channels(); - frame.samples = samples_buf; - frame.opaque = samples_frame; - int sample_size = data_size / av_get_bytes_per_sample(sample_format); - for (int i = 0; i < planes; i++) { - BytePointer p = samples_frame.data(i); - if (!p.equals(samples_ptr[i]) || samples_ptr[i].capacity() < data_size) { - samples_ptr[i] = p.capacity(data_size); - ByteBuffer b = p.asBuffer(); - switch (sample_format) { - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_U8P: samples_buf[i] = b; break; - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S16P: samples_buf[i] = b.asShortBuffer(); break; - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S32P: samples_buf[i] = b.asIntBuffer(); break; - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: samples_buf[i] = b.asFloatBuffer(); break; - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_DBLP: samples_buf[i] = b.asDoubleBuffer(); break; - default: assert false; - } - } - samples_buf[i].position(0).limit(sample_size); - } - } - } - } - - if (pkt2.size() <= 0) { - // Free the packet that was allocated by av_read_frame - av_packet_unref(pkt); - } - } - return frame; - } - - public AVPacket grabPacket() throws Exception { - - if (oc == null || oc.isNull()) { - throw new Exception("Could not trigger: No AVFormatContext. (Has start() been called?)"); - } - - - // Return the next frame of a stream. - if (av_read_frame(oc, pkt) < 0) { - return null; - } - - return pkt; - - } + while (this.timestamp > timestamp + 1 && grabFrame(false, true, false, false) != null) { + // flush frames if seeking backwards + } + while (this.timestamp < timestamp - 1 && grabFrame(false, true, false, false) != null) { + // decode up to the desired frame + } + if (video_c != null) { + frameGrabbed = true; + } + } + } + + @Override public int getLengthInFrames() { + // best guess... + return (int)(getLengthInTime()*getFrameRate()/1000000L); + } + + @Override public long getLengthInTime() { + return oc.duration()*1000000L/AV_TIME_BASE; + } + + public AVFormatContext getFormatContext() { + + return oc; + } + + public void start() throws Exception { + synchronized (avcodec.class) { + startUnsafe(); + } + } + + void startUnsafe() throws Exception { + int ret; + img_convert_ctx = null; + oc = new AVFormatContext(null); + video_c = null; + audio_c = null; + pkt = new AVPacket(); + pkt2 = new AVPacket(); + sizeof_pkt = pkt.sizeof(); + got_frame = new int[1]; + frameGrabbed = false; + frame = new Frame(); + timestamp = 0; + frameNumber = 0; + + pkt2.size(0); + + // Open video file + AVInputFormat f = null; + if (format != null && !format.isEmpty()) { + if ((f = av_find_input_format(format)) == null) { + throw new Exception("av_find_input_format() error: Could not find input format \"" + format + "\"."); + } + } + AVDictionary options = new AVDictionary(null); + if (frameRate > 0) { + AVRational r = av_d2q(frameRate, 1001000); + av_dict_set(options, "framerate", r.num() + "/" + r.den(), 0); + } + if (pixelFormat >= 0) { + av_dict_set(options, "pixel_format", av_get_pix_fmt_name(pixelFormat).getString(), 0); + } else if (imageMode != ImageMode.RAW) { + av_dict_set(options, "pixel_format", imageMode == ImageMode.COLOR ? "bgr24" : "gray8", 0); + } + if (imageWidth > 0 && imageHeight > 0) { + av_dict_set(options, "video_size", imageWidth + "x" + imageHeight, 0); + } + if (sampleRate > 0) { + av_dict_set(options, "sample_rate", "" + sampleRate, 0); + } + if (audioChannels > 0) { + av_dict_set(options, "channels", "" + audioChannels, 0); + } + for (Entry e : this.options.entrySet()) { + av_dict_set(options, e.getKey(), e.getValue(), 0); + } + if (inputStream != null) { + if (readCallback == null) { + readCallback = new ReadCallback(); + } + if (seekCallback == null) { + seekCallback = new SeekCallback(); + } + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream, 1024*1024); + } + inputStream.mark(1024*1024); + oc = avformat_alloc_context(); + avio = avio_alloc_context(new BytePointer(av_malloc(4096)), 4096, 0, oc, readCallback, null, seekCallback); + oc.pb(avio); + + filename = inputStream.toString(); + inputStreams.put(oc, inputStream); + } + if ((ret = avformat_open_input(oc, filename, f, options)) < 0) { + av_dict_set(options, "pixel_format", null, 0); + if ((ret = avformat_open_input(oc, filename, f, options)) < 0) { + throw new Exception("avformat_open_input() error " + ret + ": Could not open input \"" + filename + "\". (Has setFormat() been called?)"); + } + } + av_dict_free(options); + + // Retrieve stream information + if ((ret = avformat_find_stream_info(oc, (PointerPointer)null)) < 0) { + throw new Exception("avformat_find_stream_info() error " + ret + ": Could not find stream information."); + } + + // Dump information about file onto standard error + av_dump_format(oc, 0, filename, 0); + + // Find the first video and audio stream, unless the user specified otherwise + video_st = audio_st = null; + AVCodecParameters video_par = null, audio_par = null; + int nb_streams = oc.nb_streams(); + for (int i = 0; i < nb_streams; i++) { + AVStream st = oc.streams(i); + // Get a pointer to the codec context for the video or audio stream + AVCodecParameters par = st.codecpar(); + if (video_st == null && par.codec_type() == AVMEDIA_TYPE_VIDEO && (videoStream < 0 || videoStream == i)) { + video_st = st; + video_par = par; + } else if (audio_st == null && par.codec_type() == AVMEDIA_TYPE_AUDIO && (audioStream < 0 || audioStream == i)) { + audio_st = st; + audio_par = par; + } + } + if (video_st == null && audio_st == null) { + throw new Exception("Did not find a video or audio stream inside \"" + filename + "\" for videoStream == " + videoStream + " and audioStream == " + audioStream + "."); + } + + if (video_st != null) { + // Find the decoder for the video stream + AVCodec codec = avcodec_find_decoder(video_par.codec_id()); + if (codec == null) { + throw new Exception("avcodec_find_decoder() error: Unsupported video format or codec not found: " + video_par.codec_id() + "."); + } + + /* Allocate a codec context for the decoder */ + if ((video_c = avcodec_alloc_context3(codec)) == null) { + throw new Exception("avcodec_alloc_context3() error: Could not allocate video decoding context."); + } + + /* copy the stream parameters from the muxer */ + if ((ret = avcodec_parameters_to_context(video_c, video_st.codecpar())) < 0) { + release(); + throw new Exception("avcodec_parameters_to_context() error: Could not copy the video stream parameters."); + } + + options = new AVDictionary(null); + for (Entry e : videoOptions.entrySet()) { + av_dict_set(options, e.getKey(), e.getValue(), 0); + } + // Open video codec + if ((ret = avcodec_open2(video_c, codec, options)) < 0) { + throw new Exception("avcodec_open2() error " + ret + ": Could not open video codec."); + } + av_dict_free(options); + + // Hack to correct wrong frame rates that seem to be generated by some codecs + if (video_c.time_base().num() > 1000 && video_c.time_base().den() == 1) { + video_c.time_base().den(1000); + } + + // Allocate video frame and an AVFrame structure for the RGB image + if ((picture = av_frame_alloc()) == null) { + throw new Exception("av_frame_alloc() error: Could not allocate raw picture frame."); + } + if ((picture_rgb = av_frame_alloc()) == null) { + throw new Exception("av_frame_alloc() error: Could not allocate RGB picture frame."); + } + + int width = imageWidth > 0 ? imageWidth : video_c.width(); + int height = imageHeight > 0 ? imageHeight : video_c.height(); + + switch (imageMode) { + case COLOR: + case GRAY: + int fmt = getPixelFormat(); + + // Determine required buffer size and allocate buffer + int size = av_image_get_buffer_size(fmt, width, height, 1); + image_ptr = new BytePointer[]{new BytePointer(av_malloc(size)).capacity(size)}; + image_buf = new Buffer[]{image_ptr[0].asBuffer()}; + + // Assign appropriate parts of buffer to image planes in picture_rgb + // Note that picture_rgb is an AVFrame, but AVFrame is a superset of AVPicture + av_image_fill_arrays(new PointerPointer(picture_rgb), picture_rgb.linesize(), image_ptr[0], fmt, width, height, 1); + picture_rgb.format(fmt); + picture_rgb.width(width); + picture_rgb.height(height); + break; + + case RAW: + image_ptr = new BytePointer[]{null}; + image_buf = new Buffer[]{null}; + break; + + default: + assert false; + } + } + + if (audio_st != null) { + // Find the decoder for the audio stream + AVCodec codec = avcodec_find_decoder(audio_par.codec_id()); + if (codec == null) { + throw new Exception("avcodec_find_decoder() error: Unsupported audio format or codec not found: " + audio_par.codec_id() + "."); + } + + /* Allocate a codec context for the decoder */ + if ((audio_c = avcodec_alloc_context3(codec)) == null) { + throw new Exception("avcodec_alloc_context3() error: Could not allocate audio decoding context."); + } + + /* copy the stream parameters from the muxer */ + if ((ret = avcodec_parameters_to_context(audio_c, audio_st.codecpar())) < 0) { + release(); + throw new Exception("avcodec_parameters_to_context() error: Could not copy the audio stream parameters."); + } + + options = new AVDictionary(null); + for (Entry e : audioOptions.entrySet()) { + av_dict_set(options, e.getKey(), e.getValue(), 0); + } + // Open audio codec + if ((ret = avcodec_open2(audio_c, codec, options)) < 0) { + throw new Exception("avcodec_open2() error " + ret + ": Could not open audio codec."); + } + av_dict_free(options); + + // Allocate audio samples frame + if ((samples_frame = av_frame_alloc()) == null) { + throw new Exception("av_frame_alloc() error: Could not allocate audio frame."); + } + } + } + + public void stop() throws Exception { + release(); + } + + @Override public void trigger() throws Exception { + if (oc == null || oc.isNull()) { + throw new Exception("Could not trigger: No AVFormatContext. (Has start() been called?)"); + } + if (pkt2.size() > 0) { + pkt2.size(0); + av_packet_unref(pkt); + } + for (int i = 0; i < numBuffers + 1; i++) { + if (av_read_frame(oc, pkt) < 0) { + return; + } + av_packet_unref(pkt); + } + } + + private void processImage() throws Exception { + frame.imageWidth = imageWidth > 0 ? imageWidth : video_c.width(); + frame.imageHeight = imageHeight > 0 ? imageHeight : video_c.height(); + frame.imageDepth = Frame.DEPTH_UBYTE; + switch (imageMode) { + case COLOR: + case GRAY: + // Deinterlace Picture + if (deinterlace) { + throw new Exception("Cannot deinterlace: Functionality moved to FFmpegFrameFilter."); + } + + // Convert the image into BGR or GRAY format that OpenCV uses + img_convert_ctx = sws_getCachedContext(img_convert_ctx, video_c.width(), video_c.height(), video_c.pix_fmt(), frame.imageWidth, frame.imageHeight, getPixelFormat(), SWS_BILINEAR, null, null, (DoublePointer)null); + if (img_convert_ctx == null) { + throw new Exception("sws_getCachedContext() error: Cannot initialize the conversion context."); + } + + // Convert the image from its native format to RGB or GRAY + sws_scale(img_convert_ctx, new PointerPointer(picture), picture.linesize(), 0, video_c.height(), new PointerPointer(picture_rgb), picture_rgb.linesize()); + frame.imageStride = picture_rgb.linesize(0); + frame.image = image_buf; + break; + + case RAW: + frame.imageStride = picture.linesize(0); + BytePointer ptr = picture.data(0); + if (ptr != null && !ptr.equals(image_ptr[0])) { + image_ptr[0] = ptr.capacity(frame.imageHeight*frame.imageStride); + image_buf[0] = ptr.asBuffer(); + } + frame.image = image_buf; + break; + + default: + assert false; + } + frame.image[0].limit(frame.imageHeight*frame.imageStride); + frame.imageChannels = frame.imageStride/frame.imageWidth; + } + + @Override public Frame grab() throws Exception { + return grabFrame(true, true, true, false); + } + + public Frame grabImage() throws Exception { + return grabFrame(false, true, true, false); + } + + public Frame grabSamples() throws Exception { + return grabFrame(true, false, true, false); + } + + public Frame grabKeyFrame() throws Exception { + return grabFrame(false, true, true, true); + } + + public Frame grabFrame( boolean doAudio, boolean doVideo, boolean processImage, boolean keyFrames ) throws Exception { + if (oc == null || oc.isNull()) { + throw new Exception("Could not grab: No AVFormatContext. (Has start() been called?)"); + } else if ((!doVideo || video_st == null) && (!doAudio || audio_st == null)) { + return null; + } + frame.keyFrame = false; + frame.imageWidth = 0; + frame.imageHeight = 0; + frame.imageDepth = 0; + frame.imageChannels = 0; + frame.imageStride = 0; + frame.image = null; + frame.sampleRate = 0; + frame.audioChannels = 0; + frame.samples = null; + frame.opaque = null; + if (doVideo && frameGrabbed) { + frameGrabbed = false; + if (processImage) { + processImage(); + } + frame.keyFrame = picture.key_frame() != 0; + frame.image = image_buf; + frame.opaque = picture; + return frame; + } + boolean done = false; + while (!done) { + if (pkt2.size() <= 0) { + if (av_read_frame(oc, pkt) < 0) { + if (doVideo && video_st != null) { + // The video codec may have buffered some frames + pkt.stream_index(video_st.index()); + pkt.flags(AV_PKT_FLAG_KEY); + pkt.data(null); + pkt.size(0); + } else { + return null; + } + } + } + + // Is this a packet from the video stream? + if (doVideo && video_st != null && pkt.stream_index() == video_st.index() && (!keyFrames || pkt.flags() == AV_PKT_FLAG_KEY)) { + // Decode video frame + int len = avcodec_decode_video2(video_c, picture, got_frame, pkt); + + // Did we get a video frame? + if (len >= 0 && got_frame[0] != 0 && (!keyFrames || picture.pict_type() == AV_PICTURE_TYPE_I)) { + long pts = av_frame_get_best_effort_timestamp(picture); + AVRational time_base = video_st.time_base(); + timestamp = 1000000L*pts*time_base.num()/time_base.den(); + // best guess, AVCodecContext.frame_number = number of decoded frames... + frameNumber = (int)(timestamp*getFrameRate()/1000000L); + if (processImage) { + processImage(); + } + done = true; + frame.keyFrame = picture.key_frame() != 0; + frame.image = image_buf; + frame.opaque = picture; + } else if (pkt.data() == null && pkt.size() == 0) { + return null; + } + } else if (doAudio && audio_st != null && pkt.stream_index() == audio_st.index()) { + if (pkt2.size() <= 0) { + // HashMap is unacceptably slow on Android + // pkt2.put(pkt); + BytePointer.memcpy(pkt2, pkt, sizeof_pkt); + } + av_frame_unref(samples_frame); + // Decode audio frame + int len = avcodec_decode_audio4(audio_c, samples_frame, got_frame, pkt2); + if (len <= 0) { + // On error, trash the whole packet + pkt2.size(0); + } else { + pkt2.data(pkt2.data().position(len)); + pkt2.size(pkt2.size() - len); + if (got_frame[0] != 0) { + long pts = av_frame_get_best_effort_timestamp(samples_frame); + AVRational time_base = audio_st.time_base(); + timestamp = 1000000L*pts*time_base.num()/time_base.den(); + /* if a frame has been decoded, output it */ + done = true; + int sample_format = samples_frame.format(); + int planes = av_sample_fmt_is_planar(sample_format) != 0 ? (int)samples_frame.channels() : 1; + int data_size = av_samples_get_buffer_size((IntPointer)null, audio_c.channels(), samples_frame.nb_samples(), audio_c.sample_fmt(), 1)/planes; + if (samples_buf == null || samples_buf.length != planes) { + samples_ptr = new BytePointer[planes]; + samples_buf = new Buffer[planes]; + } + frame.keyFrame = samples_frame.key_frame() != 0; + frame.sampleRate = audio_c.sample_rate(); + frame.audioChannels = audio_c.channels(); + frame.samples = samples_buf; + frame.opaque = samples_frame; + int sample_size = data_size/av_get_bytes_per_sample(sample_format); + for (int i = 0; i < planes; i++) { + BytePointer p = samples_frame.data(i); + if (!p.equals(samples_ptr[i]) || samples_ptr[i].capacity() < data_size) { + samples_ptr[i] = p.capacity(data_size); + ByteBuffer b = p.asBuffer(); + switch (sample_format) { + case AV_SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_U8P: + samples_buf[i] = b; + break; + case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S16P: + samples_buf[i] = b.asShortBuffer(); + break; + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32P: + samples_buf[i] = b.asIntBuffer(); + break; + case AV_SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLTP: + samples_buf[i] = b.asFloatBuffer(); + break; + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_DBLP: + samples_buf[i] = b.asDoubleBuffer(); + break; + default: + assert false; + } + } + samples_buf[i].position(0).limit(sample_size); + } + } + } + } + + if (pkt2.size() <= 0) { + // Free the packet that was allocated by av_read_frame + av_packet_unref(pkt); + } + } + return frame; + } + + public AVPacket grabPacket() throws Exception { + + if (oc == null || oc.isNull()) { + throw new Exception("Could not trigger: No AVFormatContext. (Has start() been called?)"); + } + + + // Return the next frame of a stream. + if (av_read_frame(oc, pkt) < 0) { + return null; + } + + return pkt; + } + + static class ReadCallback extends Read_packet_Pointer_BytePointer_int { + @Override public int call( Pointer opaque, BytePointer buf, int buf_size ) { + try { + byte[] b = new byte[buf_size]; + InputStream is = inputStreams.get(opaque); + int size = is.read(b, 0, buf_size); + if (size < 0) { + return 0; + } else { + buf.put(b, 0, size); + return size; + } + } catch (Throwable t) { + System.err.println("Error on InputStream.read(): " + t); + return -1; + } + } + } + + static class SeekCallback extends Seek_Pointer_long_int { + @Override public long call( Pointer opaque, long offset, int whence ) { + try { + InputStream is = inputStreams.get(opaque); + switch (whence) { + case 0: + is.reset(); + break; + case 1: + break; + default: + return -1; + } + is.skip(offset); + return 0; + } catch (Throwable t) { + System.err.println("Error on InputStream.reset() or skip(): " + t); + return -1; + } + } + } } \ No newline at end of file diff --git a/integration/boofcv-javacv/build.gradle b/integration/boofcv-javacv/build.gradle index 8e34fd3600..c69286cc6e 100644 --- a/integration/boofcv-javacv/build.gradle +++ b/integration/boofcv-javacv/build.gradle @@ -1,21 +1,11 @@ - -// Hack around gradle including stuff it shouldn't -configurations { - all*.exclude group: 'org.bytedeco', module: 'javacpp-presets' -} - dependencies { api project(':main:boofcv-ip') api project(':main:boofcv-io') api project(':main:boofcv-geo') - + api project(':integration:boofcv-swing') - api group: 'org.bytedeco', name: 'javacv', version: '1.4.4' - - native_arch.each { - implementation group: 'org.bytedeco.javacpp-presets', name: "opencv", version: '4.0.1-1.4.4', classifier: "${it}" - } + implementation group: 'org.bytedeco', name: 'opencv-platform', version: '4.10.0-1.5.11' } sourceSets { test { java { srcDir 'src/main/examples' } } } From 39ff0b6273d87911b556e9eef35ab8dd788250ae Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Fri, 14 Mar 2025 13:20:10 +0000 Subject: [PATCH 02/23] Add dummy All.java to spotless exclusion as it breaks it! --- build.gradle | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 06ac6b416a..e8b2fba828 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat plugins { id "com.peterabeles.gversion" version "1.10.3" apply false id "net.ltgt.errorprone" version "3.0.1" apply false - id "com.diffplug.spotless" version "6.9.1" apply false + id "com.diffplug.spotless" version "7.0.2" apply false } ext.libpath = file('./').absolutePath @@ -280,20 +280,19 @@ subprojects { // define the steps to apply to those files trimTrailingWhitespace() - indentWithTabs() + leadingSpacesToTabs() endWithNewline() } java { // There is currently no good way to exclude files that contain "@Generated" or are not versions. // This should catch most of them with a few false positives. target('**/java/boofcv/**/*.java') - targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*') + targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*', '**/java/boofcv/All.java') toggleOffOn('formatter:off', 'formatter:on') removeUnusedImports() endWithNewline() - - licenseHeaderFile "${project.rootDir}/misc/copyright.txt" + licenseHeaderFile("${project.rootDir}/misc/copyright.txt") } } From ab3071f86eac317ed6d0195d6474e43275afb707 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Fri, 14 Mar 2025 13:58:06 +0000 Subject: [PATCH 03/23] Silence Gradle 10 deprecation warnings RE using space assignment --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index e8b2fba828..02bda53f57 100644 --- a/build.gradle +++ b/build.gradle @@ -101,10 +101,10 @@ subprojects { reports.html.setRequired(false) // Make the error logging verbose to make debugging on CI easier testLogging.showStandardStreams = true - testLogging.exceptionFormat TestExceptionFormat.FULL - testLogging.showCauses true - testLogging.showExceptions true - testLogging.showStackTraces true + testLogging.exceptionFormat = TestExceptionFormat.FULL + testLogging.showCauses = true + testLogging.showExceptions = true + testLogging.showStackTraces = true } sourceSets { @@ -253,7 +253,7 @@ subprojects { options.addBooleanOption('html5', true) } - failOnError false + failOnError = false } // Force the release build to fail if it depends on a SNAPSHOT @@ -340,8 +340,8 @@ subprojects { def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl credentials { - username ossrhUsername - password ossrhPassword + username = ossrhUsername + password = ossrhPassword } } } From 7b47021fadd153f4c4fbac00f113ed0989b04d7f Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Sat, 15 Mar 2025 18:00:50 +0000 Subject: [PATCH 04/23] Bump Kotlin to 1.9.25 to fix Gradle 9 deprecation during build --- integration/boofcv-kotlin/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/boofcv-kotlin/build.gradle b/integration/boofcv-kotlin/build.gradle index aa8744e62e..95146e1bb0 100644 --- a/integration/boofcv-kotlin/build.gradle +++ b/integration/boofcv-kotlin/build.gradle @@ -1,12 +1,12 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '1.7.22' + id 'org.jetbrains.kotlin.jvm' version '1.9.25' } configurations.all { resolutionStrategy { force "org.jetbrains:annotations:$project.jetnull_version" - force "org.jetbrains.kotlin:kotlin-stdlib:1.7.22" - force "org.jetbrains.kotlin:kotlin-stdlib-common:1.7.22" + force "org.jetbrains.kotlin:kotlin-stdlib:1.9.25" + force "org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25" } } From f825679778e9cb7ab8bd6482f74543604affa645 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Sat, 15 Mar 2025 18:45:14 +0000 Subject: [PATCH 05/23] Seems like JavaCV's build plugin might be the way to go to avoid these nasty dependency conflicts. I'll check it out shortly. --- build.gradle | 49 +++++++++++++------------- integration/boofcv-ffmpeg/build.gradle | 4 +-- integration/boofcv-javacv/build.gradle | 17 +++++---- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/build.gradle b/build.gradle index 02bda53f57..94958bec76 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,7 @@ project.ext.jetnull_version = '23.0.0' project.ext.jsr250_version = '1.0' project.ext.jsr305_version = '3.0.2' project.ext.trove4j_version = '3.0.3' +project.ext.javacv_version = '1.5.11' // Which native platforms are supported can be specified in the command line. Otherwise // the default is to support all of them @@ -271,30 +272,30 @@ subprojects { project.tasks.publish.enabled = false } - spotless { -// ratchetFrom 'origin/SNAPSHOT' - - format 'misc', { - // define the files to apply `misc` to - target '*.gradle', '*.md', '.gitignore' - - // define the steps to apply to those files - trimTrailingWhitespace() - leadingSpacesToTabs() - endWithNewline() - } - java { - // There is currently no good way to exclude files that contain "@Generated" or are not versions. - // This should catch most of them with a few false positives. - target('**/java/boofcv/**/*.java') - targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*', '**/java/boofcv/All.java') - - toggleOffOn('formatter:off', 'formatter:on') - removeUnusedImports() - endWithNewline() - licenseHeaderFile("${project.rootDir}/misc/copyright.txt") - } - } +// spotless { +//// ratchetFrom 'origin/SNAPSHOT' +// +// format 'misc', { +// // define the files to apply `misc` to +// target '*.gradle', '*.md', '.gitignore' +// +// // define the steps to apply to those files +// trimTrailingWhitespace() +// leadingSpacesToTabs() +// endWithNewline() +// } +// java { +// // There is currently no good way to exclude files that contain "@Generated" or are not versions. +// // This should catch most of them with a few false positives. +// target('**/java/boofcv/**/*.java') +// targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*', '**/java/boofcv/All.java') +// +// toggleOffOn('formatter:off', 'formatter:on') +// removeUnusedImports() +// endWithNewline() +//// licenseHeaderFile("${project.rootDir}/misc/copyright.txt") +// } +// } if (!project.tasks.publish.enabled) return diff --git a/integration/boofcv-ffmpeg/build.gradle b/integration/boofcv-ffmpeg/build.gradle index 93f0d95558..8e370c930f 100644 --- a/integration/boofcv-ffmpeg/build.gradle +++ b/integration/boofcv-ffmpeg/build.gradle @@ -2,5 +2,5 @@ dependencies { api project(':main:boofcv-ip') api project(':main:boofcv-io') - implementation group: 'org.bytedeco', name: 'ffmpeg-platform', version: '7.1-1.5.11' -} \ No newline at end of file + implementation group: 'org.bytedeco', name: 'ffmpeg-platform', version: "7.1-$project.javacv_version" +} diff --git a/integration/boofcv-javacv/build.gradle b/integration/boofcv-javacv/build.gradle index c69286cc6e..317820b6fb 100644 --- a/integration/boofcv-javacv/build.gradle +++ b/integration/boofcv-javacv/build.gradle @@ -1,11 +1,16 @@ +plugins { + id 'org.bytedeco.gradle-javacpp-platform' version project.javacv_version +} + dependencies { - api project(':main:boofcv-ip') - api project(':main:boofcv-io') - api project(':main:boofcv-geo') - - api project(':integration:boofcv-swing') + api project(':main:boofcv-ip') + api project(':main:boofcv-io') + api project(':main:boofcv-geo') + + api project(':integration:boofcv-swing') - implementation group: 'org.bytedeco', name: 'opencv-platform', version: '4.10.0-1.5.11' +// implementation group: 'org.bytedeco', name: 'opencv-platform', version: "4.10.0-$project.javacv_version" + implementation group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version } sourceSets { test { java { srcDir 'src/main/examples' } } } From 026756413a531246d9ab1af58645d32ec0bf5df9 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Sat, 15 Mar 2025 18:45:29 +0000 Subject: [PATCH 06/23] Formatting fix to silence plugin warning --- applications/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/readme.md b/applications/readme.md index 4585bbcdee..cd132cee7c 100644 --- a/applications/readme.md +++ b/applications/readme.md @@ -12,9 +12,9 @@ Then it will print something like: ```bash Trying to run a command-line application? Here are your options! - CreateFiducialSquareImage - CreateFiducialSquareBinary - CreateFiducialRandomDot +CreateFiducialSquareImage +CreateFiducialSquareBinary +CreateFiducialRandomDot ... ``` Next you will need to select which tool / application from the list that you want to run. Then more help will be From 2cab9daeebcecbdacbd33c64017893177eb5c752 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Mon, 17 Mar 2025 22:09:14 +0000 Subject: [PATCH 07/23] Deps are happy now. Had to force some versions. TODO: readd spotless config. Also tidied up build.gradle a little. --- build.gradle | 58 ++++++++++--------- .../examples/fragment/build.gradle | 4 +- integration/boofcv-ffmpeg/build.gradle | 13 +++-- integration/boofcv-javacv/build.gradle | 5 +- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/build.gradle b/build.gradle index 94958bec76..826521ecaa 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,8 @@ import net.ltgt.gradle.errorprone.CheckSeverity -import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestExceptionFormat plugins { + id 'java' id "com.peterabeles.gversion" version "1.10.3" apply false id "net.ltgt.errorprone" version "3.0.1" apply false id "com.diffplug.spotless" version "7.0.2" apply false @@ -34,15 +35,9 @@ project.ext.jetnull_version = '23.0.0' project.ext.jsr250_version = '1.0' project.ext.jsr305_version = '3.0.2' project.ext.trove4j_version = '3.0.3' -project.ext.javacv_version = '1.5.11' -// Which native platforms are supported can be specified in the command line. Otherwise -// the default is to support all of them -if (project.hasProperty("native_arch")) { - project.ext.native_arch = [project.native_arch] -} else { - project.ext.native_arch = ["linux-x86_64", "macosx-x86_64", "windows-x86_64"] -} +project.ext.javacv_version = '1.5.11' +project.ext.gradle_javacpp_platform_version = '1.5.10' subprojects { // Abort if this is Android. It requires a different plugins @@ -61,19 +56,20 @@ subprojects { toolchain { languageVersion = JavaLanguageVersion.of(17) } } - // Prevents tons of errors if someone is using ASCII - tasks.withType(JavaCompile).configureEach { options.encoding = "UTF-8" } - // Creates Java 11 byte code but Java 17 syntax tasks.withType(JavaCompile).configureEach { + // Prevents tons of errors if someone is using ASCII + options.encoding = "UTF-8" + + // don't think these work... sourceCompatibility = 17 options.release = 11 - } - // Enable incremental compile. Should make single file changes faster - tasks.withType(JavaCompile) { options.incremental = true } + // Enable incremental compile. Should make single file changes faster + options.incremental = true + } // For some reason examplesJar has a duplicate of ffmpeg pom - tasks.withType(Jar) { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } + tasks.withType(Jar).configureEach { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } // To make ErrorProne and Kotlin plugins happy configurations.configureEach { @@ -83,12 +79,13 @@ subprojects { force "com.google.errorprone:error_prone_annotations:$project.errorprone_version" force "com.google.code.findbugs:jsr305:$project.jsr305_version" force 'org.checkerframework:checker-qual:2.10.0' + force "org.bytedeco:javacpp:$project.javacv_version" + force "org.bytedeco:javacpp-platform:$project.javacv_version" + // Fail on jar conflict + failOnVersionConflict() } } - // Fail on jar conflict - configurations.configureEach { resolutionStrategy { failOnVersionConflict() } } - repositories { mavenCentral() mavenLocal() @@ -177,6 +174,7 @@ subprojects { annotationProcessor "org.projectlombok:lombok:${project.lombok_version}" // @Getter @Setter noautoAnnotationProcessor "org.projectlombok:lombok:${project.lombok_version}" // @Getter @Setter + errorprone "com.uber.nullaway:nullaway:${project.nullaway_version}" errorprone "com.google.errorprone:error_prone_core:$project.errorprone_version" // even if it's not used you still need to include the dependency @@ -410,7 +408,8 @@ checkProjectExistsAddToList(':integration:boofcv-javafx', javadocProjects) checkProjectExistsAddToList(':integration:boofcv-android', javadocProjects) // Creates a directory with all the compiled BoofCV jars and the dependencies for main -task createLibraryDirectory(dependsOn: libraryProjects.collect { [it + ':jar', it + ':sourcesJar'] }.flatten()) { +tasks.register('createLibraryDirectory') { + dependsOn libraryProjects.collect { [it + ':jar', it + ':sourcesJar'] }.flatten() doLast { // Collects BoofCV jars and external jars ext.listJars = files(libraryProjects.collect { project(it).configurations.runtimeClasspath }) @@ -430,7 +429,8 @@ task createLibraryDirectory(dependsOn: libraryProjects.collect { [it + ':jar', i } // Creates a single jar which contains all the subprojects in main and integration -task oneJarBin(type: Jar, dependsOn: javadocProjects.collect { it + ":compileJava" }) { +tasks.register('oneJarBin', Jar) { + dependsOn javadocProjects.collect { it + ":compileJava" } archiveFile.set(file("boofcv-v${project.version}.jar")) from files(javadocProjects.collect { project(it).sourceSets.main.output }) { @@ -443,7 +443,9 @@ task oneJarBin(type: Jar, dependsOn: javadocProjects.collect { it + ":compileJav def javadocProjectsNoAndroid = javadocProjects.findAll({ !it.contains("android") }) // Generates a global javadoc from all the modules -task alljavadoc(type: Javadoc, group: "Documentation", dependsOn: javadocProjectsNoAndroid.collect { it + ":compileJava" }) { +tasks.register('alljavadoc', Javadoc) { + dependsOn javadocProjectsNoAndroid.collect { it + ":compileJava" } + // only include source code in src directory to avoid including 3rd party code which some projects do as a hack source = javadocProjectsNoAndroid.collect { project(it).sourceSets.main.allJava } + javadocProjectsNoAndroid.collect { project(it).sourceSets.noauto.allJava } @@ -470,7 +472,7 @@ task alljavadoc(type: Javadoc, group: "Documentation", dependsOn: javadocProject configure(options) { failOnError = false title = "BoofCV ($project.version)" - links = ['https://docs.oracle.com/en/java/javase/11/docs/api', + links = ['https://docs.oracle.com/en/java/javase/17/docs/api', 'https://ejml.org/javadoc/', 'https://georegression.org/javadoc/', 'https://ddogleg.org/javadoc/'] @@ -485,19 +487,19 @@ task alljavadoc(type: Javadoc, group: "Documentation", dependsOn: javadocProject } } -task alljavadocWeb() { +tasks.register('alljavadocWeb') { doFirst { alljavadoc.options.bottom = file('misc/bottom.txt').text alljavadoc.destinationDir = file("docs/api-web") } + finalizedBy(alljavadoc) } -alljavadocWeb.finalizedBy(alljavadoc) -task testReport(type: TestReport) { - getDestinationDirectory().set(file("$buildDir/reports/allTests")) +tasks.register('testReport', TestReport) { + getDestinationDirectory().set(file("$layout.buildDirectory/reports/allTests")) // Android project has a different testing system so skip it - getTestResults().from(subprojects.findAll {!it.name.contains("android")}.test) + getTestResults().from(subprojects.findAll { !it.name.contains("android") }.test) } // Sanity check to make sure that autogenerate has been called. This is the source of a lot of confusion from users diff --git a/integration/boofcv-android/examples/fragment/build.gradle b/integration/boofcv-android/examples/fragment/build.gradle index e00aaa3e34..151633cb6a 100644 --- a/integration/boofcv-android/examples/fragment/build.gradle +++ b/integration/boofcv-android/examples/fragment/build.gradle @@ -14,6 +14,6 @@ plugins { id 'org.jetbrains.kotlin.android' version '1.7.20' apply false } -task clean(type: Delete) { - delete rootProject.buildDir +tasks.register('clean', Delete) { + delete rootProject.layout.buildDirectory } \ No newline at end of file diff --git a/integration/boofcv-ffmpeg/build.gradle b/integration/boofcv-ffmpeg/build.gradle index 8e370c930f..e7fcfa61f5 100644 --- a/integration/boofcv-ffmpeg/build.gradle +++ b/integration/boofcv-ffmpeg/build.gradle @@ -1,6 +1,11 @@ -dependencies { - api project(':main:boofcv-ip') - api project(':main:boofcv-io') +plugins { + id 'org.bytedeco.gradle-javacpp-platform' version '1.5.10' +} - implementation group: 'org.bytedeco', name: 'ffmpeg-platform', version: "7.1-$project.javacv_version" +dependencies { + api project(':main:boofcv-ip') + api project(':main:boofcv-io') + + api (group: 'org.bytedeco', name: 'ffmpeg-platform', version: "7.1-$project.javacv_version") + api (group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version) } diff --git a/integration/boofcv-javacv/build.gradle b/integration/boofcv-javacv/build.gradle index 317820b6fb..1c48d39ac9 100644 --- a/integration/boofcv-javacv/build.gradle +++ b/integration/boofcv-javacv/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.bytedeco.gradle-javacpp-platform' version project.javacv_version + id 'org.bytedeco.gradle-javacpp-platform' version '1.5.10' } dependencies { @@ -9,8 +9,7 @@ dependencies { api project(':integration:boofcv-swing') -// implementation group: 'org.bytedeco', name: 'opencv-platform', version: "4.10.0-$project.javacv_version" - implementation group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version + api (group: 'org.bytedeco', name: 'javacpp-platform', version: project.javacv_version) } sourceSets { test { java { srcDir 'src/main/examples' } } } From 7c1a4d53cc15101cac6ac163e9e075d253406e0f Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Mon, 17 Mar 2025 22:09:46 +0000 Subject: [PATCH 08/23] Remove copied code. This needs manual testing. Think unit tests may be failing also. TODO: investigate! --- .../io/ffmpeg/FfmpegVideoImageSequence.java | 9 +- .../copiedstuff/FFmpegFrameGrabber.java | 950 ------------------ .../java/org/bytedeco/copiedstuff/Frame.java | 288 ------ .../bytedeco/copiedstuff/FrameConverter.java | 60 -- .../bytedeco/copiedstuff/FrameGrabber.java | 662 ------------ .../copiedstuff/Java2DFrameConverter.java | 733 -------------- 6 files changed, 5 insertions(+), 2697 deletions(-) delete mode 100644 integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java delete mode 100644 integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Frame.java delete mode 100644 integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameConverter.java delete mode 100644 integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameGrabber.java delete mode 100644 integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Java2DFrameConverter.java diff --git a/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java b/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java index 0de4ba61f2..26c18fe698 100644 --- a/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java +++ b/integration/boofcv-ffmpeg/src/main/java/boofcv/io/ffmpeg/FfmpegVideoImageSequence.java @@ -23,11 +23,12 @@ import boofcv.io.image.SimpleImageSequence; import boofcv.struct.image.ImageBase; import boofcv.struct.image.ImageType; -import org.bytedeco.copiedstuff.FFmpegFrameGrabber; -import org.bytedeco.copiedstuff.Frame; -import org.bytedeco.copiedstuff.FrameGrabber; -import org.bytedeco.copiedstuff.Java2DFrameConverter; + import org.bytedeco.ffmpeg.global.avutil; +import org.bytedeco.javacv.FFmpegFrameGrabber; +import org.bytedeco.javacv.Frame; +import org.bytedeco.javacv.FrameGrabber; +import org.bytedeco.javacv.Java2DFrameConverter; import java.awt.image.BufferedImage; diff --git a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java b/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java deleted file mode 100644 index 39ac02d4c2..0000000000 --- a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FFmpegFrameGrabber.java +++ /dev/null @@ -1,950 +0,0 @@ -/* - * Copyright (c) 2020, Peter Abeles. All Rights Reserved. - * - * This file is part of BoofCV (http://boofcv.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright (C) 2009-2017 Samuel Audet - * - * Licensed either under the Apache License, Version 2.0, or (at your option) - * under the terms of the GNU General Public License as published by - * the Free Software Foundation (subject to the "Classpath" exception), - * either version 2, or any later version (collectively, the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/ - * http://www.gnu.org/software/classpath/license.html - * - * or as provided in the LICENSE.txt file that accompanied this code. - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * - * Based on the avcodec_sample.0.5.0.c file available at - * http://web.me.com/dhoerl/Home/Tech_Blog/Entries/2009/1/22_Revised_avcodec_sample.c_files/avcodec_sample.0.5.0.c - * by Martin Böhme, Stephen Dranger, and David Hoerl - * as well as on the decoding_encoding.c file included in FFmpeg 0.11.1, - * which is covered by the following copyright notice: - * - * Copyright (c) 2001 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package org.bytedeco.copiedstuff; - -import org.bytedeco.ffmpeg.avcodec.AVCodec; -import org.bytedeco.ffmpeg.avcodec.AVCodecContext; -import org.bytedeco.ffmpeg.avcodec.AVCodecParameters; -import org.bytedeco.ffmpeg.avcodec.AVPacket; -import org.bytedeco.ffmpeg.avformat.*; -import org.bytedeco.ffmpeg.avutil.AVDictionary; -import org.bytedeco.ffmpeg.avutil.AVDictionaryEntry; -import org.bytedeco.ffmpeg.avutil.AVFrame; -import org.bytedeco.ffmpeg.avutil.AVRational; -import org.bytedeco.ffmpeg.global.*; -import org.bytedeco.ffmpeg.swscale.SwsContext; -import org.bytedeco.javacpp.*; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map.Entry; - -import static org.bytedeco.ffmpeg.global.avcodec.*; -import static org.bytedeco.ffmpeg.global.avdevice.avdevice_register_all; -import static org.bytedeco.ffmpeg.global.avformat.*; -import static org.bytedeco.ffmpeg.global.avutil.*; -import static org.bytedeco.ffmpeg.global.swscale.*; - -/** - * @author Samuel Audet - */ -@SuppressWarnings({"UnsafeFinalization", "MissingOverride", "BadImport"}) -public class FFmpegFrameGrabber extends FrameGrabber { - static HashMap inputStreams = new HashMap(); - static ReadCallback readCallback; - static SeekCallback seekCallback; - private static Exception loadingException = null; - - static { - try { - tryLoad(); - } catch (Exception ex) { - } - } - - private InputStream inputStream; - private AVIOContext avio; - private String filename; - private AVFormatContext oc; - private AVStream video_st, audio_st; - private AVCodecContext video_c, audio_c; - private AVFrame picture, picture_rgb; - private BytePointer[] image_ptr; - private Buffer[] image_buf; - private AVFrame samples_frame; - private BytePointer[] samples_ptr; - private Buffer[] samples_buf; - private AVPacket pkt, pkt2; - private int sizeof_pkt; - private int[] got_frame; - private SwsContext img_convert_ctx; - private boolean frameGrabbed; - private Frame frame; - - public FFmpegFrameGrabber( File file ) { - this(file.getAbsolutePath()); - } - - public FFmpegFrameGrabber( String filename ) { - this.filename = filename; - } - - public FFmpegFrameGrabber( InputStream inputStream ) { - this.inputStream = inputStream; - } - - public static String[] getDeviceDescriptions() throws Exception { - tryLoad(); - throw new UnsupportedOperationException("Device enumeration not support by FFmpeg."); - } - - public static FFmpegFrameGrabber createDefault( File deviceFile ) throws Exception {return new FFmpegFrameGrabber(deviceFile);} - - public static FFmpegFrameGrabber createDefault( String devicePath ) throws Exception {return new FFmpegFrameGrabber(devicePath);} - - public static FFmpegFrameGrabber createDefault( int deviceNumber ) throws Exception {throw new Exception(FFmpegFrameGrabber.class + " does not support device numbers.");} - - public static void tryLoad() throws Exception { - if (loadingException != null) { - throw loadingException; - } else { - try { - //todo: check we got the right classes here. there are two... - Loader.load(avutil.class); - Loader.load(swresample.class); - Loader.load(avcodec.class); - Loader.load(avformat.class); - Loader.load(swscale.class); - - //todo: document that these are unnecessary: - // https://www.ffmpeg.org/doxygen/4.4/group__lavc__core.html#gaf139c95c2211e49496017459033eee1f - // https://github.com/leandromoreira/ffmpeg-libav-tutorial/issues/29 -// avcodec_register_all(); -// av_register_all(); - avformat_network_init(); - - Loader.load(avdevice.class); - avdevice_register_all(); - } catch (Throwable t) { - //todo: this isn't gonna ever get hit lol - if (t instanceof Exception) { - throw loadingException = (Exception)t; - } else { - throw loadingException = new Exception("Failed to load " + FFmpegFrameGrabber.class, t); - } - } - } - } - - public void release() throws Exception { - synchronized (avcodec.class) { - releaseUnsafe(); - } - } - - void releaseUnsafe() throws Exception { - if (pkt != null && pkt2 != null) { - if (pkt2.size() > 0) { - av_packet_unref(pkt); - } - pkt = pkt2 = null; - } - - // Free the RGB image - if (image_ptr != null) { - for (int i = 0; i < image_ptr.length; i++) { - av_free(image_ptr[i]); - } - image_ptr = null; - } - if (picture_rgb != null) { - av_frame_free(picture_rgb); - picture_rgb = null; - } - - // Free the native format picture frame - if (picture != null) { - av_frame_free(picture); - picture = null; - } - - // Close the video codec - if (video_c != null) { - avcodec_free_context(video_c); - video_c = null; - } - - // Free the audio samples frame - if (samples_frame != null) { - av_frame_free(samples_frame); - samples_frame = null; - } - - // Close the audio codec - if (audio_c != null) { - avcodec_free_context(audio_c); - audio_c = null; - } - - // Close the video file - if (inputStream == null && oc != null && !oc.isNull()) { - avformat_close_input(oc); - oc = null; - } - - if (img_convert_ctx != null) { - sws_freeContext(img_convert_ctx); - img_convert_ctx = null; - } - - got_frame = null; - frameGrabbed = false; - frame = null; - timestamp = 0; - frameNumber = 0; - - if (inputStream != null) { - try { - if (oc == null) { - // when called a second time - inputStream.close(); - } else { - inputStream.reset(); - } - } catch (IOException ex) { - throw new Exception("Error on InputStream.close(): ", ex); - } finally { - inputStreams.remove(oc); - if (avio != null) { - if (avio.buffer() != null) { - av_free(avio.buffer()); - avio.buffer(null); - } - av_free(avio); - avio = null; - } - if (oc != null) { - avformat_free_context(oc); - oc = null; - } - } - } - } - - @Override protected void finalize() throws Throwable { - super.finalize(); - release(); - } - - @Override public double getGamma() { - // default to a gamma of 2.2 for cheap Webcams, DV cameras, etc. - if (gamma == 0.0) { - return 2.2; - } else { - return gamma; - } - } - - @Override public String getFormat() { - if (oc == null) { - return super.getFormat(); - } else { - return oc.iformat().name().getString(); - } - } - - @Override public int getImageWidth() { - return imageWidth > 0 || video_c == null ? super.getImageWidth() : video_c.width(); - } - - @Override public int getImageHeight() { - return imageHeight > 0 || video_c == null ? super.getImageHeight() : video_c.height(); - } - - @Override public int getAudioChannels() { - return audio_c == null ? super.getAudioChannels() : audio_c.ch_layout().nb_channels(); - } - - @Override public int getPixelFormat() { - if (imageMode == ImageMode.COLOR || imageMode == ImageMode.GRAY) { - if (pixelFormat == AV_PIX_FMT_NONE) { - return imageMode == ImageMode.COLOR ? AV_PIX_FMT_BGR24 : AV_PIX_FMT_GRAY8; - } else { - return pixelFormat; - } - } else if (video_c != null) { // RAW - return video_c.pix_fmt(); - } else { - return super.getPixelFormat(); - } - } - - @Override public int getVideoCodec() { - return video_c == null ? super.getVideoCodec() : video_c.codec_id(); - } - - @Override public int getVideoBitrate() { - return video_c == null ? super.getVideoBitrate() : (int)video_c.bit_rate(); - } - - @Override public double getAspectRatio() { - if (video_st == null) { - return super.getAspectRatio(); - } else { - AVRational r = av_guess_sample_aspect_ratio(oc, video_st, picture); - double a = (double)r.num()/r.den(); - return a == 0.0 ? 1.0 : a; - } - } - - @Override public double getFrameRate() { - if (video_st == null) { - return super.getFrameRate(); - } else { - AVRational r = video_st.avg_frame_rate(); - if (r.num() == 0 && r.den() == 0) { - r = video_st.r_frame_rate(); - } - return (double)r.num()/r.den(); - } - } - - @Override public int getAudioCodec() { - return audio_c == null ? super.getAudioCodec() : audio_c.codec_id(); - } - - @Override public int getAudioBitrate() { - return audio_c == null ? super.getAudioBitrate() : (int)audio_c.bit_rate(); - } - - @Override public int getSampleFormat() { - return audio_c == null ? super.getSampleFormat() : audio_c.sample_fmt(); - } - - @Override public int getSampleRate() { - return audio_c == null ? super.getSampleRate() : audio_c.sample_rate(); - } - - @Override public String getMetadata( String key ) { - if (oc == null) { - return super.getMetadata(key); - } - AVDictionaryEntry entry = av_dict_get(oc.metadata(), key, null, 0); - return entry == null || entry.value() == null ? null : entry.value().getString(); - } - - @Override public String getVideoMetadata( String key ) { - if (video_st == null) { - return super.getVideoMetadata(key); - } - AVDictionaryEntry entry = av_dict_get(video_st.metadata(), key, null, 0); - return entry == null || entry.value() == null ? null : entry.value().getString(); - } - - @Override public String getAudioMetadata( String key ) { - if (audio_st == null) { - return super.getAudioMetadata(key); - } - AVDictionaryEntry entry = av_dict_get(audio_st.metadata(), key, null, 0); - return entry == null || entry.value() == null ? null : entry.value().getString(); - } - - @Override public void setFrameNumber( int frameNumber ) throws Exception { - // best guess, AVSEEK_FLAG_FRAME has not been implemented in FFmpeg... - setTimestamp(Math.round(1000000L*frameNumber/getFrameRate())); - } - - @Override public void setTimestamp( long timestamp ) throws Exception { - int ret; - if (oc == null) { - super.setTimestamp(timestamp); - } else { - timestamp = timestamp*AV_TIME_BASE/1000000L; - /* add the stream start time */ - if (oc.start_time() != AV_NOPTS_VALUE) { - timestamp += oc.start_time(); - } - if ((ret = avformat_seek_file(oc, -1, Long.MIN_VALUE, timestamp, Long.MAX_VALUE, AVSEEK_FLAG_BACKWARD)) < 0) { - throw new Exception("avformat_seek_file() error " + ret + ": Could not seek file to timestamp " + timestamp + "."); - } - if (video_c != null) { - avcodec_flush_buffers(video_c); - } - if (audio_c != null) { - avcodec_flush_buffers(audio_c); - } - if (pkt2.size() > 0) { - pkt2.size(0); - av_packet_unref(pkt); - } - /* comparing to timestamp +/- 1 avoids rouding issues for framerates - which are no proper divisors of 1000000, e.g. where - av_frame_get_best_effort_timestamp in grabFrame sets this.timestamp - to ...666 and the given timestamp has been rounded to ...667 - (or vice versa) - */ - while (this.timestamp > timestamp + 1 && grabFrame(false, true, false, false) != null) { - // flush frames if seeking backwards - } - while (this.timestamp < timestamp - 1 && grabFrame(false, true, false, false) != null) { - // decode up to the desired frame - } - if (video_c != null) { - frameGrabbed = true; - } - } - } - - @Override public int getLengthInFrames() { - // best guess... - return (int)(getLengthInTime()*getFrameRate()/1000000L); - } - - @Override public long getLengthInTime() { - return oc.duration()*1000000L/AV_TIME_BASE; - } - - public AVFormatContext getFormatContext() { - - return oc; - } - - public void start() throws Exception { - synchronized (avcodec.class) { - startUnsafe(); - } - } - - void startUnsafe() throws Exception { - int ret; - img_convert_ctx = null; - oc = new AVFormatContext(null); - video_c = null; - audio_c = null; - pkt = new AVPacket(); - pkt2 = new AVPacket(); - sizeof_pkt = pkt.sizeof(); - got_frame = new int[1]; - frameGrabbed = false; - frame = new Frame(); - timestamp = 0; - frameNumber = 0; - - pkt2.size(0); - - // Open video file - AVInputFormat f = null; - if (format != null && !format.isEmpty()) { - if ((f = av_find_input_format(format)) == null) { - throw new Exception("av_find_input_format() error: Could not find input format \"" + format + "\"."); - } - } - AVDictionary options = new AVDictionary(null); - if (frameRate > 0) { - AVRational r = av_d2q(frameRate, 1001000); - av_dict_set(options, "framerate", r.num() + "/" + r.den(), 0); - } - if (pixelFormat >= 0) { - av_dict_set(options, "pixel_format", av_get_pix_fmt_name(pixelFormat).getString(), 0); - } else if (imageMode != ImageMode.RAW) { - av_dict_set(options, "pixel_format", imageMode == ImageMode.COLOR ? "bgr24" : "gray8", 0); - } - if (imageWidth > 0 && imageHeight > 0) { - av_dict_set(options, "video_size", imageWidth + "x" + imageHeight, 0); - } - if (sampleRate > 0) { - av_dict_set(options, "sample_rate", "" + sampleRate, 0); - } - if (audioChannels > 0) { - av_dict_set(options, "channels", "" + audioChannels, 0); - } - for (Entry e : this.options.entrySet()) { - av_dict_set(options, e.getKey(), e.getValue(), 0); - } - if (inputStream != null) { - if (readCallback == null) { - readCallback = new ReadCallback(); - } - if (seekCallback == null) { - seekCallback = new SeekCallback(); - } - if (!inputStream.markSupported()) { - inputStream = new BufferedInputStream(inputStream, 1024*1024); - } - inputStream.mark(1024*1024); - oc = avformat_alloc_context(); - avio = avio_alloc_context(new BytePointer(av_malloc(4096)), 4096, 0, oc, readCallback, null, seekCallback); - oc.pb(avio); - - filename = inputStream.toString(); - inputStreams.put(oc, inputStream); - } - if ((ret = avformat_open_input(oc, filename, f, options)) < 0) { - av_dict_set(options, "pixel_format", null, 0); - if ((ret = avformat_open_input(oc, filename, f, options)) < 0) { - throw new Exception("avformat_open_input() error " + ret + ": Could not open input \"" + filename + "\". (Has setFormat() been called?)"); - } - } - av_dict_free(options); - - // Retrieve stream information - if ((ret = avformat_find_stream_info(oc, (PointerPointer)null)) < 0) { - throw new Exception("avformat_find_stream_info() error " + ret + ": Could not find stream information."); - } - - // Dump information about file onto standard error - av_dump_format(oc, 0, filename, 0); - - // Find the first video and audio stream, unless the user specified otherwise - video_st = audio_st = null; - AVCodecParameters video_par = null, audio_par = null; - int nb_streams = oc.nb_streams(); - for (int i = 0; i < nb_streams; i++) { - AVStream st = oc.streams(i); - // Get a pointer to the codec context for the video or audio stream - AVCodecParameters par = st.codecpar(); - if (video_st == null && par.codec_type() == AVMEDIA_TYPE_VIDEO && (videoStream < 0 || videoStream == i)) { - video_st = st; - video_par = par; - } else if (audio_st == null && par.codec_type() == AVMEDIA_TYPE_AUDIO && (audioStream < 0 || audioStream == i)) { - audio_st = st; - audio_par = par; - } - } - if (video_st == null && audio_st == null) { - throw new Exception("Did not find a video or audio stream inside \"" + filename + "\" for videoStream == " + videoStream + " and audioStream == " + audioStream + "."); - } - - if (video_st != null) { - // Find the decoder for the video stream - AVCodec codec = avcodec_find_decoder(video_par.codec_id()); - if (codec == null) { - throw new Exception("avcodec_find_decoder() error: Unsupported video format or codec not found: " + video_par.codec_id() + "."); - } - - /* Allocate a codec context for the decoder */ - if ((video_c = avcodec_alloc_context3(codec)) == null) { - throw new Exception("avcodec_alloc_context3() error: Could not allocate video decoding context."); - } - - /* copy the stream parameters from the muxer */ - if ((ret = avcodec_parameters_to_context(video_c, video_st.codecpar())) < 0) { - release(); - throw new Exception("avcodec_parameters_to_context() error: Could not copy the video stream parameters."); - } - - options = new AVDictionary(null); - for (Entry e : videoOptions.entrySet()) { - av_dict_set(options, e.getKey(), e.getValue(), 0); - } - // Open video codec - if ((ret = avcodec_open2(video_c, codec, options)) < 0) { - throw new Exception("avcodec_open2() error " + ret + ": Could not open video codec."); - } - av_dict_free(options); - - // Hack to correct wrong frame rates that seem to be generated by some codecs - if (video_c.time_base().num() > 1000 && video_c.time_base().den() == 1) { - video_c.time_base().den(1000); - } - - // Allocate video frame and an AVFrame structure for the RGB image - if ((picture = av_frame_alloc()) == null) { - throw new Exception("av_frame_alloc() error: Could not allocate raw picture frame."); - } - if ((picture_rgb = av_frame_alloc()) == null) { - throw new Exception("av_frame_alloc() error: Could not allocate RGB picture frame."); - } - - int width = imageWidth > 0 ? imageWidth : video_c.width(); - int height = imageHeight > 0 ? imageHeight : video_c.height(); - - switch (imageMode) { - case COLOR: - case GRAY: - int fmt = getPixelFormat(); - - // Determine required buffer size and allocate buffer - int size = av_image_get_buffer_size(fmt, width, height, 1); - image_ptr = new BytePointer[]{new BytePointer(av_malloc(size)).capacity(size)}; - image_buf = new Buffer[]{image_ptr[0].asBuffer()}; - - // Assign appropriate parts of buffer to image planes in picture_rgb - // Note that picture_rgb is an AVFrame, but AVFrame is a superset of AVPicture - av_image_fill_arrays(new PointerPointer(picture_rgb), picture_rgb.linesize(), image_ptr[0], fmt, width, height, 1); - picture_rgb.format(fmt); - picture_rgb.width(width); - picture_rgb.height(height); - break; - - case RAW: - image_ptr = new BytePointer[]{null}; - image_buf = new Buffer[]{null}; - break; - - default: - assert false; - } - } - - if (audio_st != null) { - // Find the decoder for the audio stream - AVCodec codec = avcodec_find_decoder(audio_par.codec_id()); - if (codec == null) { - throw new Exception("avcodec_find_decoder() error: Unsupported audio format or codec not found: " + audio_par.codec_id() + "."); - } - - /* Allocate a codec context for the decoder */ - if ((audio_c = avcodec_alloc_context3(codec)) == null) { - throw new Exception("avcodec_alloc_context3() error: Could not allocate audio decoding context."); - } - - /* copy the stream parameters from the muxer */ - if ((ret = avcodec_parameters_to_context(audio_c, audio_st.codecpar())) < 0) { - release(); - throw new Exception("avcodec_parameters_to_context() error: Could not copy the audio stream parameters."); - } - - options = new AVDictionary(null); - for (Entry e : audioOptions.entrySet()) { - av_dict_set(options, e.getKey(), e.getValue(), 0); - } - // Open audio codec - if ((ret = avcodec_open2(audio_c, codec, options)) < 0) { - throw new Exception("avcodec_open2() error " + ret + ": Could not open audio codec."); - } - av_dict_free(options); - - // Allocate audio samples frame - if ((samples_frame = av_frame_alloc()) == null) { - throw new Exception("av_frame_alloc() error: Could not allocate audio frame."); - } - } - } - - public void stop() throws Exception { - release(); - } - - @Override public void trigger() throws Exception { - if (oc == null || oc.isNull()) { - throw new Exception("Could not trigger: No AVFormatContext. (Has start() been called?)"); - } - if (pkt2.size() > 0) { - pkt2.size(0); - av_packet_unref(pkt); - } - for (int i = 0; i < numBuffers + 1; i++) { - if (av_read_frame(oc, pkt) < 0) { - return; - } - av_packet_unref(pkt); - } - } - - private void processImage() throws Exception { - frame.imageWidth = imageWidth > 0 ? imageWidth : video_c.width(); - frame.imageHeight = imageHeight > 0 ? imageHeight : video_c.height(); - frame.imageDepth = Frame.DEPTH_UBYTE; - switch (imageMode) { - case COLOR: - case GRAY: - // Deinterlace Picture - if (deinterlace) { - throw new Exception("Cannot deinterlace: Functionality moved to FFmpegFrameFilter."); - } - - // Convert the image into BGR or GRAY format that OpenCV uses - img_convert_ctx = sws_getCachedContext(img_convert_ctx, video_c.width(), video_c.height(), video_c.pix_fmt(), frame.imageWidth, frame.imageHeight, getPixelFormat(), SWS_BILINEAR, null, null, (DoublePointer)null); - if (img_convert_ctx == null) { - throw new Exception("sws_getCachedContext() error: Cannot initialize the conversion context."); - } - - // Convert the image from its native format to RGB or GRAY - sws_scale(img_convert_ctx, new PointerPointer(picture), picture.linesize(), 0, video_c.height(), new PointerPointer(picture_rgb), picture_rgb.linesize()); - frame.imageStride = picture_rgb.linesize(0); - frame.image = image_buf; - break; - - case RAW: - frame.imageStride = picture.linesize(0); - BytePointer ptr = picture.data(0); - if (ptr != null && !ptr.equals(image_ptr[0])) { - image_ptr[0] = ptr.capacity(frame.imageHeight*frame.imageStride); - image_buf[0] = ptr.asBuffer(); - } - frame.image = image_buf; - break; - - default: - assert false; - } - frame.image[0].limit(frame.imageHeight*frame.imageStride); - frame.imageChannels = frame.imageStride/frame.imageWidth; - } - - @Override public Frame grab() throws Exception { - return grabFrame(true, true, true, false); - } - - public Frame grabImage() throws Exception { - return grabFrame(false, true, true, false); - } - - public Frame grabSamples() throws Exception { - return grabFrame(true, false, true, false); - } - - public Frame grabKeyFrame() throws Exception { - return grabFrame(false, true, true, true); - } - - public Frame grabFrame( boolean doAudio, boolean doVideo, boolean processImage, boolean keyFrames ) throws Exception { - if (oc == null || oc.isNull()) { - throw new Exception("Could not grab: No AVFormatContext. (Has start() been called?)"); - } else if ((!doVideo || video_st == null) && (!doAudio || audio_st == null)) { - return null; - } - frame.keyFrame = false; - frame.imageWidth = 0; - frame.imageHeight = 0; - frame.imageDepth = 0; - frame.imageChannels = 0; - frame.imageStride = 0; - frame.image = null; - frame.sampleRate = 0; - frame.audioChannels = 0; - frame.samples = null; - frame.opaque = null; - if (doVideo && frameGrabbed) { - frameGrabbed = false; - if (processImage) { - processImage(); - } - frame.keyFrame = picture.key_frame() != 0; - frame.image = image_buf; - frame.opaque = picture; - return frame; - } - boolean done = false; - while (!done) { - if (pkt2.size() <= 0) { - if (av_read_frame(oc, pkt) < 0) { - if (doVideo && video_st != null) { - // The video codec may have buffered some frames - pkt.stream_index(video_st.index()); - pkt.flags(AV_PKT_FLAG_KEY); - pkt.data(null); - pkt.size(0); - } else { - return null; - } - } - } - - // Is this a packet from the video stream? - if (doVideo && video_st != null && pkt.stream_index() == video_st.index() && (!keyFrames || pkt.flags() == AV_PKT_FLAG_KEY)) { - // Decode video frame - int len = avcodec_decode_video2(video_c, picture, got_frame, pkt); - - // Did we get a video frame? - if (len >= 0 && got_frame[0] != 0 && (!keyFrames || picture.pict_type() == AV_PICTURE_TYPE_I)) { - long pts = av_frame_get_best_effort_timestamp(picture); - AVRational time_base = video_st.time_base(); - timestamp = 1000000L*pts*time_base.num()/time_base.den(); - // best guess, AVCodecContext.frame_number = number of decoded frames... - frameNumber = (int)(timestamp*getFrameRate()/1000000L); - if (processImage) { - processImage(); - } - done = true; - frame.keyFrame = picture.key_frame() != 0; - frame.image = image_buf; - frame.opaque = picture; - } else if (pkt.data() == null && pkt.size() == 0) { - return null; - } - } else if (doAudio && audio_st != null && pkt.stream_index() == audio_st.index()) { - if (pkt2.size() <= 0) { - // HashMap is unacceptably slow on Android - // pkt2.put(pkt); - BytePointer.memcpy(pkt2, pkt, sizeof_pkt); - } - av_frame_unref(samples_frame); - // Decode audio frame - int len = avcodec_decode_audio4(audio_c, samples_frame, got_frame, pkt2); - if (len <= 0) { - // On error, trash the whole packet - pkt2.size(0); - } else { - pkt2.data(pkt2.data().position(len)); - pkt2.size(pkt2.size() - len); - if (got_frame[0] != 0) { - long pts = av_frame_get_best_effort_timestamp(samples_frame); - AVRational time_base = audio_st.time_base(); - timestamp = 1000000L*pts*time_base.num()/time_base.den(); - /* if a frame has been decoded, output it */ - done = true; - int sample_format = samples_frame.format(); - int planes = av_sample_fmt_is_planar(sample_format) != 0 ? (int)samples_frame.channels() : 1; - int data_size = av_samples_get_buffer_size((IntPointer)null, audio_c.channels(), samples_frame.nb_samples(), audio_c.sample_fmt(), 1)/planes; - if (samples_buf == null || samples_buf.length != planes) { - samples_ptr = new BytePointer[planes]; - samples_buf = new Buffer[planes]; - } - frame.keyFrame = samples_frame.key_frame() != 0; - frame.sampleRate = audio_c.sample_rate(); - frame.audioChannels = audio_c.channels(); - frame.samples = samples_buf; - frame.opaque = samples_frame; - int sample_size = data_size/av_get_bytes_per_sample(sample_format); - for (int i = 0; i < planes; i++) { - BytePointer p = samples_frame.data(i); - if (!p.equals(samples_ptr[i]) || samples_ptr[i].capacity() < data_size) { - samples_ptr[i] = p.capacity(data_size); - ByteBuffer b = p.asBuffer(); - switch (sample_format) { - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_U8P: - samples_buf[i] = b; - break; - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S16P: - samples_buf[i] = b.asShortBuffer(); - break; - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S32P: - samples_buf[i] = b.asIntBuffer(); - break; - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: - samples_buf[i] = b.asFloatBuffer(); - break; - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_DBLP: - samples_buf[i] = b.asDoubleBuffer(); - break; - default: - assert false; - } - } - samples_buf[i].position(0).limit(sample_size); - } - } - } - } - - if (pkt2.size() <= 0) { - // Free the packet that was allocated by av_read_frame - av_packet_unref(pkt); - } - } - return frame; - } - - public AVPacket grabPacket() throws Exception { - - if (oc == null || oc.isNull()) { - throw new Exception("Could not trigger: No AVFormatContext. (Has start() been called?)"); - } - - - // Return the next frame of a stream. - if (av_read_frame(oc, pkt) < 0) { - return null; - } - - return pkt; - } - - static class ReadCallback extends Read_packet_Pointer_BytePointer_int { - @Override public int call( Pointer opaque, BytePointer buf, int buf_size ) { - try { - byte[] b = new byte[buf_size]; - InputStream is = inputStreams.get(opaque); - int size = is.read(b, 0, buf_size); - if (size < 0) { - return 0; - } else { - buf.put(b, 0, size); - return size; - } - } catch (Throwable t) { - System.err.println("Error on InputStream.read(): " + t); - return -1; - } - } - } - - static class SeekCallback extends Seek_Pointer_long_int { - @Override public long call( Pointer opaque, long offset, int whence ) { - try { - InputStream is = inputStreams.get(opaque); - switch (whence) { - case 0: - is.reset(); - break; - case 1: - break; - default: - return -1; - } - is.skip(offset); - return 0; - } catch (Throwable t) { - System.err.println("Error on InputStream.reset() or skip(): " + t); - return -1; - } - } - } -} \ No newline at end of file diff --git a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Frame.java b/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Frame.java deleted file mode 100644 index 1e565f105c..0000000000 --- a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Frame.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2020, Peter Abeles. All Rights Reserved. - * - * This file is part of BoofCV (http://boofcv.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright (C) 2015 Samuel Audet - * - * Licensed either under the Apache License, Version 2.0, or (at your option) - * under the terms of the GNU General Public License as published by - * the Free Software Foundation (subject to the "Classpath" exception), - * either version 2, or any later version (collectively, the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/ - * http://www.gnu.org/software/classpath/license.html - * - * or as provided in the LICENSE.txt file that accompanied this code. - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bytedeco.copiedstuff; - -import org.bytedeco.javacpp.*; -import org.bytedeco.javacpp.indexer.*; - -import java.nio.*; - -/** - * A class to manage the data of audio and video frames. It it used by - * {@link CanvasFrame}, {@link FrameGrabber}, {@link FrameRecorder}, and their - * subclasses. We can also make the link with other APIs, such as Android, - * Java 2D, FFmpeg, and OpenCV, via a {@link FrameConverter}. - * - * @author Samuel Audet - */ -public class Frame implements Indexable { - /** A flag set by a FrameGrabber or a FrameRecorder to indicate a key frame. */ - public boolean keyFrame; - - /** Constants to be used for {@link #imageDepth}. */ - public static final int - DEPTH_BYTE = -8, - DEPTH_UBYTE = 8, - DEPTH_SHORT = -16, - DEPTH_USHORT = 16, - DEPTH_INT = -32, - DEPTH_LONG = -64, - DEPTH_FLOAT = 32, - DEPTH_DOUBLE = 64; - - /** Information associated with the {@link #image} field. */ - public int imageWidth, imageHeight, imageDepth, imageChannels, imageStride; - - /** - * Buffers to hold image pixels from multiple channels for a video frame. - * Most of the software supports packed data only, but an array is provided - * to allow users to store images in a planar format as well. - */ - public Buffer[] image; - - /** Information associated with the {@link #samples} field. */ - public int sampleRate, audioChannels; - - /** Buffers to hold audio samples from multiple channels for an audio frame. */ - public Buffer[] samples; - - /** The underlying data object, for example, AVFrame, IplImage, or Mat. */ - public Object opaque; - - /** Timestamp of the frame creation. */ - public long timestamp; - - /** Empty constructor. */ - public Frame() { } - - /** Allocates a new packed image frame in native memory where rows are 8-byte aligned. */ - public Frame(int width, int height, int depth, int channels) { - int pixelSize = Math.abs(depth) / 8; - this.imageWidth = width; - this.imageHeight = height; - this.imageDepth = depth; - this.imageChannels = channels; - this.imageStride = ((imageWidth * imageChannels * pixelSize + 7) & ~7) / pixelSize; // 8-byte aligned - this.image = new Buffer[1]; - - ByteBuffer buffer = ByteBuffer.allocateDirect(imageHeight * imageStride * pixelSize).order(ByteOrder.nativeOrder()); - switch (imageDepth) { - case DEPTH_BYTE: - case DEPTH_UBYTE: image[0] = buffer; break; - case DEPTH_SHORT: - case DEPTH_USHORT: image[0] = buffer.asShortBuffer(); break; - case DEPTH_INT: image[0] = buffer.asIntBuffer(); break; - case DEPTH_LONG: image[0] = buffer.asLongBuffer(); break; - case DEPTH_FLOAT: image[0] = buffer.asFloatBuffer(); break; - case DEPTH_DOUBLE: image[0] = buffer.asDoubleBuffer(); break; - default: throw new UnsupportedOperationException("Unsupported depth value: " + imageDepth); - } - } - - /** Returns {@code createIndexer(true, 0)}. */ - public I createIndexer() { - return (I)createIndexer(true, 0); - } - @Override public I createIndexer(boolean direct) { - return (I)createIndexer(direct, 0); - } - /** Returns an {@link Indexer} for the ith image plane. */ - public I createIndexer(boolean direct, int i) { - long[] sizes = {imageHeight, imageWidth, imageChannels}; - long[] strides = {imageStride, imageChannels, 1}; - Buffer buffer = image[i]; - Object array = buffer.hasArray() ? buffer.array() : null; - switch (imageDepth) { - case DEPTH_UBYTE: - return array != null ? (I)UByteIndexer.create((byte[])array, sizes, strides).indexable(this) - : direct ? (I)UByteIndexer.create((ByteBuffer)buffer, sizes, strides).indexable(this) - : (I)UByteIndexer.create(new BytePointer((ByteBuffer)buffer), sizes, strides, false).indexable(this); - case DEPTH_BYTE: - return array != null ? (I)ByteIndexer.create((byte[])array, sizes, strides).indexable(this) - : direct ? (I)ByteIndexer.create((ByteBuffer)buffer, sizes, strides).indexable(this) - : (I)ByteIndexer.create(new BytePointer((ByteBuffer)buffer), sizes, strides, false).indexable(this); - case DEPTH_USHORT: - return array != null ? (I)UShortIndexer.create((short[])array, sizes, strides).indexable(this) - : direct ? (I)UShortIndexer.create((ShortBuffer)buffer, sizes, strides).indexable(this) - : (I)UShortIndexer.create(new ShortPointer((ShortBuffer)buffer), sizes, strides, false).indexable(this); - case DEPTH_SHORT: - return array != null ? (I)ShortIndexer.create((short[])array, sizes, strides).indexable(this) - : direct ? (I)ShortIndexer.create((ShortBuffer)buffer, sizes, strides).indexable(this) - : (I)ShortIndexer.create(new ShortPointer((ShortBuffer)buffer), sizes, strides, false).indexable(this); - case DEPTH_INT: - return array != null ? (I)IntIndexer.create((int[])array, sizes, strides).indexable(this) - : direct ? (I)IntIndexer.create((IntBuffer)buffer, sizes, strides).indexable(this) - : (I)IntIndexer.create(new IntPointer((IntBuffer)buffer), sizes, strides, false).indexable(this); - case DEPTH_LONG: - return array != null ? (I)LongIndexer.create((long[])array, sizes, strides).indexable(this) - : direct ? (I)LongIndexer.create((LongBuffer)buffer, sizes, strides).indexable(this) - : (I)LongIndexer.create(new LongPointer((LongBuffer)buffer), sizes, strides, false).indexable(this); - case DEPTH_FLOAT: - return array != null ? (I)FloatIndexer.create((float[])array, sizes, strides).indexable(this) - : direct ? (I)FloatIndexer.create((FloatBuffer)buffer, sizes, strides).indexable(this) - : (I)FloatIndexer.create(new FloatPointer((FloatBuffer)buffer), sizes, strides, false).indexable(this); - case DEPTH_DOUBLE: - return array != null ? (I)DoubleIndexer.create((double[])array, sizes, strides).indexable(this) - : direct ? (I)DoubleIndexer.create((DoubleBuffer)buffer, sizes, strides).indexable(this) - : (I)DoubleIndexer.create(new DoublePointer((DoubleBuffer)buffer), sizes, strides, false).indexable(this); - default: assert false; - } - return null; - } - - /**Care must be taken if this method is to be used in conjunction with movie recordings. - * Cloning a frame containing a full HD picture (alpha channel included) would take 1920 x 1080 * 4 = 8.294.400 Bytes. - * Expect a heap overflow exception when using this method without cleaning up. - * - * @return A deep copy of this frame. - * @see {@link #cloneBufferArray} - * - * @author Extension proposed by Dragos Dutu - * */ - @Override - public Frame clone() { - Frame newFrame = new Frame(); - - - // Video part - newFrame.imageWidth = imageWidth; - newFrame.imageHeight = imageHeight; - newFrame.imageDepth = imageDepth; - newFrame.imageChannels = imageChannels; - newFrame.imageStride = imageStride; - newFrame.keyFrame = keyFrame; - newFrame.opaque = opaque; - newFrame.image = cloneBufferArray(image); - - // Audio part - newFrame.audioChannels = audioChannels; - newFrame.sampleRate = sampleRate; - newFrame.samples = cloneBufferArray(samples); - - // Add timestamp - newFrame.timestamp = timestamp; - - return newFrame; - - } - - /** - * This private method takes a buffer array as input and returns a deep copy. - * It is assumed that all buffers in the input array are of the same subclass. - * - * @param srcBuffers - Buffer array to be cloned - * @return New buffer array - * - * @author Extension proposed by Dragos Dutu - */ - private static Buffer[] cloneBufferArray(Buffer[] srcBuffers) { - - Buffer[] clonedBuffers = null; - int i; - short dataSize; - - if (srcBuffers != null) { - clonedBuffers = new Buffer[srcBuffers.length]; - - for (i = 0; i < srcBuffers.length; i++) - srcBuffers[i].rewind(); - - /* - * In order to optimize the transfer we need a type check. - * - * Most CPUs support hardware memory transfer for different data - * types, so it's faster to copy more bytes at once rather - * than one byte per iteration as in case of ByteBuffer. - * - * For example, Intel CPUs support MOVSB (byte transfer), MOVSW - * (word transfer), MOVSD (double word transfer), MOVSS (32 bit - * scalar single precision floating point), MOVSQ (quad word - * transfer) and so on... - * - * Type checking may be improved by changing the order in - * which a buffer is checked against. If it's likely that the - * expected buffer is of type "ShortBuffer", then it should be - * checked at first place. - * - */ - - if (srcBuffers[0] instanceof ByteBuffer) - // dataSize is 1 - for (i = 0; i < srcBuffers.length; i++) - clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity()) - .put((ByteBuffer) srcBuffers[i]).rewind(); - else if (srcBuffers[0] instanceof ShortBuffer) { - dataSize = Short.SIZE >> 3; // dataSize is 2 - for (i = 0; i < srcBuffers.length; i++) - clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize) - .order(ByteOrder.nativeOrder()).asShortBuffer().put((ShortBuffer) srcBuffers[i]).rewind(); - } else if (srcBuffers[0] instanceof IntBuffer) { - dataSize = Integer.SIZE >> 3; // dataSize is 4 - for (i = 0; i < srcBuffers.length; i++) - clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize) - .order(ByteOrder.nativeOrder()).asIntBuffer().put((IntBuffer) srcBuffers[i]).rewind(); - } else if (srcBuffers[0] instanceof LongBuffer) { - dataSize = Long.SIZE >> 3; // dataSize is 8 - for (i = 0; i < srcBuffers.length; i++) - clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize) - .order(ByteOrder.nativeOrder()).asLongBuffer().put((LongBuffer) srcBuffers[i]).rewind(); - } else if (srcBuffers[0] instanceof FloatBuffer) { - dataSize = Float.SIZE >> 3; // dataSize is 4 - for (i = 0; i < srcBuffers.length; i++) - clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize) - .order(ByteOrder.nativeOrder()).asFloatBuffer().put((FloatBuffer) srcBuffers[i]).rewind(); - } else if (srcBuffers[0] instanceof DoubleBuffer) { - dataSize = Double.SIZE >> 3; // dataSize is 8 - for (i = 0; i < srcBuffers.length; i++) - clonedBuffers[i] = ByteBuffer.allocateDirect(srcBuffers[i].capacity() * dataSize) - .order(ByteOrder.nativeOrder()).asDoubleBuffer().put((DoubleBuffer) srcBuffers[i]).rewind(); - } - - for (i = 0; i < srcBuffers.length; i++) - srcBuffers[i].rewind(); - - } - - return clonedBuffers; - - } - -} \ No newline at end of file diff --git a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameConverter.java b/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameConverter.java deleted file mode 100644 index 868551bc2d..0000000000 --- a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameConverter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, Peter Abeles. All Rights Reserved. - * - * This file is part of BoofCV (http://boofcv.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright (C) 2015 Samuel Audet - * - * Licensed either under the Apache License, Version 2.0, or (at your option) - * under the terms of the GNU General Public License as published by - * the Free Software Foundation (subject to the "Classpath" exception), - * either version 2, or any later version (collectively, the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/ - * http://www.gnu.org/software/classpath/license.html - * - * or as provided in the LICENSE.txt file that accompanied this code. - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bytedeco.copiedstuff; - -/** - * Defines two methods to convert between a {@link Frame} and another generic - * data object that can contain the same data. The idea with this design is - * to allow users to convert easily between multiple potentially mutually - * exclusive types of image data objects over which we have no control. Because - * of this, and for performance reasons, any object returned by this class is - * guaranteed to remain valid only until the next call to {@code convert()}, - * anywhere in a chain of {@code FrameConverter} objects, and only as long as - * the latter themselves are not garbage collected. - * - * @author Samuel Audet - */ -public abstract class FrameConverter { - protected Frame frame; - - public abstract Frame convert(F f); - public abstract F convert(Frame frame); -} \ No newline at end of file diff --git a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameGrabber.java b/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameGrabber.java deleted file mode 100644 index e151929106..0000000000 --- a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/FrameGrabber.java +++ /dev/null @@ -1,662 +0,0 @@ -/* - * Copyright (c) 2020, Peter Abeles. All Rights Reserved. - * - * This file is part of BoofCV (http://boofcv.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright (C) 2009-2015 Samuel Audet - * - * Licensed either under the Apache License, Version 2.0, or (at your option) - * under the terms of the GNU General Public License as published by - * the Free Software Foundation (subject to the "Classpath" exception), - * either version 2, or any later version (collectively, the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/ - * http://www.gnu.org/software/classpath/license.html - * - * or as provided in the LICENSE.txt file that accompanied this code. - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bytedeco.copiedstuff; - -import java.beans.PropertyEditorSupport; -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.*; - -/** - * - * @author Samuel Audet - */ -@SuppressWarnings({"UnsafeFinalization","MissingOverride","NarrowingCompoundAssignment","JavaLangClash"}) -public abstract class FrameGrabber implements Closeable { - - public static final ArrayDeque list = new ArrayDeque<>(Arrays.asList( - "DC1394", "FlyCapture", "FlyCapture2", "OpenKinect", "OpenKinect2", "RealSense", "PS3Eye", "VideoInput", "OpenCV", "FFmpeg", "IPCamera")); - public static void init() { - for (String name : list) { - try { - Class c = get(name); - c.getMethod("tryLoad").invoke(null); - } catch (Throwable t) { - continue; - } - } - } - public static Class getDefault() { - // select first frame grabber that can load and that may have some cameras.. - for (String name : list) { - try { - Class c = get(name); - c.getMethod("tryLoad").invoke(null); - boolean mayContainCameras = false; - try { - String[] s = (String[])c.getMethod("getDeviceDescriptions").invoke(null); - if (s.length > 0) { - mayContainCameras = true; - } - } catch (Throwable t) { - if (t.getCause() instanceof UnsupportedOperationException) { - mayContainCameras = true; - } - } - if (mayContainCameras) { - return c; - } - } catch (Throwable t) { - continue; - } - } - return null; - } - public static Class get(String className) throws Exception { - className = FrameGrabber.class.getPackage().getName() + "." + className; - try { - return Class.forName(className).asSubclass(FrameGrabber.class); - } catch (ClassNotFoundException e) { - String className2 = className + "FrameGrabber"; - try { - return Class.forName(className2).asSubclass(FrameGrabber.class); - } catch (ClassNotFoundException ex) { - throw new Exception("Could not get FrameGrabber class for " + className + " or " + className2, e); - } - } - } - - public static FrameGrabber create(Class c, Class p, Object o) throws Exception { - Throwable cause = null; - try { - return c.getConstructor(p).newInstance(o); - } catch (InstantiationException ex) { - cause = ex; - } catch (IllegalAccessException ex) { - cause = ex; - } catch (IllegalArgumentException ex) { - cause = ex; - } catch (NoSuchMethodException ex) { - cause = ex; - } catch (InvocationTargetException ex) { - cause = ex.getCause(); - } - throw new Exception("Could not create new " + c.getSimpleName() + "(" + o + ")", cause); - } - - public static FrameGrabber createDefault(File deviceFile) throws Exception { - return create(getDefault(), File.class, deviceFile); - } - public static FrameGrabber createDefault(String devicePath) throws Exception { - return create(getDefault(), String.class, devicePath); - } - public static FrameGrabber createDefault(int deviceNumber) throws Exception { - try { - return create(getDefault(), int.class, deviceNumber); - } catch (Exception ex) { - return create(getDefault(), Integer.class, deviceNumber); - } - } - - public static FrameGrabber create(String className, File deviceFile) throws Exception { - return create(get(className), File.class, deviceFile); - } - public static FrameGrabber create(String className, String devicePath) throws Exception { - return create(get(className), String.class, devicePath); - } - public static FrameGrabber create(String className, int deviceNumber) throws Exception { - try { - return create(get(className), int.class, deviceNumber); - } catch (Exception ex) { - return create(get(className), Integer.class, deviceNumber); - } - } - - public static class PropertyEditor extends PropertyEditorSupport { - @Override public String getAsText() { - Class c = (Class)getValue(); - return c == null ? "null" : c.getSimpleName().split("FrameGrabber")[0]; - } - @Override public void setAsText(String s) { - if (s == null) { - setValue(null); - } - try { - setValue(get(s)); - } catch (Exception ex) { - throw new IllegalArgumentException(ex); - } - } - @Override public String[] getTags() { - return list.toArray(new String[list.size()]); - } - } - - - public static enum ImageMode { - COLOR, GRAY, RAW - } - - public static final long - SENSOR_PATTERN_RGGB = 0, - SENSOR_PATTERN_GBRG = (1L << 32), - SENSOR_PATTERN_GRBG = 1, - SENSOR_PATTERN_BGGR = (1L << 32) | 1; - - protected int videoStream = -1, audioStream = -1; - protected String format = null; - protected int imageWidth = 0, imageHeight = 0, audioChannels = 0; - protected ImageMode imageMode = ImageMode.COLOR; - protected long sensorPattern = -1L; - protected int pixelFormat = -1, videoCodec, videoBitrate = 0; - protected double aspectRatio = 0, frameRate = 0; - protected int sampleFormat = 0, audioCodec, audioBitrate = 0, sampleRate = 0; - protected boolean triggerMode = false; - protected int bpp = 0; - protected int timeout = 10000; - protected int numBuffers = 4; - protected double gamma = 0.0; - protected boolean deinterlace = false; - protected HashMap options = new HashMap(); - protected HashMap videoOptions = new HashMap(); - protected HashMap audioOptions = new HashMap(); - protected HashMap metadata = new HashMap(); - protected HashMap videoMetadata = new HashMap(); - protected HashMap audioMetadata = new HashMap(); - protected int frameNumber = 0; - protected long timestamp = 0; - - public int getVideoStream() { - return videoStream; - } - public void setVideoStream(int videoStream) { - this.videoStream = videoStream; - } - - public int getAudioStream() { - return audioStream; - } - public void setAudioStream(int audioStream) { - this.audioStream = audioStream; - } - - public String getFormat() { - return format; - } - public void setFormat(String format) { - this.format = format; - } - - public int getImageWidth() { - return imageWidth; - } - public void setImageWidth(int imageWidth) { - this.imageWidth = imageWidth; - } - - public int getImageHeight() { - return imageHeight; - } - public void setImageHeight(int imageHeight) { - this.imageHeight = imageHeight; - } - - public int getAudioChannels() { - return audioChannels; - } - public void setAudioChannels(int audioChannels) { - this.audioChannels = audioChannels; - } - - public ImageMode getImageMode() { - return imageMode; - } - public void setImageMode(ImageMode imageMode) { - this.imageMode = imageMode; - } - - public long getSensorPattern() { - return sensorPattern; - } - public void setSensorPattern(long sensorPattern) { - this.sensorPattern = sensorPattern; - } - - public int getPixelFormat() { - return pixelFormat; - } - public void setPixelFormat(int pixelFormat) { - this.pixelFormat = pixelFormat; - } - - public int getVideoCodec() { - return videoCodec; - } - public void setVideoCodec(int videoCodec) { - this.videoCodec = videoCodec; - } - - public int getVideoBitrate() { - return videoBitrate; - } - public void setVideoBitrate(int videoBitrate) { - this.videoBitrate = videoBitrate; - } - - public double getAspectRatio() { - return aspectRatio; - } - public void setAspectRatio(double aspectRatio) { - this.aspectRatio = aspectRatio; - } - - public double getFrameRate() { - return frameRate; - } - public void setFrameRate(double frameRate) { - this.frameRate = frameRate; - } - - public int getAudioCodec() { - return audioCodec; - } - public void setAudioCodec(int audioCodec) { - this.audioCodec = audioCodec; - } - - public int getAudioBitrate() { - return audioBitrate; - } - public void setAudioBitrate(int audioBitrate) { - this.audioBitrate = audioBitrate; - } - - public int getSampleFormat() { - return sampleFormat; - } - public void setSampleFormat(int sampleFormat) { - this.sampleFormat = sampleFormat; - } - - public int getSampleRate() { - return sampleRate; - } - public void setSampleRate(int sampleRate) { - this.sampleRate = sampleRate; - } - - public boolean isTriggerMode() { - return triggerMode; - } - public void setTriggerMode(boolean triggerMode) { - this.triggerMode = triggerMode; - } - - public int getBitsPerPixel() { - return bpp; - } - public void setBitsPerPixel(int bitsPerPixel) { - this.bpp = bitsPerPixel; - } - - public int getTimeout() { - return timeout; - } - public void setTimeout(int timeout) { - this.timeout = timeout; - } - - public int getNumBuffers() { - return numBuffers; - } - public void setNumBuffers(int numBuffers) { - this.numBuffers = numBuffers; - } - - public double getGamma() { - return gamma; - } - public void setGamma(double gamma) { - this.gamma = gamma; - } - - public boolean isDeinterlace() { - return deinterlace; - } - public void setDeinterlace(boolean deinterlace) { - this.deinterlace = deinterlace; - } - - public String getOption(String key) { - return options.get(key); - } - public void setOption(String key, String value) { - options.put(key, value); - } - - public String getVideoOption(String key) { - return videoOptions.get(key); - } - public void setVideoOption(String key, String value) { - videoOptions.put(key, value); - } - - public String getAudioOption(String key) { - return audioOptions.get(key); - } - public void setAudioOption(String key, String value) { - audioOptions.put(key, value); - } - - public Map getMetadata() { - return metadata; - } - - public String getMetadata(String key) { - return metadata.get(key); - } - public void setMetadata(String key, String value) { - metadata.put(key, value); - } - - public String getVideoMetadata(String key) { - return videoMetadata.get(key); - } - public void setVideoMetadata(String key, String value) { - videoMetadata.put(key, value); - } - - public String getAudioMetadata(String key) { - return audioMetadata.get(key); - } - public void setAudioMetadata(String key, String value) { - audioMetadata.put(key, value); - } - - public int getFrameNumber() { - return frameNumber; - } - public void setFrameNumber(int frameNumber) throws Exception { - this.frameNumber = frameNumber; - } - - public long getTimestamp() { - return timestamp; - } - public void setTimestamp(long timestamp) throws Exception { - this.timestamp = timestamp; - } - - public int getLengthInFrames() { - return 0; - } - public long getLengthInTime() { - return 0; - } - - public static class Exception extends IOException { - public Exception(String message) { super(message); } - public Exception(String message, Throwable cause) { super(message, cause); } - } - - public abstract void start() throws Exception; - public abstract void stop() throws Exception; - public abstract void trigger() throws Exception; - - @Override public void close() throws Exception { - stop(); - release(); - } - - /** - * Each call to grab stores the new image in the memory address for the previously returned frame.
- * IE.
- * - * grabber.grab() == grabber.grab() - * - *
- * This means that if you need to cache images returned from grab you should {@link Frame#clone()} the - * returned frame as the next call to grab will overwrite your existing image's memory. - *
- * Why?
- * Using this method instead of allocating a new buffer every time a frame - * is grabbed improves performance by reducing the frequency of garbage collections. - * Almost no additional heap space is typically allocated per frame. - * - * @return The frame returned from the grabber - * @throws Exception If there is a problem grabbing the frame. - */ - public abstract Frame grab() throws Exception; - public Frame grabFrame() throws Exception { return grab(); } - public abstract void release() throws Exception; - - public void restart() throws Exception { - stop(); - start(); - } - public void flush() throws Exception { - for (int i = 0; i < numBuffers+1; i++) { - grab(); - } - } - - private ExecutorService executor = Executors.newSingleThreadExecutor(); - private Future future = null; - private Frame delayedFrame = null; - private long delayedTime = 0; - public void delayedGrab(final long delayTime) { - delayedFrame = null; - delayedTime = 0; - final long start = System.nanoTime()/1000; - if (future != null && !future.isDone()) { - return; - } - future = executor.submit(new Callable() { public Void call() throws Exception { - do { - delayedFrame = grab(); - delayedTime = System.nanoTime()/1000 - start; - } while (delayedTime < delayTime); - return null; - }}); - } - public long getDelayedTime() throws InterruptedException, ExecutionException { - if (future == null) { - return 0; - } - future.get(); - return delayedTime; - } - public Frame getDelayedFrame() throws InterruptedException, ExecutionException { - if (future == null) { - return null; - } - future.get(); - return delayedFrame; - } - - public static class Array { - // declared protected to force users to use createArray(), which - // can be overridden without changing the calling code... - protected Array(FrameGrabber[] frameGrabbers) { - setFrameGrabbers(frameGrabbers); - } - - private Frame[] grabbedFrames = null; - private long[] latencies = null; - private long[] bestLatencies = null; - private long lastNewestTimestamp = 0; - private long bestInterval = Long.MAX_VALUE; - - protected FrameGrabber[] frameGrabbers = null; - public FrameGrabber[] getFrameGrabbers() { - return frameGrabbers; - } - public void setFrameGrabbers(FrameGrabber[] frameGrabbers) { - this.frameGrabbers = frameGrabbers; - grabbedFrames = new Frame[frameGrabbers.length]; - latencies = new long[frameGrabbers.length]; - bestLatencies = null; - lastNewestTimestamp = 0; - } - public int size() { - return frameGrabbers.length; - } - - public void start() throws Exception { - for (FrameGrabber f : frameGrabbers) { - f.start(); - } - } - public void stop() throws Exception { - for (FrameGrabber f : frameGrabbers) { - f.stop(); - } - } - // should be overriden to implement a broadcast trigger... - public void trigger() throws Exception { - for (FrameGrabber f : frameGrabbers) { - if (f.isTriggerMode()) { - f.trigger(); - } - } - } - // should be overriden to implement a broadcast grab... - public Frame[] grab() throws Exception { - if (frameGrabbers.length == 1) { - grabbedFrames[0] = frameGrabbers[0].grab(); - return grabbedFrames; - } - - // assume we sometimes get perfectly synchronized images, - // so save the best latencies we find as the perfectly - // synchronized case, so we know what to aim for in - // cases of missing/dropped frames ... - long newestTimestamp = 0; - boolean unsynchronized = false; - for (int i = 0; i < frameGrabbers.length; i++) { - grabbedFrames[i] = frameGrabbers[i].grab(); - if (grabbedFrames[i] != null) { - newestTimestamp = Math.max(newestTimestamp, frameGrabbers[i].getTimestamp()); - } - if (frameGrabbers[i].getClass() != frameGrabbers[(i + 1) % frameGrabbers.length].getClass()) { - // assume we can't synchronize different types of cameras with each other - unsynchronized = true; - } - } - if (unsynchronized) { - return grabbedFrames; - } - for (int i = 0; i < frameGrabbers.length; i++) { - if (grabbedFrames[i] != null) { - latencies[i] = newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp()); - } - } - if (bestLatencies == null) { - bestLatencies = Arrays.copyOf(latencies, latencies.length); - } else { - int sum1 = 0, sum2 = 0; - for (int i = 0; i < frameGrabbers.length; i++) { - sum1 += latencies[i]; - sum2 += bestLatencies[i]; - } - if (sum1 < sum2) { - bestLatencies = Arrays.copyOf(latencies, latencies.length); - } - } - - // we cannot have latencies higher than the time between frames.. - // or something too close to it anyway... 90% is good? - bestInterval = Math.min(bestInterval, newestTimestamp-lastNewestTimestamp); - for (int i = 0; i < bestLatencies.length; i++) { - bestLatencies[i] = Math.min(bestLatencies[i], bestInterval*9/10); - } - - // try to synchronize by attempting to land within 10% of - // the bestLatencies looking up to 2 frames ahead ... - for (int j = 0; j < 2; j++) { - for (int i = 0; i < frameGrabbers.length; i++) { - if (frameGrabbers[i].isTriggerMode() || grabbedFrames[i] == null) { - continue; - } - int latency = (int)(newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp())); - while (latency-bestLatencies[i] > 0.1*bestLatencies[i]) { - grabbedFrames[i] = frameGrabbers[i].grab(); - if (grabbedFrames[i] == null) { - break; - } - latency = (int)(newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp())); - if (latency < 0) { - // woops, a camera seems to have dropped a frame somewhere... - // bump up the newestTimestamp - newestTimestamp = Math.max(0, frameGrabbers[i].getTimestamp()); - break; - } - } - } - } - -//for (int i = 0; i < frameGrabbers.length; i++) { -// long latency = newestTimestamp - Math.max(0, frameGrabbers[i].getTimestamp()); -// System.out.print(bestLatencies[i] + " " + latency + " "); -//} -//System.out.println(" " + bestInterval); - - lastNewestTimestamp = newestTimestamp; - - return grabbedFrames; - } - public void release() throws Exception { - for (FrameGrabber f : frameGrabbers) { - f.release(); - } - } - } - - public Array createArray(FrameGrabber[] frameGrabbers) { - return new Array(frameGrabbers); - } -} \ No newline at end of file diff --git a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Java2DFrameConverter.java b/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Java2DFrameConverter.java deleted file mode 100644 index 2b3931e15e..0000000000 --- a/integration/boofcv-ffmpeg/src/main/java/org/bytedeco/copiedstuff/Java2DFrameConverter.java +++ /dev/null @@ -1,733 +0,0 @@ -/* - * Copyright (c) 2020, Peter Abeles. All Rights Reserved. - * - * This file is part of BoofCV (http://boofcv.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright (C) 2015 Samuel Audet - * - * Licensed either under the Apache License, Version 2.0, or (at your option) - * under the terms of the GNU General Public License as published by - * the Free Software Foundation (subject to the "Classpath" exception), - * either version 2, or any later version (collectively, the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/ - * http://www.gnu.org/software/classpath/license.html - * - * or as provided in the LICENSE.txt file that accompanied this code. - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bytedeco.copiedstuff; - -import java.awt.*; -import java.awt.color.ColorSpace; -import java.awt.image.*; -import java.nio.*; - -/** - * A utility class to copy data between {@link Frame} and {@link BufferedImage}. - * Since {@link BufferedImage} does not support NIO buffers, we cannot share - * allocated memory with {@link Frame}. - * - * @author Samuel Audet - */ -@SuppressWarnings({"UnsafeFinalization", "MissingOverride", "NarrowingCompoundAssignment"}) -public class Java2DFrameConverter extends FrameConverter { - - @Override public Frame convert( BufferedImage img ) { - return getFrame(img); - } - - @Override public BufferedImage convert( Frame frame ) { - return getBufferedImage(frame); - } - - public static BufferedImage cloneBufferedImage( BufferedImage bufferedImage ) { - if (bufferedImage == null) { - return null; - } - BufferedImage bi = bufferedImage; - int type = bi.getType(); - if (type == BufferedImage.TYPE_CUSTOM) { - return new BufferedImage(bi.getColorModel(), - bi.copyData(null), bi.isAlphaPremultiplied(), null); - } else { - return new BufferedImage(bi.getWidth(), bi.getHeight(), type); - } - } - - public static final byte[] - gamma22 = new byte[256], - gamma22inv = new byte[256]; - - static { - for (int i = 0; i < 256; i++) { - gamma22[i] = (byte)Math.round(Math.pow(i/255.0, 2.2)*255.0); - gamma22inv[i] = (byte)Math.round(Math.pow(i/255.0, 1/2.2)*255.0); - } - } - - public static int decodeGamma22( int value ) { - return gamma22[value & 0xFF] & 0xFF; - } - - public static int encodeGamma22( int value ) { - return gamma22inv[value & 0xFF] & 0xFF; - } - - public static void flipCopyWithGamma( ByteBuffer srcBuf, int srcStep, - ByteBuffer dstBuf, int dstStep, boolean signed, double gamma, boolean flip, int channels ) { - assert srcBuf != dstBuf; - int w = Math.min(srcStep, dstStep); - int srcLine = srcBuf.position(), dstLine = dstBuf.position(); - byte[] buffer = new byte[channels]; - while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) { - if (flip) { - srcBuf.position(srcBuf.capacity() - srcLine - srcStep); - } else { - srcBuf.position(srcLine); - } - dstBuf.position(dstLine); - w = Math.min(Math.min(w, srcBuf.remaining()), dstBuf.remaining()); - if (signed) { - if (channels > 1) { - for (int x = 0; x < w; x += channels) { - for (int z = 0; z < channels; z++) { - int in = srcBuf.get(); - byte out; - if (gamma == 1.0) { - out = (byte)in; - } else { - out = (byte)Math.round(Math.pow((double)in/Byte.MAX_VALUE, gamma)*Byte.MAX_VALUE); - } - buffer[z] = out; - } - for (int z = channels - 1; z >= 0; z--) { - dstBuf.put(buffer[z]); - } - } - } else { - for (int x = 0; x < w; x++) { - int in = srcBuf.get(); - byte out; - if (gamma == 1.0) { - out = (byte)in; - } else { - out = (byte)Math.round(Math.pow((double)in/Byte.MAX_VALUE, gamma)*Byte.MAX_VALUE); - } - dstBuf.put(out); - } - } - } else { - if (channels > 1) { - for (int x = 0; x < w; x += channels) { - for (int z = 0; z < channels; z++) { - byte out; - int in = srcBuf.get() & 0xFF; - if (gamma == 1.0) { - out = (byte)in; - } else if (gamma == 2.2) { - out = gamma22[in]; - } else if (gamma == 1/2.2) { - out = gamma22inv[in]; - } else { - out = (byte)Math.round(Math.pow((double)in/0xFF, gamma)*0xFF); - } - buffer[z] = out; - } - for (int z = channels - 1; z >= 0; z--) { - dstBuf.put(buffer[z]); - } - } - } else { - for (int x = 0; x < w; x++) { - byte out; - int in = srcBuf.get() & 0xFF; - if (gamma == 1.0) { - out = (byte)in; - } else if (gamma == 2.2) { - out = gamma22[in]; - } else if (gamma == 1/2.2) { - out = gamma22inv[in]; - } else { - out = (byte)Math.round(Math.pow((double)in/0xFF, gamma)*0xFF); - } - dstBuf.put(out); - } - } - } - srcLine += srcStep; - dstLine += dstStep; - } - } - - public static void flipCopyWithGamma( ShortBuffer srcBuf, int srcStep, - ShortBuffer dstBuf, int dstStep, boolean signed, double gamma, boolean flip, int channels ) { - assert srcBuf != dstBuf; - int w = Math.min(srcStep, dstStep); - int srcLine = srcBuf.position(), dstLine = dstBuf.position(); - short[] buffer = new short[channels]; - while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) { - if (flip) { - srcBuf.position(srcBuf.capacity() - srcLine - srcStep); - } else { - srcBuf.position(srcLine); - } - dstBuf.position(dstLine); - w = Math.min(Math.min(w, srcBuf.remaining()), dstBuf.remaining()); - if (signed) { - if (channels > 1) { - for (int x = 0; x < w; x += channels) { - for (int z = 0; z < channels; z++) { - int in = srcBuf.get(); - short out; - if (gamma == 1.0) { - out = (short)in; - } else { - out = (short)Math.round(Math.pow((double)in/Short.MAX_VALUE, gamma)*Short.MAX_VALUE); - } - buffer[z] = out; - } - for (int z = channels - 1; z >= 0; z--) { - dstBuf.put(buffer[z]); - } - } - } else { - for (int x = 0; x < w; x++) { - int in = srcBuf.get(); - short out; - if (gamma == 1.0) { - out = (short)in; - } else { - out = (short)Math.round(Math.pow((double)in/Short.MAX_VALUE, gamma)*Short.MAX_VALUE); - } - dstBuf.put(out); - } - } - } else { - if (channels > 1) { - for (int x = 0; x < w; x += channels) { - for (int z = 0; z < channels; z++) { - int in = srcBuf.get(); - short out; - if (gamma == 1.0) { - out = (short)in; - } else { - out = (short)Math.round(Math.pow((double)in/0xFFFF, gamma)*0xFFFF); - } - buffer[z] = out; - } - for (int z = channels - 1; z >= 0; z--) { - dstBuf.put(buffer[z]); - } - } - } else { - for (int x = 0; x < w; x++) { - int in = srcBuf.get() & 0xFFFF; - short out; - if (gamma == 1.0) { - out = (short)in; - } else { - out = (short)Math.round(Math.pow((double)in/0xFFFF, gamma)*0xFFFF); - } - dstBuf.put(out); - } - } - } - srcLine += srcStep; - dstLine += dstStep; - } - } - - public static void flipCopyWithGamma( IntBuffer srcBuf, int srcStep, - IntBuffer dstBuf, int dstStep, double gamma, boolean flip, int channels ) { - assert srcBuf != dstBuf; - int w = Math.min(srcStep, dstStep); - int srcLine = srcBuf.position(), dstLine = dstBuf.position(); - int[] buffer = new int[channels]; - while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) { - if (flip) { - srcBuf.position(srcBuf.capacity() - srcLine - srcStep); - } else { - srcBuf.position(srcLine); - } - dstBuf.position(dstLine); - w = Math.min(Math.min(w, srcBuf.remaining()), dstBuf.remaining()); - if (channels > 1) { - for (int x = 0; x < w; x += channels) { - for (int z = 0; z < channels; z++) { - int in = srcBuf.get(); - int out; - if (gamma == 1.0) { - out = (int)in; - } else { - out = (int)Math.round(Math.pow((double)in/Integer.MAX_VALUE, gamma)*Integer.MAX_VALUE); - } - buffer[z] = out; - } - for (int z = channels - 1; z >= 0; z--) { - dstBuf.put(buffer[z]); - } - } - } else { - for (int x = 0; x < w; x++) { - int in = srcBuf.get(); - int out; - if (gamma == 1.0) { - out = in; - } else { - out = (int)Math.round(Math.pow((double)in/Integer.MAX_VALUE, gamma)*Integer.MAX_VALUE); - } - dstBuf.put(out); - } - } - srcLine += srcStep; - dstLine += dstStep; - } - } - - public static void flipCopyWithGamma( FloatBuffer srcBuf, int srcStep, - FloatBuffer dstBuf, int dstStep, double gamma, boolean flip, int channels ) { - assert srcBuf != dstBuf; - int w = Math.min(srcStep, dstStep); - int srcLine = srcBuf.position(), dstLine = dstBuf.position(); - float[] buffer = new float[channels]; - while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) { - if (flip) { - srcBuf.position(srcBuf.capacity() - srcLine - srcStep); - } else { - srcBuf.position(srcLine); - } - dstBuf.position(dstLine); - w = Math.min(Math.min(w, srcBuf.remaining()), dstBuf.remaining()); - if (channels > 1) { - for (int x = 0; x < w; x += channels) { - for (int z = 0; z < channels; z++) { - float in = srcBuf.get(); - float out; - if (gamma == 1.0) { - out = in; - } else { - out = (float)Math.pow(in, gamma); - } - buffer[z] = out; - } - for (int z = channels - 1; z >= 0; z--) { - dstBuf.put(buffer[z]); - } - } - } else { - for (int x = 0; x < w; x++) { - float in = srcBuf.get(); - float out; - if (gamma == 1.0) { - out = in; - } else { - out = (float)Math.pow(in, gamma); - } - dstBuf.put(out); - } - } - srcLine += srcStep; - dstLine += dstStep; - } - } - - public static void flipCopyWithGamma( DoubleBuffer srcBuf, int srcStep, - DoubleBuffer dstBuf, int dstStep, double gamma, boolean flip, int channels ) { - assert srcBuf != dstBuf; - int w = Math.min(srcStep, dstStep); - int srcLine = srcBuf.position(), dstLine = dstBuf.position(); - double[] buffer = new double[channels]; - while (srcLine < srcBuf.capacity() && dstLine < dstBuf.capacity()) { - if (flip) { - srcBuf.position(srcBuf.capacity() - srcLine - srcStep); - } else { - srcBuf.position(srcLine); - } - dstBuf.position(dstLine); - w = Math.min(Math.min(w, srcBuf.remaining()), dstBuf.remaining()); - if (channels > 1) { - for (int x = 0; x < w; x += channels) { - for (int z = 0; z < channels; z++) { - double in = srcBuf.get(); - double out; - if (gamma == 1.0) { - out = in; - } else { - out = Math.pow(in, gamma); - } - buffer[z] = out; - } - for (int z = channels - 1; z >= 0; z--) { - dstBuf.put(buffer[z]); - } - } - } else { - for (int x = 0; x < w; x++) { - double in = srcBuf.get(); - double out; - if (gamma == 1.0) { - out = in; - } else { - out = Math.pow(in, gamma); - } - dstBuf.put(out); - } - } - srcLine += srcStep; - dstLine += dstStep; - } - } - - public static void applyGamma( Frame frame, double gamma ) { - applyGamma(frame.image[0].position(0), frame.imageDepth, frame.imageStride, gamma); - } - - public static void applyGamma( Buffer buffer, int depth, int stride, double gamma ) { - if (gamma == 1.0) { - return; - } - switch (depth) { - case Frame.DEPTH_UBYTE: - flipCopyWithGamma(((ByteBuffer)buffer).asReadOnlyBuffer(), stride, (ByteBuffer)buffer, stride, false, gamma, false, 0); - break; - case Frame.DEPTH_BYTE: - flipCopyWithGamma(((ByteBuffer)buffer).asReadOnlyBuffer(), stride, (ByteBuffer)buffer, stride, true, gamma, false, 0); - break; - case Frame.DEPTH_USHORT: - flipCopyWithGamma(((ShortBuffer)buffer).asReadOnlyBuffer(), stride, (ShortBuffer)buffer, stride, false, gamma, false, 0); - break; - case Frame.DEPTH_SHORT: - flipCopyWithGamma(((ShortBuffer)buffer).asReadOnlyBuffer(), stride, (ShortBuffer)buffer, stride, true, gamma, false, 0); - break; - case Frame.DEPTH_INT: - flipCopyWithGamma(((IntBuffer)buffer).asReadOnlyBuffer(), stride, (IntBuffer)buffer, stride, gamma, false, 0); - break; - case Frame.DEPTH_FLOAT: - flipCopyWithGamma(((FloatBuffer)buffer).asReadOnlyBuffer(), stride, (FloatBuffer)buffer, stride, gamma, false, 0); - break; - case Frame.DEPTH_DOUBLE: - flipCopyWithGamma(((DoubleBuffer)buffer).asReadOnlyBuffer(), stride, (DoubleBuffer)buffer, stride, gamma, false, 0); - break; - default: - assert false; - } - } - - public static void copy( Frame frame, BufferedImage bufferedImage ) { - copy(frame, bufferedImage, 1.0); - } - - public static void copy( Frame frame, BufferedImage bufferedImage, double gamma ) { - copy(frame, bufferedImage, gamma, false, null); - } - - public static void copy( Frame frame, BufferedImage bufferedImage, double gamma, boolean flipChannels, Rectangle roi ) { - Buffer in = frame.image[0].position(roi == null ? 0 : roi.y*frame.imageStride + roi.x*frame.imageChannels); - SampleModel sm = bufferedImage.getSampleModel(); - Raster r = bufferedImage.getRaster(); - DataBuffer out = r.getDataBuffer(); - int x = -r.getSampleModelTranslateX(); - int y = -r.getSampleModelTranslateY(); - int step = sm.getWidth()*sm.getNumBands(); - int channels = sm.getNumBands(); - if (sm instanceof ComponentSampleModel) { - step = ((ComponentSampleModel)sm).getScanlineStride(); - channels = ((ComponentSampleModel)sm).getPixelStride(); - } else if (sm instanceof SinglePixelPackedSampleModel) { - step = ((SinglePixelPackedSampleModel)sm).getScanlineStride(); - channels = 1; - } else if (sm instanceof MultiPixelPackedSampleModel) { - step = ((MultiPixelPackedSampleModel)sm).getScanlineStride(); - channels = ((MultiPixelPackedSampleModel)sm).getPixelBitStride()/8; // ?? - } - int start = y*step + x*channels; - - if (out instanceof DataBufferByte) { - byte[] a = ((DataBufferByte)out).getData(); - flipCopyWithGamma((ByteBuffer)in, frame.imageStride, ByteBuffer.wrap(a, start, a.length - start), step, false, gamma, false, flipChannels ? channels : 0); - } else if (out instanceof DataBufferDouble) { - double[] a = ((DataBufferDouble)out).getData(); - flipCopyWithGamma((DoubleBuffer)in, frame.imageStride, DoubleBuffer.wrap(a, start, a.length - start), step, gamma, false, flipChannels ? channels : 0); - } else if (out instanceof DataBufferFloat) { - float[] a = ((DataBufferFloat)out).getData(); - flipCopyWithGamma((FloatBuffer)in, frame.imageStride, FloatBuffer.wrap(a, start, a.length - start), step, gamma, false, flipChannels ? channels : 0); - } else if (out instanceof DataBufferInt) { - int[] a = ((DataBufferInt)out).getData(); - int stride = frame.imageStride; - if (in instanceof ByteBuffer) { - in = ((ByteBuffer)in).order(flipChannels ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN).asIntBuffer(); - stride /= 4; - } - flipCopyWithGamma((IntBuffer)in, stride, IntBuffer.wrap(a, start, a.length - start), step, gamma, false, flipChannels ? channels : 0); - } else if (out instanceof DataBufferShort) { - short[] a = ((DataBufferShort)out).getData(); - flipCopyWithGamma((ShortBuffer)in, frame.imageStride, ShortBuffer.wrap(a, start, a.length - start), step, true, gamma, false, flipChannels ? channels : 0); - } else if (out instanceof DataBufferUShort) { - short[] a = ((DataBufferUShort)out).getData(); - flipCopyWithGamma((ShortBuffer)in, frame.imageStride, ShortBuffer.wrap(a, start, a.length - start), step, false, gamma, false, flipChannels ? channels : 0); - } else { - assert false; - } - } - - public static void copy( BufferedImage image, Frame frame ) { - copy(image, frame, 1.0); - } - - public static void copy( BufferedImage image, Frame frame, double gamma ) { - copy(image, frame, gamma, false, null); - } - - public static void copy( BufferedImage image, Frame frame, double gamma, boolean flipChannels, Rectangle roi ) { - Buffer out = frame.image[0].position(roi == null ? 0 : roi.y*frame.imageStride + roi.x*frame.imageChannels); - SampleModel sm = image.getSampleModel(); - Raster r = image.getRaster(); - DataBuffer in = r.getDataBuffer(); - int x = -r.getSampleModelTranslateX(); - int y = -r.getSampleModelTranslateY(); - int step = sm.getWidth()*sm.getNumBands(); - int channels = sm.getNumBands(); - if (sm instanceof ComponentSampleModel) { - step = ((ComponentSampleModel)sm).getScanlineStride(); - channels = ((ComponentSampleModel)sm).getPixelStride(); - } else if (sm instanceof SinglePixelPackedSampleModel) { - step = ((SinglePixelPackedSampleModel)sm).getScanlineStride(); - channels = 1; - } else if (sm instanceof MultiPixelPackedSampleModel) { - step = ((MultiPixelPackedSampleModel)sm).getScanlineStride(); - channels = ((MultiPixelPackedSampleModel)sm).getPixelBitStride()/8; // ?? - } - int start = y*step + x*channels; - - if (in instanceof DataBufferByte) { - byte[] a = ((DataBufferByte)in).getData(); - flipCopyWithGamma(ByteBuffer.wrap(a, start, a.length - start), step, (ByteBuffer)out, frame.imageStride, false, gamma, false, flipChannels ? channels : 0); - } else if (in instanceof DataBufferDouble) { - double[] a = ((DataBufferDouble)in).getData(); - flipCopyWithGamma(DoubleBuffer.wrap(a, start, a.length - start), step, (DoubleBuffer)out, frame.imageStride, gamma, false, flipChannels ? channels : 0); - } else if (in instanceof DataBufferFloat) { - float[] a = ((DataBufferFloat)in).getData(); - flipCopyWithGamma(FloatBuffer.wrap(a, start, a.length - start), step, (FloatBuffer)out, frame.imageStride, gamma, false, flipChannels ? channels : 0); - } else if (in instanceof DataBufferInt) { - int[] a = ((DataBufferInt)in).getData(); - int stride = frame.imageStride; - if (out instanceof ByteBuffer) { - out = ((ByteBuffer)out).order(flipChannels ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN).asIntBuffer(); - stride /= 4; - } - flipCopyWithGamma(IntBuffer.wrap(a, start, a.length - start), step, (IntBuffer)out, stride, gamma, false, flipChannels ? channels : 0); - } else if (in instanceof DataBufferShort) { - short[] a = ((DataBufferShort)in).getData(); - flipCopyWithGamma(ShortBuffer.wrap(a, start, a.length - start), step, (ShortBuffer)out, frame.imageStride, true, gamma, false, flipChannels ? channels : 0); - } else if (in instanceof DataBufferUShort) { - short[] a = ((DataBufferUShort)in).getData(); - flipCopyWithGamma(ShortBuffer.wrap(a, start, a.length - start), step, (ShortBuffer)out, frame.imageStride, false, gamma, false, flipChannels ? channels : 0); - } else { - assert false; - } - } - - protected BufferedImage bufferedImage = null; - - public static int getBufferedImageType( Frame frame ) { - // precanned BufferedImage types are confusing... in practice though, - // they all use the sRGB color model when blitting: - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5051418 - // and we should use them because they are *A LOT* faster with Java 2D. - // workaround: do gamma correction ourselves ("gamma" parameter) - // since we'll never use getRGB() and setRGB(), right? - int type = BufferedImage.TYPE_CUSTOM; - if (frame.imageChannels == 1) { - if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) { - type = BufferedImage.TYPE_BYTE_GRAY; - } else if (frame.imageDepth == Frame.DEPTH_USHORT) { - type = BufferedImage.TYPE_USHORT_GRAY; - } - } else if (frame.imageChannels == 3) { - if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) { - type = BufferedImage.TYPE_3BYTE_BGR; - } - } else if (frame.imageChannels == 4) { - // The channels end up reversed of what we need for OpenCL. - // We work around this in copyTo() and copyFrom() by - // inversing the channels to let us use RGBA in our IplImage. - if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) { - type = BufferedImage.TYPE_4BYTE_ABGR; - } - } - return type; - } - - public BufferedImage getBufferedImage( Frame frame ) { - return getBufferedImage(frame, 1.0); - } - - public BufferedImage getBufferedImage( Frame frame, double gamma ) { - return getBufferedImage(frame, gamma, false, null); - } - - public BufferedImage getBufferedImage( Frame frame, double gamma, boolean flipChannels, ColorSpace cs ) { - if (frame == null || frame.image == null) { - return null; - } - int type = getBufferedImageType(frame); - - if (bufferedImage == null || bufferedImage.getWidth() != frame.imageWidth - || bufferedImage.getHeight() != frame.imageHeight || bufferedImage.getType() != type) { - bufferedImage = type == BufferedImage.TYPE_CUSTOM || cs != null ? null - : new BufferedImage(frame.imageWidth, frame.imageHeight, type); - } - - if (bufferedImage == null) { - boolean alpha = false; - int[] offsets = null; - if (frame.imageChannels == 1) { - alpha = false; - if (cs == null) { - cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); - } - offsets = new int[]{0}; - } else if (frame.imageChannels == 3) { - alpha = false; - if (cs == null) { - cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); - } - // raster in "BGR" order like OpenCV.. - offsets = new int[]{2, 1, 0}; - } else if (frame.imageChannels == 4) { - alpha = true; - if (cs == null) { - cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); - } - // raster in "RGBA" order for OpenCL.. alpha needs to be last - offsets = new int[]{0, 1, 2, 3}; - } else { - assert false; - } - - ColorModel cm = null; - WritableRaster wr = null; - if (frame.imageDepth == Frame.DEPTH_UBYTE || frame.imageDepth == Frame.DEPTH_BYTE) { - cm = new ComponentColorModel(cs, alpha, - false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); - wr = Raster.createWritableRaster(new ComponentSampleModel( - DataBuffer.TYPE_BYTE, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride, - offsets), null); - } else if (frame.imageDepth == Frame.DEPTH_USHORT) { - cm = new ComponentColorModel(cs, alpha, - false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT); - wr = Raster.createWritableRaster(new ComponentSampleModel( - DataBuffer.TYPE_USHORT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride, - offsets), null); - } else if (frame.imageDepth == Frame.DEPTH_SHORT) { - cm = new ComponentColorModel(cs, alpha, - false, Transparency.OPAQUE, DataBuffer.TYPE_SHORT); - wr = Raster.createWritableRaster(new ComponentSampleModel( - DataBuffer.TYPE_SHORT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride, - offsets), null); - } else if (frame.imageDepth == Frame.DEPTH_INT) { - cm = new ComponentColorModel(cs, alpha, - false, Transparency.OPAQUE, DataBuffer.TYPE_INT); - wr = Raster.createWritableRaster(new ComponentSampleModel( - DataBuffer.TYPE_INT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride, - offsets), null); - } else if (frame.imageDepth == Frame.DEPTH_FLOAT) { - cm = new ComponentColorModel(cs, alpha, - false, Transparency.OPAQUE, DataBuffer.TYPE_FLOAT); - wr = Raster.createWritableRaster(new ComponentSampleModel( - DataBuffer.TYPE_FLOAT, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride, - offsets), null); - } else if (frame.imageDepth == Frame.DEPTH_DOUBLE) { - cm = new ComponentColorModel(cs, alpha, - false, Transparency.OPAQUE, DataBuffer.TYPE_DOUBLE); - wr = Raster.createWritableRaster(new ComponentSampleModel( - DataBuffer.TYPE_DOUBLE, frame.imageWidth, frame.imageHeight, frame.imageChannels, frame.imageStride, - offsets), null); - } else { - assert false; - } - - bufferedImage = new BufferedImage(cm, wr, false, null); - } - - if (bufferedImage != null) { - copy(frame, bufferedImage, gamma, flipChannels, null); - } - - return bufferedImage; - } - - /** - * Returns a Frame based on a BufferedImage. - */ - public Frame getFrame( BufferedImage image ) { - return getFrame(image, 1.0); - } - - /** - * Returns a Frame based on a BufferedImage, and given gamma. - */ - public Frame getFrame( BufferedImage image, double gamma ) { - return getFrame(image, gamma, false); - } - - /** - * Returns a Frame based on a BufferedImage, given gamma, and inverted channels flag. - */ - public Frame getFrame( BufferedImage image, double gamma, boolean flipChannels ) { - if (image == null) { - return null; - } - SampleModel sm = image.getSampleModel(); - int depth = 0, numChannels = sm.getNumBands(); - switch (image.getType()) { - case BufferedImage.TYPE_INT_RGB: - case BufferedImage.TYPE_INT_ARGB: - case BufferedImage.TYPE_INT_ARGB_PRE: - case BufferedImage.TYPE_INT_BGR: - depth = Frame.DEPTH_UBYTE; - numChannels = 4; - break; - } - if (depth == 0 || numChannels == 0) { - switch (sm.getDataType()) { - case DataBuffer.TYPE_BYTE: depth = Frame.DEPTH_UBYTE; break; - case DataBuffer.TYPE_USHORT: depth = Frame.DEPTH_USHORT; break; - case DataBuffer.TYPE_SHORT: depth = Frame.DEPTH_SHORT; break; - case DataBuffer.TYPE_INT: depth = Frame.DEPTH_INT; break; - case DataBuffer.TYPE_FLOAT: depth = Frame.DEPTH_FLOAT; break; - case DataBuffer.TYPE_DOUBLE: depth = Frame.DEPTH_DOUBLE; break; - default: assert false; - } - } - if (frame == null || frame.imageWidth != image.getWidth() || frame.imageHeight != image.getHeight() - || frame.imageDepth != depth || frame.imageChannels != numChannels) { - frame = new Frame(image.getWidth(), image.getHeight(), depth, numChannels); - } - copy(image, frame, gamma, flipChannels, null); - return frame; - } -} \ No newline at end of file From 2cacdc76cac4c3eabcd447b6ddf6f521ce7e7822 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Mon, 17 Mar 2025 23:31:16 +0000 Subject: [PATCH 09/23] Fix dep and compilation errors, safely close file handles. We shouldn't be swallowing exceptions --- integration/boofcv-javacv/build.gradle | 2 +- .../java/boofcv/javacv/ConvertIplImage.java | 32 ++++---- .../main/java/boofcv/javacv/UtilOpenCV.java | 75 ++++++++++--------- .../boofcv/javacv/TestConvertIplImage.java | 3 +- 4 files changed, 54 insertions(+), 58 deletions(-) diff --git a/integration/boofcv-javacv/build.gradle b/integration/boofcv-javacv/build.gradle index 1c48d39ac9..ffe40b1361 100644 --- a/integration/boofcv-javacv/build.gradle +++ b/integration/boofcv-javacv/build.gradle @@ -9,7 +9,7 @@ dependencies { api project(':integration:boofcv-swing') - api (group: 'org.bytedeco', name: 'javacpp-platform', version: project.javacv_version) + api (group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version) } sourceSets { test { java { srcDir 'src/main/examples' } } } diff --git a/integration/boofcv-javacv/src/main/java/boofcv/javacv/ConvertIplImage.java b/integration/boofcv-javacv/src/main/java/boofcv/javacv/ConvertIplImage.java index cc1b55c799..1ac8cb08c8 100644 --- a/integration/boofcv-javacv/src/main/java/boofcv/javacv/ConvertIplImage.java +++ b/integration/boofcv-javacv/src/main/java/boofcv/javacv/ConvertIplImage.java @@ -20,11 +20,12 @@ import boofcv.core.image.GeneralizedImageOps; import boofcv.struct.image.*; +import org.bytedeco.opencv.opencv_core.IplImage; import org.jetbrains.annotations.Nullable; import java.nio.*; -import static org.bytedeco.javacpp.opencv_core.*; +import static org.bytedeco.opencv.global.opencv_core.*; /** * Functions for converting between JavaCV's IplImage data type and BoofCV image types @@ -59,7 +60,7 @@ public static > T convertFrom( IplImage input, @Nullable + dataType + " found " + output.getDataType() + " instead"); output.reshape(width, height); } else { - output = (T)GeneralizedImageOps.createSingleBand(dataType, width, height); + output = GeneralizedImageOps.createSingleBand(dataType, width, height); } switch (dataType) { @@ -104,7 +105,7 @@ public static > T convertFrom( IplImage input, @Nu output.numBands = numBands; output.reshape(width, height); } else { - output = (T)GeneralizedImageOps.createInterleaved(dataType, width, height, numBands); + output = GeneralizedImageOps.createInterleaved(dataType, width, height, numBands); } switch (dataType) { @@ -264,21 +265,14 @@ private static void convertFrom_I( IplImage input, InterleavedF64 output ) { } public static ImageDataType depthToBoofType( int depth ) { - switch (depth) { - case IPL_DEPTH_8U: - return ImageDataType.U8; - case IPL_DEPTH_8S: - return ImageDataType.S8; - case IPL_DEPTH_16S: - return ImageDataType.S16; - case IPL_DEPTH_32S: - return ImageDataType.S32; - case IPL_DEPTH_32F: - return ImageDataType.F32; - case IPL_DEPTH_64F: - return ImageDataType.F64; - default: - throw new IllegalArgumentException("Unknown IPL depth " + depth); - } + return switch (depth) { + case IPL_DEPTH_8U -> ImageDataType.U8; + case IPL_DEPTH_8S -> ImageDataType.S8; + case IPL_DEPTH_16S -> ImageDataType.S16; + case IPL_DEPTH_32S -> ImageDataType.S32; + case IPL_DEPTH_32F -> ImageDataType.F32; + case IPL_DEPTH_64F -> ImageDataType.F64; + default -> throw new IllegalArgumentException("Unknown IPL depth " + depth); + }; } } diff --git a/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java b/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java index 9258f4eb51..db42eced58 100644 --- a/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java +++ b/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java @@ -22,11 +22,15 @@ import boofcv.struct.calib.CameraPinholeBrown; import org.bytedeco.javacpp.IntPointer; import org.bytedeco.javacpp.indexer.DoubleRawIndexer; +import org.bytedeco.opencv.opencv_core.FileStorage; +import org.bytedeco.opencv.opencv_core.Mat; import org.ejml.data.DMatrixRMaj; import java.io.File; -import static org.bytedeco.javacpp.opencv_core.*; +import static org.bytedeco.opencv.global.opencv_core.read; +import static org.bytedeco.opencv.global.opencv_core.write; +import static org.opencv.core.CvType.CV_64F; /** * Various utility functions for working with OpenCV @@ -41,18 +45,18 @@ public class UtilOpenCV { * @return CameraPinholeRadial */ public static CameraPinholeBrown loadPinholeRadial(String fileName ) { - FileStorage fs = new FileStorage( - new File(fileName).getAbsolutePath(), FileStorage.READ); - IntPointer width = new IntPointer(1); IntPointer height = new IntPointer(1); - read(fs.get("image_width"),width,-1); - read(fs.get("image_height"),height,-1); - Mat K = new Mat(); - read(fs.get("camera_matrix"),K); Mat distortion = new Mat(); - read(fs.get("distortion_coefficients"),distortion); + Mat K = new Mat(); + + try (FileStorage fs = new FileStorage(new File(fileName).getAbsolutePath(), FileStorage.READ)) { + read(fs.get("image_width"), width, -1); + read(fs.get("image_height"), height, -1); + read(fs.get("camera_matrix"), K); + read(fs.get("distortion_coefficients"), distortion); + } CameraPinholeBrown boof = new CameraPinholeBrown(); boof.width = width.get(); @@ -67,43 +71,38 @@ public static CameraPinholeBrown loadPinholeRadial(String fileName ) { DoubleRawIndexer indexerD = distortion.createIndexer(); - if( distortion.rows() >= 5 ) - boof.setRadial(indexerD.get(0,0),indexerD.get(1,0),indexerD.get(4,0)); - else if( distortion.rows() >= 2 ) - boof.setRadial(indexerD.get(0,0),indexerD.get(1,0)); - if( distortion.rows() >= 5 ) - boof.fsetTangential(indexerD.get(2,0),indexerD.get(3,0)); + if (distortion.rows() >= 5) + boof.setRadial(indexerD.get(0, 0), indexerD.get(1, 0), indexerD.get(4, 0)); + else if (distortion.rows() >= 2) + boof.setRadial(indexerD.get(0, 0), indexerD.get(1, 0)); + if (distortion.rows() >= 5) + boof.fsetTangential(indexerD.get(2, 0), indexerD.get(3, 0)); return boof; } - public static void save(CameraPinholeBrown model , String fileName ) { - FileStorage fs = new FileStorage( - new File(fileName).getAbsolutePath(), FileStorage.WRITE); + public static void save( CameraPinholeBrown model, String fileName ) { + try (FileStorage fs = new FileStorage(new File(fileName).getAbsolutePath(), FileStorage.WRITE)) { - DMatrixRMaj K = PerspectiveOps.pinholeToMatrix(model, (DMatrixRMaj)null); + DMatrixRMaj K = PerspectiveOps.pinholeToMatrix(model, (DMatrixRMaj)null); - write(fs,"image_width", model.width); - write(fs,"image_height", model.height); - write(fs,"camera_matrix", toMat(K)); + write(fs, "image_width", model.width); + write(fs, "image_height", model.height); + write(fs, "camera_matrix", toMat(K)); - DMatrixRMaj D = new DMatrixRMaj(2+3,1); - if( model.radial != null ) { - if( model.radial.length > 0 ) - D.set(0, 0, model.radial[0]); - if( model.radial.length > 1 ) - D.set(1, 0, model.radial[1]); - if( model.radial.length > 2 ) - D.set(4, 0, model.radial[2]); - } - D.set(2,0,model.t1); - D.set(3,0,model.t2); - - write(fs,"distortion_coefficients", toMat(D)); - - try { fs.close(); } catch (Exception ignore) {} + DMatrixRMaj D = new DMatrixRMaj(2 + 3, 1); + if (model.radial != null) { + if (model.radial.length > 0) D.set(0, 0, model.radial[0]); + if (model.radial.length > 1) D.set(1, 0, model.radial[1]); + if (model.radial.length > 2) D.set(4, 0, model.radial[2]); + } + + D.set(2, 0, model.t1); + D.set(3, 0, model.t2); + write(fs, "distortion_coefficients", toMat(D)); + } } public static Mat toMat(DMatrixRMaj in ) { @@ -120,6 +119,8 @@ public static Mat toMat(DMatrixRMaj in ) { return out; } + //todo: JH investigate why these aren't working... + // can't find library issues // public static List listWebcams() { // List output = new ArrayList<>(); diff --git a/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestConvertIplImage.java b/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestConvertIplImage.java index 6048414e2d..c5ca7c654b 100644 --- a/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestConvertIplImage.java +++ b/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestConvertIplImage.java @@ -19,11 +19,12 @@ package boofcv.javacv; import boofcv.struct.image.*; +import org.bytedeco.opencv.opencv_core.IplImage; import org.junit.jupiter.api.Test; import java.nio.*; -import static org.bytedeco.javacpp.opencv_core.*; +import static org.bytedeco.opencv.global.opencv_core.*; import static org.junit.jupiter.api.Assertions.assertEquals; public class TestConvertIplImage { From 12571b4d4b0336a45a8b99b1662499ac1d97fc0f Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 09:27:22 +0000 Subject: [PATCH 10/23] Remove commented out webcam enumeration code, add impl note saying so. videoLibInput only supports Windows :( --- .../main/java/boofcv/javacv/UtilOpenCV.java | 48 +++++-------------- .../java/boofcv/javacv/TestUtilOpenCV.java | 1 + 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java b/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java index db42eced58..d023178f28 100644 --- a/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java +++ b/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java @@ -36,6 +36,8 @@ * Various utility functions for working with OpenCV * * @author Peter Abeles + * @implNote Webcam support can't currently be implemented in a platform-agnostic way as videoLibInput only supports + * Windows. */ public class UtilOpenCV { /** @@ -44,7 +46,7 @@ public class UtilOpenCV { * @param fileName path to file * @return CameraPinholeRadial */ - public static CameraPinholeBrown loadPinholeRadial(String fileName ) { + public static CameraPinholeBrown loadPinholeRadial( String fileName ) { IntPointer width = new IntPointer(1); IntPointer height = new IntPointer(1); @@ -63,11 +65,11 @@ public static CameraPinholeBrown loadPinholeRadial(String fileName ) { boof.height = height.get(); DoubleRawIndexer indexerK = K.createIndexer(); - boof.fx = indexerK.get(0,0); - boof.skew = indexerK.get(0,1); - boof.fy = indexerK.get(1,1); - boof.cx = indexerK.get(0,2); - boof.cy = indexerK.get(1,2); + boof.fx = indexerK.get(0, 0); + boof.skew = indexerK.get(0, 1); + boof.fy = indexerK.get(1, 1); + boof.cx = indexerK.get(0, 2); + boof.cy = indexerK.get(1, 2); DoubleRawIndexer indexerD = distortion.createIndexer(); @@ -97,7 +99,7 @@ public static void save( CameraPinholeBrown model, String fileName ) { if (model.radial.length > 1) D.set(1, 0, model.radial[1]); if (model.radial.length > 2) D.set(4, 0, model.radial[2]); } - + D.set(2, 0, model.t1); D.set(3, 0, model.t2); @@ -105,43 +107,17 @@ public static void save( CameraPinholeBrown model, String fileName ) { } } - public static Mat toMat(DMatrixRMaj in ) { - Mat out = new Mat(in.numRows,in.numCols,CV_64F); + public static Mat toMat( DMatrixRMaj in ) { + Mat out = new Mat(in.numRows, in.numCols, CV_64F); DoubleRawIndexer indexer = out.createIndexer(); for (int i = 0; i < in.numRows; i++) { for (int j = 0; j < in.numCols; j++) { - indexer.put(i,j, in.get(i,j)); + indexer.put(i, j, in.get(i, j)); } } return out; } - - //todo: JH investigate why these aren't working... - - // can't find library issues -// public static List listWebcams() { -// List output = new ArrayList<>(); -// -// int N = videoInputLib.videoInput.listDevices(); -// for (int i = 0; i < N; i++) { -// String name = videoInputLib.videoInput.getDeviceName(i).getString(); -// output.add(name); -// } -// -// return output; -// } -// -// public static OpenCVFrameGrabber findWebcam( String name ) { -// int N = videoInputLib.videoInput.listDevices(); -// for (int i = 0; i < N; i++) { -// String device = videoInputLib.videoInput.getDeviceName(i).getString(); -// if( device.equals(name)) { -// return new OpenCVFrameGrabber(i); -// } -// } -// return null; -// } } diff --git a/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java b/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java index f6d80b8567..7a9df501b5 100644 --- a/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java +++ b/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java @@ -20,6 +20,7 @@ import boofcv.io.calibration.CalibrationIO; import boofcv.struct.calib.CameraPinholeBrown; +import org.bytedeco.javacv.FrameGrabber; import org.junit.jupiter.api.Test; import java.io.File; From 9d82054a6f2035ae355af2519e000f1cf056a760 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 09:42:19 +0000 Subject: [PATCH 11/23] Mark UtilIU.load as deprecated. This method is DANGEROUS - it uses Java inbuilt Serialization and allows for arbitrary code execution. Need to ask pabeles what he wants to replace it with... --- main/boofcv-io/src/main/java/boofcv/io/UtilIO.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java b/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java index 0b685e0455..81b619cf3e 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java +++ b/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java @@ -577,6 +577,14 @@ public static void save( Object o, String fileName ) { } } + /** + * DO NOT USE THIS METHOD! + * This method uses Java serialisation. If you use it to load untrusted files, a bad actor could craft a file to + * execute arbitrary code within your JVM. + * + * @see Serializable + */ + @Deprecated(forRemoval = true) public static T load( String fileName ) { @Nullable URL url = UtilIO.ensureURL(fileName); if (url == null) From ae77d6aa0f12d9d7a5bd580c711d6dc104b401e0 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 09:52:36 +0000 Subject: [PATCH 12/23] Readd spotless config --- build.gradle | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index 826521ecaa..9af4b292fa 100644 --- a/build.gradle +++ b/build.gradle @@ -270,30 +270,30 @@ subprojects { project.tasks.publish.enabled = false } -// spotless { -//// ratchetFrom 'origin/SNAPSHOT' -// -// format 'misc', { -// // define the files to apply `misc` to -// target '*.gradle', '*.md', '.gitignore' -// -// // define the steps to apply to those files -// trimTrailingWhitespace() -// leadingSpacesToTabs() -// endWithNewline() -// } -// java { -// // There is currently no good way to exclude files that contain "@Generated" or are not versions. -// // This should catch most of them with a few false positives. -// target('**/java/boofcv/**/*.java') -// targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*', '**/java/boofcv/All.java') -// -// toggleOffOn('formatter:off', 'formatter:on') -// removeUnusedImports() -// endWithNewline() -//// licenseHeaderFile("${project.rootDir}/misc/copyright.txt") -// } -// } + spotless { + ratchetFrom 'origin/SNAPSHOT' + + format 'misc', { + // define the files to apply `misc` to + target '*.gradle', '*.md', '.gitignore' + + // define the steps to apply to those files + trimTrailingWhitespace() + leadingSpacesToTabs() + endWithNewline() + } + java { + // There is currently no good way to exclude files that contain "@Generated" or are not versions. + // This should catch most of them with a few false positives. + target('**/java/boofcv/**/*.java') + targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*', '**/java/boofcv/All.java') + + toggleOffOn('formatter:off', 'formatter:on') + removeUnusedImports() + endWithNewline() + licenseHeaderFile("${project.rootDir}/misc/copyright.txt") + } + } if (!project.tasks.publish.enabled) return From fad2a69118d3b6c3b1bb77c5858c9178a865aaa3 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 16:50:11 +0000 Subject: [PATCH 13/23] Change to run CI --- integration/boofcv-kotlin/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/boofcv-kotlin/build.gradle b/integration/boofcv-kotlin/build.gradle index 95146e1bb0..b93f8a7675 100644 --- a/integration/boofcv-kotlin/build.gradle +++ b/integration/boofcv-kotlin/build.gradle @@ -2,7 +2,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version '1.9.25' } -configurations.all { +configurations.configureEach { resolutionStrategy { force "org.jetbrains:annotations:$project.jetnull_version" force "org.jetbrains.kotlin:kotlin-stdlib:1.9.25" From a470c8b3a2cfb90922d240301afae18824b429a3 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 16:54:45 +0000 Subject: [PATCH 14/23] Temp change github actions so we can run on our feature branch --- .github/workflows/gradle.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5899d5c01d..7b30448d26 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,9 +5,7 @@ name: Java CI with Gradle on: push: - branches: [ SNAPSHOT ] pull_request: - branches: [ SNAPSHOT ] jobs: build: From 85e064b8aebe4d823e7f338fe0c5296123a3a1dd Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 17:03:12 +0000 Subject: [PATCH 15/23] WIP --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9af4b292fa..31ad464755 100644 --- a/build.gradle +++ b/build.gradle @@ -271,7 +271,7 @@ subprojects { } spotless { - ratchetFrom 'origin/SNAPSHOT' +// ratchetFrom 'origin/SNAPSHOT' format 'misc', { // define the files to apply `misc` to From 764acd7913e62ea5f3e7d3c36c15bf459d65a45e Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 17:06:48 +0000 Subject: [PATCH 16/23] Temp disable spotless rule --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 31ad464755..765563d965 100644 --- a/build.gradle +++ b/build.gradle @@ -291,7 +291,7 @@ subprojects { toggleOffOn('formatter:off', 'formatter:on') removeUnusedImports() endWithNewline() - licenseHeaderFile("${project.rootDir}/misc/copyright.txt") +// licenseHeaderFile("${project.rootDir}/misc/copyright.txt") } } From a9ee20eb5fd84ebc4d62d5c4c48a4cfcfa409e5b Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Tue, 18 Mar 2025 17:07:06 +0000 Subject: [PATCH 17/23] Spotless lints --- integration/boofcv-ffmpeg/build.gradle | 12 +++++----- integration/boofcv-javacv/build.gradle | 12 +++++----- .../java/boofcv/javacv/TestUtilOpenCV.java | 1 - integration/boofcv-swing/build.gradle | 22 +++++++++---------- main/boofcv-ip/build.gradle | 13 ++++++----- main/boofcv-types/build.gradle | 2 +- .../src/test/java/boofcv/TestBoofVersion.java | 2 +- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/integration/boofcv-ffmpeg/build.gradle b/integration/boofcv-ffmpeg/build.gradle index e7fcfa61f5..326ad608ae 100644 --- a/integration/boofcv-ffmpeg/build.gradle +++ b/integration/boofcv-ffmpeg/build.gradle @@ -1,11 +1,11 @@ plugins { - id 'org.bytedeco.gradle-javacpp-platform' version '1.5.10' + id 'org.bytedeco.gradle-javacpp-platform' version '1.5.10' } dependencies { - api project(':main:boofcv-ip') - api project(':main:boofcv-io') - - api (group: 'org.bytedeco', name: 'ffmpeg-platform', version: "7.1-$project.javacv_version") - api (group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version) + api project(':main:boofcv-ip') + api project(':main:boofcv-io') + + api (group: 'org.bytedeco', name: 'ffmpeg-platform', version: "7.1-$project.javacv_version") + api (group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version) } diff --git a/integration/boofcv-javacv/build.gradle b/integration/boofcv-javacv/build.gradle index ffe40b1361..b562eb31fd 100644 --- a/integration/boofcv-javacv/build.gradle +++ b/integration/boofcv-javacv/build.gradle @@ -1,15 +1,15 @@ plugins { - id 'org.bytedeco.gradle-javacpp-platform' version '1.5.10' + id 'org.bytedeco.gradle-javacpp-platform' version '1.5.10' } dependencies { - api project(':main:boofcv-ip') - api project(':main:boofcv-io') - api project(':main:boofcv-geo') + api project(':main:boofcv-ip') + api project(':main:boofcv-io') + api project(':main:boofcv-geo') - api project(':integration:boofcv-swing') + api project(':integration:boofcv-swing') - api (group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version) + api (group: 'org.bytedeco', name: 'javacv-platform', version: project.javacv_version) } sourceSets { test { java { srcDir 'src/main/examples' } } } diff --git a/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java b/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java index 7a9df501b5..f6d80b8567 100644 --- a/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java +++ b/integration/boofcv-javacv/src/test/java/boofcv/javacv/TestUtilOpenCV.java @@ -20,7 +20,6 @@ import boofcv.io.calibration.CalibrationIO; import boofcv.struct.calib.CameraPinholeBrown; -import org.bytedeco.javacv.FrameGrabber; import org.junit.jupiter.api.Test; import java.io.File; diff --git a/integration/boofcv-swing/build.gradle b/integration/boofcv-swing/build.gradle index 604ebc37c2..4098052516 100644 --- a/integration/boofcv-swing/build.gradle +++ b/integration/boofcv-swing/build.gradle @@ -1,14 +1,14 @@ dependencies { - api project(':main:boofcv-types') - api project(':main:boofcv-io') - api project(':main:boofcv-feature') - api project(':main:boofcv-geo') - api project(':main:boofcv-sfm') - api project(':main:boofcv-recognition') - api 'com.fifesoft:rsyntaxtextarea:2.6.1' - api 'io.github.vincenzopalazzo:material-ui-swing:1.1.1_pre-release_6.1' - api 'com.github.weisj:darklaf-core:1.4.3.1' + api project(':main:boofcv-types') + api project(':main:boofcv-io') + api project(':main:boofcv-feature') + api project(':main:boofcv-geo') + api project(':main:boofcv-sfm') + api project(':main:boofcv-recognition') + api 'com.fifesoft:rsyntaxtextarea:2.6.1' + api 'io.github.vincenzopalazzo:material-ui-swing:1.1.1_pre-release_6.1' + api 'com.github.weisj:darklaf-core:1.4.3.1' - implementation("org.yaml:snakeyaml:$snakeyaml_version") - implementation("commons-io:commons-io:$commons_io_version") + implementation("org.yaml:snakeyaml:$snakeyaml_version") + implementation("commons-io:commons-io:$commons_io_version") } diff --git a/main/boofcv-ip/build.gradle b/main/boofcv-ip/build.gradle index e147932b51..5ab0824758 100644 --- a/main/boofcv-ip/build.gradle +++ b/main/boofcv-ip/build.gradle @@ -1,9 +1,10 @@ dependencies { - api project(':main:boofcv-types') - testImplementation project(':main:boofcv-geo') + api project(':main:boofcv-types') + testImplementation project(':main:boofcv-geo') } -task(autogenerateSource, dependsOn: 'generateClasses', type: JavaExec) { - setMainClass('boofcv.GenerateImageProcessing') - classpath = sourceSets.generate.runtimeClasspath -} \ No newline at end of file +tasks.register('autogenerateSource', JavaExec) { + dependsOn 'generateClasses' + setMainClass('boofcv.GenerateImageProcessing') + classpath = sourceSets.generate.runtimeClasspath +} diff --git a/main/boofcv-types/build.gradle b/main/boofcv-types/build.gradle index 3041d856b9..e03031105f 100644 --- a/main/boofcv-types/build.gradle +++ b/main/boofcv-types/build.gradle @@ -1,4 +1,4 @@ dependencies { project.configurations.compileOnly.extendsFrom(project.sourceSets.noauto.compileClasspath) implementation project.sourceSets.noauto.runtimeClasspath -} \ No newline at end of file +} diff --git a/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java b/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java index b9f9c098a3..fc7cf4a4d8 100644 --- a/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java +++ b/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java @@ -30,4 +30,4 @@ class TestBoofVersion extends BoofStandardJUnit { @Test void basic() { assertFalse(BoofVersion.VERSION.isEmpty()); } -} \ No newline at end of file +} From 492efedbfe8925c4614a0ea87374adb46df683f3 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Wed, 19 Mar 2025 21:07:58 +0000 Subject: [PATCH 18/23] Revert "Temp disable spotless rule" This reverts commit 764acd7913e62ea5f3e7d3c36c15bf459d65a45e. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 765563d965..31ad464755 100644 --- a/build.gradle +++ b/build.gradle @@ -291,7 +291,7 @@ subprojects { toggleOffOn('formatter:off', 'formatter:on') removeUnusedImports() endWithNewline() -// licenseHeaderFile("${project.rootDir}/misc/copyright.txt") + licenseHeaderFile("${project.rootDir}/misc/copyright.txt") } } From fd973416049b3b8f40c51156be87259a1df07e1b Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Wed, 19 Mar 2025 21:07:59 +0000 Subject: [PATCH 19/23] Revert "WIP" This reverts commit 85e064b8aebe4d823e7f338fe0c5296123a3a1dd. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 31ad464755..9af4b292fa 100644 --- a/build.gradle +++ b/build.gradle @@ -271,7 +271,7 @@ subprojects { } spotless { -// ratchetFrom 'origin/SNAPSHOT' + ratchetFrom 'origin/SNAPSHOT' format 'misc', { // define the files to apply `misc` to From 4a840f8a94d2160cf5bdda796ce1e19231ebb709 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Wed, 19 Mar 2025 21:08:13 +0000 Subject: [PATCH 20/23] Revert "Change to run CI" This reverts commit fad2a69118d3b6c3b1bb77c5858c9178a865aaa3. --- integration/boofcv-kotlin/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/boofcv-kotlin/build.gradle b/integration/boofcv-kotlin/build.gradle index b93f8a7675..95146e1bb0 100644 --- a/integration/boofcv-kotlin/build.gradle +++ b/integration/boofcv-kotlin/build.gradle @@ -2,7 +2,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version '1.9.25' } -configurations.configureEach { +configurations.all { resolutionStrategy { force "org.jetbrains:annotations:$project.jetnull_version" force "org.jetbrains.kotlin:kotlin-stdlib:1.9.25" From 8976f02f6753c657fbbdc171df843e0bc09083b2 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Wed, 19 Mar 2025 21:17:49 +0000 Subject: [PATCH 21/23] Remove non-OpenCV-related changes. We can cherry-pick these into their own branch --- .github/workflows/gradle.yml | 2 + applications/readme.md | 6 +- build.gradle | 69 +++++++++---------- .../examples/fragment/build.gradle | 4 +- .../main/java/boofcv/javacv/UtilOpenCV.java | 26 ++++++- integration/boofcv-kotlin/build.gradle | 6 +- integration/boofcv-swing/build.gradle | 22 +++--- .../src/main/java/boofcv/io/UtilIO.java | 8 --- main/boofcv-ip/build.gradle | 13 ++-- main/boofcv-types/build.gradle | 2 +- .../src/test/java/boofcv/TestBoofVersion.java | 2 +- 11 files changed, 85 insertions(+), 75 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 7b30448d26..5899d5c01d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,7 +5,9 @@ name: Java CI with Gradle on: push: + branches: [ SNAPSHOT ] pull_request: + branches: [ SNAPSHOT ] jobs: build: diff --git a/applications/readme.md b/applications/readme.md index cd132cee7c..4585bbcdee 100644 --- a/applications/readme.md +++ b/applications/readme.md @@ -12,9 +12,9 @@ Then it will print something like: ```bash Trying to run a command-line application? Here are your options! -CreateFiducialSquareImage -CreateFiducialSquareBinary -CreateFiducialRandomDot + CreateFiducialSquareImage + CreateFiducialSquareBinary + CreateFiducialRandomDot ... ``` Next you will need to select which tool / application from the list that you want to run. Then more help will be diff --git a/build.gradle b/build.gradle index 9af4b292fa..c6ef90441b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,10 @@ import net.ltgt.gradle.errorprone.CheckSeverity -import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestExceptionFormat plugins { - id 'java' id "com.peterabeles.gversion" version "1.10.3" apply false id "net.ltgt.errorprone" version "3.0.1" apply false - id "com.diffplug.spotless" version "7.0.2" apply false + id "com.diffplug.spotless" version "6.9.1" apply false } ext.libpath = file('./').absolutePath @@ -56,20 +55,19 @@ subprojects { toolchain { languageVersion = JavaLanguageVersion.of(17) } } + // Prevents tons of errors if someone is using ASCII + tasks.withType(JavaCompile).configureEach { options.encoding = "UTF-8" } + // Creates Java 11 byte code but Java 17 syntax tasks.withType(JavaCompile).configureEach { - // Prevents tons of errors if someone is using ASCII - options.encoding = "UTF-8" - - // don't think these work... sourceCompatibility = 17 options.release = 11 - - // Enable incremental compile. Should make single file changes faster - options.incremental = true } + // Enable incremental compile. Should make single file changes faster + tasks.withType(JavaCompile) { options.incremental = true } + // For some reason examplesJar has a duplicate of ffmpeg pom - tasks.withType(Jar).configureEach { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } + tasks.withType(Jar) { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } // To make ErrorProne and Kotlin plugins happy configurations.configureEach { @@ -81,11 +79,12 @@ subprojects { force 'org.checkerframework:checker-qual:2.10.0' force "org.bytedeco:javacpp:$project.javacv_version" force "org.bytedeco:javacpp-platform:$project.javacv_version" - // Fail on jar conflict - failOnVersionConflict() } } + // Fail on jar conflict + configurations.configureEach { resolutionStrategy { failOnVersionConflict() } } + repositories { mavenCentral() mavenLocal() @@ -99,10 +98,10 @@ subprojects { reports.html.setRequired(false) // Make the error logging verbose to make debugging on CI easier testLogging.showStandardStreams = true - testLogging.exceptionFormat = TestExceptionFormat.FULL - testLogging.showCauses = true - testLogging.showExceptions = true - testLogging.showStackTraces = true + testLogging.exceptionFormat TestExceptionFormat.FULL + testLogging.showCauses true + testLogging.showExceptions true + testLogging.showStackTraces true } sourceSets { @@ -174,7 +173,6 @@ subprojects { annotationProcessor "org.projectlombok:lombok:${project.lombok_version}" // @Getter @Setter noautoAnnotationProcessor "org.projectlombok:lombok:${project.lombok_version}" // @Getter @Setter - errorprone "com.uber.nullaway:nullaway:${project.nullaway_version}" errorprone "com.google.errorprone:error_prone_core:$project.errorprone_version" // even if it's not used you still need to include the dependency @@ -252,7 +250,7 @@ subprojects { options.addBooleanOption('html5', true) } - failOnError = false + failOnError false } // Force the release build to fail if it depends on a SNAPSHOT @@ -279,19 +277,20 @@ subprojects { // define the steps to apply to those files trimTrailingWhitespace() - leadingSpacesToTabs() + indentWithTabs() endWithNewline() } java { // There is currently no good way to exclude files that contain "@Generated" or are not versions. // This should catch most of them with a few false positives. target('**/java/boofcv/**/*.java') - targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*', '**/java/boofcv/All.java') + targetExclude('**/*_MT*.java', '**/*_F32.java', '**/generated/*') toggleOffOn('formatter:off', 'formatter:on') removeUnusedImports() endWithNewline() - licenseHeaderFile("${project.rootDir}/misc/copyright.txt") + + licenseHeaderFile "${project.rootDir}/misc/copyright.txt" } } @@ -339,8 +338,8 @@ subprojects { def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl credentials { - username = ossrhUsername - password = ossrhPassword + username ossrhUsername + password ossrhPassword } } } @@ -408,8 +407,7 @@ checkProjectExistsAddToList(':integration:boofcv-javafx', javadocProjects) checkProjectExistsAddToList(':integration:boofcv-android', javadocProjects) // Creates a directory with all the compiled BoofCV jars and the dependencies for main -tasks.register('createLibraryDirectory') { - dependsOn libraryProjects.collect { [it + ':jar', it + ':sourcesJar'] }.flatten() +task createLibraryDirectory(dependsOn: libraryProjects.collect { [it + ':jar', it + ':sourcesJar'] }.flatten()) { doLast { // Collects BoofCV jars and external jars ext.listJars = files(libraryProjects.collect { project(it).configurations.runtimeClasspath }) @@ -429,8 +427,7 @@ tasks.register('createLibraryDirectory') { } // Creates a single jar which contains all the subprojects in main and integration -tasks.register('oneJarBin', Jar) { - dependsOn javadocProjects.collect { it + ":compileJava" } +task oneJarBin(type: Jar, dependsOn: javadocProjects.collect { it + ":compileJava" }) { archiveFile.set(file("boofcv-v${project.version}.jar")) from files(javadocProjects.collect { project(it).sourceSets.main.output }) { @@ -443,9 +440,7 @@ tasks.register('oneJarBin', Jar) { def javadocProjectsNoAndroid = javadocProjects.findAll({ !it.contains("android") }) // Generates a global javadoc from all the modules -tasks.register('alljavadoc', Javadoc) { - dependsOn javadocProjectsNoAndroid.collect { it + ":compileJava" } - +task alljavadoc(type: Javadoc, group: "Documentation", dependsOn: javadocProjectsNoAndroid.collect { it + ":compileJava" }) { // only include source code in src directory to avoid including 3rd party code which some projects do as a hack source = javadocProjectsNoAndroid.collect { project(it).sourceSets.main.allJava } + javadocProjectsNoAndroid.collect { project(it).sourceSets.noauto.allJava } @@ -472,7 +467,7 @@ tasks.register('alljavadoc', Javadoc) { configure(options) { failOnError = false title = "BoofCV ($project.version)" - links = ['https://docs.oracle.com/en/java/javase/17/docs/api', + links = ['https://docs.oracle.com/en/java/javase/11/docs/api', 'https://ejml.org/javadoc/', 'https://georegression.org/javadoc/', 'https://ddogleg.org/javadoc/'] @@ -487,19 +482,19 @@ tasks.register('alljavadoc', Javadoc) { } } -tasks.register('alljavadocWeb') { +task alljavadocWeb() { doFirst { alljavadoc.options.bottom = file('misc/bottom.txt').text alljavadoc.destinationDir = file("docs/api-web") } - finalizedBy(alljavadoc) } +alljavadocWeb.finalizedBy(alljavadoc) -tasks.register('testReport', TestReport) { - getDestinationDirectory().set(file("$layout.buildDirectory/reports/allTests")) +task testReport(type: TestReport) { + getDestinationDirectory().set(file("$buildDir/reports/allTests")) // Android project has a different testing system so skip it - getTestResults().from(subprojects.findAll { !it.name.contains("android") }.test) + getTestResults().from(subprojects.findAll {!it.name.contains("android")}.test) } // Sanity check to make sure that autogenerate has been called. This is the source of a lot of confusion from users diff --git a/integration/boofcv-android/examples/fragment/build.gradle b/integration/boofcv-android/examples/fragment/build.gradle index 151633cb6a..e00aaa3e34 100644 --- a/integration/boofcv-android/examples/fragment/build.gradle +++ b/integration/boofcv-android/examples/fragment/build.gradle @@ -14,6 +14,6 @@ plugins { id 'org.jetbrains.kotlin.android' version '1.7.20' apply false } -tasks.register('clean', Delete) { - delete rootProject.layout.buildDirectory +task clean(type: Delete) { + delete rootProject.buildDir } \ No newline at end of file diff --git a/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java b/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java index d023178f28..57aecada03 100644 --- a/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java +++ b/integration/boofcv-javacv/src/main/java/boofcv/javacv/UtilOpenCV.java @@ -36,8 +36,6 @@ * Various utility functions for working with OpenCV * * @author Peter Abeles - * @implNote Webcam support can't currently be implemented in a platform-agnostic way as videoLibInput only supports - * Windows. */ public class UtilOpenCV { /** @@ -120,4 +118,28 @@ public static Mat toMat( DMatrixRMaj in ) { return out; } + + // can't find library issues +// public static List listWebcams() { +// List output = new ArrayList<>(); +// +// int N = videoInputLib.videoInput.listDevices(); +// for (int i = 0; i < N; i++) { +// String name = videoInputLib.videoInput.getDeviceName(i).getString(); +// output.add(name); +// } +// +// return output; +// } +// +// public static OpenCVFrameGrabber findWebcam( String name ) { +// int N = videoInputLib.videoInput.listDevices(); +// for (int i = 0; i < N; i++) { +// String device = videoInputLib.videoInput.getDeviceName(i).getString(); +// if( device.equals(name)) { +// return new OpenCVFrameGrabber(i); +// } +// } +// return null; +// } } diff --git a/integration/boofcv-kotlin/build.gradle b/integration/boofcv-kotlin/build.gradle index 95146e1bb0..aa8744e62e 100644 --- a/integration/boofcv-kotlin/build.gradle +++ b/integration/boofcv-kotlin/build.gradle @@ -1,12 +1,12 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '1.9.25' + id 'org.jetbrains.kotlin.jvm' version '1.7.22' } configurations.all { resolutionStrategy { force "org.jetbrains:annotations:$project.jetnull_version" - force "org.jetbrains.kotlin:kotlin-stdlib:1.9.25" - force "org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25" + force "org.jetbrains.kotlin:kotlin-stdlib:1.7.22" + force "org.jetbrains.kotlin:kotlin-stdlib-common:1.7.22" } } diff --git a/integration/boofcv-swing/build.gradle b/integration/boofcv-swing/build.gradle index 4098052516..604ebc37c2 100644 --- a/integration/boofcv-swing/build.gradle +++ b/integration/boofcv-swing/build.gradle @@ -1,14 +1,14 @@ dependencies { - api project(':main:boofcv-types') - api project(':main:boofcv-io') - api project(':main:boofcv-feature') - api project(':main:boofcv-geo') - api project(':main:boofcv-sfm') - api project(':main:boofcv-recognition') - api 'com.fifesoft:rsyntaxtextarea:2.6.1' - api 'io.github.vincenzopalazzo:material-ui-swing:1.1.1_pre-release_6.1' - api 'com.github.weisj:darklaf-core:1.4.3.1' + api project(':main:boofcv-types') + api project(':main:boofcv-io') + api project(':main:boofcv-feature') + api project(':main:boofcv-geo') + api project(':main:boofcv-sfm') + api project(':main:boofcv-recognition') + api 'com.fifesoft:rsyntaxtextarea:2.6.1' + api 'io.github.vincenzopalazzo:material-ui-swing:1.1.1_pre-release_6.1' + api 'com.github.weisj:darklaf-core:1.4.3.1' - implementation("org.yaml:snakeyaml:$snakeyaml_version") - implementation("commons-io:commons-io:$commons_io_version") + implementation("org.yaml:snakeyaml:$snakeyaml_version") + implementation("commons-io:commons-io:$commons_io_version") } diff --git a/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java b/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java index 81b619cf3e..0b685e0455 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java +++ b/main/boofcv-io/src/main/java/boofcv/io/UtilIO.java @@ -577,14 +577,6 @@ public static void save( Object o, String fileName ) { } } - /** - * DO NOT USE THIS METHOD! - * This method uses Java serialisation. If you use it to load untrusted files, a bad actor could craft a file to - * execute arbitrary code within your JVM. - * - * @see Serializable - */ - @Deprecated(forRemoval = true) public static T load( String fileName ) { @Nullable URL url = UtilIO.ensureURL(fileName); if (url == null) diff --git a/main/boofcv-ip/build.gradle b/main/boofcv-ip/build.gradle index 5ab0824758..e147932b51 100644 --- a/main/boofcv-ip/build.gradle +++ b/main/boofcv-ip/build.gradle @@ -1,10 +1,9 @@ dependencies { - api project(':main:boofcv-types') - testImplementation project(':main:boofcv-geo') + api project(':main:boofcv-types') + testImplementation project(':main:boofcv-geo') } -tasks.register('autogenerateSource', JavaExec) { - dependsOn 'generateClasses' - setMainClass('boofcv.GenerateImageProcessing') - classpath = sourceSets.generate.runtimeClasspath -} +task(autogenerateSource, dependsOn: 'generateClasses', type: JavaExec) { + setMainClass('boofcv.GenerateImageProcessing') + classpath = sourceSets.generate.runtimeClasspath +} \ No newline at end of file diff --git a/main/boofcv-types/build.gradle b/main/boofcv-types/build.gradle index e03031105f..3041d856b9 100644 --- a/main/boofcv-types/build.gradle +++ b/main/boofcv-types/build.gradle @@ -1,4 +1,4 @@ dependencies { project.configurations.compileOnly.extendsFrom(project.sourceSets.noauto.compileClasspath) implementation project.sourceSets.noauto.runtimeClasspath -} +} \ No newline at end of file diff --git a/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java b/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java index fc7cf4a4d8..b9f9c098a3 100644 --- a/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java +++ b/main/boofcv-types/src/test/java/boofcv/TestBoofVersion.java @@ -30,4 +30,4 @@ class TestBoofVersion extends BoofStandardJUnit { @Test void basic() { assertFalse(BoofVersion.VERSION.isEmpty()); } -} +} \ No newline at end of file From 2789b9c45e93a57114801af985f0f97a1352d797 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Wed, 19 Mar 2025 22:13:56 +0000 Subject: [PATCH 22/23] TestUtilImageIO#saveJpeg passing, need to work out why this failed before --- .../src/test/java/boofcv/io/image/TestUtilImageIO.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java b/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java index 7e7b252c36..7c5d2b3ea7 100644 --- a/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java +++ b/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java @@ -111,14 +111,17 @@ public class TestUtilImageIO extends BoofStandardJUnit { File temp = File.createTempFile("temp", ".jpg"); UtilImageIO.saveJpeg(buff, temp.getPath(), 0.95); double sizeA = temp.length(); - UtilImageIO.saveJpeg(buff, temp.getPath(), 0.2); - double sizeB = temp.length(); + + File temp2 = File.createTempFile("temp2", ".jpg"); + UtilImageIO.saveJpeg(buff, temp2.getPath(), 0.2); + double sizeB = temp2.length(); // Make sure quality did something assertTrue(sizeB < sizeA); // clean up - temp.delete(); + assertTrue(temp.delete()); + assertTrue(temp2.delete()); } /** From 85b748238709ddbbcb7bc530df75674d8cf2d3f3 Mon Sep 17 00:00:00 2001 From: "Joshua J. A. Harwood" Date: Wed, 19 Mar 2025 22:23:48 +0000 Subject: [PATCH 23/23] Remove delete assertions as OS file locks present... --- .../src/test/java/boofcv/io/image/TestUtilImageIO.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java b/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java index 7c5d2b3ea7..621fd82dd7 100644 --- a/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java +++ b/main/boofcv-io/src/test/java/boofcv/io/image/TestUtilImageIO.java @@ -102,7 +102,9 @@ public class TestUtilImageIO extends BoofStandardJUnit { temp.delete();// no assertTrue() here because in windows it will fail } - @Test void saveJpeg() throws IOException { + @SuppressWarnings("ResultOfMethodCallIgnored") + @Test + void saveJpeg() throws IOException { var orig = new GrayU8(width, height); GImageMiscOps.fillUniform(orig, rand, 0, 256); var buff = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); @@ -120,8 +122,8 @@ public class TestUtilImageIO extends BoofStandardJUnit { assertTrue(sizeB < sizeA); // clean up - assertTrue(temp.delete()); - assertTrue(temp2.delete()); + temp.delete(); + temp2.delete(); } /**