From b20d0bb3e25ab189ed65dd733296e33d4877df69 Mon Sep 17 00:00:00 2001 From: Michael Zucchi Date: Mon, 22 Apr 2019 11:57:15 +0930 Subject: [PATCH] Initial new modular pre-4.0 commit. --- .gitignore | 5 + Makefile | 19 + README | 210 ++ TODO | 6 + build.xml | 65 + config.make | 40 + java.make | 308 +++ nbproject/build-impl.xml | 1758 +++++++++++++++++ nbproject/genfiles.properties | 8 + nbproject/project.properties | 106 + nbproject/project.xml | 26 + .../au/notzed/jjmpeg/awt/JavaPixelReader.java | 108 + .../au/notzed/jjmpeg/awt/JavaPixelWriter.java | 106 + .../classes/module-info.java | 25 + src/notzed.jjmpeg.awt/legal/LICENSE | 674 +++++++ .../notzed/jjmpeg/demo/audio/AudioPlay.java | 160 ++ .../notzed/jjmpeg/demo/video/VideoExport.java | 128 ++ .../notzed/jjmpeg/demo/video/VideoPlay.java | 236 +++ .../notzed/jjmpeg/demo/video/VideoScan.java | 68 + .../classes/module-info.java | 26 + .../au/notzed/jjmpeg/fx/FXPixelReader.java | 108 + .../au/notzed/jjmpeg/fx/FXPixelWriter.java | 126 ++ src/notzed.jjmpeg.fx/classes/module-info.java | 25 + src/notzed.jjmpeg.fx/legal/LICENSE | 674 +++++++ .../au/notzed/jjmpeg/AVChannelLayout.java | 54 + .../classes/au/notzed/jjmpeg/AVCodec.java | 78 + .../au/notzed/jjmpeg/AVCodecContext.java | 297 +++ .../classes/au/notzed/jjmpeg/AVCodecID.java | 37 + .../au/notzed/jjmpeg/AVCodecParameters.java | 96 + .../classes/au/notzed/jjmpeg/AVDevice.java | 67 + .../au/notzed/jjmpeg/AVDictionary.java | 64 + .../classes/au/notzed/jjmpeg/AVError.java | 27 + .../au/notzed/jjmpeg/AVFormatContext.java | 165 ++ .../classes/au/notzed/jjmpeg/AVFrame.java | 250 +++ .../classes/au/notzed/jjmpeg/AVIOContext.java | 109 + .../au/notzed/jjmpeg/AVIOException.java | 57 + .../au/notzed/jjmpeg/AVInputFormat.java | 43 + .../classes/au/notzed/jjmpeg/AVLogger.java | 78 + .../classes/au/notzed/jjmpeg/AVMediaType.java | 30 + .../classes/au/notzed/jjmpeg/AVObject.java | 70 + .../classes/au/notzed/jjmpeg/AVOptions.java | 127 ++ .../au/notzed/jjmpeg/AVOutputFormat.java | 69 + .../classes/au/notzed/jjmpeg/AVPacket.java | 120 ++ .../au/notzed/jjmpeg/AVPixelFormat.java | 34 + .../au/notzed/jjmpeg/AVPixelReader.java | 50 + .../au/notzed/jjmpeg/AVPixelWriter.java | 36 + .../classes/au/notzed/jjmpeg/AVRational.java | 139 ++ .../au/notzed/jjmpeg/AVSampleFormat.java | 36 + .../au/notzed/jjmpeg/AVSampleReader.java | 69 + .../au/notzed/jjmpeg/AVSampleWriter.java | 40 + .../classes/au/notzed/jjmpeg/AVSize.java | 37 + .../classes/au/notzed/jjmpeg/AVStream.java | 72 + .../classes/au/notzed/jjmpeg/AVUtil.java | 88 + .../classes/au/notzed/jjmpeg/SwrContext.java | 71 + .../classes/au/notzed/jjmpeg/SwsContext.java | 81 + .../classes/au/notzed/jjmpeg/SwsFilter.java | 41 + .../au/notzed/jjmpeg/io/JJMediaReader.java | 649 ++++++ .../au/notzed/jjmpeg/io/JJMediaWriter.java | 419 ++++ src/notzed.jjmpeg/classes/module-info.java | 27 + src/notzed.jjmpeg/gen/extract-defines.pl | 192 ++ src/notzed.jjmpeg/gen/extract-enum.pl | 114 ++ src/notzed.jjmpeg/gen/gen.make | 92 + src/notzed.jjmpeg/jni/extract-proto.pl | 168 ++ src/notzed.jjmpeg/jni/jj-avcodec.c | 166 ++ src/notzed.jjmpeg/jni/jj-avcodec.def | 14 + src/notzed.jjmpeg/jni/jj-avcodeccontext.c | 227 +++ src/notzed.jjmpeg/jni/jj-avcodeccontext.def | 13 + src/notzed.jjmpeg/jni/jj-avcodecparameters.c | 91 + .../jni/jj-avcodecparameters.def | 7 + src/notzed.jjmpeg/jni/jj-avdevice.c | 108 + src/notzed.jjmpeg/jni/jj-avdevice.def | 16 + src/notzed.jjmpeg/jni/jj-avformatcontext.c | 453 +++++ src/notzed.jjmpeg/jni/jj-avformatcontext.def | 45 + src/notzed.jjmpeg/jni/jj-avframe.c | 685 +++++++ src/notzed.jjmpeg/jni/jj-avframe.def | 38 + src/notzed.jjmpeg/jni/jj-aviocontext.c | 167 ++ src/notzed.jjmpeg/jni/jj-aviocontext.def | 19 + src/notzed.jjmpeg/jni/jj-avmisc.c | 415 ++++ src/notzed.jjmpeg/jni/jj-avmisc.def | 57 + src/notzed.jjmpeg/jni/jj-avoptions.c | 329 +++ src/notzed.jjmpeg/jni/jj-avoptions.def | 38 + src/notzed.jjmpeg/jni/jj-avpacket.c | 104 + src/notzed.jjmpeg/jni/jj-avpacket.def | 10 + src/notzed.jjmpeg/jni/jj-avstream.c | 51 + src/notzed.jjmpeg/jni/jj-avstream.def | 4 + src/notzed.jjmpeg/jni/jj-avutil.c | 71 + src/notzed.jjmpeg/jni/jj-avutil.def | 10 + src/notzed.jjmpeg/jni/jjmpeg-jni.c | 345 ++++ src/notzed.jjmpeg/jni/jjmpeg-jni.def | 58 + src/notzed.jjmpeg/jni/jjmpeg.h | 222 +++ src/notzed.jjmpeg/jni/jni.make | 64 + src/notzed.jjmpeg/legal/LICENSE | 674 +++++++ 92 files changed, 13809 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 build.xml create mode 100644 config.make create mode 100644 java.make create mode 100644 nbproject/build-impl.xml create mode 100644 nbproject/genfiles.properties create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml create mode 100644 src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelReader.java create mode 100644 src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelWriter.java create mode 100644 src/notzed.jjmpeg.awt/classes/module-info.java create mode 100644 src/notzed.jjmpeg.awt/legal/LICENSE create mode 100644 src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/audio/AudioPlay.java create mode 100644 src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoExport.java create mode 100644 src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoPlay.java create mode 100644 src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoScan.java create mode 100644 src/notzed.jjmpeg.demo/classes/module-info.java create mode 100644 src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelReader.java create mode 100644 src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelWriter.java create mode 100644 src/notzed.jjmpeg.fx/classes/module-info.java create mode 100644 src/notzed.jjmpeg.fx/legal/LICENSE create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVChannelLayout.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodec.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecContext.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecID.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecParameters.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDevice.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDictionary.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVError.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFormatContext.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFrame.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOContext.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOException.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVInputFormat.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVLogger.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVMediaType.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVObject.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOptions.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOutputFormat.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPacket.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelFormat.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelReader.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelWriter.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVRational.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleFormat.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleReader.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleWriter.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSize.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVStream.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVUtil.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwrContext.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsContext.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsFilter.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaReader.java create mode 100644 src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaWriter.java create mode 100644 src/notzed.jjmpeg/classes/module-info.java create mode 100644 src/notzed.jjmpeg/gen/extract-defines.pl create mode 100644 src/notzed.jjmpeg/gen/extract-enum.pl create mode 100644 src/notzed.jjmpeg/gen/gen.make create mode 100755 src/notzed.jjmpeg/jni/extract-proto.pl create mode 100644 src/notzed.jjmpeg/jni/jj-avcodec.c create mode 100644 src/notzed.jjmpeg/jni/jj-avcodec.def create mode 100644 src/notzed.jjmpeg/jni/jj-avcodeccontext.c create mode 100644 src/notzed.jjmpeg/jni/jj-avcodeccontext.def create mode 100644 src/notzed.jjmpeg/jni/jj-avcodecparameters.c create mode 100644 src/notzed.jjmpeg/jni/jj-avcodecparameters.def create mode 100644 src/notzed.jjmpeg/jni/jj-avdevice.c create mode 100644 src/notzed.jjmpeg/jni/jj-avdevice.def create mode 100644 src/notzed.jjmpeg/jni/jj-avformatcontext.c create mode 100644 src/notzed.jjmpeg/jni/jj-avformatcontext.def create mode 100644 src/notzed.jjmpeg/jni/jj-avframe.c create mode 100644 src/notzed.jjmpeg/jni/jj-avframe.def create mode 100644 src/notzed.jjmpeg/jni/jj-aviocontext.c create mode 100644 src/notzed.jjmpeg/jni/jj-aviocontext.def create mode 100644 src/notzed.jjmpeg/jni/jj-avmisc.c create mode 100644 src/notzed.jjmpeg/jni/jj-avmisc.def create mode 100644 src/notzed.jjmpeg/jni/jj-avoptions.c create mode 100644 src/notzed.jjmpeg/jni/jj-avoptions.def create mode 100644 src/notzed.jjmpeg/jni/jj-avpacket.c create mode 100644 src/notzed.jjmpeg/jni/jj-avpacket.def create mode 100644 src/notzed.jjmpeg/jni/jj-avstream.c create mode 100644 src/notzed.jjmpeg/jni/jj-avstream.def create mode 100644 src/notzed.jjmpeg/jni/jj-avutil.c create mode 100644 src/notzed.jjmpeg/jni/jj-avutil.def create mode 100644 src/notzed.jjmpeg/jni/jjmpeg-jni.c create mode 100644 src/notzed.jjmpeg/jni/jjmpeg-jni.def create mode 100644 src/notzed.jjmpeg/jni/jjmpeg.h create mode 100644 src/notzed.jjmpeg/jni/jni.make create mode 100644 src/notzed.jjmpeg/legal/LICENSE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..470b0ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/nbproject/private/ +/bin/ +/build/ +/dist/ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5c68871 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ + +dist_VERSION=3.99 +dist_NAME=jjmpeg +dist_EXTRA=README \ + build.xml \ + nbproject/build-impl.xml \ + nbproject/genfiles.properties \ + nbproject/project.properties \ + nbproject/project.xml + +include config.make + +java_MODULES=notzed.jjmpeg notzed.jjmpeg.fx notzed.jjmpeg.awt notzed.jjmpeg.demo + +notzed.jjmpeg.awt_JDEPMOD=notzed.jjmpeg +notzed.jjmpeg.fx_JDEPMOD=notzed.jjmpeg +notzed.jjmpeg.demo_JDEPMOD=notzed.jjmpeg notzed.jjmpeg.awt notzed.jjmpeg.fx + +include java.make diff --git a/README b/README new file mode 100644 index 0000000..5012b15 --- /dev/null +++ b/README @@ -0,0 +1,210 @@ + +Copyright (C) 2017-2019 Michael Zucchi + +See the section LICENSE for details. + +Introduction +------------ + +jjmpeg is a JNI based Java binding for the suite of libraries provided +by FFmpeg. + +The original version of jjmpeg was released sometime in the distant +past and has slowly got further and further behind the current FFmpeg +version. This is a completely new implementation intended as a +project to catch up with the current API. + +Compile +------- + +jdk (javac, jar) and native compiler tools (GNU make, cc, cpp, perl) +must be in path. In addition the program cproto must be installed. +JDK 11 is required (JDK 9+ may work?). + +The prequisitve project notzed.nativez must also have previously been +compiled and will be automatically used if it is present in the +directory above this one (../nativez/). + +See config.make for configuration parameters. + +Build everything for default target of linux-amd64: + +$ make + +Build everything for a specific target: + +$ make TARGET= + +All intermediate and final results are place in `bin/'. + +`bin//.jar' Cross platform modular .jar. +`bin///lib' Platform specific libraries. + +`bin///.jmod' Target specific .jmod. + +$ make bin + +Build everything but the jars and jmods. + +$ make dist + +Create a source archive. The source is found via $(find) so +this will include any droppings. + +NetBeans +-------- + +Before the source can be easily edited in NetBeans one must +first run the makefile to generate any auto-generated files. + +$ make gen + +This needs to be done each time `make clean' is executed or +files in `src/notzed.jjmpeg/gen' are edited. + +Development +----------- + +Some notes on internals. + +Java Native Interface +- - - - - - - - - - - + +All jj-*.c files are independent of one another, they may only +reference functions and variables from jjmpeg.c. + +The native interface is implemented using the notzed.nativez project. +This allows it to support full garbage collection of all relevant +objects. In some cases this currently requires references to be +manually kept to ensure they survive for the relevant C side objects. +All objects are unique (and keyed) based on their C pointer address. + +If used with care, objects can also be manually released safely. + +Unlike earlier versions of jjmpeg, version 3+ is implemented almost +entirely using hand-rolled Java and macro-assisted C source-code. +Also unlike earlier versions almost all code is written directly in C +with only skeleton Java wrappers of native method signatures. This +requires less work and produces smaller code than previous versions. + +To further reduce code-size most initialisation is implemented via +tables. + +Enumerations +- - - - - - + +A set of small(ish) perl scripts are used to extract various +enumeration and #define tables directly from the FFmpeg development +header files. They generate interfaces which are "implemented" to add +the defines to the desired structures. These reside in the scripts +directory. + +All enumerations were converted to final integer values for +consistency. Where the corresponding functions exist, static +valueOf() and toString() are available for obvious purposes. + +The perl scripts generate C code which is then compiled and executed +to create the Java interfaces. + +Exceptions +- - - - - + +Exceptions fall into two categories, AVIOException and AVException, +both checked exceptions. The former derives from IOException for +simplicity. + +They contain the error code (errno and AVERROR defines) and a +description. + +Where it makes sense other Java checked and unchecked exceptions such +as FileNotFoundException or OutOfMemoryError are also thrown. + +Linkage +- - - - + +All libraries are dynamically linked at runtime. Missing functions +result in an initialisation failure. + +Method signatures are automatically extracted from headers via the +script src/notzed.jjmpeg/jni/extract-proto.pl based on per-file .def +files. This also generates a table for initialising the pointers. + +Build System +- - - - - - + +See java.make. It's a lot simpler than it looks and is based on +meta-make programming via macros and pattern variables. + +Documentation +- - - - - - - + +Very lacking, but at least most of the enumerations now have the +comments present in the header files. + +Status +------ + +Enough core classes to read and write video frames is implemented as +well as a pair of high-level interfaces JJMediaReader and +JJMediaWriter. These have been simplified somewhat from the previous +implementation, but it takes less code to use them anyway. + +Most functions that should throw exceptions do although that which +they throw may be tweaked. + +Many accessors have been implemented, in some cases they have been +Javaised or NotZedised (e.g. setFlags(mask, value)) for a cleaner +interface. Because of the complexity of the libraries and objects it +is not always clear which members need to be accessed by user code so +some of the choices are arbitrary. Or I just gave up part way +through. They are easy to add. + +There are two sub-modules notzed.jjmpeg.fx and notzed.jjmpeg.awt which +supply the JavaFX and Swing/AWT specific functionality. + +See also TODO. + +Compatibility +------------- + +This version is compatible with at least version 4.0 of FFmpeg. + +No deprecated functions or fields have been made accessible from Java +or are used in the native code. + +Links +----- + +* nativez +* jjmpeg +* FFmpeg +* OpenJDK + +LICENSE +------- + +Most of the code is licensed under GNU General Public License version +3, see src//legal/LICENSE for the full details for each module. + + Copyright (C) 2017-2019 Michael Zucchi + + This program is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + +Portions are auto-generated from FFmpeg header files and are thus +copyright various authors but are also distributed under the GNU +General Public License Version 3 (or later) as per the copyright +header permissions. + +These portions are clearly marked in the bin/gen/*Bits.java files. diff --git a/TODO b/TODO new file mode 100644 index 0000000..7fea4a1 --- /dev/null +++ b/TODO @@ -0,0 +1,6 @@ + +* AVSampleWriter for audio +* Improve Exception design. +* Documentation +* JNI stuff like exception checks are very under-done. +* Simplify jni .def file for Java diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..5999d20 --- /dev/null +++ b/build.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + Builds, tests, and runs the project notzed.jjmpeg. + + + diff --git a/config.make b/config.make new file mode 100644 index 0000000..e77b258 --- /dev/null +++ b/config.make @@ -0,0 +1,40 @@ + +TARGET ?= linux-amd64 + +JAVA_HOME ?= /usr/local/jdk-11.0.2 +JAVAFX_HOME ?= /usr/local/javafx-sdk-11.0.2 +FFMPEG_HOME ?= /opt/ffmpeg/4.0 + +# See also JAVACFLAGS +NATIVEZ_HOME=../nativez/bin/notzed.nativez/$(TARGET) + +JAVACFLAGS += --module-path $(JAVAFX_HOME)/lib:../nativez/bin/notzed.nativez +JAVACFLAGS += -source 11 + +JAVAC ?= javac +JAR ?= jar +JMOD ?= jmod + +# Linux options +linux-amd64_CPPFLAGS = \ + -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux +linux-amd64_CFLAGS = -fPIC -Wall -Os +linux-amd64_CC = cc +linux-amd64_LD = ld + +linux-amd64_SO = .so +linux-amd64_LIB = lib +linux-amd64_JMOD_LIBS = --libs + +# Windows options +windows-amd64_CPPFLAGS= \ + -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \ + -DHAVE_ALIGNED_MALLOC \ + -DWIN32 +windows-amd64_CC = x86_64-w64-mingw32-gcc +windows-amd64_LD = x86_64-w64-mingw32-ld +windows-amd64_LDFLAGS = -Wl,--subsystem,windows + +windows-amd64_SO = .dll +windows-amd64_LIB = +windows-amd64_JMOD_LIBS = --cmds diff --git a/java.make b/java.make new file mode 100644 index 0000000..ac60f16 --- /dev/null +++ b/java.make @@ -0,0 +1,308 @@ +# +# Copyright (C) 2019 Michael Zucchi +# +# This is the copyright for java.make +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# General purpose modular java makefile that supports native library +# compilation directly. Non-recrusve implementation. +# +# Uses metamake programming with some file conventions to implement +# auto-make-like features. + +# Define modules +# -------------- +# java_MODULES list of java modules to compile. The sources must +# exist in src//classes. Resource files are +# stored in src//classes. Source-code +# generators must exist in src//gen. Native +# libraries must exist in src//jni. + +# Global variables + +# JAVA_HOME location of jdk. +# JAVAC java compiler to use. Default is 'javac' on the path. +# JAVACFLAGS javac flags applied to all invocations. +# JAR jar command. +# JARFLAGS jar flags +# JMOD jmod command. +# JMODFLAGS jmod flags. + +# Module specific variables + +# _JDEPMOD Lists modules which this one depends on. + +# _JAVACFLAGS Extra module-specific flags for each command. +# _JARFLAGS +# _JMODFLAGS + +# _JAVA Java sources. If not set it is found from src//classes/(*.java) +# _RESOURCES .jar resources. If not set it is found from src//classes/(not *.java) +# _JAVA_GENERATED Java generated sources. These must be relative to the package name. + +# Variables for use in fragments + +# gen.make and jni.make can additionally make use of these variables + +# _gendir Location for files used in Java generation process (per project). +# _genjavadir Location where _JAVA_GENERATED .java files will be created (per project). +# _jnidir Location for jni generated files (per target). +# _objdir Location for c objects (per target). +# _incdir Location for output includes, .jmod staging. +# _libdir Location for output libraries, .jmod staging. + +# Define libraries +# ---------------- + +# Each module can define one or more native libraries. + +# These are compiled after the java sources have been compiled as that +# process also generates any native binding headers. + +# _JNI_LIBRARIES list of libraries to build. +# library names match System.loadLibrary(). + +# Global variables + +# _LDFLAGS +# _LDLIBS +# _CPPFLAGS +# _CFLAGS +# SO shared library suffix +# LIB shared library prefix + +# Utility functions +# +# $(call library-path,,) will resolve to the library file name. + +# Per library variables. + +# _SOURCES .c, .cc, .C - source files for library. Paths are relative to src//jni. +# _HEADERS header files for jmod + +# _LDFLAGS link flags +# _LIBADD extra objects to add to link line +# _LDLIBS link libraries +# _CPPFLAGS c pre-processor flags. "-Isrc//jni -Ibin/include/" is implicit. +# _CCFLAGS c compiler flags + +# _DEPENDENCIES A list of other objects on which this library depends before linking. + +# .c files have dependencies automatically generated + +# Targets +# ------- + +# make gen only generate java sources +# make clean rm -rf bin +# make dist create dist tar in bin/ +# make | make jar make all jars and jmods +# make bin make everything but jars and mods + +# Outputs +# ------- + +# All intermediate and output files are written to bin/ + +# This layout is enforced by javac +# bin/include// .h files from javac -h +# bin/modules// .class files from javac + +# This layout is convenient for netbeans +# bin/gen//gen/ .c, exe files for generator free use +# bin/gen//classes/ .java files from generator _JAVA_GENERATED + +# bin/status/ marker files for makefile + +# bin//.jar .jar modular + +# Native code + +# bin///lib .so librareies for jmod _LIBRARIES = libname +# bin///obj .o, .d files for library _SOURCES +# bin///include .h files for jmod _HEADERS +# bin///.jmod .jmod module + +# ###################################################################### + +E:= +S:=$(E) $(E) + +# All modules with native code +java_JMODS=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/jni/jni.make),$(module))) +# Only modules with no native code +java_JARS=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/jni/jni.make),,$(module))) +# Modules with generated java source +java_JGEN=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/gen/gen.make),$(module))) + +# Define some useful variables before including fragments +define java_variables= +$(1)_gendir:=bin/gen/$(1)/gen +$(1)_genjavadir:=bin/gen/$(1)/classes +$(1)_jnidir:=bin/$(1)/$(TARGET)/jni +$(1)_objdir:=bin/$(1)/$(TARGET)/obj +$(1)_incdir:=bin/$(1)/$(TARGET)/include +$(1)_libdir:=bin/$(1)/$(TARGET)/lib +ifndef $(1)_JAVA +$(1)_JAVA := $$(shell find src/$(1)/classes -type f -name '*.java') +endif +ifndef $(1)_RESOURCES +$(1)_RESOURCES := $$(shell find src/$(1)/classes -type f \! -name '*.java') +endif +endef + +$(foreach module,$(java_MODULES),$(eval $(call java_variables,$(module)))) + +# ###################################################################### + +all: jar +bin: +gen: + +.PHONY: all clean jar bin gen +clean: + rm -rf bin + +include $(patsubst %,src/%/gen/gen.make,$(java_JGEN)) +include $(patsubst %,src/%/jni/jni.make,$(java_JMODS)) + +# ###################################################################### +# Java +# ###################################################################### + +define java_targets= +# Rules for module $(1) +$(1)_JAVA_generated = $$(addprefix $$($(1)_genjavadir)/,$$($(1)_JAVA_GENERATED)) + +bin/status/$(1).data: $$($(1)_RESOURCES) +bin/status/$(1).classes: $(patsubst %,bin/status/%.classes,$($(1)_JDEPMOD)) $$($(1)_JAVA) $$($(1)_JAVA_generated) +jar $(1): bin/$(1)/$(1).jar $(if $(wildcard src/$(1)/jni/jni.make),bin/$(1)/$(TARGET)/$(1).jmod) +bin: bin/status/$(1).classes bin/status/$(1).data +sources: bin/$(1)/$(1)-sources.zip +gen: $$($(1)_JAVA_generated) + +# Create modular jar +bin/$(1)/$(1).jar: bin/status/$(1).classes bin/status/$(1).data + @install -d $$(@D) + $(JAR) cf $$@ \ + $(JARFLAGS) $$($(1)_JARFLAGS) \ + -C bin/modules/$(1) . + +# Create a jmod +bin/$(1)/$(TARGET)/$(1).jmod: bin/status/$(1).classes bin/status/$(1).data + rm -f $$@ + @install -d $$(@D) + $$(JMOD) create \ + $$(JMODFLAGS) $$($(1)_JMODFLAGS) \ + --target-platform $(TARGET) \ + --class-path bin/modules/$(1) \ + $$(if $$(wildcard bin/$(1)/$(TARGET)/include),--header-files bin/$(1)/$(TARGET)/include) \ + $$(if $$(wildcard src/$(1)/legal),--legal-notices src/$(1)/legal) \ + $$($(TARGET)_JMOD_LIBS) bin/$(1)/$(TARGET)/lib \ + $$@ + +# Create an IDE source zip, paths have to match --module-source-path +bin/$(1)/$(1)-sources.zip: bin/status/$(1).classes + @install -d $$(@D) + jar -c -f $$@ -M \ + $$(patsubst src/$(1)/classes/%,-C src/$(1)/classes %,$$(filter src/$(1)/classes/%,$$($(1)_JAVA))) \ + $$(patsubst bin/gen/$(1)/classes/%,-C bin/gen/$(1)/classes %,$$(filter bin/gen/$(1)/classes/%,$$($(1)_JAVA))) + +endef + +#$(foreach module,$(java_MODULES),$(info $(call java_targets,$(module)))) +$(foreach module,$(java_MODULES),$(eval $(call java_targets,$(module)))) + +# ###################################################################### +# Global pattern rules + +# Stage resources +bin/status/%.data: + @install -d $(@D) + for data in $(patsubst src/$*/classes/%,"%",$($*_RESOURCES)) ; do \ + install -vDC "src/$*/classes/$$data" "bin/modules/$*/$$data" || exit 1 ; \ + done + touch $@ + +# Compile one module. This only updates (javac -h) headers if they changed. +bin/status/%.classes: + @install -d $(@D) + $(JAVAC) \ + --module-source-path "src/*/classes:bin/gen/*/classes" \ + $(JAVACFLAGS) $($*_JAVACFLAGS) \ + -h bin/inc \ + -d bin/modules \ + -m $* \ + $($*_JAVA) $($*_JAVA_generated) + if [ -d bin/inc/$* ] ; then \ + install -DC -t bin/include/$* bin/inc/$*/*.h ; \ + fi + touch $@ + +# ###################################################################### +# C stuff +# ###################################################################### + +SUFFIXES=.c .C .cc +SO=$($(TARGET)_SO) +LIB=$($(TARGET)_LIB) + +# functions to find cross-module stuff $(call library-path,modname,libname) +library-path=bin/$(1)/$(TARGET)/lib/$(LIB)$(2)$(SO) +library-dir=bin/$(1)/$(TARGET)/lib/ + +define jni_library= +# Rule for library $(2) in module $(1) +$(2)_OBJS = $(foreach sx,$(SUFFIXES),$(patsubst %$(sx), $($(1)_objdir)/%.o, $(filter %$(sx),$($(2)_SOURCES)))) +$(2)_SRCS = $(addprefix src/$(1)/jni/,$($(2)_SOURCES)) + +$($(1)_libdir)/$(LIB)$(2)$(SO): $$($(2)_OBJS) $($(2)_LIBADD) $($(2)_DEPENDENCIES) + @install -d $$(@D) + $($(TARGET)_CC) -o $$@ -shared \ + $($(TARGET)_LDFLAGS) $($(2)_LDFLAGS) $$($(2)_OBJS) $($(2)_LIBADD) $($(TARGET)_LDLIBS) $($(2)_LDLIBS) + +$($(1)_objdir)/%.o: src/$(1)/jni/%.c bin/status/$(1).classes + @install -d $$(@D) + $($(TARGET)_CC) -Isrc/$(1)/jni -Ibin/include/$(1) $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) \ + $($(TARGET)_CFLAGS) $($(2)_CFLAGS) -c -o $$@ $$< + +$($(1)_incdir)/%.h: src/$(1)/jni/%.h + install -D $$< $$@ + +$($(1)_objdir)/%.d: src/$(1)/jni/%.c bin/status/$(1).classes + @install -d $$(@D) + @rm -f $$@ + @$($(TARGET)_CC) -MM -MT "bin/$(1)/$(TARGET)/obj/$$*.o" -Isrc/$(1)/jni -Ibin/include/$(1) \ + $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) $$< -o $$@.d 2>/dev/null + @sed 's,\($$*\.o\) *:,\1 $$@ : ,g' $$@.d > $$@ ; rm $$@.d + +bin jni $(1) bin/$(1)/$(TARGET)/$(1).jmod: $($(1)_libdir)/$(LIB)$(2)$(SO) $(addprefix $($(1)_incdir)/,$($(2)_HEADERS)) + +$(if $(filter clean dist gen,$(MAKECMDGOALS)),,-include $$($(2)_OBJS:.o=.d)) +endef + +#$(foreach module,$(java_JMODS),$(foreach library,$($(module)_JNI_LIBRARIES),$(info $(call jni_library,$(module),$(library))))) +$(foreach module,$(java_JMODS),$(foreach library,$($(module)_JNI_LIBRARIES),$(eval $(call jni_library,$(module),$(library))))) + +# ###################################################################### + +dist: + @install -d bin + tar cfz bin/$(dist_NAME)-$(dist_VERSION).tar.gz \ + --transform=s,^,$(dist_NAME)-$(dist_VERSION)/, \ + config.make java.make Makefile src \ + $(dist_EXTRA) + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..b99e03e --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1758 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self.setSelected(!new java.io.File(file, "module-info.java").exists()); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @{paths} + + + + + + + + + + + + @{paths} + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set src.gen.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.modules.dir + Must set dist.javadoc.dir + Must set build.test.modules.dir + Must set build.test.results.dir + Must set build.classes.excludes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + function coalesce(input, keyValueSeparator, multiSeparator, entrySeparator) { + var result = []; + var values = {}; + + (typeof input === "string" ? input.split(entrySeparator) : input).forEach(function(entry) { + var idx = entry.indexOf(keyValueSeparator); + if (idx < 1) { + result.push(entry); + } else { + var key = entry.substring(0, idx); + var val = entry.substring(idx + 1); + if (!values[key]) { + values[key] = []; + } + values[key].push(val.trim()); + } + }); + Object.keys(values).sort().forEach(function(k) { + result.push(k + keyValueSeparator + values[k].join(multiSeparator)); + }); + return result.join(" " + entrySeparator); + } + self.project.setProperty(attributes.get("property"), + coalesce(attributes.get("value"), + attributes.get("value-sep"), + attributes.get("entry-sep"), + attributes.get("multi-sep") + )); + + + + + + + + + function expandGroup(grp) { + var exp = []; + var item = ""; + var depth = 0; + + for (i = 0; i < grp.length; i++) { + var c = grp[i]; + switch (c) { + case '{': + if (depth++ === 0) { + continue; + } + break; + case '}': + if (--depth === 0) { + exp.push(item); + continue; + } + break; + case ',': + if (depth === 1) { + exp.push(item); + item = ""; + continue; + } + default: + break; + } + item = item + c; + } + return exp; + } + + function pathVariants(spec, res) { + res = res || []; + var start = spec.indexOf('{'); + if (start === -1) { + res.push(spec); + return res; + } + var depth = 1; + var end; + for (end = start + 1; end < spec.length && depth > 0; end++) { + var c = spec[end]; + switch (c) { + case '{': depth++; break; + case '}': depth--; break; + } + } + var prefix = spec.substring(0, start); + var suffix = spec.substring(end); + expandGroup(spec.slice(start, end)).forEach(function (item) { + pathVariants(prefix + item + suffix, res); + }) + return res; + } + + function toRegexp2(spec, filepattern, separator) { + var prefixes = []; + var suffixes = []; + pathVariants(spec).forEach(function(item) { + suffixes.push(item); + }); + var tail = ""; + var separatorString = separator; + if (separatorString == "\\") { + separatorString = "\\\\"; + } + if (filepattern && filepattern != tail) { + tail = separatorString + filepattern; + } + return "([^" + separatorString +"]+)\\Q" + separator + "\\E(" + suffixes.join("|") + ")" + tail; + } + self.project.setProperty(attributes.get("property"), + toRegexp2(attributes.get("modsource"), attributes.get("filepattern"), self.project.getProperty("file.separator"))); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No main class specified + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self.setSelected(!new java.io.File(file, "module-info.class").exists()); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..25527d6 --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=508a6882 +build.xml.script.CRC32=b55362bc +build.xml.stylesheet.CRC32=32069288@1.6.1 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=6ecd1560 +nbproject/build-impl.xml.script.CRC32=2c0bce54 +nbproject/build-impl.xml.stylesheet.CRC32=0f0529df@1.6.1 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..7b849cf --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,106 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=notzed.jjmpeg +application.vendor=notzed +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +build.modules.dir=${build.dir}/modules +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.modules.dir=${build.dir}/test/modules +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.modulepath=\ + ${run.modulepath} +debug.test.classpath=\ + ${run.test.classpath} +debug.test.modulepath=\ + ${run.test.modulepath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.javadoc.dir=${dist.dir}/javadoc +dist.jlink.dir=${dist.dir}/jlink +dist.jlink.output=${dist.jlink.dir}/notzed.jjmpeg +endorsed.classpath= +excludes= +file.reference.javafx.base.jar=/data/linux-amd64/javafx-sdk-11.0.2/lib/javafx.base.jar +file.reference.javafx.controls.jar=/data/linux-amd64/javafx-sdk-11.0.2/lib/javafx.controls.jar +file.reference.javafx.graphics.jar=/data/linux-amd64/javafx-sdk-11.0.2/lib/javafx.graphics.jar +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs=-Xlint:unchecked +javac.deprecation=false +javac.external.vm=false +javac.modulepath=\ + ${file.reference.javafx.base.jar}:\ + ${file.reference.javafx.graphics.jar}:\ + ${file.reference.javafx.controls.jar}:\ + ${reference.notzed_nativez.notzed_nativez_jar} +javac.processormodulepath= +javac.processorpath=\ + ${javac.classpath} +javac.source=11 +javac.target=11 +javac.test.classpath=\ + ${javac.classpath} +javac.test.modulepath=\ + ${javac.modulepath}:\ + ${build.modules.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.html5=false +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +# The jlink additional root modules to resolve +jlink.additionalmodules= +# The jlink additional command line parameters +jlink.additionalparam= +jlink.launcher=true +jlink.launcher.name=notzed.jjmpeg +platform.active=default_platform +project.notzed_nativez=../nativez +reference.notzed_nativez.notzed_nativez_jar=${project.notzed_nativez}/dist/notzed.nativez.jar +run.classpath= +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs=-Djava.library.path=bin/notzed.jjmpeg/linux-amd64/lib:../nativez/bin/notzed.nativez/linux-amd64/lib +run.modulepath=\ + ${javac.modulepath}:\ + ${build.modules.dir} +run.test.classpath=\ + ${javac.test.classpath} +run.test.modulepath=\ + ${javac.test.modulepath}:\ + ${build.test.modules.dir} +source.encoding=UTF-8 +src.dir=src +src.dir.path=classes +src.gen.dir=bin/gen +src.gen.dir.path=classes +test.src.dir=src +test.src.dir.path=tests diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..b52b907 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,26 @@ + + + org.netbeans.modules.java.j2semodule + + + notzed.jjmpeg + + + + + + + + + + + notzed_nativez + jar + + jar + clean + notzed.nativez.jar + + + + diff --git a/src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelReader.java b/src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelReader.java new file mode 100644 index 0000000..8c60c9f --- /dev/null +++ b/src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelReader.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg.awt; + +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.AVPixelReader; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.DataBufferUShort; +import java.nio.Buffer; + +/** + * A java2d PixelReader that reads from an AVPixelReader directly. + *

+ * There are some limitations in that the pixel reader should only + * convert the whole image at a time. Stripes may work in the future. + */ +public class JavaPixelReader implements AVPixelReader { + + final AVPixelReader reader; + + public JavaPixelReader(AVPixelReader reader) { + this.reader = reader; + } + + private int typeToFormat(int type) { + switch (type) { + case BufferedImage.TYPE_3BYTE_BGR: + return AVPixelFormat.AV_PIX_FMT_BGR24; + case BufferedImage.TYPE_BYTE_GRAY: + return AVPixelFormat.AV_PIX_FMT_GRAY8; + case BufferedImage.TYPE_USHORT_GRAY: + return AVPixelFormat.AV_PIX_FMT_GRAY16; + case BufferedImage.TYPE_INT_BGR: + return AVPixelFormat.AV_PIX_FMT_RGBA; + case BufferedImage.TYPE_INT_ARGB: + return AVPixelFormat.AV_PIX_FMT_BGRA; + } + throw new RuntimeException("Unsupported Java image conversion format"); + } + + public void getPixels(int y, int h, BufferedImage dst) { + int type = dst.getType(); + int fmt = typeToFormat(type); + + switch (type) { + case BufferedImage.TYPE_3BYTE_BGR: { + byte[] data = ((DataBufferByte) dst.getRaster().getDataBuffer()).getData(); + getPixels(y, h, fmt, data, 0, dst.getWidth() * 3); + break; + } + case BufferedImage.TYPE_BYTE_GRAY: { + byte[] data = ((DataBufferByte) dst.getRaster().getDataBuffer()).getData(); + getPixels(y, h, fmt, data, 0, dst.getWidth()); + break; + } + case BufferedImage.TYPE_USHORT_GRAY: { + short[] data = ((DataBufferUShort) dst.getRaster().getDataBuffer()).getData(); + getPixels(y, h, fmt, data, 0, dst.getWidth()); + break; + } + case BufferedImage.TYPE_INT_BGR: + case BufferedImage.TYPE_INT_ARGB: { + int[] data = ((DataBufferInt) dst.getRaster().getDataBuffer()).getData(); + + getPixels(y, h, fmt, data, 0, dst.getWidth()); + break; + } + } + } + + @Override + public void getPixels(int y, int h, int fmt, T buffer, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, scanlineStride); + } + + @Override + public void getPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void getPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void getPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, offset, scanlineStride); + } +} diff --git a/src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelWriter.java b/src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelWriter.java new file mode 100644 index 0000000..97fd45b --- /dev/null +++ b/src/notzed.jjmpeg.awt/classes/au/notzed/jjmpeg/awt/JavaPixelWriter.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg.awt; + +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.AVPixelWriter; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.DataBufferUShort; +import java.nio.Buffer; + +/** + * + */ +public class JavaPixelWriter implements AVPixelWriter { + + AVPixelWriter writer; + + public JavaPixelWriter(AVPixelWriter writer) { + this.writer = writer; + } + + private int typeToFormat(int type) { + switch (type) { + case BufferedImage.TYPE_3BYTE_BGR: + return AVPixelFormat.AV_PIX_FMT_BGR24; + case BufferedImage.TYPE_BYTE_GRAY: + return AVPixelFormat.AV_PIX_FMT_GRAY8; + case BufferedImage.TYPE_USHORT_GRAY: + return AVPixelFormat.AV_PIX_FMT_GRAY16; + case BufferedImage.TYPE_INT_BGR: + return AVPixelFormat.AV_PIX_FMT_RGBA; + case BufferedImage.TYPE_INT_ARGB: + return AVPixelFormat.AV_PIX_FMT_BGRA; + } + throw new RuntimeException("Unsupported Java image conversion format"); + } + + public void setPixels(int y, int h, BufferedImage dst) { + int type = dst.getType(); + int fmt = typeToFormat(type); + + switch (type) { + case BufferedImage.TYPE_3BYTE_BGR: { + byte[] data = ((DataBufferByte) dst.getRaster().getDataBuffer()).getData(); + setPixels(y, h, fmt, data, 0, dst.getWidth() * 3); + break; + } + case BufferedImage.TYPE_BYTE_GRAY: { + byte[] data = ((DataBufferByte) dst.getRaster().getDataBuffer()).getData(); + setPixels(y, h, fmt, data, 0, dst.getWidth()); + break; + } + case BufferedImage.TYPE_USHORT_GRAY: { + short[] data = ((DataBufferUShort) dst.getRaster().getDataBuffer()).getData(); + setPixels(y, h, fmt, data, 0, dst.getWidth()); + break; + } + case BufferedImage.TYPE_INT_BGR: + case BufferedImage.TYPE_INT_ARGB: { + int[] data = ((DataBufferInt) dst.getRaster().getDataBuffer()).getData(); + + setPixels(y, h, fmt, data, 0, dst.getWidth()); + break; + } + } + } + + @Override + public void setPixels(int y, int h, int fmt, T buffer, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, scanlineStride); + } + + @Override + public void setPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void setPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void setPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, offset, scanlineStride); + } + +} diff --git a/src/notzed.jjmpeg.awt/classes/module-info.java b/src/notzed.jjmpeg.awt/classes/module-info.java new file mode 100644 index 0000000..ea1d74c --- /dev/null +++ b/src/notzed.jjmpeg.awt/classes/module-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +module notzed.jjmpeg.awt { + + requires transitive java.desktop; + requires transitive notzed.jjmpeg; + + exports au.notzed.jjmpeg.awt; +} diff --git a/src/notzed.jjmpeg.awt/legal/LICENSE b/src/notzed.jjmpeg.awt/legal/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/src/notzed.jjmpeg.awt/legal/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/audio/AudioPlay.java b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/audio/AudioPlay.java new file mode 100644 index 0000000..70bbc2e --- /dev/null +++ b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/audio/AudioPlay.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2012 Michael Zucchi + * + * This file is part of jjmpeg, a java binding to ffmpeg's libraries. + * + * jjmpeg is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jjmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jjmpeg. If not, see . + */ +package au.notzed.jjmpeg.demo.audio; + +import au.notzed.jjmpeg.AVChannelLayout; +import au.notzed.jjmpeg.AVSampleFormat; +import au.notzed.jjmpeg.AVSampleReader; +import au.notzed.jjmpeg.io.JJMediaReader; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.SourceDataLine; + +/** + * Plays audio files. + *

+ * For a bit of fun one can override the output sample rate (arbitrary based + * on backend), output bit depth (8,16,32), and layout (stero, mono, + * 5.1, 7,1, etc). + */ +public class AudioPlay { + + static void usage(String why) { + System.err.println(); + System.err.print("Error: "); + System.err.println(why); + System.err.println(); + System.err.println("Usage: AudioPlay [ --rate rate ] [ --bits bits ] [ --layout layout ] file"); + System.err.println(); + System.err.println(" rate output sample rate"); + System.err.println(" bits 8, 16, or 32."); + System.err.println(" layout mono, stereo, downmix, quad, 2.1, 3.0, 3.1, ..."); + System.err.println(); + System.exit(1); + } + + public static void main(String[] args) { + String path = null; + int outputRate = 0; + int outputBits = 16; + long outputLayout = 0; + + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "--rate": + outputRate = Integer.valueOf(args[++i]); + break; + case "--bits": + outputBits = Integer.valueOf(args[++i]); + break; + case "--layout": + outputLayout = AVChannelLayout.valueOf(args[++i]); + if (outputLayout == 0) + usage("Output layout invalid"); + break; + default: + path = args[i]; + break; + } + } + + if (path == null) + usage("No audio file supplied"); + + int dstFormat; + AudioFormat.Encoding dstEncoding; + switch (outputBits) { + case 8: + dstFormat = AVSampleFormat.AV_SAMPLE_FMT_U8; + dstEncoding = AudioFormat.Encoding.PCM_UNSIGNED; + break; + case 16: + dstFormat = AVSampleFormat.AV_SAMPLE_FMT_S16; + dstEncoding = AudioFormat.Encoding.PCM_SIGNED; + break; + case 32: + dstFormat = AVSampleFormat.AV_SAMPLE_FMT_S32; + dstEncoding = AudioFormat.Encoding.PCM_SIGNED; + break; + default: + usage("Bits must be 8, 16, or 32"); + return; + } + + try (JJMediaReader mr = new JJMediaReader(path)) { + JJMediaReader.JJReaderAudio as = mr.openDefaultStream(JJMediaReader.TYPE_AUDIO); + + if (as == null) + throw new IOException("No audio stream found"); + + int rate = as.getSampleRate(); + int channels = as.getNumChannels(); + long layout = as.getChannelLayout(); + int fmt = as.getSampleFormat(); + + long dstLayout = outputLayout == 0 ? AVChannelLayout.getDefaultLayout(channels) : outputLayout; + int dstRate = (outputRate == 0) ? rate : outputRate; + int dstChannels = AVChannelLayout.getNumChannels(dstLayout); + int dstSampleSize = ((outputBits + 7) >> 3) * dstChannels; + + AVSampleReader sampleReader = as.getFrame().getSampleReader( + dstLayout, dstFormat, dstRate); + AudioFormat format = new AudioFormat( + dstEncoding, dstRate, outputBits, dstChannels, dstSampleSize, dstRate, false); + byte[] buffer = new byte[8192]; + + System.err.printf("Audio: %s\n", path); + System.err.printf(" source format: %s %.1f Hz %s\n", AVSampleFormat.toString(fmt), (double) rate, AVChannelLayout.toString(layout)); + System.err.printf(" output format: %s\n", format); + + SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss.SSS"); + time.setTimeZone(TimeZone.getTimeZone("GMT")); + + try (SourceDataLine source = AudioSystem.getSourceDataLine(format)) { + source.open(); + source.start(); + + while (mr.readFrame() == as) { + int nsamples = sampleReader.getNumSamples() * dstSampleSize; + long stamp = as.convertPTS(mr.getPTS()); + + if (buffer.length < nsamples) + buffer = new byte[nsamples]; + + nsamples = sampleReader.getSamples(buffer, 0, buffer.length) * dstSampleSize; + source.write(buffer, 0, nsamples); + + System.out.print(time.format(new Date(stamp))); + System.out.print("\r"); + } + System.out.println(); + } + } catch (IOException | LineUnavailableException ex) { + System.err.printf("`%s': %s\n", path, ex.getLocalizedMessage()); + System.exit(1); + } + + } + +} diff --git a/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoExport.java b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoExport.java new file mode 100644 index 0000000..a65705a --- /dev/null +++ b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoExport.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2019 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg.demo.video; + +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.awt.JavaPixelReader; +import au.notzed.jjmpeg.io.JJMediaReader; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.IllegalFormatException; +import javax.imageio.ImageIO; + +/** + * Export a video as frames. + *

+ * This demonstrates using JavaPixelReader from the notzed.jjmpeg.awt module. + */ +public class VideoExport { + + public static void main(String[] args) { + int start = 0, count = Integer.MAX_VALUE; + String format = "frame-%05d.jpeg"; + String path = null; + String type; + + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "--start": + start = Integer.valueOf(args[++i]); + break; + case "--count": + count = Integer.valueOf(args[++i]); + break; + case "--format": + format = args[++i]; + default: + path = args[i]; + break; + } + } + + if (path == null) { + System.err.println("No video supplied"); + System.exit(1); + } + + String base = null; + try { + base = String.format(format, 0); + } catch (IllegalFormatException x) { + System.err.println("Format invalid"); + System.exit(1); + } + + int dot = format.lastIndexOf('.'); + if (dot == -1) + System.err.println("Unknown extension"); + type = format.substring(dot + 1).toLowerCase(); + + try (JJMediaReader mr = new JJMediaReader(path)) { + int index = 0; + JJMediaReader.JJReaderVideo vs = mr.openDefaultStream(JJMediaReader.TYPE_VIDEO); + + if (vs == null) + throw new IOException("No video stream"); + + int width = vs.getWidth(); + int height = vs.getHeight(); + BufferedImage image; + JavaPixelReader frameReader = new JavaPixelReader(vs.getFrame().getPixelReader(width, height, 0)); + + // Handle greyscale and high-bit greyscale specifically. + switch (vs.getPixelFormat()) { + case AVPixelFormat.AV_PIX_FMT_GRAY8: + image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + break; + case AVPixelFormat.AV_PIX_FMT_GRAY16: + case AVPixelFormat.AV_PIX_FMT_GRAY9: + case AVPixelFormat.AV_PIX_FMT_GRAY10: + case AVPixelFormat.AV_PIX_FMT_GRAY12: + image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY); + break; + default: + image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + break; + } + + File dir = new File(base).getParentFile(); + if (dir != null && !dir.mkdirs()) { + System.err.println("Unable to create directory"); + System.exit(1); + } + + // main loop + while (index < start + count && mr.readFrame() == vs) { + if (index >= start) { + String name = String.format(format, index); + + System.err.println(name); + frameReader.getPixels(0, height, image); + ImageIO.write(image, type, new File(name)); + } + index += 1; + } + } catch (IOException ex) { + System.err.printf("`%s': %s\n", path, ex.getLocalizedMessage()); + System.exit(1); + } + + } +} diff --git a/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoPlay.java b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoPlay.java new file mode 100644 index 0000000..a2252d8 --- /dev/null +++ b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoPlay.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2019 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg.demo.video; + +import au.notzed.jjmpeg.AVIOException; +import au.notzed.jjmpeg.fx.FXPixelReader; +import au.notzed.jjmpeg.io.JJMediaReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import static java.lang.Double.min; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import java.util.concurrent.CountDownLatch; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.geometry.Insets; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.image.ImageView; +import javafx.scene.image.WritableImage; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import javafx.scene.transform.Scale; +import javafx.scene.transform.Translate; +import javafx.stage.Stage; + +/** + * A simple JavaFX demo which plays a video. + *

+ * This demonstrates reading into a JavaFX WritableImage via + * the notzed.jjmpeg.fx module and some basic video handling. + *

+ * Audio is ignored. + */ +public class VideoPlay extends Application { + + @Override + public void start(Stage stage) throws Exception { + List args = getParameters().getUnnamed(); + + if (args.size() != 1) { + Alert a = new Alert(Alert.AlertType.ERROR, "Must supply one file.", ButtonType.CLOSE); + a.showAndWait(); + return; + } + + new Thread(() -> { + reader(args.get(0)); + }).start(); + } + + @Override + public void stop() throws Exception { + super.stop(); + cancel = true; + } + + int width; + int height; + FXPixelReader frameReader; + ImageView iv; + WritableImage image; + Text label; + Stage stage; + SimpleDateFormat time; + long time0 = -1; + long stamp0; + + void videoReady(JJMediaReader.JJReaderVideo vs) { + width = vs.getWidth(); + height = vs.getHeight(); + frameReader = new FXPixelReader(vs.getFrame().getPixelReader(width, height, 0)); + + time = new SimpleDateFormat("HH:mm:ss.SSS"); + time.setTimeZone(TimeZone.getTimeZone("GMT")); + + Platform.runLater(() -> { + label = new Text(); + label.setStyle("-fx-font: 24 monospace; -fx-fill: orange; -fx-effect: dropshadow(two-pass-box, orangered, 6, 0.5, 0, 0);"); + + iv = new ImageView(); + image = new WritableImage(width, height); + + iv.setImage(image); + + AnchorPane ap = new AnchorPane(iv, label); + + AnchorPane.setLeftAnchor(label, 16.0); + AnchorPane.setTopAnchor(label, 16.0); + + ap.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY))); + + Scene scene = new Scene(ap, width, height); + + stage = new Stage(); + stage.setScene(scene); + stage.show(); + + // Keep image centred. Assumes AVFrame.aspectRatio = 1/1. + Scale fit = new Scale(1, 1); + Translate centre = new Translate(); + + iv.getTransforms().add(centre); + iv.getTransforms().add(fit); + + InvalidationListener fitVideo = (Observable observable) -> { + double xs = ap.getWidth() / width; + double ys = ap.getHeight() / height; + double s = min(xs, ys); + + double sw = s * width; + double sh = s * height; + + fit.setX(s); + fit.setY(s); + + centre.setX((ap.getWidth() - sw) * 0.5); + centre.setY((ap.getHeight() - sh) * 0.5); + }; + + ap.widthProperty().addListener(fitVideo); + ap.heightProperty().addListener(fitVideo); + }); + } + + /** + * Handle a video frame. + *

+ * This basic example writes the image to the javfx thread synchronously + * to avoid garbage. Whether it is worth it is another matter. + *

+ * It implements a simple timing mechanism which throttles the rate + * at which frames are read. + * + * @param vs + * @param stamp + */ + void videoFrame(JJMediaReader.JJReaderVideo vs, long stamp) { + String ts = time.format(new Date(stamp)); + CountDownLatch latch = new CountDownLatch(1); + + // Display the frame. Must run on javafx thread. + Platform.runLater(() -> { + image.getPixelWriter().setPixels(0, 0, width, height, frameReader, 0, 0); + label.setText(ts); + latch.countDown(); + }); + + try { + // Very basic timing. It works ok enough. + if (time0 == -1) { + time0 = System.currentTimeMillis(); + stamp0 = stamp; + } + + long diff = (stamp - stamp0) - (System.currentTimeMillis() - time0); + if (diff > 0) + Thread.sleep(diff); + + // Must wait for frame to be copied to javafx + latch.await(); + } catch (InterruptedException ex) { + } + } + + /** + * Handle a video error. + *

+ * Just spits out a dialog and quits. + * + * @param x + */ + void videoError(IOException x) { + Alert a = new Alert(Alert.AlertType.ERROR, x.getLocalizedMessage(), ButtonType.CLOSE); + a.showAndWait(); + Platform.exit(); + } + + volatile boolean cancel = false; + + /** + * Main read loop. + *

+ * This opens the video and reads each decoded video frame. It runs until + * completion or it is cancelled. + * + * @param path + */ + void reader(String path) { + try (JJMediaReader mr = new JJMediaReader(path)) { + JJMediaReader.JJReaderVideo vs = mr.openDefaultStream(JJMediaReader.TYPE_VIDEO); + + if (vs != null) { + videoReady(vs); + + while (!cancel && mr.readFrame() == vs) { + videoFrame(vs, vs.convertPTS(mr.getPTS())); + } + } else { + videoError(new IOException("No video stream found")); + } + + } catch (AVIOException | FileNotFoundException ex) { + videoError(ex); + } + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoScan.java b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoScan.java new file mode 100644 index 0000000..f518b03 --- /dev/null +++ b/src/notzed.jjmpeg.demo/classes/au/notzed/jjmpeg/demo/video/VideoScan.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg.demo.video; + +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.io.JJMediaReader; +import java.io.IOException; + +/** + * A simple video scanner. + *

+ * This displays some basic information then reads all (decoded) frames and + * counts them. + */ +public class VideoScan { + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("No video supplied"); + System.exit(1); + } + + String path = args[0]; + + try (JJMediaReader mr = new JJMediaReader(path)) { + int count = 0; + JJMediaReader.JJReaderVideo vs = mr.openDefaultStream(JJMediaReader.TYPE_VIDEO); + + if (vs == null) + throw new IOException("No video stream found"); + + int width = vs.getWidth(); + int height = vs.getHeight(); + String fmt = AVPixelFormat.toString(vs.getPixelFormat()); + + System.err.printf("Video: %s\n", path); + System.err.printf(" width: %d\n", width); + System.err.printf(" height: %d\n", height); + System.err.printf(" format: %s\n", fmt); + + while (mr.readFrame() == vs) { + System.err.printf(" %08d\r", count); + count += 1; + } + + System.err.printf(" frames: %d\n", count); + } catch (IOException ex) { + System.err.printf("`%s': %s\n", path, ex.getLocalizedMessage()); + System.exit(1); + } + } +} diff --git a/src/notzed.jjmpeg.demo/classes/module-info.java b/src/notzed.jjmpeg.demo/classes/module-info.java new file mode 100644 index 0000000..6569380 --- /dev/null +++ b/src/notzed.jjmpeg.demo/classes/module-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +module notzed.jjmpeg.demo { + requires javafx.controls; + + requires notzed.jjmpeg.fx; + requires notzed.jjmpeg.awt; + + exports au.notzed.jjmpeg.demo.video to javafx.graphics; +} diff --git a/src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelReader.java b/src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelReader.java new file mode 100644 index 0000000..957c08a --- /dev/null +++ b/src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelReader.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg.fx; + +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.AVPixelReader; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import javafx.scene.image.PixelFormat; +import javafx.scene.image.PixelReader; +import javafx.scene.image.WritablePixelFormat; +import javafx.scene.paint.Color; + +/** + * A javafx PixelReader that reads from an AVPixelReader directly. + *

+ * There are some limitations in that the pixel reader should only + * convert the whole image at a time. Stripes may work in the future. + */ +public class FXPixelReader implements PixelReader, AVPixelReader { + + AVPixelReader reader; + + public FXPixelReader(AVPixelReader reader) { + this.reader = reader; + } + + @Override + public PixelFormat getPixelFormat() { + throw new UnsupportedOperationException(); + } + + @Override + public int getArgb(int x, int y) { + throw new UnsupportedOperationException(); + } + + @Override + public Color getColor(int x, int y) { + throw new UnsupportedOperationException(); + } + + @Override + public void getPixels(int x, int y, int w, int h, WritablePixelFormat pixelformat, T buffer, int scanlineStride) { + // Make sure x is 0, w == width? + if (buffer.hasArray()) { + switch (pixelformat.getType()) { + case BYTE_BGRA: + case BYTE_BGRA_PRE: + getPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, ((ByteBuffer) buffer).array(), buffer.arrayOffset() + buffer.position(), scanlineStride); + break; + case INT_ARGB: + case INT_ARGB_PRE: + getPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, ((IntBuffer) buffer).array(), buffer.arrayOffset() + buffer.position() / 4, scanlineStride); + break; + } + } else { + getPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, buffer, scanlineStride); + } + } + + @Override + public void getPixels(int x, int y, int w, int h, WritablePixelFormat pixelformat, byte[] buffer, int offset, int scanlineStride) { + getPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, buffer, offset, scanlineStride); + } + + @Override + public void getPixels(int x, int y, int w, int h, WritablePixelFormat pixelformat, int[] buffer, int offset, int scanlineStride) { + getPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, buffer, offset, scanlineStride); + } + + @Override + public void getPixels(int y, int h, int fmt, T buffer, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, scanlineStride); + } + + @Override + public void getPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void getPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void getPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride) { + reader.getPixels(y, h, fmt, buffer, offset, scanlineStride); + } +} diff --git a/src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelWriter.java b/src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelWriter.java new file mode 100644 index 0000000..393ae6a --- /dev/null +++ b/src/notzed.jjmpeg.fx/classes/au/notzed/jjmpeg/fx/FXPixelWriter.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg.fx; + +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.AVPixelWriter; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import javafx.scene.image.PixelFormat; +import javafx.scene.image.PixelReader; +import javafx.scene.image.PixelWriter; +import javafx.scene.image.WritablePixelFormat; +import javafx.scene.paint.Color; + +/** + * A javafx PixelReader that reads from an AVPixelReader directly. + *

+ * There are some limitations in that the pixel writer should only + * convert the whole image at a time. Stripes may work in the future. + */ +public class FXPixelWriter implements PixelWriter, AVPixelWriter { + + AVPixelWriter writer; + private final int sfmt; + private final int swidth; + private final int sheight; + + public FXPixelWriter(AVPixelWriter writer, int sfmt, int swidth, int sheight) { + this.writer = writer; + this.sfmt = sfmt; + this.swidth = swidth; + this.sheight = sheight; + } + + @Override + public PixelFormat getPixelFormat() { + return WritablePixelFormat.getByteBgraPreInstance(); + } + + @Override + public void setArgb(int x, int y, int argb) { + throw new UnsupportedOperationException(); + } + + @Override + public void setColor(int x, int y, Color c) { + throw new UnsupportedOperationException(); + } + + @Override + public void setPixels(int x, int y, int w, int h, PixelFormat pixelformat, T buffer, int scanlineStride) { + // Make sure x is 0, w == width? + if (buffer.hasArray()) { + switch (pixelformat.getType()) { + case BYTE_BGRA: + case BYTE_BGRA_PRE: + setPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, ((ByteBuffer) buffer).array(), buffer.arrayOffset() + buffer.position(), scanlineStride); + break; + case INT_ARGB: + case INT_ARGB_PRE: + setPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, ((IntBuffer) buffer).array(), buffer.arrayOffset() + buffer.position() / 4, scanlineStride); + break; + } + } else { + setPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, buffer, scanlineStride); + } + } + + @Override + public void setPixels(int x, int y, int w, int h, PixelFormat pixelformat, byte[] buffer, int offset, int scanlineStride) { + setPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, buffer, offset, scanlineStride); + } + + @Override + public void setPixels(int x, int y, int w, int h, PixelFormat pixelformat, int[] buffer, int offset, int scanlineStride) { + setPixels(y, h, AVPixelFormat.AV_PIX_FMT_BGRA, buffer, offset, scanlineStride); + } + + @Override + public void setPixels(int dstx, int dsty, int w, int h, PixelReader reader, int srcx, int srcy) { + ByteBuffer dbuffer = ByteBuffer.allocateDirect(w * h * 4).order(ByteOrder.nativeOrder()); + + reader.getPixels(dstx, dsty, w, h, WritablePixelFormat.getByteBgraPreInstance(), dbuffer, w * 4); + + writer.setPixels(dsty, h, AVPixelFormat.AV_PIX_FMT_BGRA, dbuffer, w * 4); + } + + @Override + public void setPixels(int y, int h, int fmt, T buffer, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, scanlineStride); + } + + @Override + public void setPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void setPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, offset, scanlineStride); + } + + @Override + public void setPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride) { + writer.setPixels(y, h, fmt, buffer, offset, scanlineStride); + } + +} diff --git a/src/notzed.jjmpeg.fx/classes/module-info.java b/src/notzed.jjmpeg.fx/classes/module-info.java new file mode 100644 index 0000000..1e2e451 --- /dev/null +++ b/src/notzed.jjmpeg.fx/classes/module-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +module notzed.jjmpeg.fx { + + requires javafx.graphics; + requires transitive notzed.jjmpeg; + + exports au.notzed.jjmpeg.fx; +} diff --git a/src/notzed.jjmpeg.fx/legal/LICENSE b/src/notzed.jjmpeg.fx/legal/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/src/notzed.jjmpeg.fx/legal/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVChannelLayout.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVChannelLayout.java new file mode 100644 index 0000000..aed143a --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVChannelLayout.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 Michael Zucchi + * + * This file is part of jjmpeg + * + * jjmpeg is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jjmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jjmpeg. If not, see . + */ +package au.notzed.jjmpeg; + +/** + */ +public class AVChannelLayout extends AVObject implements AVChannelLayoutBits { + + /** + * Call av_get_channel_layout(name) + */ + public static native long valueOf(String name); + + /** + * Call av_get_channel_name(value) + */ + public static native String toString(long value); + + /** + * call av_get_channel_layout_nb_channels(layout) + * + * @param layout + * @return + */ + public static native int getNumChannels(long layout); + + /** + * call av_get_default_channel_layout(nb_channels) + * + * @param nchannels + * @return + */ + public static native long getDefaultLayout(int nchannels); + + private AVChannelLayout(long p) { + super(p); + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodec.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodec.java new file mode 100644 index 0000000..38fe130 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodec.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVCodec extends AVObject implements AVCodecBits, AVDiscardBits { + + private AVCodec(long p) { + super(p); + } + + private static native void release(long p); + + public static native int getVersion(); + + public static native String getConfiguration(); + + public static native String getLicense(); + + public static native void registerAll(); + + public static native AVCodec next(AVCodec val); + + public static Iterable codecs() { + return () -> new AVObjectIterator<>(AVCodec::next); + } + + public static native AVCodec findDecoder(int codecID); + + public static native AVCodec findDecoder(String name); + + public static native AVCodec findEncoder(int codecID); + + public static native AVCodec findEncoder(String name); + + /* + Accessors + */ + public native String getName(); + + public native String getLongName(); + + public native int getType(); + + public native int getID(); + + public native int getCapabilities(); + + public native AVRational[] getFramerates(); + + public native int[] getPixelFormats(); + + public native int[] getSampleRates(); + + public native int[] getSampleFormats(); + + public native long[] getChannelLayouts(); + + public native int getMaxLowres(); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecContext.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecContext.java new file mode 100644 index 0000000..9e5e8e5 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecContext.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * AVCodecContext accessors. + *

+ */ +public class AVCodecContext extends AVOptions implements AVCodecContextBits { + + private AVCodecContext(long p) { + super(p); + } + + private static native void release(long p); + + /* + * Methods + */ + /** + * + * @param codec + * @return + */ + public static native AVCodecContext allocContext(AVCodec codec); + + // avcodec_open2 + public native void open(AVCodec codec, AVDictionary options) throws AVIOException; + + public native void flushBuffers(); + + public native AVSize alignDimensions(AVSize input); + + /** + * Calls avcodec_send_packet() + *

+ * If the return value is EAGAIN or AVERROR_EOF then this function + * returns false, for all other error values it throws an exception. + * + * @param pkt + * @return true if the packet was accepted, false if a frame must be received first. + * @throws AVIOException + */ + public native boolean sendPacket(AVPacket pkt) throws AVIOException; + + /** + * Calls avcodec_receive_frame(). + *

+ * If the return value is EAGAIN or AVERROR_EOF then this function + * returns false, for all other error values it throws an exception. + * + * @param frame + * @return Returns true if the frame is complete, false otherwise. + * @throws AVIOException + */ + public native boolean receiveFrame(AVFrame frame) throws AVIOException; + + /** + * Calls avcodec_send_frame(). + *

+ * If the return value is EAGAIN or AVERROR_EOF then this function + * returns false, for all other error values it throws an exception. + * + * @param frame + * @return + * @throws AVIOException + */ + public native boolean sendFrame(AVFrame frame) throws AVIOException; + + /** + * Calls avcodec_receive_packet(). + *

+ * If the return value is EAGAIN or AVERROR_EOF then this function + * returns false, for all other error values it throws an exception. + * + * @param pkt + * @return + * @throws AVIOException + */ + public native boolean receivePacket(AVPacket pkt) throws AVIOException; + + /* + * Accessors + */ + public native int getCodecType(); + + public native void setCodecType(int val); + + public native int getCodecID(); + + public native void setCodecID(int val); + + public native int getCodecTag(); + + public native void setCodecTag(int val); + + public native long getBitRate(); + + public native void setBitRate(long val); + + public native int getBitRateTolerance(); + + public native void setBitRateTolerance(int val); + + public native int getGlobalQuality(); + + public native void setGlobalQuality(int val); + + public native int getCompressionLevel(); + + public native void setCompressionLevel(int val); + + /** + * Get the contents of flags and flags2 together as a single 64-bit value. + * + * @return + */ + public native long getFlags(); + + /** + * Set or clear flags and flags2. + * + * @param mask Mask of bits to change. Only flags with a bit set here will be altered. + * @param flags Value to set bits to. + */ + public native void setFlags(long mask, long flags); + + /** + * Get the timebase. + *

+ * Note that the return value is a value type (copy) not a reference. + * + * @return + */ + public native AVRational getTimeBase(); + + public native void setTimeBase(AVRational val); + + public native int getTicksPerFrame(); + + public native void setTicksPerFrame(int val); + + public native int getDelay(); + + /* + Video only options + */ + public native int getWidth(); + + public native void setWidth(int val); + + public native int getHeight(); + + public native void setHeight(int val); + + public native AVRational getAspectRatio(); + + public native void setAspectRatio(AVRational r); + + // codedWidth, codedHeight + public native int getGOPSize(); + + public native void setGOPSize(int val); + + public native int getPixelFormat(); + + public native void setPixelFormat(int val); + + // + public native int getMaxBFrames(); + + public native void setMaxBFrames(int val); + + public native float getBQuantFactor(); + + public native void setBQuantFactor(float val); + + public native float getBQuantOffset(); + + public native void setBQuantOffset(float val); + + public native boolean hasBFrames(); + + public native float getIQuantFactor(); + + public native void setIQuantFactor(float val); + + public native float getIQuantOffset(); + + public native void setIQuantOffset(float val); +// And a whole lot more ... +// + + public native int getMBDecision(); + + public native void setMBDecision(int val); +// ... + + /* + Audtio only options + */ + public native int getSampleRate(); + + public native void setSampleRate(int val); + + public native int getNumChannels(); + + public native void setNumChannels(int val); + + public native int getSampleFormat(); + + public native void setSampleFormat(int val); + + public native int getFrameSize(); + + public native void setFrameSize(int val); + + public native int getFrameNumber(); + +// int block_align +// int cutoff + public native long getChannelLayout(); + + public native void setChannelLayout(long val); + + public native long getRequestChannelLayout(); + + public native void setRequestChannelLayout(long val); + +// enum AVAudioServiceType audio_service_type +// enum AVSampleFormat request_sample_fmt; + + /* + Encoding parameters + */ +// float qcompress +// float qblur +// float qmin +// float qmax +// float max_qdiff +// int rc_buffer_size +// int rc_override_count +// RCOverride *rc_override +// int64_t rc_max_rate +// int64_t rc_min_rate +// ... more rc stuff +// ... +// int trellis +// ... +// int workaround_bugs + public native int getStrictStdCompliance(); + + public native void setStrictStdCompliance(int val); + + public native int getErrorConcealment(); + + public native void setErrorConcealment(int val); +// int debug + + public native int getErrorRecognition(); + + public native void setErrorRecognition(int val); + + public native int getDCTAlgo(); + + public native void setDCTAlgo(int val); + + public native int getIDCTAlgo(); + + public native void setIDCTAlgo(int val); + + public native int getThreadCount(); + + public native void setThreadCount(int val); +// ... + + public native int getProfile(); + + public native void setProfile(int val); +// ... +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecID.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecID.java new file mode 100644 index 0000000..46b21b7 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecID.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + */ +public class AVCodecID implements AVCodecIDBits { + + /** + * Call avcodec_get_name(value) + */ + public static native String toString(int value); + + /** + * Call avcodec_get_type(value) + * + * @param value AVCodecID. + * @return Corresponding AVMediaType. + */ + public static native int getType(int value); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecParameters.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecParameters.java new file mode 100644 index 0000000..c5115df --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVCodecParameters.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVCodecParameters extends AVObject { + + private AVCodecParameters(long p) { + super(p); + } + + private static native void release(long p); + + public static native AVCodecParameters alloc(); + + public native void copy(AVCodecParameters src); + + public native void fromContext(AVCodecContext cc); + + public native void toContext(AVCodecContext cc); + + /* */ + public native int getCodecType(); + + public native void setCodecType(int type); + + public native int getCodecID(); + + public native void setCodecID(int id); + + public native int getCodecTag(); + + public native void setCodecTag(int val); + + public native long getBitRate(); + + public native void setBitRate(long val); + + /* + Video only options + */ + public native int getPixelFormat(); + + public native void setPixelFormat(int val); + + public native int getWidth(); + + public native void setWidth(int val); + + public native int getHeight(); + + public native void setHeight(int val); + + public native AVRational getAspectRatio(); + + public native void setAspectRatio(AVRational r); + + /* + * Audio stuff + */ + public native int getSampleFormat(); + + public native void setSampleFormat(int val); + + public native long getChannelLayout(); + + public native void setChannelLayout(long val); + + public native int getNumChannels(); + + public native void setNumChannels(int val); + + public native int getSampleRate(); + + public native void setSampleRate(int val); + + // ryv? +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDevice.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDevice.java new file mode 100644 index 0000000..705078c --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDevice.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * Interface to libavdevice. + *

+ * This is currently a dummy object but could hold AVDeviceInfo if that was useful. + */ +public class AVDevice extends AVObject { + + private AVDevice(long p) { + super(p); + } + + private static void release(long p) { + } + + public static native int getVersion(); + + public static native String getConfiguration(); + + public static native String getLicense(); + + public static native void registerAll(); + + public native static AVInputFormat nextAudioInputDevice(AVInputFormat last); + + public static Iterable audioInputDevices() { + return () -> new AVObjectIterator<>(AVDevice::nextAudioInputDevice); + } + + public native static AVInputFormat nextVideoInputDevice(AVInputFormat last); + + public static Iterable videoInputDevices() { + return () -> new AVObjectIterator<>(AVDevice::nextVideoInputDevice); + } + + public native static AVOutputFormat nextAudioOutputDevice(AVOutputFormat last); + + public static Iterable audioOutputDevices() { + return () -> new AVObjectIterator<>(AVDevice::nextAudioOutputDevice); + } + + public native static AVOutputFormat nextVideoOutputDevice(AVOutputFormat last); + + public static Iterable videoOutputDevices() { + return () -> new AVObjectIterator<>(AVDevice::nextVideoOutputDevice); + } + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDictionary.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDictionary.java new file mode 100644 index 0000000..44e05c0 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVDictionary.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.util.Collection; +import java.util.Map.Entry; +import java.util.TreeMap; + +/** + * AVDictionary is a remarkably shithouse "ADT" inherited by ffmpeg from libav. + *

+ * Because the pointer can be changed by any of the update functions this + * doesn't bother trying to wrap the C api. Instead it maintains it's own + * set of data and converts it to an array of strings on demand. + *

+ * It's a tree because ... I don't know. + */ +public class AVDictionary extends TreeMap { + + public Object[] toArray() { + return entrySet().toArray(); + } + + public void setAll(Collection set) { + this.entrySet().retainAll(set); + } + + public String[] toDictionary() { + String[] list = new String[size() * 2]; + int i = 0; + + for (Entry e: entrySet()) { + list[i++] = e.getKey(); + list[i++] = e.getValue(); + } + + return list; + } + + public void fromDictionary(String[] set) { + clear(); + if (set != null) { + for (int i = 0; i < set.length; i += 2) { + put(set[i], set[i + 1]); + } + } + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVError.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVError.java new file mode 100644 index 0000000..b374db7 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVError.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVError implements AVErrorBits { + + public static native String toString(int errno); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFormatContext.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFormatContext.java new file mode 100644 index 0000000..24eae74 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFormatContext.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.io.FileNotFoundException; + +/** + * + */ +public class AVFormatContext extends AVOptions { + + private AVFormatContext(long p) { + super(p); + } + + private static native void release(long p); + + // Fields + public native AVInputFormat getInputFormat(); + + public native AVOutputFormat getOutputFormat(); + + // hmm? + //public native void setOutputFormat(AVOutputFormat val); + + public native AVIOContext getIOContext(); + + public native void setIOContext(AVIOContext val); + + /* stream info */ + public native int getCtxFlags(); + + public native int getNumStreams(); + + public native AVStream getStream(int index); + + //public native String getFilename(); + //public native void setFilename(String name); + // + public native long getStartTime(); + + public native long getDuration(); + + public native long getBitRate(); +// packet_size +// max_delay + + public native int getFlags(); + + public native void setFlags(int val); + + public native long getProbeSize(); + + public native void setProbeSize(long val); + + public native long getMaxAnalyseDuration(); + + public native void setMaxAnalyseDuration(long val); +// ... + + public native int getVideoCodecID(); + + public native void setVideoCodecID(int val); + + public native int getAudioCodecID(); + + public native void setAudioCodecID(int val); + + public native int getSubtitleCodecID(); + + public native void setSubtitleCodecID(int val); +// ... + + public native AVDictionary getMetadata(); + + public native void setMetadata(AVDictionary val); +// ... + + public native void setDebug(int val); +// ... + + public native int getSeek2Any(); + + public native void setSeek2Any(int val); + + // Native Methods + public static native int getVersion(); + + public static native String getConfiguration(); + + public static native String getLicense(); + + public static native void registerAll(); + + // registerInputFormat() + // registerOutputFormat() + public static native void networkInit(); + + public static native void networkDeinit(); + + // + static public native AVFormatContext allocContext(); + + public native AVStream newStream(AVCodec c); + + // newProgram + static public native AVFormatContext allocContext(AVOutputFormat of, String format_name, String filename) throws AVIOException; + + // probeInputFormat / 2 / 3 + // probeInputBuffer / 2 + public static native AVFormatContext openInput(String url, AVInputFormat fmt, AVDictionary options) throws AVIOException, FileNotFoundException; + + // options is updated by result + public native void findStreamInfo(AVDictionary[] options) throws AVIOException; + + // returns false on end of file + public native boolean readFrame(AVPacket pkt) throws AVIOException; + + public native void seekFrame(int stream_index, long timestamp, int flags) throws AVIOException; + + public native void seekFile(int stream_index, long min_ts, long ts, long max_ts, int flags) throws AVIOException; + + public native void flush() throws AVIOException; + + public native void readPlay() throws AVIOException; + + public native void readPause() throws AVIOException; + + // two separate ways to close!! + // closeInput() -> has to p <= null + // freeContext() + // + public native int writeHeader(AVDictionary options) throws AVIOException; + + public native int initOutput(AVDictionary options) throws AVIOException; + + // returns true if flushed + public native boolean writeFrame(AVPacket pktkt) throws AVIOException; + + public native void interleavedWriteFrame(AVPacket pkt) throws AVIOException; + + public native void writeUncodedFrame(int stream_index, AVFrame frame) throws AVIOException; + + public native void interleavedWriteUncodedFrame(int stream_index, AVFrame frame) throws AVIOException; + + public native void writeTrailer() throws AVIOException; + + public native int findDefaultStreamIndex(); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFrame.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFrame.java new file mode 100644 index 0000000..05b9026 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVFrame.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.Buffer; +import java.nio.ByteBuffer; + +/** + * Accessor for a struct AVFrame. + *

+ * Both video and audio frames are stored in the same object; various fields + * will only make sense for the corresponding data. + *

+ * To read and write pixels for video frames use {@link getPixelReader} and {@link getPixelWriter}. + *

+ * To read and write samples for audio frames use {@link getSampleReader} and {@link getSampleWriter}. + */ +public class AVFrame extends AVObject { + + private AVFrame(long p) { + super(p); + } + + private native static void release(long p); + + public native static AVFrame alloc(); + + public native void copy(AVFrame source); + + public native void copyProperties(AVFrame source); + + /* + * Data stuff. + *

+ * TODO: perhaps i want audio/video specific versions, as the interpretation + * of the fields depends upon it. It depends on whether this is easy to determine + * from other fields. + */ + @Deprecated + public native ByteBuffer getPlane(int i); + + @Deprecated + public native int getStride(int i); + + /* + * Video stuff + */ + /** + * Create a new AVFrame and allocate video frame information. + *

+ * This calls av_frame_alloc(), initialises the provided fields and + * then calls av_frame_get_buffer(). + * + * @param fmt + * @param width + * @param height + * @return + */ + public native static AVFrame alloc(int fmt, int width, int height); + + public native int getWidth(); + + public native void setWidth(int width); + + public native int getHeight(); + + public native void setHeight(int height); + + /** + * .format for video. + * + * @return + */ + public native int getPixelFormat(); + + public native boolean isKeyFrame(); + + public native AVRational getAspectRatio(); + + public native long getPTS(); + + public native void setPTS(long val); + + public native int getDisplayPictureNumber(); + + public native int getCodedPictureNumber(); + + public native boolean isInterlaced(); + + public native boolean isTopFieldFirst(); + + /* + * Audio stuff + */ + public native static AVFrame alloc(int fmt, int channels, long channel_layout, int num_samples); + + public native int getNumSamples(); + + public native void setNumSamples(int n); + + /** + * .format for audio. + * + * @return + */ + public native int getSampleFormat(); + + public native int getSampleRate(); + + public native void setSampleRate(int v); + + public native long getChannelLayout(); + + public native void setChannelLayout(long v); + + public native int getNumChannels(); + + public native void setNumChannels(int v); + + public native int getBestEffortTimestamp(); + + /** + * Create a PixelReader for this frame. Pixel readers can convert the image format and perform scaling. + *

+ * Multiple readers with differing options may be created from a given frame. + * + * @param dwidth target width + * @param dheight target height + * @param flags SwsContext creation flags + * @return A pixel reader. Note that scanlineStride is currently ignored for all getPixels() calls. + */ + public AVPixelReader getPixelReader(int dwidth, int dheight, int flags) { + return FrameReader.alloc(this, dwidth, dheight, flags); + } + + static class FrameReader extends AVObject implements AVPixelReader { + + private FrameReader(long p) { + super(p); + } + + private native static void release(long p); + + public native static FrameReader alloc(AVFrame frame, int width, int height, int flags); + + @Override + native public void getPixels(int y, int h, int fmt, T buffer, int scanlineStride); + + @Override + native public void getPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride); + + @Override + native public void getPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride); + + @Override + native public void getPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride); + } + + public AVPixelWriter getPixelWriter(int swidth, int sheight, int flags) { + return FrameWriter.alloc(this, swidth, sheight, flags); + } + + static class FrameWriter extends AVObject implements AVPixelWriter { + + private FrameWriter(long p) { + super(p); + } + + private native static void release(long p); + + public native static FrameWriter alloc(AVFrame frame, int width, int height, int flags); + + @Override + native public void setPixels(int y, int h, int fmt, T buffer, int scanlineStride); + + @Override + native public void setPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride); + + @Override + native public void setPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride); + + @Override + native public void setPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride); + } + + /** + * Create a new sample reader. + * + * @param layout AVChannelLayout. Planar formats are not supported. + * @param fmt AVSampleFormat. + * @param rate sample rate + * @return an AVSampleReader which will retrieve samples of the given + * format and layout. + */ + public AVSampleReader getSampleReader(long layout, int fmt, int rate) { + return SampleReader.alloc(this, layout, fmt, rate); + } + + static class SampleReader extends AVObject implements AVSampleReader { + + private SampleReader(long p) { + super(p); + } + + private native static void release(long p); + + public native static SampleReader alloc(AVFrame frame, long layout, int fmt, int rate); + + @Override + public native int getNumSamples(); + + @Override + public native int getSamples(byte[] buffer, int offset, int length); + + @Override + public native int getSamples(short[] buffer, int offset, int length); + + @Override + public native int getSamples(int[] buffer, int offset, int length); + + @Override + public native int getSamples(float[] buffer, int offset, int length); + + @Override + public native void getSamples(T buffer); + + } + + public AVSampleWriter getSampleWriter(long layout, int fmt, int rate) { + //return SampleReader.alloc(this, layout, fmt, rate); + throw new UnsupportedOperationException(); + } + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOContext.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOContext.java new file mode 100644 index 0000000..3b0c499 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOContext.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.ByteBuffer; + +/** + * + */ +public class AVIOContext extends AVOptions { + + // TODO: autogen? + public static final int AVSEEK_SIZE = 0x10000; + public static final int AVSEEK_FORCE = 0x20000; + // + public static final int AVSEEK_SET = 0; + public static final int AVSEEK_CUR = 1; + public static final int AVSEEK_END = 2; + // + public static final int ALLOC_WRITE = 1; + public static final int ALLOC_STREAMED = 2; + // flags for open + public static final int AVIO_FLAG_READ = 1; + public static final int AVIO_FLAG_WRITE = 2; + public static final int AVIO_FLAG_READ_WRITE = 3; + // flags for alloc seekable + /** + * Seeking works like for a local file. + */ + public static final int AVIO_SEEKABLE_NORMAL = (1 << 0); + + /** + * Seeking by timestamp with avio_seek_time() is possible. + */ + public static final int AVIO_SEEKABLE_TIME = (1 << 1); + /** + * pseudo-flag for alloc() which sets writable to avio_context_create. + */ + public static final int AVIO_WRITABLE = (1 << 31); + /** + * pseudo-flaga for alloc() which sets the direct indicator on the + * created context. + */ + public static final int AVIO_DIRECT = (1 << 30); + + private AVIOContext(long p) { + super(p); + } + + private static native void release(long p); + + //?public native int getSeekable(); + /** + * Calls avio_open. + *

+ * TODO: should it call avio_open2? + * + * @param url + * @param avio_flags AVIO_FLAGS_*. + * @return + */ + public static native AVIOContext open(String url, int avio_flags) throws AVIOException; + + /** + * + * @param buffer_size + * @param alloc_flags combination of AVIO_SEEKABLE_*, AVIO_WRITABLE, and AVIO_DIRECT. + * @param handler + * @return + */ + public static native AVIOContext alloc(int buffer_size, int alloc_flags, AVIOHandler handler); + + public interface AVIOHandler { + + /** + * Read data into dest. + * + * @param dst ByteBuffer with position and limit set to the bounds of valid data. + * @return The number of bytes actually read or -errno on error. + */ + public int readPacket(ByteBuffer dst); + + /** + * Write from src. + * + * @param src ByteBuffer with position and limit set to the bounds of valid data. + * @return Number of bytes written or -errno on error. + */ + public int writePacket(ByteBuffer src); + + public long seek(long offset, int whence); + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOException.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOException.java new file mode 100644 index 0000000..d31bb2e --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVIOException.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.io.IOException; + +/** + * + */ +public class AVIOException extends IOException { + + private final int error; + + public AVIOException(int error) { + this.error = error; + } + + public AVIOException(int error, String msg) { + super(msg); + this.error = error; + } + + public int getError() { + return error; + } + + /** + * Retreieve the strerror() description of error. + * + * @return + */ + public String getDescription() { + return AVError.toString(error); + } + + @Override + public String getLocalizedMessage() { + return String.format("%d (%s): %s", error, getDescription(), super.getLocalizedMessage()); + } + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVInputFormat.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVInputFormat.java new file mode 100644 index 0000000..af63be8 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVInputFormat.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVInputFormat extends AVObject { + + private AVInputFormat(long p) { + super(p); + } + + public static native AVInputFormat findInputFormat(String name); + + public static native AVInputFormat next(AVInputFormat val); + + public static Iterable formats() { + return () -> new AVObjectIterator<>(AVInputFormat::next); + } + + public native void register(); + + public native String getName(); + + public native String getLongName(); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVLogger.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVLogger.java new file mode 100644 index 0000000..91359f3 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVLogger.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006 Michael Niedermayer + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public interface AVLogger { + + /** + * Print no output. + */ + public static final int AV_LOG_QUIET = -8; + + /** + * Something went really wrong and we will crash now. + */ + public static final int AV_LOG_PANIC = 0; + + /** + * Something went wrong and recovery is not possible. + * For example, no header was found for a format which depends + * on headers or an illegal combination of parameters is used. + */ + public static final int AV_LOG_FATAL = 8; + + /** + * Something went wrong and cannot losslessly be recovered. + * However, not all future data is affected. + */ + public static final int AV_LOG_ERROR = 16; + + /** + * Something somehow does not look correct. This may or may not + * lead to problems. An example would be the use of '-vstrict -2'. + */ + public static final int AV_LOG_WARNING = 24; + + /** + * Standard information. + */ + public static final int AV_LOG_INFO = 32; + + /** + * Detailed information. + */ + public static final int AV_LOG_VERBOSE = 40; + + /** + * Stuff which is only useful for libav* developers. + */ + public static final int AV_LOG_DEBUG = 48; + + /** + * Extremely verbose debugging, useful for libav* development. + */ + public static final int AV_LOG_TRACE = 56; + + public void log(int level, String what); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVMediaType.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVMediaType.java new file mode 100644 index 0000000..9c88e45 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVMediaType.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVMediaType implements AVMediaTypeBits { + + /** + * Call av_get_media_type_string() + */ + public static native String toString(int value); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVObject.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVObject.java new file mode 100644 index 0000000..63ff264 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVObject.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import au.notzed.nativez.NativeZ; +import java.util.Iterator; +import java.util.function.Function; +import java.util.logging.Logger; + +/** + * + */ +public abstract class AVObject extends NativeZ { + + protected AVObject(long p) { + super(p); + } + + static { + System.loadLibrary("jjmpeg"); + + AVCodec.registerAll(); + AVFormatContext.registerAll(); + //AVDevice.registerAll(); // ignore failure? + + AVUtil.setLogger(AVUtil.createAVLogger(Logger.getLogger("notzed.jjmpeg"))); + } + + /** + * Handy iterator for various uses in the library. + */ + static class AVObjectIterator implements Iterator { + + Function nextFunc; + T next; + + public AVObjectIterator(Function nextFunc) { + this.nextFunc = nextFunc; + this.next = nextFunc.apply(null); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public T next() { + T current = next; + next = nextFunc.apply(current); + return current; + } + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOptions.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOptions.java new file mode 100644 index 0000000..ed4e4e5 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOptions.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * All objects that support av_opt* stuff should derive from this. + *

+ * Quite a lot of code here, is it worth it? Should it just + * have set from string/serialise calls instead?? + */ +public abstract class AVOptions extends AVObject implements AVOptionsBits { + + protected AVOptions(long p) { + super(p); + } + + // + // There is a lot of code here, is it useful, or can it just + // use the get/setOptions() calls? + // + public native void set(String name, String val, int flags); + + public native void setInt(String name, long val, int flags); + + public native void setDouble(String name, double val, int flags); + + public native void setQ(String name, AVRational val, int flags); + + // ?? + public native void setBin(String name, byte[] data, int offset, int len, int flags); + + public native void setImageSize(String name, AVSize size, int flags); + + public native void setPixelFormat(String name, int fmt, int flags); + + public native void setSampleFormat(String name, int fmt, int flags); + + public native void setVideoRate(String name, AVRational val, int flags); + + public native void setChannelLayout(String name, long val, int flags); + + public native void setDictionary(String name, AVDictionary val, int flags); + + /* + + */ + public native String get(String name, int flags); + + public native long getInt(String name, int flags); + + public native double getDouble(String name, int flags); + + public native AVRational getQ(String name, int flags); + + public native AVSize getImageSize(String name, int flags); + + public native int getPixelFormat(String name, int flags); + + public native int getSampleFormat(String name, int flags); + + public native AVRational getVideoRate(String name, int flags); + + public native long getChannelLayout(String name, int flags); + + public native AVDictionary getDictionary(String name, int flags); + + /** + * Call set_options_string(this, serialised, "=", ",") + * + * @param serialised + */ + public native void setOptions(String serialised); + + /** + * Call opt_serialise(this, opt_flags, ser_flags, &result, '=', ',') + * + * @param opt_flags + * @param ser_flags + * @return + */ + public native String getOptions(int opt_flags, int ser_flags); + + public native void copyOptions(T src); + + public native AVOption nextOption(AVOption val); + + public Iterable options() { + return () -> new AVObjectIterator<>(this::nextOption); + } + + public native AVOption find(String name, String unit, int opt_flags, int search_flags); + + public static class AVOption { + + private final long p; + public final String name; + public final String help; + public final int type; + public final int flags; + public final String unit; + + public AVOption(long p, String name, String help, int type, int flags, String unit) { + this.p = p; + this.name = name; + this.help = help; + this.type = type; + this.flags = flags; + this.unit = unit; + } + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOutputFormat.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOutputFormat.java new file mode 100644 index 0000000..41bc992 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVOutputFormat.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVOutputFormat extends AVObject { + + private AVOutputFormat(long p) { + super(p); + } + + public static final int AVFMT_NOFILE = 0x0001; + public static final int AVFMT_NEEDNUMBER = 0x0002; + public static final int AVFMT_GLOBALHEADER = 0x0040; + public static final int AVFMT_NOTIMESTAMPS = 0x0080; + public static final int AVFMT_VARIABLE_FPS = 0x0400; + public static final int AVFMT_NODIMENSIONS = 0x0800; + public static final int AVFMT_NOSTREAMS = 0x1000; + public static final int AVFMT_ALLOW_FLUSH = 0x10000; + public static final int AVFMT_TS_NONSTRICT = 0x20000; + public static final int AVFMT_TS_NEGATIVE = 0x40000; + + public static native AVOutputFormat guessFormat(String short_name, String filename, String mime_type); + + public native int guessCodecID(String short_name, String filename, String mime_type, int media_type); + + public static native AVOutputFormat next(AVOutputFormat val); + + public static Iterable formats() { + return () -> new AVObjectIterator<>(AVOutputFormat::next); + } + + public native void register(); + + public native String getName(); + + public native String getLongName(); + + public native String getMimeType(); + + public native String getExtensions(); + + public native int getVideoCodec(); + + public native int getAudioCodec(); + + public native int getSubtitleCodec(); + + public native int getFlags(); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPacket.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPacket.java new file mode 100644 index 0000000..b218a38 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPacket.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.ByteBuffer; + +/** + * AVPacket stores compressed stream data. + *

+ * This contains some data pointers and is mostly handled by + * libavcodec/libavformat, all we really do is pass them between + * encoders/decoders to/from formats. + */ +public class AVPacket extends AVObject implements Cloneable { + + private AVPacket(long p) { + super(p); + } + + /** + * The packet contains a keyframe + */ + public static final int AV_PKT_FLAG_KEY = 0x0001; + /** + * The packet content is corrupted + */ + public static final int AV_PKT_FLAG_CORRUPT = 0x0002; + /** + * Flag is used to discard packets which are required to maintain valid + * decoder state but are not required for output and should be dropped + * after decoding. + * + */ + public static final int AV_PKT_FLAG_DISCARD = 0x0004; + /** + * The packet comes from a trusted source. + *

+ * Otherwise-unsafe constructs such as arbitrary pointers to data + * outside the packet may be followed. + */ + public static final int AV_PKT_FLAG_TRUSTED = 0x0008; + + /** + * Calls av_packet_free. + * + * @param p + */ + private static native void release(long p); + + /** + * Creates a new packet (initialised) with no data. + * Calls av_packet_alloc + * + * @return + */ + public static native AVPacket alloc(); + + /** + * Retrieve the current data buffer pointer and size. + *

+ * This value should not be saved between calls which might update the packet. + *

+ * TODO: do i even need this? + * + * @return + */ + public native ByteBuffer getData(); + + /** + * Clear and free any data pointers and returns the packet to an initialised state. + *

+ * It needs to be called ... at certain points I guess. + *

+ * This calls av_packet_unref(). + */ + public native void clear(); + + @Override + public native AVPacket clone(); + + public native void rescaleTS(AVRational src, AVRational dst); + + /* */ + public native long getPTS(); + + public native void setPTS(long val); + + public native long getDTS(); + + public native void setDTS(long val); + + public native int getSize(); + + public native int getStreamIndex(); + + public native void setStreamIndex(int val); + + public native int getFlags(); + + public native void setFlags(int mask, int val); + + public native long getPosition(); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelFormat.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelFormat.java new file mode 100644 index 0000000..65166dc --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelFormat.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * jjmpeg is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jjmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jjmpeg. If not, see . + */ +package au.notzed.jjmpeg; + +/** + */ +public class AVPixelFormat implements AVPixelFormatBits { + + /** + * Call av_get_pix_fmt(name) + */ + public static native int valueOf(String name); + + /** + * Call av_get_pix_fmt_name(value) + */ + public static native String toString(int value); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelReader.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelReader.java new file mode 100644 index 0000000..8966122 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelReader.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.Buffer; + +/** + * AVPixelReader is a high level interface to SwsContext. + *

+ * This interface allows one to retrieve pixels by bands to a specific format. + * + * @see AVFrame:getPixelReader + */ +public interface AVPixelReader { + + /** + * Read pixels into a Buffer. The buffer must be a Direct*Buffer. + * + * @param + * @param y Start scanline. Currently only 0 works. + * @param h End scanline output. Currently only the output height works. + * @param fmt Pixel format to write to. + * @param buffer Target for pixels. + * @param scanlineStride Should match the output width and may be ignored. + */ + public void getPixels(int y, int h, int fmt, T buffer, int scanlineStride); + + public void getPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride); + + public void getPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride); + + public void getPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelWriter.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelWriter.java new file mode 100644 index 0000000..72cd077 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVPixelWriter.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.Buffer; + +/** + * + */ +public interface AVPixelWriter { + + public void setPixels(int y, int h, int fmt, T buffer, int scanlineStride); + + public void setPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride); + + public void setPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride); + + public void setPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVRational.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVRational.java new file mode 100644 index 0000000..0dcac36 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVRational.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2003 Michael Niedermayer + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * AVRational holds a rational number. + *

+ * In previous versions of jjmpeg this was a reference type, it has + * changed to a value type and made a Number. + */ +public class AVRational extends Number implements Comparable { + + public final int num; + public final int den; + + public AVRational(int num, int den) { + this.num = num; + this.den = den; + } + + /** + * Calls av_d2q + * + * @param d + * @param max + * @return + */ + public static native AVRational d2q(double d, int max); + + /** + * Calls av_rescale(). + * + * @param a + * @param b + * @param c + * @return a * b / c + */ + public static native long rescale(long a, long b, long c); + + /** + * + * @param c + * @return this * c + */ + public native AVRational mul(AVRational c); + + /** + * + * @param c + * @return this / c + */ + public native AVRational div(AVRational c); + + public native AVRational add(AVRational c); + + public native AVRational sub(AVRational c); + + /** + * Calculate ts * (num * scale) / den + * + * @param ts + * @param scale + * @return + */ + public long rescale(long ts, int scale) { + return rescale(ts, num * scale, den); + } + + @Override + public String toString() { + return String.format("%d/%d", num, den); + } + + @Override + public int intValue() { + throw new UnsupportedOperationException(); + } + + @Override + public long longValue() { + throw new UnsupportedOperationException(); + } + + @Override + public float floatValue() { + return (float) num / den; + } + + @Override + public double doubleValue() { + return (double) num / den; + } + + /** + * Compare two rationals. + * + * @param b Second rational + * + * Taken from avutil/rational.c + * + * @return One of the following values: + *

    + *
  • 0 if `a == b` + *
  • 1 if `a > b` + *
  • -1 if `a < b` + *
  • `INT_MIN` if one of the values is of the form `0 / 0` + *
+ */ + @Override + public int compareTo(AVRational b) { + long tmp = num * (long) b.den - b.num * (long) den; + + if (tmp != 0) + return (int) ((tmp ^ den ^ b.den) >> 63) | 1; + else if (b.den != 0 && den != 0) + return 0; + else if (num != 0 && b.num != 0) + return (num >> 31) - (b.num >> 31); + else + return Integer.MIN_VALUE; + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleFormat.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleFormat.java new file mode 100644 index 0000000..c5bd2e8 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleFormat.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of jjmpeg, a java binding to ffmpeg's libraries. + * + * jjmpeg is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jjmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jjmpeg. If not, see . + */ +package au.notzed.jjmpeg; + +/** + */ +public class AVSampleFormat implements AVSampleFormatBits { + + /** + * Call av_get_sample_fmt(name) + */ + + public static native int valueOf(String name); + + /** + * Call av_get_sample_fmt_name(value) + */ + public static native String toString(int value); +} + diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleReader.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleReader.java new file mode 100644 index 0000000..4ab774a --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleReader.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.Buffer; + +/** + * High level interface to libswresample. + *

+ * This allows reading samples directly in a desired format. + *

+ * This is somewhat simpler than AVPixelReader as it doesn't need to support + * external interfaces. + */ +public interface AVSampleReader { + + /** + * Retrieve the number of samples available. + * + * @return + */ + public int getNumSamples(); + + /** + * Retrieve converted samples. + *

+ * + * @param buffer output buffer + * @param offset output buffer offset + * @param length maximum amount to read, this must currently be at least {@link getNumSamples} * sample data size * channels. + * @return The number of valid samples converted. + * @todo pass in lengh and keep calling until drained. problem is how to track the state internally. + */ + public int getSamples(byte[] buffer, int offset, int length); + + public int getSamples(short[] buffer, int offset, int length); + + public int getSamples(int[] buffer, int offset, int length); + + public int getSamples(float[] buffer, int offset, int length); + + /** + * Read into a direct byte buffer. + *

+ * The remaining size of the buffer must have sufficient space for {@link getNumSamples} samples. + * + * @param + * @param buffer The data is written to the buffer at it's current + * position, and the position is updated. + */ + public void getSamples(T buffer); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleWriter.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleWriter.java new file mode 100644 index 0000000..64b385f --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSampleWriter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.Buffer; + +/** + * AVSampleWriter. + *

+ * This is only a placeholder. + */ +public interface AVSampleWriter { + + public int setSamples(byte[] buffer, int offset, int length); + + public int setSamples(short[] buffer, int offset, int length); + + public int setSamples(int[] buffer, int offset, int length); + + public int setSamples(float[] buffer, int offset, int length); + + public void setSamples(T buffer); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSize.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSize.java new file mode 100644 index 0000000..9aa488b --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVSize.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVSize { + + public int width; + public int height; + + public AVSize() { + } + + public AVSize(int width, int height) { + this.width = width; + this.height = height; + } + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVStream.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVStream.java new file mode 100644 index 0000000..c38fed1 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVStream.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * + */ +public class AVStream extends AVObject { + + private AVStream(long p) { + super(p); + } + + public native int getIndex(); + + public native int getID(); + + public native void setID(int id); + + public native AVRational getTimeBase(); + + public native void setTimeBase(AVRational value); + + public native long getStartTime(); + + public native void setStartTime(long val); + + public native long getDuration(); + + public native void setDuration(long val); + + public native long getNumFrames(); + + public native void setNumFrames(long val); + + public native int getDiscard(); + + public native void setDiscard(int val); + + public native AVRational getAverageFrameRate(); + + public native void setAverageFrameRate(AVRational val); + + public native AVRational getSampleAspectRatio(); + + public native void setSampleAspectRatio(AVRational val); + + public native AVRational getDisplayAspectRatio(); + + public native void setDisplayAspectRatio(AVRational val); + + public native AVCodecParameters getCodecParameters(); + + // not sure about this? + public native void setCodecParameters(AVCodecParameters val); +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVUtil.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVUtil.java new file mode 100644 index 0000000..5a42751 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/AVUtil.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import static java.lang.Math.min; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * Static methods for other non-class based interfaces. + */ +public class AVUtil { + + /** + * Call av_log_set_callback() with the provided logger. + * + * @param logger Logger callback. Use null to set the default ffmpeg callback. + */ + public native static void setLogger(AVLogger logger); + + private static final int levels[] = { + AVLogger.AV_LOG_QUIET, + AVLogger.AV_LOG_PANIC, + AVLogger.AV_LOG_FATAL, + AVLogger.AV_LOG_ERROR, + AVLogger.AV_LOG_WARNING, + AVLogger.AV_LOG_INFO, + AVLogger.AV_LOG_VERBOSE, + AVLogger.AV_LOG_DEBUG, + AVLogger.AV_LOG_TRACE + }; + + private static final Level jlevels[] = { + Level.SEVERE, + Level.SEVERE, + Level.SEVERE, + Level.SEVERE, + Level.WARNING, + Level.INFO, + Level.FINE, + Level.FINER, + Level.FINEST + }; + + public static AVLogger createAVLogger(Logger log) { + return (int l, String what) -> { + int jl = Arrays.binarySearch(levels, l); + + // Map to java.util.logging level + if (jl < 0) + jl = -jl; + jl = min(jl, levels.length - 1); + + if (log.isLoggable(jlevels[jl])) { + LogRecord lr = new LogRecord(jlevels[jl], what); + Throwable t = new Throwable(); + + // Look up the actual method name which logged the message. + // The LogRecord routine will uselessly just find this method instead. + StackTraceElement[] list = t.getStackTrace(); + if (list.length > 1) { + lr.setSourceClassName(list[1].getClassName()); + lr.setSourceMethodName(list[1].getMethodName()); + } + + log.log(lr); + } + }; + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwrContext.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwrContext.java new file mode 100644 index 0000000..7b4937a --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwrContext.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * Software Audio Scaling Operations. + *

+ * @deprecated unimplemented! Use AVFrame.getSampleReader(). + */ +@Deprecated +public class SwrContext extends AVObject { + + public static final int SWR_DITHER_NONE = 0; + public static final int SWR_DITHER_RECTANGULAR = 1; + public static final int SWR_DITHER_TRIANGULAR = 2; + public static final int SWR_DITHER_TRIANGULAR_HIGHPASS = 3; + /* Resampling engine */ + public static final int SWR_ENGINE_SWR = 0; + public static final int SWR_ENGINE_SOXR = 1; + /* Filtering */ + public static final int SWR_FILTER_TYPE_CUBIC = 0; + public static final int SWR_FILTER_TYPE_BLACKMAN_NUTTALL = 1; + public static final int SWR_FILTER_TYPE_KAISER = 2; + + private SwrContext(long p) { + super(p); + } + + private static native void release(long p); + + public static native int getVersion(); + + public static native String getConfiguration(); + + public static native String getLicense(); + + public native static SwrContext create(); + + public native static void setOptions( + long out_ch_layout, int out_sample_fmt, int out_sample_rate, + long int_ch_layout, int in_sample_fmt, int in_sample_rate); // throws some error + + public native void setOptions(AVFrame out, AVFrame in); + + public native void initialise(); + + public native int getOutSamples(int in_samples); + + public native int convert(byte[][] out, int out_count, byte[][] in, int in_count); + + // throws AVERROR_INPUT_CHANGED, AVERROR_OUTPUT_CHANGED + // or do i just reconfigure/hide it? + public native void convert(AVFrame out, AVFrame in); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsContext.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsContext.java new file mode 100644 index 0000000..c293c46 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsContext.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +import java.nio.ByteBuffer; + +/** + * Software Scaling Operations. + *

+ * See also AVFrame.getPixelReader() for a higher level implementation. + *

+ * @deprecated Not fully implemented Use {@link AVFrame:getPixelReader} + */ +@Deprecated +public class SwsContext extends AVObject { + + public static final int SWS_FAST_BILINEAR = 1; + public static final int SWS_BILINEAR = 2; + public static final int SWS_BICUBIC = 4; + public static final int SWS_X = 8; + public static final int SWS_POINT = 0x10; + public static final int SWS_AREA = 0x20; + public static final int SWS_BICUBLIN = 0x40; + public static final int SWS_GAUSS = 0x80; + public static final int SWS_SINC = 0x100; + public static final int SWS_LANCZOS = 0x200; + public static final int SWS_SPLINE = 0x400; + // more here + + private SwsContext(long p) { + super(p); + } + + private static native void release(long p); + + public static native int getVersion(); + + public static native String getConfiguration(); + + public static native String getLicense(); + + public native static SwsContext alloc( + int srcW, int srcH, int srcFormat, + int dstW, int dstH, int dstFormat, + int flags, + SwsFilter srcFilter, + SwsFilter dstFilter, + double[] params); + + public native int scale(AVFrame src, int srcSliceY, int srcSliceH, AVFrame dst); + + /* these aren't implemented yet, they need size and stuff passed in */ + public native int scale(AVFrame src, int srcSliceY, int srcSliceH, int[] dst); + + public native int scale(AVFrame src, int srcSliceY, int srcSliceH, byte[] dst); + + public native int scale(byte[] src, int srcSliceY, int srcSliceH, AVFrame dst); + + public native int scale(ByteBuffer src, int srcSliceY, int srcSliceH, AVFrame dst); + + /* + Other than AVFrame<>AVFrame the interfaces above cannot be easily made to work. + + */ +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsFilter.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsFilter.java new file mode 100644 index 0000000..186b221 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/SwsFilter.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package au.notzed.jjmpeg; + +/** + * TODO: fill this out + * + * @deprecated not implemented. + */ +@Deprecated +public class SwsFilter extends AVObject { + + private SwsFilter(long p) { + super(p); + } + + private native static void release(long p); + + public static native SwsFilter create( + float lumaGBlur, float chromaGBlur, + float lumaSharpen, float chromaSharpen, + float chromaHShift, float chromaVShift, + int verbose); + +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaReader.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaReader.java new file mode 100644 index 0000000..e891247 --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaReader.java @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2012 Michael Zucchi + * + * This file is part of jjmpeg, a java binding to ffmpeg's libraries. + * + * jjmpeg is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jjmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jjmpeg. If not, see . + */ +package au.notzed.jjmpeg.io; + +import au.notzed.jjmpeg.AVCodec; +import au.notzed.jjmpeg.AVCodecContext; +import au.notzed.jjmpeg.AVCodecID; +import au.notzed.jjmpeg.AVCodecParameters; +import au.notzed.jjmpeg.AVDictionary; +import au.notzed.jjmpeg.AVDiscardBits; +import au.notzed.jjmpeg.AVError; +import au.notzed.jjmpeg.AVFormatContext; +import au.notzed.jjmpeg.AVFrame; +import au.notzed.jjmpeg.AVIOException; +import au.notzed.jjmpeg.AVInputFormat; +import au.notzed.jjmpeg.AVMediaType; +import au.notzed.jjmpeg.AVPacket; +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.AVRational; +import au.notzed.jjmpeg.AVSampleFormat; +import au.notzed.jjmpeg.AVStream; +import java.io.FileNotFoundException; +import java.util.Iterator; +import java.util.logging.Logger; + +/** + * High level interface for scanning audio and video frames. + *

+ * TODO: handle all frames. + * + * @author notzed + */ +public class JJMediaReader implements AutoCloseable { + + JJReaderStream[] streams; + AVFormatContext format; + private AVPacket packet; + private boolean freePacket = false; + // + long seekid = -1; + long seekms = -1; + // + + /** + * VIDEO Type indicator for openDefaultStream(). + */ + public static final Class TYPE_VIDEO = JJReaderVideo.class; + /** + * AUDIO Type indicator for openDefaultStream(). + */ + public static final Class TYPE_AUDIO = JJReaderAudio.class; + + /** + * Create a new media reader, will scan the file for available streams. + * + * @param name + * @throws AVIOException + */ + public JJMediaReader(String name) throws AVIOException, FileNotFoundException { + this(name, null, null); + } + + public JJMediaReader(String name, AVInputFormat fmt, AVDictionary options) throws AVIOException, FileNotFoundException { + this(AVFormatContext.openInput(name, fmt, options)); + } + + public JJMediaReader(AVFormatContext format) throws AVIOException { + this.format = format; + + //System.out.println("scan streams"); + //format.setProbesize(1024 * 64); + //format.setMaxAnalyzeDuration(500000); + format.findStreamInfo(null); + + // find all streams and map to something + int nstreams = format.getNumStreams(); + streams = new JJReaderStream[nstreams]; + for (int i = 0; i < nstreams; i++) { + AVStream s = format.getStream(i); + AVCodecParameters cp = s.getCodecParameters(); + switch (cp.getCodecType()) { + case AVMediaType.AVMEDIA_TYPE_VIDEO: + streams[i] = new JJReaderVideo(s, cp); + break; + case AVMediaType.AVMEDIA_TYPE_AUDIO: + streams[i] = new JJReaderAudio(s, cp); + break; + default: + streams[i] = new JJReaderUnknown(s, cp); + s.setDiscard(AVDiscardBits.AVDISCARD_ALL); + break; + } + } + + packet = AVPacket.alloc(); + } + + /** + * Open the first stream of the given type. + *

+ * A note on the implementation. The complexity here is to + * ensure runtime type checking works in a generic context. + *

+ * Is it worth it? + *

+ * TODO: ignore open failures and go to the next one? + * + * @param + * @param type + * @return + * @throws AVIOException + */ + public T openDefaultStream(Class type) throws AVIOException { + for (JJReaderStream rs : streams) { + if (type.isInstance(rs)) { + T m = type.cast(rs); + if (!m.isOpened()) + m.open(); + return m; + } + } + return null; + } + + public Iterable streams() { + return () -> { + return new Iterator() { + int i = 0; + + @Override + public boolean hasNext() { + return i < streams.length; + } + + @Override + public JJReaderStream next() { + return streams[i++]; + } + }; + }; + } + + public void release() { + for (JJReaderStream m : streams) { + m.release(); + } + format.release(); + packet.release(); + } + + @Override + public void close() { + release(); + } + + /** + * Get source AVFormatContext. + * + * @return + */ + public AVFormatContext getFormat() { + return format; + } + long pts; + + /** + * Retrieve (calculated) pts of the last frame decoded. + *

+ * Well be -1 at EOF + * + * @return + */ + public long getPTS() { + return pts; + } + + /** + * call flushBuffers() on all opened streams codecs. + *

+ * e.g. after a seek. + */ + public void flushCodec() { + for (JJReaderStream rs : streams) { + rs.flushCodec(); + } + } + + private long currentVideoMS() { + for (JJReaderStream r : streams) { + if (r instanceof JJReaderVideo) { + return r.convertPTS(getPTS()); + } + } + return -1; + } + + /** + * Attempt to seek to the nearest millisecond. + *

+ * The next frame will have pts (in milliseconds) >= stamp. + * + * @param stamp + * @throws AVIOException + */ + public void seekMS(long stamp) throws AVIOException { + // This is an attempt at an optimisation, for small seeks (< 1 second) + // just parse/discard frames. + // This is going to be faster on many codecs than a flush/seek which will probably + // have to jump through more frames after a keyframe anyway + long current = currentVideoMS(); + if (current != -1 && (stamp - current) >= 0 && (stamp - current) < 1000) { + seekms = stamp; + } else { + format.seekFile(-1, 0, stamp * 1000, stamp * 1000, 0); + seekms = stamp; + + flushCodec(); + } + } + + /** + * Seek in stream units + *

+ * The next frame will have pts >= stamp + * + * @param stamp + * @throws AVIOException + */ + public void seek(long stamp) throws AVIOException { + format.seekFile(-1, 0, stamp, stamp, 0); + seekid = stamp; + + flushCodec(); + } + + /** + * Reads and decodes packets until data is ready in one of the opened streams. + * + * @return + */ + public JJReaderStream readFrame() throws AVIOException { + + if (freePacket) { + packet.clear(); + } + freePacket = false; + + while (format.readFrame(packet)) { + try { + int index = packet.getStreamIndex(); + JJReaderStream ms = streams[index]; + + if (ms != null) { + if (ms.isOpened() && ms.decode(packet)) { + pts = packet.getDTS(); + + // If seeking, attempt to get to the exact frame + if (seekid != -1 + && pts < seekid) { + continue; + } else if (seekms != -1 + && ms.convertPTS(pts) < seekms) { + continue; + } + seekid = -1; + seekms = -1; + freePacket = true; + return ms; + } + } + } finally { + if (!freePacket) { + packet.clear(); + } + } + } + + return null; + } + + JJReaderStream last; + + public JJReaderStream readFrameX() throws AVIOException { + if (freePacket) { + packet.clear(); + } + freePacket = false; + + // Drain all frames from last stream first + if (last != null && last.c.receiveFrame(last.frame)) { + last.frameIndex += 1; + pts = packet.getDTS(); + return last; + } + last = null; + + while (format.readFrame(packet)) { + try { + int index = packet.getStreamIndex(); + JJReaderStream ms = streams[index]; + + if (ms != null && ms.isOpened()) { + ms.c.sendPacket(packet); + while (ms.c.receiveFrame(ms.frame)) { + ms.frameIndex += 1; + pts = packet.getDTS(); + + // If seeking, attempt to get to the exact frame + if (seekid != -1 + && pts < seekid) { + continue; + } else if (seekms != -1 + && ms.convertPTS(pts) < seekms) { + continue; + } + last = ms; + seekid = -1; + seekms = -1; + freePacket = true; + return ms; + } + } + } finally { + if (!freePacket) { + packet.clear(); + } + } + } + + return null; + } + + static public abstract class JJReaderStream { + + AVStream stream; + AVCodecContext c; + AVCodecParameters cp; + int streamID = -1; + protected AVCodec codec; + protected boolean opened = false; + // timebase + AVRational tb; + // start pts + long startpts; + // start ms + long startms; + // + long duration; + long durationms; + // Raw decoded frame + final AVFrame frame; + // Incremented every time a frame is decoded + long frameIndex; + + public JJReaderStream(AVStream stream, AVCodecParameters cp) throws AVIOException { + this.stream = stream; + + this.cp = cp; + // Hmm, what if i want to override CodedID? + codec = AVCodec.findDecoder(cp.getCodecID()); + if (codec == null) + throw new AVIOException(AVError.AVERROR_DECODER_NOT_FOUND); + + c = AVCodecContext.allocContext(codec); + + frame = AVFrame.alloc(); + + tb = stream.getTimeBase(); + + startpts = stream.getStartTime(); + startms = tb.rescale(startpts, 1000); + duration = stream.getDuration(); + durationms = tb.rescale(duration, 1000); + } + + public void open() throws AVIOException { + cp.toContext(c); + c.setCodecID(codec.getID()); + c.open(codec, null); + + opened = true; + } + + public void release() { + if (codec != null) { + codec.release(); + c.release(); + } + frame.release(); + stream.release(); + } + + /** + * Retrieve the codec paramters. + *

+ * These may be modified before the codec context is opened. + * + * @return + */ + public AVCodecParameters getCodecParameters() { + return cp; + } + + public AVStream getStream() { + return stream; + } + + public AVCodec getCodec() { + return codec; + } + + /** + * Retrieve duration of sequence, in milliseconds. + * + * @return + */ + public long getDurationMS() { + return durationms; + } + + /** + * Get duration in timebase units (i.e. frames?) + * + * @return + */ + public long getDuration() { + return duration; + } + + public boolean isOpened() { + return opened; + } + + /** + * Convert the 'pts' provided to milliseconds relative to the start of the stream. + * + * @param pts + * @return + */ + public long convertPTS(long pts) { + return tb.rescale(pts, 1000) - startms; + } + + /** + * Decode a packet. Returns true if data is now ready. + *

+ * It is ok to call this on an unopened stream: return false. + *

+ * TODO: this is now common across different formats, up-class the code. + * + * @param packet + * @return + */ + public boolean decode(AVPacket packet) throws AVIOException { + boolean done = false; + if (isOpened()) { + if (packet != null && packet.getSize() == 0) { + Logger.getLogger("jjmpeg.io") + .fine(() + -> String.format("stream %d packet size 0", streamID)); + return done; + } + + c.sendPacket(packet); + done = c.receiveFrame(frame); + + frameIndex += 1; + } + return done; + } + + /** + * Retrieve the decoded frame. This is only valid after a call to decode() returns true. + * + * @return + */ + public AVFrame getFrame() { + return frame; + } + + public long getFrameIndex() { + return frameIndex; + } + + /** + * Retreive the AVMEDIA_TYPE_* for this stream. + * + * @return + */ + abstract public int getType(); + + void flushCodec() { + if (opened) { + c.flushBuffers(); + } + } + } + + public class JJReaderUnknown extends JJReaderStream { + + public JJReaderUnknown(AVStream stream, AVCodecParameters cp) throws AVIOException { + super(stream, cp); + } + + @Override + public boolean decode(AVPacket packet) throws AVIOException { + return false; + } + + @Override + public void open() throws AVIOException { + throw new AVIOException(AVError.AVERROR_UNKNOWN, "Unknown stream type"); + } + + @Override + public int getType() { + return AVMediaType.AVMEDIA_TYPE_UNKNOWN; + } + + @Override + void flushCodec() { + } + } + + static public class JJReaderVideo extends JJReaderStream { + + public JJReaderVideo(AVStream stream, AVCodecParameters cp) throws AVIOException { + super(stream, cp); + } + + @Override + public void open() throws AVIOException { + if (cp.getPixelFormat() == AVPixelFormat.AV_PIX_FMT_NONE) { + throw new AVIOException(AVError.AVERROR_INVALIDDATA, "No decodable video present"); + } + + Logger.getLogger("jjmpeg.io") + .fine(() -> { + return String.format("Open video reader\n" + + " video %dx%d %s [aspect %s]\n" + + " codec %s\n", + cp.getWidth(), + cp.getHeight(), + AVPixelFormat.toString(cp.getPixelFormat()), + cp.getAspectRatio(), + AVCodecID.toString(cp.getCodecID())); + }); + + super.open(); + } + + @Override + public void release() { + super.release(); + + if (c != null) + c.release(); + } + + @Override + public int getType() { + return AVMediaType.AVMEDIA_TYPE_VIDEO; + } + + public int getWidth() { + return c.getWidth(); + } + + public int getHeight() { + return c.getHeight(); + } + + public int getPixelFormat() { + return c.getPixelFormat(); + } + + @Override + public String toString() { + AVRational fr = stream.getAverageFrameRate(); + + return String.format("[Video %6.3fs %dx%d @ %d/%d %s]", getDurationMS() * 1E-3, cp.getWidth(), cp.getHeight(), fr.num, fr.den, AVPixelFormat.toString(cp.getPixelFormat())); + } + } + + static public class JJReaderAudio extends JJReaderStream { + + public JJReaderAudio(AVStream stream, AVCodecParameters cp) throws AVIOException { + super(stream, cp); + } + + @Override + public void open() throws AVIOException { + Logger.getLogger("jjmpeg.io") + .fine(() -> { + return String.format("Open audio reader\n" + + " audio x%d %dHz %s\n" + + " codec %s\n", + cp.getNumChannels(), + cp.getSampleRate(), + AVSampleFormat.toString(cp.getSampleFormat()), + AVCodecID.toString(cp.getCodecID())); + }); + + super.open(); + } + + @Override + public int getType() { + return AVMediaType.AVMEDIA_TYPE_AUDIO; + } + + public int getSampleFormat() { + return c.getSampleFormat(); + } + + public int getSampleRate() { + return c.getSampleRate(); + } + + public int getNumChannels() { + return c.getNumChannels(); + } + + public long getChannelLayout() { + return c.getChannelLayout(); + } + + @Override + public String toString() { + return String.format("[Audio %d @ %dHz %s]", cp.getNumChannels(), cp.getSampleRate(), AVSampleFormat.toString(cp.getSampleFormat())); + } + } +} diff --git a/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaWriter.java b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaWriter.java new file mode 100644 index 0000000..66acaec --- /dev/null +++ b/src/notzed.jjmpeg/classes/au/notzed/jjmpeg/io/JJMediaWriter.java @@ -0,0 +1,419 @@ +/* + * Based on Libavformat API example: Output a media file in any supported + * libavformat format. The default codecs are used. + * + * Copyright (c) 2003 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. + */ + /* + * See: http://cekirdek.pardus.org.tr/~ismail/ffmpeg-docs/output-example_8c-source.html + */ +package au.notzed.jjmpeg.io; + +import au.notzed.jjmpeg.AVCodec; +import au.notzed.jjmpeg.AVCodecContext; +import au.notzed.jjmpeg.AVFormatContext; +import au.notzed.jjmpeg.AVFrame; +import au.notzed.jjmpeg.AVOutputFormat; +import au.notzed.jjmpeg.AVPacket; +import au.notzed.jjmpeg.AVRational; +import au.notzed.jjmpeg.AVStream; +import au.notzed.jjmpeg.AVCodecID; +import au.notzed.jjmpeg.AVError; +import au.notzed.jjmpeg.AVIOContext; +import au.notzed.jjmpeg.AVPixelFormat; +import au.notzed.jjmpeg.AVIOException; +import au.notzed.jjmpeg.AVMediaType; +import au.notzed.jjmpeg.AVSampleFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * High level av file writer interface + *

+ * @author notzed + */ +public class JJMediaWriter { + + List streams = new ArrayList<>(); + String filename; + AVFormatContext oc; + AVIOContext output; // need to keep ref around + + /** + * Create a new stream for writing to a file. + *

+ * Once created, call addVideoStream, (addAudioStream) and so on, before calling open(). + * Frames are then added using the various addFrame() methods until finished, + * and then call close(). + * + * @param filename + *

+ * @throws AVInvalidFormatException + */ + public JJMediaWriter(String filename) throws AVIOException { + this.filename = filename; + + /* allocate the output media context */ + oc = AVFormatContext.allocContext(null, null, filename); + + // Caller now calls addVideoStream, etc. + } + + /** + * Get the output format being used. + *

+ * @return + */ + public AVOutputFormat getFormat() { + return oc.getOutputFormat(); + } + + /** + * The stream must be opened after adding streams and before writing to them. + *

+ * @throws AVInvalidFormatException + * @throws AVInvalidCodecException + * @throws AVIOException + */ + public void open() throws AVIOException { + /* now that all the parameters are set, we can open the audio and + video codecs and allocate the necessary encode buffers */ + for (JJWriterStream sd: streams) { + sd.open(); + } + + /* open the output file, if needed */ + //if (!(format->flags & AVFMT_NOFILE)) { + output = AVIOContext.open(filename, AVIOContext.AVIO_FLAG_WRITE); + oc.setIOContext(output); + + /* write the stream header, if any */ + oc.writeHeader(null); + } + + /** + * Add a stream using the default video codec for this stream. + * The streamid is the index of the stream in the streams list. + *

+ * @param width + * @param height + * @param frame_rate + * @param bit_rate + *

+ * @return + */ + public JJWriterVideo addVideoStream(int width, int height, int frame_rate, long bit_rate) throws AVIOException { + return addVideoStream(getFormat().getVideoCodec(), streams.size(), width, height, 1, frame_rate, bit_rate); + } + + public JJWriterVideo addVideoStream(int width, int height, int frame_rate_num, int frame_rate_den, long bit_rate) throws AVIOException { + return addVideoStream(getFormat().getVideoCodec(), streams.size(), width, height, frame_rate_num, frame_rate_den, bit_rate); + } + + public JJWriterVideo addVideoStream(int codec_id, int streamid, int width, int height, int frame_rate_den, long bit_rate) throws AVIOException { + return addVideoStream(codec_id, streamid, width, height, 1, frame_rate_den, bit_rate); + } + + /** + * Add a video stream. + *

+ * @param codec_id + * @param width + * @param height + * @param frame_rate + * @param bit_rate + *

+ * @return + * @throws AVInvalidStreamException + */ + public JJWriterVideo addVideoStream(int codec_id, int streamid, int width, int height, int frame_rate_num, int frame_rate_den, long bit_rate) throws AVIOException { + AVCodecContext c; + AVStream st; + + // TODO: all this crap should probably go into JJWriterVideo constructor + AVCodec codec = AVCodec.findEncoder(codec_id); + + c = AVCodecContext.allocContext(codec); + + st = oc.newStream(codec); + if (st == null) { + throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND); + } + st.setID(streams.size()); + + c.setCodecID(codec_id); + c.setCodecType(AVMediaType.AVMEDIA_TYPE_VIDEO); + /* put sample parameters */ + c.setBitRate(bit_rate); + + /* resolution must be a multiple of two */ + c.setWidth(width); + c.setHeight(height); + /* time base: this is the fundamental unit of time (in seconds) in terms + of which frame timestamps are represented. for fixed-fps content, + timebase should be 1/framerate and timestamp increments should be + identically 1. */ + st.setTimeBase(new AVRational(frame_rate_num, frame_rate_den)); + c.setTimeBase(new AVRational(frame_rate_num, frame_rate_den)); + c.setGOPSize(12); + /* emit one intra frame every twelve frames at most */ + + c.setPixelFormat(AVPixelFormat.AV_PIX_FMT_YUV420P); + if (codec_id == AVCodecID.AV_CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B frames */ + c.setMaxBFrames(2); + } + if (codec_id == AVCodecID.AV_CODEC_ID_MPEG1VIDEO) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + This does not happen with normal video, it just happens here as + the motion of the chroma plane does not match the luma plane. */ + c.setMBDecision(2); + } + // FIXME: some formats want stream headers to be separate + if ((oc.getOutputFormat().getFlags() & AVOutputFormat.AVFMT_GLOBALHEADER) != 0) { + c.setFlags(AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER, AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER); + } + + Logger.getLogger("jjmpeg.io") + .fine(() + -> String.format("add avideo stream %s %d [%dx%d %s aspect %s]\n", + codec.getName(), c.getBitRate(), + c.getWidth(), c.getHeight(), AVPixelFormat.toString(c.getPixelFormat()), + c.getAspectRatio())); + + JJWriterVideo sd = new JJWriterVideo(st, c); + streams.add(sd); + + return sd; + } + + /** + * Add a new audio stream. + *

+ * @param codec_id + * @param streamid + * @param fmt + * @param sample_rate + * @param bit_rate + *

+ * @return + * @throws AVInvalidStreamException + */ + public JJWriterAudio addAudioStream(int codec_id, int streamid, int fmt, int sample_rate, int channels, long bit_rate) throws AVIOException { + AVCodecContext c; + AVStream st; + + AVCodec codec = AVCodec.findEncoder(codec_id); + + c = AVCodecContext.allocContext(codec); + + st = oc.newStream(codec); + if (st == null) { + throw new AVIOException(AVError.AVERROR_STREAM_NOT_FOUND); + } + + c.setCodecID(codec_id); + c.setCodecType(AVMediaType.AVMEDIA_TYPE_AUDIO); + + c.setSampleFormat(fmt); + c.setBitRate(bit_rate); + c.setSampleRate(sample_rate); + + c.setNumChannels(channels); + c.setChannelLayout(4); + + if ((oc.getOutputFormat().getFlags() & AVOutputFormat.AVFMT_GLOBALHEADER) != 0) { + c.setFlags(AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER, AVCodecContext.AV_CODEC_FLAG_GLOBAL_HEADER); + } + + Logger.getLogger("jjmpeg.io") + .fine(() + -> String.format("add audio stream audio %s %d [x%d %dHz %s]\n", + codec.getName(), c.getBitRate(), + c.getNumChannels(), c.getSampleRate(), AVSampleFormat.toString(c.getSampleFormat()))); + + JJWriterAudio as = new JJWriterAudio(st, c); + + streams.add(as); + + return as; + } + + public void flush() throws AVIOException { + for (JJWriterStream sd: streams) { + sd.addFrame(null); + } + } + + /** + * Close the file, completing the stream and freeing resources. + */ + public void close() throws AVIOException { + + flush(); + /* write the trailer, if any. the trailer must be written + * before you close the CodecContexts open when you wrote the + * header; otherwise write_trailer may try to use memory that + * was freed on av_codec_close() */ + oc.writeTrailer(); + + /* close each codec */ + for (JJWriterStream sd: streams) { + sd.close(); + } + + /* close the output file */ + //output.close(); + + /* free the stream */ + oc.release(); + } + + public abstract class JJWriterStream { + + AVStream stream; + AVCodecContext c; + AVPacket packet; + boolean opened; + AVFrame frame; + + public JJWriterStream(AVStream stream, AVCodecContext c) { + this.stream = stream; + this.c = c; + this.packet = AVPacket.alloc(); + } + + /** + * Retrieve the av stream. + *

+ * @return + */ + public AVStream getStream() { + return stream; + } + + /** + * Retrieve the codec context for this stream. + *

+ * This may be modified before opening. + *

+ * @return + */ + public AVCodecContext getContext() { + return c; + } + + public void open() throws AVIOException { + AVCodec codec = AVCodec.findEncoder(c.getCodecID()); + + c.open(codec, null); + + stream.getCodecParameters().fromContext(c); + + opened = true; + } + + public void addFrame(AVFrame frame) throws AVIOException { + if (c.sendFrame(frame)) { + while (c.receivePacket(packet)) { + packet.rescaleTS(c.getTimeBase(), stream.getTimeBase()); + packet.setStreamIndex(stream.getIndex()); + oc.interleavedWriteFrame(packet); + } + } + } + + void close() { + stream.release(); + packet.release(); + } + } + + /** + * Represents a video stream. + */ + public class JJWriterVideo extends JJWriterStream { + + int nextPTS; + + public JJWriterVideo(AVStream stream, AVCodecContext c) { + super(stream, c); + } + + @Override + public void open() throws AVIOException { + super.open(); + + frame = AVFrame.alloc(c.getPixelFormat(), c.getWidth(), c.getHeight()); + } + + @Override + void close() { + super.close(); + + frame.release(); + } + + public AVFrame getFrame() { + return frame; + } + + /** + * Write next video frame. + *

+ * @param frame Video frame to write, or null to flush buffers. + *

+ * @throws AVEncodingError + * @throws AVIOException + */ + public void addFrame(AVFrame frame) throws AVIOException { + if (frame != null) + frame.setPTS(nextPTS++); + super.addFrame(frame); + } + + } + + public class JJWriterAudio extends JJWriterStream { + + public JJWriterAudio(AVStream stream, AVCodecContext c) { + super(stream, c); + } + + @Override + public void open() throws AVIOException { + super.open(); + + // TODO: pcm hack stuff + if (c.getFrameSize() <= 1) { + } + + // ??? unchecked + frame = AVFrame.alloc(c.getSampleFormat(), c.getNumChannels(), 2048); + } + + @Override + void close() { + super.close(); + } + } +} diff --git a/src/notzed.jjmpeg/classes/module-info.java b/src/notzed.jjmpeg/classes/module-info.java new file mode 100644 index 0000000..ee36244 --- /dev/null +++ b/src/notzed.jjmpeg/classes/module-info.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +module notzed.jjmpeg { + requires java.logging; + requires notzed.nativez; + + exports au.notzed.jjmpeg; + exports au.notzed.jjmpeg.io; + + opens au.notzed.jjmpeg to notzed.nativez; +} diff --git a/src/notzed.jjmpeg/gen/extract-defines.pl b/src/notzed.jjmpeg/gen/extract-defines.pl new file mode 100644 index 0000000..cd82b46 --- /dev/null +++ b/src/notzed.jjmpeg/gen/extract-defines.pl @@ -0,0 +1,192 @@ +#!/usr/bin/perl + +# +# Copyright (C) 2017 Michael Zucchi +# +# This file is part of jjmpeg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +$ff = "/opt/ffmpeg/3.4"; + +$inc = ""; +$start = ""; +$stop = ""; +# keep all comments +$keepc = 1; +$shift = ""; +$interface = ""; +$class = ""; +$type = "int"; + +@defines = (); +%process = (); + +# read global options +while ($#ARGV >= 0) { + $cmd = shift; + if ($cmd =~ m/-I(.*)/) { + if ($1 ne "") { + $path = $1; + } else { + $path = shift; + } + $inc = $inc." -I".$path; + } elsif ($cmd eq "-f") { + $ff = shift; + } elsif ($cmd eq "-c") { + $keepc = 1; + } elsif ($cmd eq "-interface") { + $class = "interface ".shift(); + } elsif ($cmd eq "-class") { + $class = "class ".shift(); + } elsif ($cmd eq "-type") { + $type = shift; + } elsif ($cmd eq "-shift") { + $shift = shift; + } elsif ($cmd eq "-header") { + $header = shift; + } elsif ($cmd eq "-d") { + $define = shift; + + if ($type eq "long") { + $ctype = "(int64_t)"; + $cfmt = "0x%lxL"; + } else { + $ctype = ""; + $cfmt = "0x%x"; + } + + $process{$define} = { + "type" => "$type", + "class" => "$class", + "header" => "$header", + "define" => "$define", + "ctype" => $ctype, + "cfmt" => $cfmt, + "shift" => "$shift", + }; + push @defines, $define; + } else { + $output = $cmd; + } +} + +die ("No class defined") if ($class eq ""); +die ("No output defined") if ($output eq ""); + +print STDERR "Creating $output for $class\n"; + +open (C,">$output")|| die("Unable to open $output"); + +print C "#include \n"; +%included = (); +foreach $define (@defines) { + %proc = %{$process{$define}}; + + if ($proc{"header"} ne "" && !$included{$proc{header}}) { + print C "#include <$proc{header}>\n"; + $included{$proc{header}} = 1; + } +} + +print C "int main(int argc, char **argv) {\n"; +$date = `date`; +chop $date; + +print C "\tprintf(\"\\n/* This file was autogenerated on $date: */\\n\");\n"; +%included = (); +foreach $define (@defines) { + %proc = %{$process{$define}}; + + if ($proc{"header"} ne "" && !$included{$proc{header}}) { + print C "printf(\"/* from $ff/include/$proc{header} */\\n\");\n"; + $included{$proc{header}} = 1; + } +} + +print C "\tprintf(\"\\npackage au.notzed.jjmpeg;\\n\");\n"; +print C "\tprintf(\"public $class {\\n\");\n"; + +foreach $define (@defines) { + %proc = %{$process{$define}}; + + scan ($process{$define}); +} + +print C "\tprintf(\"}\\n\");\n"; +print C "}\n"; + +exit 0; + +sub scanCopyright { + my $h = $_[0]; + my $copyright = ""; + + open IN,"<$h" || die("Can't find include file: $h"); + while () { + if (m@(copyright.*)@i) { + $copyright .= "\\t * $1\\n"; + } + } + close IN; + print C "printf(\"\\n\t/*\\n\\t * from $h\\n\");\n"; + if ($copyright ne "") { + print C "printf(\"\\t *\\n%s\", \"$copyright\");\n"; + } + print C "printf(\"\\t */\\n\\n\");\n"; +} + +sub scan { + my %o = %{$_[0]}; + my $d = $o{define}; + my $lastc = ""; + my $h = $ff."/include/".$o{header}; + + print STDOUT "Scanning for $d in $h\n"; + + scanCopyright($h); + + open (IN,"cpp -dD -CC -I$ff/include $inc $ff/include/$o{header}|") || die("Can't find include file: $h"); + while () { + # handle multi-line comments + if ($keepc && m@(/\*\*.*)@) { + $lastc = "\t".$1."\\n"; + $lastc =~ s/; + chop; + $lastc .= "\t".$_."\\n"; + } + } + if (m/#define ($d\w*)/) { + my $def = $1; + #print $1."\n"; + if ($lastc ne "") { + #$lastc =~ s@\t+@\t@g; + print C "\tprintf(\"%s\", \"$lastc\");\n"; + } elsif (m@///< (.*)@ || m@/\*\|<(.*)\*@) { + # handle single-line comments, perhaps these should override + print C "\tprintf(\"\\t/**\\n\\t * $1\\n\\t */\\n\");\n"; + } + + print C "\tprintf(\"\\tpublic final static $o{type} $def = $o{cfmt};\\n\", $o{ctype}$def$o{shift});\n"; + + $lastc = ""; + } + } + close IN; + +} diff --git a/src/notzed.jjmpeg/gen/extract-enum.pl b/src/notzed.jjmpeg/gen/extract-enum.pl new file mode 100644 index 0000000..32c1c15 --- /dev/null +++ b/src/notzed.jjmpeg/gen/extract-enum.pl @@ -0,0 +1,114 @@ +#!/usr/bin/perl + +# +# Copyright (C) 2017 Michael Zucchi +# +# This file is part of jjmpeg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +$inc = ""; + +while ($ARGV[0] =~ m/^-I(.*)/) { + shift; + if ($1 ne "") { + $path = $1; + } else { + $path = shift; + } + $inc = $inc." -I".$path; +} + +$t = $ARGV[0]; +$d = $ARGV[1]; +$h = $ARGV[2]; +$c = $ARGV[3]; + +open (C,">$c")|| die("Unable to open $c"); + +print C "#include \n"; +print C "#include <$h>\n"; +print C "int main(int argc, char **argv) {\n"; +$date = `date`; +chop $date; +print C "printf(\"\\n/* This file was autogenerated on $date */\\n\");\n"; +print C "printf(\"/* from $h */\\n\");\n"; +print C "printf(\"package au.notzed.jjmpeg;\\n\");\n"; +print C "printf(\"public interface $t"."Bits {\\n\");\n"; + +scanCopyright($h); + +open (IN,"cpp -dD -C $inc $h|") || die("Can't find include file: $h"); + +while () { + last if m/^enum $t ?\{/; +} + +while () { + last if m/^\};/; + + if (m@///< (.*)@) { + s@\*/@@g; + s@/\*@@g; + print C "\tprintf(\"\\t/**\\n\\t * $1\\n\\t */\\n\");\n"; + } + + if (m/($d\w*) = (0x[0-9x]+)/) { + $id = hex($2); + print C "\tprintf(\"\\tpublic final static int $1 = 0x%x;\\n\", $1);\n"; + } elsif (m/^\s*($d\w*) *= *([-0-9]+)/) { + $id = int($2); + print C "\tprintf(\"\\tpublic final static int $1 = %d;\\n\", $1);\n"; + } elsif (m/^\s*($d\w*) *= *($d\w*)/) { + print C "\tprintf(\"\\tpublic final static int $1 = $2;\\n\");\n"; + } elsif (m/^\s*($d\w*)/) { + print C "\tprintf(\"\\tpublic final static int $1 = 0x%x;\\n\", $1);\n"; + } elsif (m/^#\s*define\s+($d\w*)\s+\w+/) { + print C "\tprintf(\"\\tpublic final static int $1 = 0x%x;\\n\", $1); // #define\n"; + } +} + +#print C "\tprintf(\"\\t/* extra host-specific endianness */\\n\");\n"; +while () { + if (m/^#\s*define\s+($d\w*)\s+\w+/) { + print C "\tprintf(\"\\tpublic final static int $1 = 0x%x;\\n\", $1); // #define\n"; + } +} + +print C "printf(\"}\\n\");\n"; +print C "}\n"; +close C; + +close IN; + +exit 0; + +sub scanCopyright { + my $h = $_[0]; + my $copyright = ""; + + open IN,"<$h" || die("Can't find include file: $h"); + while () { + if (m@(copyright.*)@i) { + $copyright .= "\\t * $1\\n"; + } + } + close IN; + print C "printf(\"\\n\t/*\\n\\t * from $h\\n\");\n"; + if ($copyright ne "") { + print C "printf(\"\\t *\\n%s\", \"$copyright\");\n"; + } + print C "printf(\"\\t */\\n\\n\");\n"; +} diff --git a/src/notzed.jjmpeg/gen/gen.make b/src/notzed.jjmpeg/gen/gen.make new file mode 100644 index 0000000..9bada1d --- /dev/null +++ b/src/notzed.jjmpeg/gen/gen.make @@ -0,0 +1,92 @@ +# +# Copyright (C) 2017 Michael Zucchi +# +# This file is part of jjmpeg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +extract_enum=src/notzed.jjmpeg/gen/extract-enum.pl +extract_defines=src/notzed.jjmpeg/gen/extract-defines.pl + +# Relative to module! +notzed.jjmpeg_JAVA_GENERATED = \ + au/notzed/jjmpeg/AVChannelLayoutBits.java \ + au/notzed/jjmpeg/AVCodecBits.java \ + au/notzed/jjmpeg/AVCodecContextBits.java \ + au/notzed/jjmpeg/AVCodecIDBits.java \ + au/notzed/jjmpeg/AVDiscardBits.java \ + au/notzed/jjmpeg/AVErrorBits.java \ + au/notzed/jjmpeg/AVMediaTypeBits.java \ + au/notzed/jjmpeg/AVOptionsBits.java \ + au/notzed/jjmpeg/AVPixelFormatBits.java \ + au/notzed/jjmpeg/AVSampleFormatBits.java + +HOST_CC=cc +HOST_CPPFLAGS=-I$(FFMPEG_HOME)/include + +$(notzed.jjmpeg_genjavadir)/%.java: $(notzed.jjmpeg_gendir)/%-gen + @install -d $(@D) + $< > $@ || ( rm $@ ; exit 1) + +$(notzed.jjmpeg_gendir)/%-gen: $(notzed.jjmpeg_gendir)/%-gen.c + $(HOST_CC) $(HOST_CPPFLAGS) -o $@ $< + +# dependent on itself as it contains buisiness logic in the rules +tmp:=$(notzed.jjmpeg_gendir)/au/notzed/jjmpeg +dep:=src/notzed.jjmpeg/gen/gen.make + +$(tmp)/AVCodecIDBits-gen.c: $(extract_enum) $(dep) + @install -d $(@D) + perl $(extract_enum) -I$(FFMPEG_HOME)/include AVCodecID AV_CODEC_ID_ $(FFMPEG_HOME)/include/libavcodec/avcodec.h $@ + +$(tmp)/AVPixelFormatBits-gen.c: $(extract_enum) $(dep) + @install -d $(@D) + perl $(extract_enum) -I$(FFMPEG_HOME)/include AVPixelFormat AV_PIX_FMT_ $(FFMPEG_HOME)/include/libavutil/pixfmt.h $@ + +$(tmp)/AVSampleFormatBits-gen.c: $(extract_enum) $(dep) + @install -d $(@D) + perl $(extract_enum) -I$(FFMPEG_HOME)/include AVSampleFormat AV_SAMPLE_FMT_ $(FFMPEG_HOME)/include/libavutil/samplefmt.h $@ + +$(tmp)/AVMediaTypeBits-gen.c: $(extract_enum) $(dep) + @install -d $(@D) + perl $(extract_enum) -I$(FFMPEG_HOME)/include AVMediaType AVMEDIA_TYPE_ $(FFMPEG_HOME)/include/libavutil/avutil.h $@ + +$(tmp)/AVDiscardBits-gen.c: $(extract_enum) $(dep) + @install -d $(@D) + perl $(extract_enum) -I$(FFMPEG_HOME)/include AVDiscard AVDISCARD_ $(FFMPEG_HOME)/include/libavcodec/avcodec.h $@ + +$(tmp)/AVChannelLayoutBits-gen.c: $(extract_enum) $(dep) + @install -d $(@D) + perl $(extract_defines) -f $(FFMPEG_HOME) -interface AVChannelLayoutBits -header libavutil/channel_layout.h -type long -d AV_CH_LAYOUT_ $@ + +$(tmp)/AVErrorBits-gen.c: $(extract_defines) $(dep) + @install -d $(@D) + perl $(extract_defines) -f $(FFMPEG_HOME) -interface AVErrorBits -header libavutil/avutil.h -d AVERROR_ $@ + +$(tmp)/AVCodecContextBits-gen.c: $(extract_defines) $(dep) + @install -d $(@D) + perl $(extract_defines) -c -f ${FFMPEG_HOME} \ + -interface AVCodecContextBits -header libavcodec/avcodec.h \ + -type long -d AV_CODEC_FLAG_ -shift "<<32" -d AV_CODEC_FLAG2_ \ + -shift "" -type int -d AV_INPUT_BUFFER_ \ + $@ + +$(tmp)/AVCodecBits-gen.c: $(extract_defines) $(dep) + @install -d $(@D) + perl $(extract_defines) -c -interface AVCodecBits -f $(FFMPEG_HOME) -header libavcodec/avcodec.h -d AV_CODEC_CAP_ $@ + +$(tmp)/AVOptionsBits-gen.c: $(extract_defines) $(dep) + @install -d $(@D) + perl $(extract_defines) -c -interface AVOptionsBits -f $(FFMPEG_HOME) -header libavutil/opt.h -d AV_OPT_ $@ diff --git a/src/notzed.jjmpeg/jni/extract-proto.pl b/src/notzed.jjmpeg/jni/extract-proto.pl new file mode 100755 index 0000000..835eb7f --- /dev/null +++ b/src/notzed.jjmpeg/jni/extract-proto.pl @@ -0,0 +1,168 @@ +#!/usr/bin/perl + +$ffmpeg_home = "/opt/ffmpeg/4.0"; + +$args = "$0 ".join " ", @ARGV; + +while ($#ARGV >= 0) { + my $cmd = shift; + if ($cmd eq "-f") { + $ffmpeg_home = shift; + } else { + $in = $cmd; + } +} + +die ("No input file") if ! -f $in; + +$header = ""; +$librart = ""; +@functions = (); +@headers = (); +%proto = (); +$last_library = ""; +$mode = ""; + +open IN,"<$in"; + +while () { + chop; + + if (m/^#/) { + next; + } elsif (m/^header (.*) (.*) \{/) { + $library = $1; + $header = $2; + push @headers, $header; + + %proto = (); + open PROTO, "cproto -x -I$ffmpeg_home/include $ffmpeg_home/include/$header|"; + + while () { + chop; + $cproto = $_; + if (m/([a-zA-Z0-9_]*)\(/) { + $func = $1; + $proto{$func} = $cproto; + } + } + close PROTO; + + if ($library ne $last_library) { + push @functions, "#$library"; + $last_library = $library; + } + $mode = "proto"; + } elsif (m/^java (.*) (.*) \{/) { + # wouldn't it be nice to get this from the .class ... + $name = $1; + $class = $2; + $mode = "java"; + push @classes,"#$name:$class"; + } elsif (m/^}$/) { + $mode = ""; + } elsif ($mode eq "proto") { + if (m/\s*([a-zA-Z0-9_]+)/) { + my $func= $1; + my $cproto = $proto{$func}; + + die ("No function $func in $header") if !defined($cproto); + + push @functions, $cproto; + } + } elsif ($mode eq "java") { + if (m/(static)? *([\w<>]*) *, *([\[\w<>\(\)\/;]*)/) { + push @classes,"$1,$2,$3"; + } + } +} +close IN; + +$date = `date`; +chop $date; +print "/* This file was autogenerated on $date: */\n"; +print "/* $0 $args */\n"; + +if ($#headers >= 0) { + # Handle C prototype mappings + foreach $h (@headers) { + print "#include <$h>\n"; + } + + print "static struct functable {\n"; + foreach $func (@functions) { + if ($func =~ m/^\#(.+)/) { + print "\t/* lib$1 */\n"; + } else { + $dfunc = $func; + $dfunc =~ s/([a-zA-Z0-9_]*)(\(.*;)/(*\1)\2/; + print "\t$dfunc\n"; + } + } + print "} fn;\n"; + print "static const char *fn_names =\n"; + foreach $func (@functions) { + if ($func =~ m/^(\#.+)/) { + print "\t\"$func\\0\"\n"; + } else { + $func =~ m/([a-zA-Z0-9_]*)\(/; + print "\t\"$1\\0\"\n"; + } + } + print "\t;\n"; +} + +if ($#classes >= 0) { + # Handle java defines + $name = ""; + $class = ""; + print "static struct {\n"; + foreach $func (@classes) { + if ($func =~ m/^#(.+):(.+)/) { + $name = $1; + $class = $2; + print "\t// $class\n"; + print "\tjclass $name"."_classid;\n"; + printf "#define $name"."_classid java.$name"."_classid\n"; + } elsif ($func =~ m/(.*),(.+),\((.*)\).*/) { + my $method = $2; + my $args = $3; + + $args =~ s/L.*?;/l/g; + $args =~ s/\[/_/g; + $args =~ tr/A-Z/a-z/; + + $method =~ s//new/; + + print "\tjmethodID $name"."_$method"."_$args;\n"; + print "#define $name"."_$method"."_$args java.$name"."_$method"."_$args\n"; + } elsif ($func =~ m/(.*),(.+),(.+)/) { + my $field = $2; + my $type = $3; + + print "\tjfieldID $name"."_$field;\n"; + print "#define $name"."_$field java.$name"."_$field\n"; + } else { + die("can't parse java signature $func"); + } + } + print "} java;\n"; + print "static const char *java_names =\n"; + foreach $func (@classes) { + if ($func =~ m/^#(.+):(.+)/) { + $name = $1; + $class = $2; + + print "\t\"#$class\\0\"\n"; + } elsif ($func =~ m/static,(.*),(.*\(.*\).*)/) { + print "\t\":$1\\0$2\\0\"\n"; + } elsif ($func =~ m/,(.*),(.*\(.*\).*)/) { + print "\t\".$1\\0$2\\0\"\n"; + } elsif ($func =~ m/static,(.*),(.*)/) { + print "\t\";$1\\0$2\\0\"\n"; + } elsif ($func =~ m/,(.*),(.*)/) { + print "\t\",$1\\0$2\\0\"\n"; + } + } + print "\t;\n"; +} diff --git a/src/notzed.jjmpeg/jni/jj-avcodec.c b/src/notzed.jjmpeg/jni/jj-avcodec.c new file mode 100644 index 0000000..5b4b59b --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avcodec.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "au_notzed_jjmpeg_AVCodec.h" + +#include "jjmpeg.h" + +#include "jj-avcodec.h" +#undef DLCALL +#define DLCALL(x) (fn.x) + +jint AVCodec_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ +/* Fields */ + +GET_string(AVCodec, Name, name) +GET_string(AVCodec, LongName, long_name) +GET_prim(jint, AVCodec, ID, id) +GET_prim(jint, AVCodec, Capabilities, capabilities) +GET_prim(jint, AVCodec, MaxLowres, max_lowres) + +JNIEXPORT jobjectArray JNICALL Java_au_notzed_jjmpeg_AVCodec_getFramerates +(JNIEnv *env, jobject jo) { + AVCodec *o = NativeZ_getP(env, jo); + jobjectArray ret = NULL; + + if (o->supported_framerates) { + int len = 0; + + while (o->supported_framerates[len].den != 0) + len++; + ret = AVRational_newArray(env, len); + for (int i=0;iSetObjectArrayElement(env, ret, i, AVRational_new(env, o->supported_framerates[i])); + } + + return ret; +} + +JNIEXPORT jobjectArray JNICALL Java_au_notzed_jjmpeg_AVCodec_getPixelFormats +(JNIEnv *env, jobject jo) { + AVCodec *o = NativeZ_getP(env, jo); + + return jjnewIntArrayT(env, o->pix_fmts, -1); +} + +JNIEXPORT jintArray JNICALL Java_au_notzed_jjmpeg_AVCodec_getSampleRates +(JNIEnv *env, jobject jo) { + AVCodec *o = NativeZ_getP(env, jo); + + return jjnewIntArrayT(env, o->supported_samplerates, 0); +} + +JNIEXPORT jobjectArray JNICALL Java_au_notzed_jjmpeg_AVCodec_getSampleFormats +(JNIEnv *env, jobject jo) { + AVCodec *o = NativeZ_getP(env, jo); + + return jjnewIntArrayT(env, o->sample_fmts, -1); +} + +JNIEXPORT jlongArray JNICALL Java_au_notzed_jjmpeg_AVCodec_getChannelLayouts +(JNIEnv *env, jobject jo) { + AVCodec *o = NativeZ_getP(env, jo); + jlongArray ret = NULL; + + if (o->pix_fmts) { + int len = 0; + + while (o->channel_layouts[len]) + len++; + ret = (*env)->NewLongArray(env, len); + (*env)->SetLongArrayRegion(env, ret, 0, len, o->channel_layouts); + } + + return ret; +} + +/* ********************************************************************** */ +/* Methods */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodec_release +(JNIEnv *env, jclass jc, jlong p) { +} + +/* *** */ + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVCodec_getVersion +(JNIEnv *env, jclass jc) { + return DLCALL(avcodec_version)(); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVCodec_getConfiguration +(JNIEnv *env, jclass jc) { + return nativez_NewString(env, DLCALL(avcodec_configuration)()); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVCodec_getLicense +(JNIEnv *env, jclass jc) { + return nativez_NewString(env, DLCALL(avcodec_license)()); +} + +/* *** */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodec_registerAll +(JNIEnv *env, jclass jc) { + DLCALL(avcodec_register_all)(); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodec_next +(JNIEnv *env, jclass jc, jobject jo) { + AVCodec *o = NativeZ_getP(env, jo); + o = DLCALL(av_codec_next)(o); + return NativeZ_refer(env, jc, o); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodec_findDecoder__I +(JNIEnv *env, jclass jc, jint id) { + AVCodec *o = DLCALL(avcodec_find_decoder)(id); + return NativeZ_refer(env, jc, o); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodec_findDecoder__Ljava_lang_String_2 +(JNIEnv *env, jclass jc, jstring jname) { + const char *name = nativez_GetString(env, jname); + AVCodec *o; + + o = DLCALL(avcodec_find_decoder_by_name)(name); + nativez_ReleaseString(env, jname, name); + return NativeZ_refer(env, jc, o); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodec_findEncoder__I +(JNIEnv *env, jclass jc, jint id) { + return NativeZ_refer(env, jc, DLCALL(avcodec_find_encoder)(id)); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodec_findEncoder__Ljava_lang_String_2 +(JNIEnv *env, jclass jc, jstring jname) { + const char *name = nativez_GetString(env, jname); + AVCodec *o; + + o = DLCALL(avcodec_find_encoder_by_name)(name); + nativez_ReleaseString(env, jname, name); + return NativeZ_refer(env, jc, o); +} diff --git a/src/notzed.jjmpeg/jni/jj-avcodec.def b/src/notzed.jjmpeg/jni/jj-avcodec.def new file mode 100644 index 0000000..1ac4ae9 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avcodec.def @@ -0,0 +1,14 @@ +header avcodec libavcodec/avcodec.h { + avcodec_version + avcodec_configuration + avcodec_license + + avcodec_register_all + + av_codec_next + + avcodec_find_decoder + avcodec_find_decoder_by_name + avcodec_find_encoder + avcodec_find_encoder_by_name +} diff --git a/src/notzed.jjmpeg/jni/jj-avcodeccontext.c b/src/notzed.jjmpeg/jni/jj-avcodeccontext.c new file mode 100644 index 0000000..c852d76 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avcodeccontext.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "au_notzed_jjmpeg_AVCodecContext.h" + +#include "jjmpeg.h" +#include "jj-avcodeccontext.h" + +#undef DLCALL +#define DLCALL(x) (fn.x) + +jint AVCodecContext_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ +/* Fields */ + +/* General options */ + +GS_int(AVCodecContext, CodecType, codec_type) +GS_int(AVCodecContext, CodecID, codec_id) +GS_int(AVCodecContext, CodecTag, codec_tag) +GS_long(AVCodecContext, BitRate, bit_rate) +GS_int(AVCodecContext, BitRateTolerance, bit_rate_tolerance) +GS_int(AVCodecContext, GlobalQuality, global_quality) +GS_int(AVCodecContext, CompressionLevel, compression_level) +//GS_int(AVCodecContext, Flags1, flags) +//GS_int(AVCodecContext, Flags2, flags2) + +JNIEXPORT jlong JNICALL MAKE_JJNAME(AVCodecContext, getFlags) +(JNIEnv *env , jobject jo) { + AVCodecContext *o = NativeZ_getP(env, jo); + uint64_t flags; + + flags = (uint64_t)o->flags | ((uint64_t)o->flags2 << 32); + + return flags; +} + +JNIEXPORT void JNICALL MAKE_JJNAME(AVCodecContext, setFlags) + (JNIEnv *env, jobject jo, jlong mask, jlong flags) { + AVCodecContext *o = NativeZ_getP(env, jo); + int mask1 = (int)mask; + int mask2 = (int)(mask>>32); + int flags1 = (int)flags; + int flags2 = (int)(flags>>32); + + o->flags = (o->flags & ~mask1) | (mask1 & flags1); + o->flags2 = (o->flags2 & ~mask2) | (mask2 & flags2); +} + +JNIEXPORT jobject JNICALL MAKE_JJNAME(AVCodecContext, getTimeBase) +(JNIEnv *env , jobject jo) { + AVCodecContext *o = NativeZ_getP(env, jo); + return AVRational_new(env, o->time_base); +} + +JNIEXPORT void JNICALL MAKE_JJNAME(AVCodecContext, setTimeBase) +(JNIEnv *env, jobject jo, jobject jtb) { + AVCodecContext *o = NativeZ_getP(env, jo); + o->time_base = AVRational_get(env, jtb); +} + +GS_int(AVCodecContext, TicksPerFrame, ticks_per_frame) +GET_prim(jint, AVCodecContext, Delay, delay) + +/* Video options */ +GS_int(AVCodecContext, Width, width) +GS_int(AVCodecContext, Height, height) +GS_value(AVRational_new, AVRational_get, AVCodecContext, AspectRatio, sample_aspect_ratio) +GS_int(AVCodecContext, GOPSize, gop_size) + +GS_int(AVCodecContext, PixelFormat, pix_fmt) + +GS_int(AVCodecContext, MaxBFrames, max_b_frames) +GS_float(AVCodecContext, BQuantFactor, b_quant_factor) +GS_float(AVCodecContext, BQuantOffset, b_quant_offset) + +GET_bool(AVCodecContext, hasBFrames, has_b_frames) + +GS_float(AVCodecContext, IQuantFactor, i_quant_factor) +GS_float(AVCodecContext, IQuantOffset, i_quant_offset) + +GS_int(AVCodecContext, MBDecision, mb_decision) + +/* Audio options */ +GS_int(AVCodecContext, SampleRate, sample_rate) +GS_int(AVCodecContext, NumChannels, channels) +GS_int(AVCodecContext, SampleFormat, sample_fmt) +GS_int(AVCodecContext, FrameSize, frame_size) +GET_prim(jint, AVCodecContext, FrameNumber, frame_number) +GS_long(AVCodecContext, ChannelLayout, channel_layout) +GS_long(AVCodecContext, RequestChannelLayout, request_channel_layout) +GS_int(AVCodecContext, StrictStdCompliance, strict_std_compliance) + +GS_int(AVCodecContext, ErrorConcealment, error_concealment) +GS_int(AVCodecContext, ErrorRecognition, err_recognition) // !! + +GS_int(AVCodecContext, DCTAlgo, dct_algo) +GS_int(AVCodecContext, IDCTAlgo, idct_algo) + +GS_int(AVCodecContext, ThreadCount, thread_count) +GS_int(AVCodecContext, Profile, profile) + +/* ********************************************************************** */ +/* Methods */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodecContext_release +(JNIEnv *env, jclass jc, jlong p) { + AVCodecContext *cc = (void *)(intptr_t)p; + + DLCALL(avcodec_free_context)(&cc); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodecContext_allocContext +(JNIEnv *env, jclass jc, jobject jcodec) { + AVCodec *codec = NativeZ_getP(env, jcodec); + AVCodecContext *cc = DLCALL(avcodec_alloc_context3)(codec); + + // NULL means what? Out of memory? + + return NativeZ_create(env, jc, cc); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodecContext_open +(JNIEnv *env, jobject jcc, jobject jcodec, jobject jdict) { + AVCodecContext *cc = NativeZ_getP(env, jcc); + AVCodec *codec = NativeZ_getP(env, jcodec); + AVDictionary *dict = AVDictionary_get(env, jdict); + int res; + + res = DLCALL(avcodec_open2)(cc, codec, &dict); + + AVDictionary_set(env, jdict, dict, 1); + + if (res < 0) + jjthrowAVIOException(env, res, "Opening Codec"); +} + +/* *** */ + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodecContext_alignDimensions +(JNIEnv *env, jobject jcc, jobject jdims) { + AVCodecContext *cc = NativeZ_getP(env, jcc); + AVSize dims = AVSize_get(env, jdims); + + DLCALL(avcodec_align_dimensions)(cc, &dims.width, &dims.height); + + return AVSize_new(env, dims); +} + +/* *** */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodecContext_flushBuffers +(JNIEnv *env, jobject jcc) { + AVCodecContext *cc = NativeZ_getP(env, jcc); + + DLCALL(avcodec_flush_buffers)(cc); +} + +static jboolean returnError(JNIEnv *env, int res, const char *what) { + if (res < 0) { + if (AVUNERROR(res) != EAGAIN && res != AVERROR_EOF) + jjthrowAVIOException(env, res, what); + return JNI_FALSE; + } + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL Java_au_notzed_jjmpeg_AVCodecContext_sendPacket +(JNIEnv *env, jobject jcc, jobject jpacket) { + AVCodecContext *cc = NativeZ_getP(env, jcc); + AVPacket *packet = NativeZ_getP(env, jpacket); + int res; + + res = DLCALL(avcodec_send_packet)(cc, packet); + return returnError(env, res, "avcodec_send_packet"); +} + +JNIEXPORT jboolean JNICALL Java_au_notzed_jjmpeg_AVCodecContext_receivePacket +(JNIEnv *env, jobject jcc, jobject jpacket) { + AVCodecContext *cc = NativeZ_getP(env, jcc); + AVPacket *packet = NativeZ_getP(env, jpacket); + int res; + + res = DLCALL(avcodec_receive_packet)(cc, packet); + return returnError(env, res, "avcodec_receive_packet"); +} + +JNIEXPORT jboolean JNICALL Java_au_notzed_jjmpeg_AVCodecContext_sendFrame +(JNIEnv *env, jobject jcc, jobject jframe) { + AVCodecContext *cc = NativeZ_getP(env, jcc); + AVFrame *frame = NativeZ_getP(env, jframe); + int res; + + res = DLCALL(avcodec_send_frame)(cc, frame); + return returnError(env, res, "avcodec_send_frame"); +} + +JNIEXPORT jboolean JNICALL Java_au_notzed_jjmpeg_AVCodecContext_receiveFrame +(JNIEnv *env, jobject jcc, jobject jframe) { + AVCodecContext *cc = NativeZ_getP(env, jcc); + AVFrame *frame = NativeZ_getP(env, jframe); + int res; + + res = DLCALL(avcodec_receive_frame)(cc, frame); + return returnError(env, res, "avcodec_receive_frame"); +} diff --git a/src/notzed.jjmpeg/jni/jj-avcodeccontext.def b/src/notzed.jjmpeg/jni/jj-avcodeccontext.def new file mode 100644 index 0000000..3a1a327 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avcodeccontext.def @@ -0,0 +1,13 @@ +header avcodec libavcodec/avcodec.h { + avcodec_alloc_context3 + avcodec_free_context + avcodec_open2 + avcodec_flush_buffers + + avcodec_align_dimensions + + avcodec_send_packet + avcodec_receive_packet + avcodec_send_frame + avcodec_receive_frame +} diff --git a/src/notzed.jjmpeg/jni/jj-avcodecparameters.c b/src/notzed.jjmpeg/jni/jj-avcodecparameters.c new file mode 100644 index 0000000..16622e6 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avcodecparameters.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "au_notzed_jjmpeg_AVCodecParameters.h" + +#include "jjmpeg.h" + +#include "jj-avcodecparameters.h" +#undef DLCALL +#define DLCALL(x) (fn.x) + +jint AVCodecParameters_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ +/* Fields */ + +GS_int(AVCodecParameters, CodecType, codec_type) +GS_int(AVCodecParameters, CodecID, codec_id) +GS_int(AVCodecParameters, CodecTag, codec_tag) +GS_long(AVCodecParameters, BitRate, bit_rate) +GS_int(AVCodecParameters, PixelFormat, format) +GS_int(AVCodecParameters, Width, width) +GS_int(AVCodecParameters, Height, height) +GS_value(AVRational_new, AVRational_get, AVCodecParameters, AspectRatio, sample_aspect_ratio) + +GS_int(AVCodecParameters, SampleFormat, format) +GS_long(AVCodecParameters, ChannelLayout, channel_layout) +GS_int(AVCodecParameters, NumChannels, channels) +GS_int(AVCodecParameters, SampleRate, sample_rate) + +/* ********************************************************************** */ +/* Methods */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodecParameters_release +(JNIEnv *env, jclass jc, jlong jcp) { + AVCodecParameters *cp = (void *)(uintptr_t)jcp; + + DLCALL(avcodec_parameters_free)(&cp); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVCodecParameters_alloc +(JNIEnv *env, jclass jc) { + return NativeZ_create(env, jc, DLCALL(avcodec_parameters_alloc)()); +} + +/* **************************************** */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodecParameters_copy +(JNIEnv *env, jobject jdst, jobject jsrc) { + AVCodecParameters *dst = NativeZ_getP(env, jdst); + AVCodecParameters *src = NativeZ_getP(env, jsrc); + + DLCALL(avcodec_parameters_copy)(dst, src); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodecParameters_fromContext +(JNIEnv *env, jobject jdst, jobject jsrc) { + AVCodecParameters *dst = NativeZ_getP(env, jdst); + AVCodecContext *src = NativeZ_getP(env, jsrc); + + DLCALL(avcodec_parameters_from_context)(dst, src); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVCodecParameters_toContext +(JNIEnv *env, jobject jsrc, jobject jdst) { + AVCodecParameters *src = NativeZ_getP(env, jsrc); + AVCodecContext *dst = NativeZ_getP(env, jdst); + + DLCALL(avcodec_parameters_to_context)(dst, src); +} + diff --git a/src/notzed.jjmpeg/jni/jj-avcodecparameters.def b/src/notzed.jjmpeg/jni/jj-avcodecparameters.def new file mode 100644 index 0000000..56545a7 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avcodecparameters.def @@ -0,0 +1,7 @@ +header avcodec libavcodec/avcodec.h { + avcodec_parameters_alloc + avcodec_parameters_free + avcodec_parameters_copy + avcodec_parameters_from_context + avcodec_parameters_to_context +} diff --git a/src/notzed.jjmpeg/jni/jj-avdevice.c b/src/notzed.jjmpeg/jni/jj-avdevice.c new file mode 100644 index 0000000..6db0739 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avdevice.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + libavdevice + + This is an optional library, if not present then any calls return NullPointerException. + */ +#include + +#include "au_notzed_jjmpeg_AVDevice.h" + +#include "jjmpeg.h" +#include "jj-avdevice.h" + +jint AVDevice_OnLoad(JavaVM *vmi, JNIEnv *env) { + if (jj_ResolveReferences(env, java_names, &java)) + return -1; + jj_ResolveFunctions(env, fn_names, &fn); + return 0; +} + +/* ********************************************************************** */ +/* Methods */ + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVDevice_getVersion +(JNIEnv *env, jclass jc) { + DLCHECK_RET(env, avdevice_version, -1); + return DLCALL(avdevice_version)(); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVDevice_getConfiguration +(JNIEnv *env, jclass jc) { + DLCHECK_RET(env, avdevice_configuration, NULL); + return nativez_NewString(env, DLCALL(avdevice_configuration)()); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVDevice_getLicense +(JNIEnv *env, jclass jc) { + DLCHECK_RET(env, avdevice_license, NULL); + return nativez_NewString(env, DLCALL(avdevice_license)()); +} + +/* *** */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVDevice_registerAll +(JNIEnv *env, jclass jc) { + DLCHECK_RET(env, avdevice_register_all, ); + DLCALL(avdevice_register_all)(); +} + +/* *** */ + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVDevice_nextAudioInputDevice +(JNIEnv *env, jclass jc, jobject jiformat) { + AVInputFormat *iformat = NativeZ_getP(env, jiformat); + + DLCHECK_RET(env, av_input_audio_device_next, NULL); + + iformat = DLCALL(av_input_audio_device_next)(iformat); + return NativeZ_refer(env, AVInputFormat_classid, iformat); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVDevice_nextVideoInputDevice +(JNIEnv *env, jclass jc, jobject jiformat) { + AVInputFormat *iformat = NativeZ_getP(env, jiformat); + + DLCHECK_RET(env, av_input_video_device_next, NULL); + + iformat = DLCALL(av_input_video_device_next)(iformat); + return NativeZ_refer(env, AVInputFormat_classid, iformat); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVDevice_nextAudioOutputDevice +(JNIEnv *env, jclass jc, jobject joformat) { + AVOutputFormat *oformat = NativeZ_getP(env, joformat); + + DLCHECK_RET(env, av_output_audio_device_next, NULL); + + oformat = DLCALL(av_output_audio_device_next)(oformat); + return NativeZ_refer(env, AVOutputFormat_classid, oformat); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVDevice_nextVideoOutputDevice +(JNIEnv *env, jclass jc, jobject joformat) { + AVOutputFormat *oformat = NativeZ_getP(env, joformat); + + DLCHECK_RET(env, av_output_video_device_next, NULL); + + oformat = DLCALL(av_output_video_device_next)(oformat); + return NativeZ_refer(env, AVOutputFormat_classid, oformat); +} diff --git a/src/notzed.jjmpeg/jni/jj-avdevice.def b/src/notzed.jjmpeg/jni/jj-avdevice.def new file mode 100644 index 0000000..70c55b5 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avdevice.def @@ -0,0 +1,16 @@ +header avdevice libavdevice/avdevice.h { + avdevice_version + avdevice_configuration + avdevice_license + avdevice_register_all + av_input_audio_device_next + av_input_video_device_next + av_output_audio_device_next + av_output_video_device_next +} + +java AVInputFormat au/notzed/jjmpeg/AVInputFormat { +} + +java AVOutputFormat au/notzed/jjmpeg/AVOutputFormat { +} \ No newline at end of file diff --git a/src/notzed.jjmpeg/jni/jj-avformatcontext.c b/src/notzed.jjmpeg/jni/jj-avformatcontext.c new file mode 100644 index 0000000..d7c0160 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avformatcontext.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "au_notzed_jjmpeg_AVFormatContext.h" + +#include "jjmpeg.h" +#include "jj-avformatcontext.h" + +/* + Note: AVFormatContext.opaque is used to store a pointer to the free function to use. + + avformat_open_input() requires the use of avformat_close_input(). + + avformat_alloc_context() and avformat_alloc_output_context2() + require the use of avformat_free_context(). + */ + +jint AVFormatContext_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveReferences(env, java_names, &java) + || jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ +/* Fields */ + +REF_object(AVInputFormat, AVFormatContext, InputFormat, iformat) +REF_object(AVOutputFormat, AVFormatContext, OutputFormat, oformat) +GS_object(AVIOContext, AVFormatContext, IOContext, pb) + +GET_prim(jint, AVFormatContext, CtxFlags, ctx_flags) +GET_prim(jint, AVFormatContext, NumStreams, nb_streams) + +JNIEXPORT jobject JNICALL MAKE_JJNAME(AVFormatContext, getStream) +(JNIEnv *env, jobject jo, jint index) { + AVFormatContext *o = NativeZ_getP(env, jo); + + if (index >= 0 && index < o->nb_streams) + return NativeZ_refer(env, AVStream_classid, o->streams[index]); + + nativez_ThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "index outside of nb_streams"); + return NULL; +} + +GET_prim(jlong, AVFormatContext, StartTime, start_time) +GET_prim(jlong, AVFormatContext, Duration, duration) +GET_prim(jlong, AVFormatContext, BitRate, bit_rate) + +GS_int(AVFormatContext, Flags, flags) + +GS_long(AVFormatContext, ProbeSize, probesize) +GS_long(AVFormatContext, MaxAnalyseDuration, max_analyze_duration) + +GS_int(AVFormatContext, VideoCodecID, video_codec_id) +GS_int(AVFormatContext, AudioCodecID, audio_codec_id) +GS_int(AVFormatContext, SubtitleCodecID, subtitle_codec_id) + +// Groan +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFormatContext_getMetadata +(JNIEnv *env, jobject jo) { + AVFormatContext *o = NativeZ_getP(env, jo); + jobject jdict = AVDictionary_new(env); + + AVDictionary_set(env, jdict, o->metadata, 0); + + return jdict; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_setMetadata +(JNIEnv *env, jobject jo, jobject jdict) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVDictionary *dict = AVDictionary_get(env, jdict); + + // if (check exception) return; + + DLCALL(av_dict_free)(&o->metadata); + o->metadata = dict; +} + + +SET_prim(jint, AVFormatContext, Debug, debug) +GS_int(AVFormatContext, Seek2Any, seek2any) + +/* ********************************************************************** */ +/* Methods */ + +JNIEXPORT void JNICALL MAKE_JJNAME(AVFormatContext, release) +(JNIEnv *env, jclass jc, jlong p) { + AVFormatContext *o = (void *)p; + + // And of course they take different parameters. + if (o->opaque == DLCALL(avformat_free_context)) + DLCALL(avformat_free_context)(o); + else if (o->opaque == DLCALL(avformat_close_input)) + DLCALL(avformat_close_input)(&o); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFormatContext_getVersion +(JNIEnv *env, jclass jc) { + return DLCALL(avformat_version)(); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVFormatContext_getConfiguration +(JNIEnv *env, jclass jc) { + return nativez_NewString(env, DLCALL(avformat_configuration)()); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVFormatContext_getLicense +(JNIEnv *env, jclass jc) { + return nativez_NewString(env, DLCALL(avformat_license)()); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_registerAll +(JNIEnv *env, jclass jc) { + DLCALL(av_register_all)(); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_networkInit +(JNIEnv *env, jclass jc) { + DLCALL(avformat_network_init)(); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_networkDeinit +(JNIEnv *env, jclass jc) { + DLCALL(avformat_network_deinit)(); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFormatContext_allocContext__ +(JNIEnv *env, jclass jc) { + AVFormatContext *ctx = DLCALL(avformat_alloc_context)(); + + if (ctx) { + ctx->opaque = DLCALL(avformat_free_context); + return NativeZ_create(env, jc, ctx); + } + + nativez_ThrowOutOfMemoryError(env, "Allocating context"); + return NULL; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFormatContext_newStream +(JNIEnv *env, jobject jo, jobject jc) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVCodec *c = NativeZ_getP(env, jc); + + // avformatcontext manages the memory + AVStream *s = DLCALL(avformat_new_stream)(o, c); + if (s) + return NativeZ_refer(env, AVStream_classid, s); + + nativez_ThrowOutOfMemoryError(env, "Allocating stream"); + return NULL; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFormatContext_allocContext__Lau_notzed_jjmpeg_AVOutputFormat_2Ljava_lang_String_2Ljava_lang_String_2 +(JNIEnv *env, jclass jc, jobject joformat, jstring jformat_name, jstring jfilename) { + AVOutputFormat *oformat = NativeZ_getP(env, joformat); + const char *format_name = nativez_GetString(env, jformat_name); + const char *filename = nativez_GetString(env, jfilename); + int res; + AVFormatContext *ctx = NULL; + + res = DLCALL(avformat_alloc_output_context2)(&ctx, oformat, format_name, filename); + + nativez_ReleaseString(env, jformat_name, format_name); + nativez_ReleaseString(env, jfilename, filename); + + // One assumes ctx==NULL is enough to indicate error + if (ctx) { + ctx->opaque = DLCALL(avformat_free_context); + return NativeZ_create(env, jc, ctx); + } + + jjthrowAVIOException(env, res, "allocOutputContext"); + return NULL; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFormatContext_openInput +(JNIEnv *env, jclass jc, jstring jname, jobject jiformat, jobject joptions) { + if (!nativez_NonNull(env, "name == NULL", jname)) return NULL; + const char *name = nativez_GetString(env, jname); + AVFormatContext *ctx = NULL; + AVInputFormat *iformat = NativeZ_getP(env, jiformat); + AVDictionary *options = AVDictionary_get(env, joptions); + int res; + jobject jret = NULL; + + // TODO: this is supposed to take a user-supplied context, ok that's easy. + // Problem is it frees any data if it fails. Nice one. + // So need to clear the 'P' value if it fails? + // Messy messy. + // Just implement the obvious case first + + res = DLCALL(avformat_open_input)(&ctx, name, iformat, &options); + + AVDictionary_set(env, joptions, options, 1); + + switch (res) { + case 0: + ctx->opaque = DLCALL(avformat_close_input); + jret = NativeZ_create(env, jc, ctx); + break; + case AVERROR(ENOENT): + nativez_ThrowException(env, "java/io/FileNotFoundException", name); + break; + default: + jjthrowAVIOException(env, res, name); + break; + } + + nativez_ReleaseString(env, jname, name); + + return jret; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_findStreamInfo +(JNIEnv *env, jobject jo, jobjectArray joptions) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVDictionary **options = NULL; + jsize len = 0; + int res; + + if (joptions) { + len = (*env)->GetArrayLength(env, joptions); + if (len != o->nb_streams) { + nativez_ThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "index outside of nb_streams"); + return; + } + + options = alloca(sizeof(*options) * len); + for (int i=0;iGetObjectArrayElement(env, joptions, i)); + } + + res = DLCALL(avformat_find_stream_info)(o, options); + + if (res >= 0) { + for (int i=0;iGetObjectArrayElement(env, joptions, i), options[i], 1); + } + return; + } + + jjthrowAVIOException(env, res, "findStreamInfo"); +} + +JNIEXPORT jboolean JNICALL Java_au_notzed_jjmpeg_AVFormatContext_readFrame +(JNIEnv *env, jobject jo, jobject jpkt) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVPacket *pkt = NativeZ_getP(env, jpkt); + int res; + + res = DLCALL(av_read_frame)(o, pkt); + + if (res >= 0) + return JNI_TRUE; + + if (res == AVERROR_EOF) + return JNI_FALSE; + + jjthrowAVIOException(env, res, "readFrame"); + return JNI_FALSE; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_seekFrame +(JNIEnv *env, jobject jo, jint stream_index, jlong timestamp, jint flags) { + AVFormatContext *o = NativeZ_getP(env, jo); + int res; + + res = DLCALL(av_seek_frame)(o, stream_index, timestamp, flags); + + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "seekFrame"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_seekFile +(JNIEnv *env, jobject jo, jint stream_index, jlong min_ts, jlong ts, jlong max_ts, jint flags) { + AVFormatContext *o = NativeZ_getP(env, jo); + int res; + + res = DLCALL(avformat_seek_file)(o, stream_index, min_ts, ts, max_ts, flags); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "seekFile"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_flush +(JNIEnv *env, jobject jo) { + AVFormatContext *o = NativeZ_getP(env, jo); + int res; + + res = DLCALL(avformat_flush)(o); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "flush"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_readPlay +(JNIEnv *env, jobject jo) { + AVFormatContext *o = NativeZ_getP(env, jo); + int res; + + res = DLCALL(av_read_play)(o); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "readPlay"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_readPause +(JNIEnv *env, jobject jo) { + AVFormatContext *o = NativeZ_getP(env, jo); + int res; + + res = DLCALL(av_read_pause)(o); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "readPause"); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFormatContext_writeHeader +(JNIEnv *env, jobject jo, jobject joptions) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVDictionary *options = AVDictionary_get(env, joptions); + int res; + + res = DLCALL(avformat_write_header)(o, &options); + + AVDictionary_set(env, joptions, options, 1); + + if (res >= 0) + return res; + + jjthrowAVIOException(env, res, "writeHeader"); + return res; +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFormatContext_initOutput +(JNIEnv *env, jobject jo, jobject joptions) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVDictionary *options = AVDictionary_get(env, joptions); + int res; + + res = DLCALL(avformat_init_output)(o, &options); + + AVDictionary_set(env, joptions, options, 1); + + if (res >= 0) + return res; + + jjthrowAVIOException(env, res, "initOutput"); + return res; +} + +JNIEXPORT jboolean JNICALL Java_au_notzed_jjmpeg_AVFormatContext_writeFrame +(JNIEnv *env, jobject jo, jobject jpkt) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVPacket *pkt = NativeZ_getP(env, jpkt); + int res; + + res = DLCALL(av_write_frame)(o, pkt); + + if (res >= 0) // 1 == flushed == true + return res; + + jjthrowAVIOException(env, res, "writeFrame"); + return 0; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_interleavedWriteFrame +(JNIEnv *env, jobject jo, jobject jpkt) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVPacket *pkt = NativeZ_getP(env, jpkt); + int res; + + // TODO: this function takes ownership of the packet! + // CHECK: It either makes a ref+ or a copy, so this should be ok with java + res = DLCALL(av_interleaved_write_frame)(o, pkt); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "interleavedWriteFrame"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_writeUncodedFrame +(JNIEnv *env, jobject jo, jint stream_index, jobject jframe) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVFrame *frame = NativeZ_getP(env, jframe); + int res; + + res = DLCALL(av_write_uncoded_frame)(o, stream_index, frame); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "writeUncodedFrame"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_interleavedWriteUncodedFrame + (JNIEnv *env, jobject jo, jint stream_index, jobject jframe) { + AVFormatContext *o = NativeZ_getP(env, jo); + AVFrame *frame = NativeZ_getP(env, jframe); + int res; + + res = DLCALL(av_interleaved_write_uncoded_frame)(o, stream_index, frame); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "interleavedWriteUncodedFrame"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFormatContext_writeTrailer +(JNIEnv *env, jobject jo) { + AVFormatContext *o = NativeZ_getP(env, jo); + int res; + + res = DLCALL(av_write_trailer)(o); + if (res >= 0) + return; + + jjthrowAVIOException(env, res, "writeTrailer"); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFormatContext_findDefaultStreamIndex +(JNIEnv *env, jobject jo) { + AVFormatContext *o = NativeZ_getP(env, jo); + + return DLCALL(av_find_default_stream_index)(o); +} + +// TODO: av_get_output_timestamp + diff --git a/src/notzed.jjmpeg/jni/jj-avformatcontext.def b/src/notzed.jjmpeg/jni/jj-avformatcontext.def new file mode 100644 index 0000000..9b293e2 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avformatcontext.def @@ -0,0 +1,45 @@ +header avformat libavformat/avformat.h { + avformat_version + avformat_configuration + avformat_license + av_register_all + avformat_network_init + avformat_network_deinit + avformat_alloc_context + avformat_free_context + avformat_new_stream + avformat_alloc_output_context2 + avformat_open_input + avformat_find_stream_info + av_read_frame + av_seek_frame + avformat_seek_file + avformat_flush + av_read_play + av_read_pause + avformat_close_input + avformat_write_header + avformat_init_output + av_write_frame + av_interleaved_write_frame + av_write_uncoded_frame + av_interleaved_write_uncoded_frame + av_write_trailer + av_find_default_stream_index +} + +header avutil libavutil/dict.h { + av_dict_free +} + +java AVInputFormat au/notzed/jjmpeg/AVInputFormat { +} + +java AVOutputFormat au/notzed/jjmpeg/AVOutputFormat { +} + +java AVIOContext au/notzed/jjmpeg/AVIOContext { +} + +java AVStream au/notzed/jjmpeg/AVStream { +} diff --git a/src/notzed.jjmpeg/jni/jj-avframe.c b/src/notzed.jjmpeg/jni/jj-avframe.c new file mode 100644 index 0000000..822362c --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avframe.c @@ -0,0 +1,685 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "au_notzed_jjmpeg_AVFrame.h" +#include "au_notzed_jjmpeg_AVFrame_FrameReader.h" +#include "au_notzed_jjmpeg_AVFrame_FrameWriter.h" +#include "au_notzed_jjmpeg_AVFrame_SampleReader.h" + +#include "jjmpeg.h" + +#include "jj-avframe.h" +#undef DLCALL +#define DLCALL(x) (fn.x) + +#define MAX(a, b) ((a)>(b)?(a):(b)) + +jint AVFrame_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ +/* Fields */ + +/** + TODO: how do we know this is a video or audio frame? +*/ + +static int getPlaneCount(const AVPixFmtDescriptor *desc) { + if (desc->flags & (AV_PIX_FMT_FLAG_PAL | AV_PIX_FMT_FLAG_PSEUDOPAL)) { + return 2; + } else { + int nplanes = 0; + + for (int i=0;inb_components;i++) + nplanes = MAX(nplanes, desc->comp[i].plane+1); + + return nplanes; + } +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFrame_getPlane +(JNIEnv *env, jobject jframe, jint index) { + AVFrame *frame = NativeZ_getP(env, jframe); + + // TODO: do i want a more descriptive object here, e.g. VideoPlane or something + + // Video frame only! + + const AVPixFmtDescriptor *desc = DLCALL(av_pix_fmt_desc_get)(frame->format); + int nplanes = getPlaneCount(desc); + + if ((unsigned int)index >= nplanes) { + nativez_ThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "Index out of range"); + return NULL; + } + + if (desc->flags & (AV_PIX_FMT_FLAG_PAL | AV_PIX_FMT_FLAG_PSEUDOPAL)) { + // Plane 0 is index byte, Plane 1 is palette. + switch (index) { + case 0: + return nativez_NewDirectBuffer(env, frame->data[0], frame->height * frame->linesize[0]); + case 1: + return nativez_NewDirectBuffer(env, frame->data[1], 256*4); + } + return NULL; // for compiler only + } else { + //? int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align); + int shift = (index == 1 || index == 2) ? desc->log2_chroma_h : 0; + int height = (frame->height + (1<> shift; + + return nativez_NewDirectBuffer(env, frame->data[index], height * frame->linesize[index]); + } +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_getStride +(JNIEnv *env, jobject jframe, jint index) { + AVFrame *frame = NativeZ_getP(env, jframe); + + if ((unsigned int)index >= AV_NUM_DATA_POINTERS) { + nativez_ThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "Index out of range"); + return 0; + } + + return frame->linesize[index]; +} + +/* ********************************************************************** */ + +GS_int(AVFrame, Width, width) +GS_int(AVFrame, Height, height) + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_getPixelFormat +(JNIEnv *env, jobject jframe) { + AVFrame *frame = NativeZ_getP(env, jframe); + + // TODO: check is image + + return frame->format; +} + +GET_bool(AVFrame, isKeyFrame, key_frame) +GS_long(AVFrame, PTS, pts) +GET_value(AVRational_new, AVFrame, AspectRatio, sample_aspect_ratio) +GET_prim(jint, AVFrame, DisplayPictureNumber, display_picture_number) +GET_prim(jint, AVFrame, CodedPictureNumber, coded_picture_number) +GET_bool(AVFrame, isInterlaced, interlaced_frame) +GET_bool(AVFrame, isTopFieldFirst, top_field_first) + +/* ********************************************************************** */ +/* Audio */ + +GS_int(AVFrame, NumSamples, nb_samples) + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_getSampleFormat +(JNIEnv *env, jobject jframe) { + AVFrame *frame = NativeZ_getP(env, jframe); + + // TODO: check is audio + + return frame->format; +} + +GS_int(AVFrame, SampleRate, sample_rate) +GS_long(AVFrame, ChannelLayout, channel_layout) +GS_int(AVFrame, NumChannels, channels) + +GET_prim(jint, AVFrame, BestEffortTimestamp, best_effort_timestamp) + +/* ********************************************************************** */ +/* Methods */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_release +(JNIEnv *env, jclass jc, jlong p) { + AVFrame *frame = (void *)(uintptr_t)p; + + DLCALL(av_frame_free)(&frame); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFrame_alloc__ +(JNIEnv *env, jclass jc) { + return NativeZ_create(env, jc, DLCALL(av_frame_alloc)()); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFrame_alloc__III +(JNIEnv *env, jclass jc, jint jfmt, jint width, jint height) { + AVFrame *f = DLCALL(av_frame_alloc)(); + + if (f) { + int res; + + f->format = jfmt; + f->width = width; + f->height = height; + + res = DLCALL(av_frame_get_buffer)(f, 0); + if (res == 0) + return NativeZ_create(env, jc, f); + + DLCALL(av_frame_free)(&f); + jjthrowAVIOException(env, res, "Allocating video frame data"); + return NULL; + } + + nativez_ThrowOutOfMemoryError(env, "Allocating frame"); + return NULL; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFrame_alloc__IIJI +(JNIEnv *env, jclass jc, jint jfmt, jint channels, jlong channel_layout, jint nsamples) { + AVFrame *f = DLCALL(av_frame_alloc)(); + + if (f) { + int res; + + f->format = jfmt; + f->nb_samples = nsamples; + f->channels = channels; + f->channel_layout = channel_layout; + + res = DLCALL(av_frame_get_buffer)(f, 0); + if (res == 0) + return NativeZ_create(env, jc, f); + + DLCALL(av_frame_free)(&f); + jjthrowAVIOException(env, res, "Allocating audio frame data"); + return NULL; + } + + nativez_ThrowOutOfMemoryError(env, "Allocating frame"); + return NULL; +} + +/* **************************************** */ +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_copy +(JNIEnv *env, jobject jdst, jobject jsrc) { + AVFrame *dst = NativeZ_getP(env, jdst); + AVFrame *src = NativeZ_getP(env, jsrc); + int res; + + res = DLCALL(av_frame_copy)(dst, src); + // FIXME: this is not an i/o error + if (res < 0) + jjthrowAVIOException(env, res, "Frame copy failed"); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_copyProperties +(JNIEnv *env, jobject jdst, jobject jsrc) { + AVFrame *dst = NativeZ_getP(env, jdst); + AVFrame *src = NativeZ_getP(env, jsrc); + int res; + + res = DLCALL(av_frame_copy_props)(dst, src); + // FIXME: this is not an i/o error + if (res < 0) + jjthrowAVIOException(env, res, "Frame properties copy failed"); +} + + +/* ********************************************************************** */ +/* FrameReader */ + +typedef struct FrameReader { + // The initialisation values + int dwidth, dheight; + int flags; + + // Current context + struct SwsContext *ctx; + + jobject jframe; // reference + AVFrame *frame; +} FrameReader; + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameReader_release +(JNIEnv *env, jclass jc, jlong ifr) { + FrameReader *fr = (void *)(intptr_t)ifr; + + (*env)->DeleteGlobalRef(env, fr->jframe); + DLCALL(sws_freeContext)(fr->ctx); + free(fr); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameReader_alloc +(JNIEnv *env, jclass jc, jobject jframe, jint width, jint height, jint flags) { + FrameReader *pr = calloc(1, sizeof(*pr)); + AVFrame *frame = NativeZ_getP(env, jframe); + + pr->dwidth = width; + pr->dheight = height; + pr->flags = flags; + + pr->jframe = (*env)->NewGlobalRef(env, jframe); + pr->frame = frame; + + return NativeZ_create(env, jc, pr); +} + +// stride? +static void read_image +(JNIEnv *env, FrameReader *pr, int dstY, int dstHeight, enum AVPixelFormat dfmt, void *pixels, size_t length, size_t offset) { + AVFrame *frame = pr->frame; + int srcY = dstY * frame->height / pr->dheight; + int srcHeight = (dstY+dstHeight) * frame->height / pr->dheight - srcY; + int strides[4]; + uint8_t *data[4]; + int res; + + res = DLCALL(av_image_fill_linesizes)(strides, dfmt, pr->dwidth); + res = DLCALL(av_image_fill_pointers)(data, dfmt, dstHeight, pixels + offset, strides); + + if (res > 0 && res <= length - offset) { + res = DLCALL(sws_scale)(pr->ctx, + (const uint8_t * const*)frame->data, frame->linesize, + srcY, srcHeight, + data, strides); + } else { + nativez_ThrowException(env, "java/nio/BufferOverflowException", NULL); + } +} + +static int check_format(int fmt) { + // FIXME: this should also set "srcRange" on swscontext - except getCachedContext inits it too early + switch (fmt) { + case AV_PIX_FMT_YUVJ420P: + return AV_PIX_FMT_YUV420P; + case AV_PIX_FMT_YUVJ422P: + return AV_PIX_FMT_YUV422P; + case AV_PIX_FMT_YUVJ444P: + return AV_PIX_FMT_YUV444P; + case AV_PIX_FMT_YUVJ440P: + return AV_PIX_FMT_YUV440P; + default: + return fmt; + } +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameReader_getPixels__IIILjava_nio_Buffer_2I +(JNIEnv *env, jobject jpr, jint dstY, jint dstHeight, jint dfmt, jobject jbuffer, jint stride) { + FrameReader *pr = NativeZ_getP(env, jpr); + AVFrame *frame = pr->frame; + + pr->ctx = DLCALL(sws_getCachedContext)(pr->ctx, + frame->width, frame->height, check_format(frame->format), + pr->dwidth, pr->dheight, dfmt, + pr->flags, NULL, NULL, NULL); + if (!pr->ctx) { + // FIXME: placeholder exception + nativez_ThrowException(env, "java/lang/RuntimeException", "Unable to create scale context"); + return; + } + + void *pixels = (*env)->GetDirectBufferAddress(env, jbuffer); + jsize length = (*env)->GetDirectBufferCapacity(env, jbuffer); + jint offset = nativez_BufferPosition(env, jbuffer); + + if (!pixels || length < 0) { + nativez_ThrowException(env, "java/lang/UnsupportedOperationException", "Must be a direct buffer"); + return; + } + + read_image(env, pr, dstY, dstHeight, dfmt, pixels, length, offset); +} + +static void read_image_primitive +(JNIEnv *env, jobject jpr, int dstY, int dstHeight, jint dfmt, jarray jbuffer, jint offset, jint stride, int shift) { + FrameReader *pr = NativeZ_getP(env, jpr); + AVFrame *frame = pr->frame; + + pr->ctx = DLCALL(sws_getCachedContext)(pr->ctx, + frame->width, frame->height, check_format(frame->format), + pr->dwidth, pr->dheight, dfmt, + pr->flags, NULL, NULL, NULL); + if (!pr->ctx) { + // FIXME: placeholder exception + nativez_ThrowException(env, "java/lang/RuntimeException", "Unable to create scale context"); + return; + } + + offset <<= shift; + stride <<= shift; + + jsize length = (*env)->GetArrayLength(env, jbuffer) << shift; + void *pixels; + + pixels = (*env)->GetPrimitiveArrayCritical(env, jbuffer, NULL); + read_image(env, pr, dstY, dstHeight, dfmt, pixels, length, offset); + (*env)->ReleasePrimitiveArrayCritical(env, jbuffer, pixels, 0); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameReader_getPixels__III_3BII +(JNIEnv *env, jobject jc, jint dstY, jint dstHeight, jint jfmt, jbyteArray jdata, jint offset, jint stride) { + read_image_primitive(env, jc, dstY, dstHeight, jfmt, jdata, offset, stride, 0); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameReader_getPixels__III_3SII +(JNIEnv *env, jobject jc, jint dstY, jint dstHeight, jint jfmt, jshortArray jdata, jint offset, jint stride) { + read_image_primitive(env, jc, dstY, dstHeight, jfmt, jdata, offset, stride, 1); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameReader_getPixels__III_3III +(JNIEnv *env, jobject jc, jint dstY, jint dstHeight, jint jfmt, jintArray jdata, jint offset, jint stride) { + read_image_primitive(env, jc, dstY, dstHeight, jfmt, jdata, offset, stride, 2); +} + +/* ********************************************************************** */ +/* FrameWriter */ + +typedef struct FrameWriter { + // The initialisation values + int swidth, sheight; + int flags; + + // Current context + struct SwsContext *ctx; + + jobject jframe; // reference + AVFrame *frame; +} FrameWriter; + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameWriter_release +(JNIEnv *env, jclass jc, jlong ifr) { + FrameWriter *fr = (void *)(intptr_t)ifr; + + (*env)->DeleteGlobalRef(env, fr->jframe); + DLCALL(sws_freeContext)(fr->ctx); + free(fr); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameWriter_alloc +(JNIEnv *env, jclass jc, jobject jframe, jint width, jint height, jint flags) { + FrameWriter *pr = calloc(1, sizeof(*pr)); + AVFrame *frame = NativeZ_getP(env, jframe); + + pr->swidth = width; + pr->sheight = height; + pr->flags = flags; + + pr->jframe = (*env)->NewGlobalRef(env, jframe); + pr->frame = frame; + + return NativeZ_create(env, jc, pr); +} + +// stride? +static void write_image +(JNIEnv *env, FrameWriter *pr, int srcY, int srcHeight, enum AVPixelFormat sfmt, void *pixels, size_t length, size_t offset) { + AVFrame *frame = pr->frame; + int strides[4]; + uint8_t *data[4]; + int res; + + res = DLCALL(av_image_fill_linesizes)(strides, sfmt, pr->swidth); + res = DLCALL(av_image_fill_pointers)(data, sfmt, srcHeight, pixels + offset, strides); + + if (res > 0 && res <= length - offset) { + res = DLCALL(sws_scale)(pr->ctx, + (const uint8_t * const *)data, strides, + srcY, srcHeight, + frame->data, frame->linesize); + } else { + nativez_ThrowException(env, "java/nio/BufferOverflowException", NULL); + } +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameWriter_setPixels__IIILjava_nio_Buffer_2I +(JNIEnv *env, jobject jpr, jint srcY, jint srcHeight, jint sfmt, jobject jbuffer, jint stride) { + FrameWriter *pr = NativeZ_getP(env, jpr); + AVFrame *frame = pr->frame; + + pr->ctx = DLCALL(sws_getCachedContext)(pr->ctx, + pr->swidth, pr->sheight, sfmt, + frame->width, frame->height, frame->format, + pr->flags, NULL, NULL, NULL); + if (!pr->ctx) { + // FIXME: placeholder exception + nativez_ThrowException(env, "java/lang/RuntimeException", "Unable to create scale context"); + return; + } + + void *pixels = (*env)->GetDirectBufferAddress(env, jbuffer); + jsize length = (*env)->GetDirectBufferCapacity(env, jbuffer); + jint offset = nativez_BufferPosition(env, jbuffer); + + if (!pixels || length < 0) { + nativez_ThrowException(env, "java/lang/UnsupportedOperationException", "Must be a direct buffer"); + return; + } + + write_image(env, pr, srcY, srcHeight, sfmt, pixels, length, offset); +} + +static void write_image_primitive +(JNIEnv *env, jobject jpr, int srcY, int srcHeight, jint sfmt, jarray jbuffer, jint offset, jint stride, int shift) { + FrameWriter *pr = NativeZ_getP(env, jpr); + AVFrame *frame = pr->frame; + + pr->ctx = DLCALL(sws_getCachedContext)(pr->ctx, + pr->swidth, pr->sheight, sfmt, + frame->width, frame->height, frame->format, + pr->flags, NULL, NULL, NULL); + if (!pr->ctx) { + // FIXME: placeholder exception + nativez_ThrowException(env, "java/lang/RuntimeException", "Unable to create scale context"); + return; + } + + offset <<= shift; + stride <<= shift; + + jsize length = (*env)->GetArrayLength(env, jbuffer) << shift; + void *pixels; + + pixels = (*env)->GetPrimitiveArrayCritical(env, jbuffer, NULL); + write_image(env, pr, srcY, srcHeight, sfmt, pixels, length, offset); + (*env)->ReleasePrimitiveArrayCritical(env, jbuffer, pixels, 0); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameWriter_setPixels__III_3BII +(JNIEnv *env, jobject jc, jint srcY, jint srcHeight, jint jfmt, jbyteArray jdata, jint offset, jint stride) { + write_image_primitive(env, jc, srcY, srcHeight, jfmt, jdata, offset, stride, 0); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameWriter_setPixels__III_3SII +(JNIEnv *env, jobject jc, jint srcY, jint srcHeight, jint jfmt, jshortArray jdata, jint offset, jint stride) { + write_image_primitive(env, jc, srcY, srcHeight, jfmt, jdata, offset, stride, 1); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024FrameWriter_setPixels__III_3III +(JNIEnv *env, jobject jc, jint srcY, jint srcHeight, jint jfmt, jintArray jdata, jint offset, jint stride) { + write_image_primitive(env, jc, srcY, srcHeight, jfmt, jdata, offset, stride, 2); +} + +/* ********************************************************************** */ +/* SampleReader */ + +typedef struct SampleReader { + // Current context + struct SwrContext *ctx; + + // Cache of current settings + jlong src_layout, dst_layout; + jint src_fmt, dst_fmt; + jint src_rate, dst_rate; + + // per sample byte size + int dst_size; + + jobject jframe; // reference + AVFrame *frame; +} SampleReader; + +static int update_rate(JNIEnv *env, SampleReader *pr) { + AVFrame *frame = pr->frame; + + if (pr->ctx == NULL + || frame->sample_rate != pr->src_rate + || frame->format != pr->src_fmt + || frame->channel_layout != pr->src_layout) { + pr->ctx = DLCALL(swr_alloc_set_opts) + (pr->ctx, + pr->dst_layout, pr->dst_fmt, pr->dst_rate, + frame->channel_layout, frame->format, frame->sample_rate, + 0, NULL); + + if (!pr->ctx) { + nativez_ThrowException(env, "java/lang/UnsupportedOperationException", "Unsupported rate/format conversion"); + return -1; + } + + pr->src_layout = frame->channel_layout; + pr->src_fmt = frame->format; + pr->src_rate = frame->sample_rate; + + DLCALL(swr_init)(pr->ctx); + } + + return 0; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_release +(JNIEnv *env, jclass jc, jlong ifr) { + SampleReader *fr = (void *)(intptr_t)ifr; + + (*env)->DeleteGlobalRef(env, fr->jframe); + DLCALL(swr_free)(&fr->ctx); + free(fr); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_alloc +(JNIEnv *env, jclass jc, jobject jframe, jlong layout, jint fmt, jint rate) { + + if (DLCALL(av_sample_fmt_is_planar)(layout)) { + nativez_ThrowException(env, "java/lang/UnsupportedOperationException", "Unsupported rate/format conversion"); + return NULL; + } + + SampleReader *pr = calloc(1, sizeof(*pr)); + AVFrame *frame = NativeZ_getP(env, jframe); + + pr->jframe = (*env)->NewGlobalRef(env, jframe); + pr->frame = frame; + pr->dst_layout = layout; + pr->dst_fmt = fmt; + pr->dst_rate = rate; + + pr->dst_size = DLCALL(av_get_bytes_per_sample)(pr->dst_fmt) * DLCALL(av_get_channel_layout_nb_channels)(pr->dst_layout); + + return NativeZ_create(env, jc, pr); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_getNumSamples +(JNIEnv *env, jobject jpr) { + SampleReader *pr = NativeZ_getP(env, jpr); + + if (update_rate(env, pr) == 0) + return DLCALL(swr_get_out_samples)(pr->ctx, pr->frame->nb_samples); + + return 0; +} + +static int read_samples(JNIEnv *env, SampleReader *pr, uint8_t *samples, jsize size) { + AVFrame *frame = pr->frame; + int nsamples = DLCALL(swr_get_out_samples)(pr->ctx, pr->frame->nb_samples); + int required = pr->dst_size * nsamples; + int res; + + if (required > size) { + nativez_ThrowException(env, "java/nio/BufferOverflowException", NULL); + return 0; + } + + res = DLCALL(swr_convert)(pr->ctx, + (uint8_t **)&samples, nsamples, + (const uint8_t **)frame->data, frame->nb_samples); + + if (res < 0) + nativez_ThrowException(env, "java/lang/RuntimeException", "swr_convert failed"); + + return res; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_getSamples__Ljava_nio_Buffer_2 +(JNIEnv *env, jobject jpr, jobject jbuffer) { + SampleReader *pr = NativeZ_getP(env, jpr); + int res; + + if (update_rate(env, pr) != 0) + return; + + uint8_t *samples = (*env)->GetDirectBufferAddress(env, jbuffer); + jsize length = nativez_BufferLimit(env, jbuffer); + jint offset = nativez_BufferPosition(env, jbuffer); + + if (!samples) { + nativez_ThrowException(env, "java/lang/UnsupportedOperationException", "Buffer must be direct"); + return; + } + + res = read_samples(env, pr, samples + offset, length - offset); + if (res > 0) + nativez_BufferSetPosition(env, jbuffer, offset + res * pr->dst_size); +} + +static int read_samples_primitive(JNIEnv *env, jobject jpr, jarray jbuffer, jint offset, jsize size, int shift) { + SampleReader *pr = NativeZ_getP(env, jpr); + int res; + + if (update_rate(env, pr) != 0) + return 0; + + offset <<= shift; + size <<= shift; + + jsize length = (*env)->GetArrayLength(env, jbuffer) << shift; + uint8_t *samples; + + if (size + offset > length) { + nativez_ThrowException(env, "java/nio/BufferOverflowException", NULL); + return 0; + } + + samples = (*env)->GetPrimitiveArrayCritical(env, jbuffer, NULL); + res = read_samples(env, pr, samples + offset, size); + (*env)->ReleasePrimitiveArrayCritical(env, jbuffer, samples, 0); + + return res; +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_getSamples___3BII +(JNIEnv *env, jobject jpr, jbyteArray samples, jint offset, jint size) { + return read_samples_primitive(env, jpr, samples, offset, size, 0); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_getSamples___3SII +(JNIEnv *env, jobject jpr, jshortArray samples, jint offset, jint size) { + return read_samples_primitive(env, jpr, samples, offset, size, 1); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_getSamples___3III +(JNIEnv *env, jobject jpr, jintArray samples, jint offset, jint size) { + return read_samples_primitive(env, jpr, samples, offset, size, 2); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVFrame_00024SampleReader_getSamples___3FII +(JNIEnv *env, jobject jpr, jfloatArray samples, jint offset, jint size) { + return read_samples_primitive(env, jpr, samples, offset, size, 2); +} diff --git a/src/notzed.jjmpeg/jni/jj-avframe.def b/src/notzed.jjmpeg/jni/jj-avframe.def new file mode 100644 index 0000000..f7fc80b --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avframe.def @@ -0,0 +1,38 @@ +header avutil libavutil/frame.h { + av_frame_alloc + av_frame_free + av_frame_copy_props + av_frame_copy + + av_frame_get_buffer +} + +header avutil libavutil/imgutils.h { + av_pix_fmt_desc_get + av_image_fill_linesizes + av_image_fill_pointers +} + +header avutil libavutil/samplefmt.h { + av_sample_fmt_is_planar + av_get_bytes_per_sample + +} + +header avutil libavutil/channel_layout.h { + av_get_channel_layout_nb_channels +} + +header swscale libswscale/swscale.h { + sws_getCachedContext + sws_freeContext + sws_scale +} + +header swresample libswresample/swresample.h { + swr_alloc_set_opts + swr_free + swr_init + swr_convert + swr_get_out_samples +} \ No newline at end of file diff --git a/src/notzed.jjmpeg/jni/jj-aviocontext.c b/src/notzed.jjmpeg/jni/jj-aviocontext.c new file mode 100644 index 0000000..26841a2 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-aviocontext.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "au_notzed_jjmpeg_AVIOContext.h" + +#include "jjmpeg.h" + +#include "jj-aviocontext.h" + +jint AVIOContext_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveReferences(env, java_names, &java) + || jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ + +struct jj_io { + // AVIOHandler global reference + jobject jhandler; + // direct ByteBuffer for this data + // I don't think this will work looking at avio*.c + //jobject jbuffer; + //uint8_t *buffer; +}; + +static int jj_read_packet(void *opaque, uint8_t *buf, int buf_size) { + JNIEnv *env = nativez_AttachCurrentThread(); + + if (env) { + struct jj_io *jjio = opaque; + //size_t position = (buf - jjio->buffer); + jvalue args[1]; + + //jjbufferSetLimit(env, jjio->jbuffer, (int)(position + buf_size)); + //jjbufferSetPosition(env, jjio->jbuffer, (int)(position)); + //args[0].l = jjio->jbuffer; + + args[0].l = nativez_NewDirectBuffer(env, buf, buf_size); + + return (*env)->CallIntMethodA(env, jjio->jhandler, AVIOHandler_readPacket_l, args); + } else { + return AVERROR(EIO); + } +} + +static int jj_write_packet(void *opaque, uint8_t *buf, int buf_size) { + JNIEnv *env = nativez_AttachCurrentThread(); + + if (env) { + struct jj_io *jjio = opaque; + //size_t position = (buf - jjio->buffer); + jvalue args[1]; + + //jjbufferSetLimit(env, jjio->jbuffer, (int)(position + buf_size)); + //jjbufferSetPosition(env, jjio->jbuffer, (int)(position)); + //args[0].l = jjio->jbuffer; + + args[0].l = nativez_NewDirectBuffer(env, buf, buf_size); + + return (*env)->CallIntMethodA(env, jjio->jhandler, AVIOHandler_writePacket_l, args); + } else { + return AVERROR(EIO); + } +} + +static int64_t jj_seek(void *opaque, int64_t offset, int whence) { + JNIEnv *env = nativez_AttachCurrentThread(); + + if (env) { + struct jj_io *jjio = opaque; + jvalue args[2]; + + args[0].j = offset; + args[1].i = whence; + + return (*env)->CallLongMethodA(env, jjio->jhandler, AVIOHandler_seek_ji, args); + } else { + return AVERROR(EIO); + } +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVIOContext_release +(JNIEnv *env, jclass jc, jlong jic) { + AVIOContext *ic = (void *)(uintptr_t)jic; + + if (ic->read_packet == jj_read_packet) { + struct jj_io *jjio = ic->opaque; + + (*env)->DeleteGlobalRef(env, jjio->jhandler); + //(*env)->DeleteGlobalRef(env, jjio->jbuffer); + //DLCALL(av_free)(jjio->buffer); + free(jjio); + + DLCALL(av_free)(ic->buffer); + DLCALL(avio_context_free)(&ic); + } else { + DLCALL(avio_close)(ic); + } +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVIOContext_open +(JNIEnv *env, jclass jc, jstring jurl, jint flags) { + const char *url = nativez_GetString(env, jurl); + AVIOContext *ic = NULL; + int res; + + res = DLCALL(avio_open)(&ic, url, flags); + if (res < 0) + jjthrowAVIOException(env, res, url); + nativez_ReleaseString(env, jurl, url); + + return NativeZ_create(env, jc, ic); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVIOContext_alloc +(JNIEnv *env, jclass jc, jint buffer_size, jint jflags, jobject jhandler) { + AVIOContext *io = NULL; + struct jj_io *jjio; + uint8_t *buffer; + + jjio = malloc(sizeof(*jjio)); + if (jjio) { + buffer = DLCALL(av_malloc)(buffer_size); + if (buffer) { + int write_flag = (jflags & au_notzed_jjmpeg_AVIOContext_AVIO_WRITABLE) != 0; + + jjio->jhandler = (*env)->NewGlobalRef(env, jhandler); + //jjio->jbuffer = jjnewDirectBuffer(env, jjio->buffer, buffer_size); + + io = DLCALL(avio_alloc_context)(buffer, buffer_size, write_flag, jjio, + jj_read_packet, + jj_write_packet, + jj_seek); + if (io) { + io->seekable = jflags & 3; + io->direct = (jflags & au_notzed_jjmpeg_AVIOContext_AVIO_DIRECT) != 0; + } else { + nativez_ThrowOutOfMemoryError(env, "Allocating io context"); + free(buffer); + free(jjio); + } + } else { + nativez_ThrowOutOfMemoryError(env, "Allocating buffer"); + free(jjio); + } + } else { + nativez_ThrowOutOfMemoryError(env, "Allocating handler"); + } + + return NativeZ_create(env, jc, io); +} diff --git a/src/notzed.jjmpeg/jni/jj-aviocontext.def b/src/notzed.jjmpeg/jni/jj-aviocontext.def new file mode 100644 index 0000000..f1a2fe7 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-aviocontext.def @@ -0,0 +1,19 @@ +header avformat libavformat/avio.h { + + avio_alloc_context + avio_context_free + + avio_open + avio_close +} + +header avutil libavutil/mem.h { + av_malloc + av_free +} + +java AVIOHandler au/notzed/jjmpeg/AVIOContext$AVIOHandler { + readPacket, (Ljava/nio/ByteBuffer;)I + writePacket, (Ljava/nio/ByteBuffer;)I + seek, (JI)J +} diff --git a/src/notzed.jjmpeg/jni/jj-avmisc.c b/src/notzed.jjmpeg/jni/jj-avmisc.c new file mode 100644 index 0000000..26e8fa8 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avmisc.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + Several of the smaller objects together here. + */ + +#include + +#include "au_notzed_jjmpeg_AVInputFormat.h" +#include "au_notzed_jjmpeg_AVOutputFormat.h" +#include "au_notzed_jjmpeg_AVRational.h" +#include "au_notzed_jjmpeg_SwsContext.h" + +#include "au_notzed_jjmpeg_AVCodecID.h" +#include "au_notzed_jjmpeg_AVPixelFormat.h" +#include "au_notzed_jjmpeg_AVSampleFormat.h" +#include "au_notzed_jjmpeg_AVChannelLayout.h" +#include "au_notzed_jjmpeg_AVMediaType.h" + +#include "au_notzed_jjmpeg_AVError.h" + +#include "jjmpeg.h" + +#include "jj-avmisc.h" +#undef DLCALL +#define DLCALL(x) (fn.x) + +jint AVMisc_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ +/* AVInputFormat */ + +GET_string(AVInputFormat, Name, name) +GET_string(AVInputFormat, LongName, long_name) + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVInputFormat_findInputFormat +(JNIEnv *env, jclass jc, jstring jname) { + const char *name = nativez_GetString(env, jname); + AVInputFormat *iformat; + + iformat = DLCALL(av_find_input_format)(name); + + nativez_ReleaseString(env, jname, name); + + return NativeZ_refer(env, jc, iformat); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVInputFormat_next +(JNIEnv *env, jclass jc, jobject jiformat) { + AVInputFormat *iformat = NativeZ_getP(env, jiformat); + + iformat = DLCALL(av_iformat_next)(iformat); + + return NativeZ_refer(env, jc, iformat); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVInputFormat_register +(JNIEnv *env, jobject jiformat) { + AVInputFormat *iformat = NativeZ_getP(env, jiformat); + + DLCALL(av_register_input_format)(iformat); +} + +/* ********************************************************************** */ +/* AVOutputFormat */ + +GET_string(AVOutputFormat, Name, name) +GET_string(AVOutputFormat, LongName, long_name) +GET_string(AVOutputFormat, MimeType, mime_type) +GET_string(AVOutputFormat, Extensions, extensions) + +GET_prim(jint, AVOutputFormat, VideoCodec, video_codec) +GET_prim(jint, AVOutputFormat, AudioCodec, audio_codec) +GET_prim(jint, AVOutputFormat, SubtitleCodec, subtitle_codec) + +GET_prim(jint, AVOutputFormat, Flags, flags) + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVOutputFormat_guessFormat +(JNIEnv *env, jclass jc, jstring jname, jstring jfile, jstring jmime) { + const char *name = nativez_GetString(env, jname); + const char *file = nativez_GetString(env, jfile); + const char *mime = nativez_GetString(env, jmime); + AVOutputFormat *oformat; + + oformat = DLCALL(av_guess_format)(name, file, mime); + + nativez_ReleaseString(env, jname, name); + nativez_ReleaseString(env, jfile, file); + nativez_ReleaseString(env, jmime, mime); + + return NativeZ_refer(env, jc, oformat); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVOutputFormat_guessCodecID +(JNIEnv *env, jobject joformat, jstring jname, jstring jfile, jstring jmime, jint jtype) { + AVOutputFormat *oformat = NativeZ_getP(env, joformat); + const char *name = nativez_GetString(env, jname); + const char *file = nativez_GetString(env, jfile); + const char *mime = nativez_GetString(env, jmime); + enum AVCodecID codecID; + + codecID = DLCALL(av_guess_codec)(oformat, name, file, mime, jtype); + + nativez_ReleaseString(env, jname, name); + nativez_ReleaseString(env, jfile, file); + nativez_ReleaseString(env, jmime, mime); + + return codecID; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVOutputFormat_next +(JNIEnv *env, jclass jc, jobject joformat) { + AVOutputFormat *oformat = NativeZ_getP(env, joformat); + + oformat = DLCALL(av_oformat_next)(oformat); + + return NativeZ_refer(env, jc, oformat); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOutputFormat_register +(JNIEnv *env, jobject joformat) { + AVOutputFormat *oformat = NativeZ_getP(env, joformat); + + DLCALL(av_register_output_format)(oformat); +} + +/* ********************************************************************** */ +/* AVRational */ + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVRational_d2q +(JNIEnv *env, jclass jc, jdouble jd, jint jmax) { + AVRational r = DLCALL(av_d2q)(jd, jmax); + + return AVRational_new(env, r); +} + +JNIEXPORT jlong JNICALL Java_au_notzed_jjmpeg_AVRational_rescale +(JNIEnv *env, jclass jc, jlong a, jlong b, jlong c) { + return DLCALL(av_rescale)(a, b, c); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVRational_mul +(JNIEnv *env, jobject jb, jobject jc) { + AVRational b = AVRational_get(env, jb); + AVRational c = AVRational_get(env, jc); + AVRational a = DLCALL(av_mul_q)(b, c); + + return AVRational_new(env, a); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVRational_div +(JNIEnv *env, jobject jb, jobject jc) { + AVRational b = AVRational_get(env, jb); + AVRational c = AVRational_get(env, jc); + AVRational a = DLCALL(av_div_q)(b, c); + + return AVRational_new(env, a); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVRational_add +(JNIEnv *env, jobject jb, jobject jc) { + AVRational b = AVRational_get(env, jb); + AVRational c = AVRational_get(env, jc); + AVRational a = DLCALL(av_add_q)(b, c); + + return AVRational_new(env, a); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVRational_sub +(JNIEnv *env, jobject jb, jobject jc) { + AVRational b = AVRational_get(env, jb); + AVRational c = AVRational_get(env, jc); + AVRational a = DLCALL(av_sub_q)(b, c); + + return AVRational_new(env, a); +} + +/* ********************************************************************** */ +/* AVError */ + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVError_toString +(JNIEnv *env, jclass jc, jint jval) { + char errormsg[AV_ERROR_MAX_STRING_SIZE]; + + DLCALL(av_strerror)(jval, errormsg, sizeof(errormsg)); + + return nativez_NewString(env, errormsg); +} + +/* ********************************************************************** */ +/* AVPixelFormat */ + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVPixelFormat_valueOf +(JNIEnv *env, jclass jc, jstring jname) { + const char *name = nativez_GetString(env, jname); + enum AVPixelFormat fmt = DLCALL(av_get_pix_fmt)(name); + nativez_ReleaseString(env, jname, name); + return fmt; +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVPixelFormat_toString +(JNIEnv *env, jclass jc, jint jval) { + const char *name = DLCALL(av_get_pix_fmt_name)(jval); + + if (!name) + name = "none"; + + return nativez_NewString(env, name); +} + +/* ********************************************************************** */ +/* AVSampleFormat */ + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVSampleFormat_valueOf +(JNIEnv *env, jclass jc, jstring jname) { + const char *name = nativez_GetString(env, jname); + enum AVSampleFormat fmt = DLCALL(av_get_sample_fmt)(name); + nativez_ReleaseString(env, jname, name); + return fmt; +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVSampleFormat_toString +(JNIEnv *env, jclass jc, jint jval) { + const char *name = DLCALL(av_get_sample_fmt_name)(jval); + return nativez_NewString(env, name); +} + +/* ********************************************************************** */ +/* AVChannelLayout */ + +JNIEXPORT jlong JNICALL Java_au_notzed_jjmpeg_AVChannelLayout_valueOf +(JNIEnv *env, jclass jc, jstring jname) { + const char *name = nativez_GetString(env, jname); + enum AVSampleFormat fmt = DLCALL(av_get_channel_layout)(name); + nativez_ReleaseString(env, jname, name); + return fmt; +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVChannelLayout_toString +(JNIEnv *env, jclass jc, jlong jval) { + //const char *name = DLCALL(av_get_channel_name)(jval); // only handles 1 channel at a time + char name[256]; + + DLCALL(av_get_channel_layout_string)(name, sizeof(name), 0, jval); + + return nativez_NewString(env, name); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVChannelLayout_getNumChannels +(JNIEnv *env, jclass jc, jlong jval) { + return DLCALL(av_get_channel_layout_nb_channels)(jval); +} + +JNIEXPORT jlong JNICALL Java_au_notzed_jjmpeg_AVChannelLayout_getDefaultLayout +(JNIEnv *env, jclass jc, jint jval) { + return DLCALL(av_get_default_channel_layout)(jval); +} + +/* ********************************************************************** */ +/* AVMediaType */ + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVMediaType_toString +(JNIEnv *env, jclass jc, jint jval) { + const char *name = DLCALL(av_get_media_type_string)(jval); + return nativez_NewString(env, name); +} + +/* ********************************************************************** */ +/* AVCodecID */ + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVCodecID_toString +(JNIEnv *env, jclass jc, jint jval) { + const char *name = DLCALL(avcodec_get_name)(jval); + return nativez_NewString(env, name); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVCodecID_getType +(JNIEnv *env, jclass jc, jint jval) { + return DLCALL(avcodec_get_type)(jval); +} + +/* ********************************************************************** */ +/* SwsContext */ + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_SwsContext_getVersion +(JNIEnv *env, jclass jc) { + return DLCALL(swscale_version)(); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_SwsContext_getConfiguration +(JNIEnv *env, jclass jc) { + return nativez_NewString(env, DLCALL(swscale_configuration)()); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_SwsContext_getLicense +(JNIEnv *env, jclass jc) { + return nativez_NewString(env, DLCALL(swscale_license)()); +} + +/* *** */ + +// FIXME: implement filters +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_SwsContext_alloc +(JNIEnv *env, jclass jc, jint srcW, jint srcH, jint srcFormat, jint dstW, jint dstH, jint dstFormat, jint flags, jobject jsrcFilter, jobject jdstFilter, jdoubleArray jparams) { + jdouble *params = NULL; + struct SwsContext *ctx; + + if (jparams) { + jsize len = (*env)->GetArrayLength(env, jparams); + + params = alloca(sizeof(*params) * len); + (*env)->GetDoubleArrayRegion(env, jparams, 0, len, params); + } + + ctx = DLCALL(sws_getContext)(srcW, srcH, srcFormat, dstW, dstH, dstFormat, flags, NULL, NULL, params); + if (!ctx) { + jjthrowAVIOException(env, 0, "Unable to create SWS Context"); + return NULL; + } + + return NativeZ_create(env, jc, ctx); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_SwsContext_scale__Lau_notzed_jjmpeg_AVFrame_2IILau_notzed_jjmpeg_AVFrame_2 +(JNIEnv *env, jobject jctx, jobject jsrc, jint sliceY, jint sliceH, jobject jdst) { + struct SwsContext *ctx = NativeZ_getP(env, jctx); + AVFrame *src = NativeZ_getP(env, jsrc); + AVFrame *dst = NativeZ_getP(env, jdst); + + return DLCALL(sws_scale)(ctx, (const uint8_t * const *)src->data, src->linesize, + sliceY, sliceH, + dst->data, dst->linesize); +} + +/* +static int scaleToArray(JNIEnv *env, SwsContext *sws, AVFrame *src, jint srcSliceY, jint srcSliceH, jarray jdst, int dsize, enum AVPixelFormat dfmt, jint dwidth, jint dheight) { + struct AVPicture dst; + void *cdst; + int res = -1; + jsize alength = (*env)->GetArrayLength(env, jdst) * dsize; + int linesizes[4]; + uint8_t datas[4]; + + res = DLCALL(av_image_fill_linesizes)(linesizes, dfmt, dwidth); + + cdst = (*env)->GetPrimitiveArrayCritical(env, jdst, NULL); + + if (!cdst) + // FIXME: exception + return -1; + + res = DLCALL(av_image_fill_pointers)(data, dfmt, dheight, cdst, linesizes); + // erorr handling + // size checking (res is size if positive) + + + res = DLCALL(sws_scale)(sws, (const uint8_t * const *)src->data, src->linesize, + sliceY, sliceH, + datas, linesizes); + + (*env)->ReleasePrimitiveArrayCritical(env, jdst, cdst, 0); + + return res; + }*/ + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_SwsContext_scale__Lau_notzed_jjmpeg_AVFrame_2II_3I +(JNIEnv *env, jobject jctx, jobject jsrc, jint sliceY, jint sliceH, jintArray jdst) { + //struct SwsContext *ctx = NativeZ_getP(env, jctx); + //AVFrame *src = NativeZ_getP(env, jsrc); + + //scaleToArray(env, ctx, sliceY, sliceH, jdst, 4, + // I need to think about this some more, it needs width/height pixformat + // -> this is in swscontext but private. + return 0; +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_SwsContext_scale__Lau_notzed_jjmpeg_AVFrame_2II_3B +(JNIEnv *env, jobject jctx, jobject jsrc, jint sliceY, jint slliceH, jbyteArray jdst) { + //struct SwsContext *ctx = NativeZ_getP(env, jctx); + //AVFrame *src = NativeZ_getP(env, jsrc); + return 0; +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_SwsContext_scale___3BIILau_notzed_jjmpeg_AVFrame_2 +(JNIEnv *env, jobject jctx, jbyteArray jsrc, jint sliceY, jint sliceH, jobject jdst) { + //struct SwsContext *ctx = NativeZ_getP(env, jctx); + //AVFrame *dst = NativeZ_getP(env, jdst); + return 0; +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_SwsContext_scale__Ljava_nio_ByteBuffer_2IILau_notzed_jjmpeg_AVFrame_2 +(JNIEnv *env, jobject jctx, jobject jsrc, jint sliceY, jint sliceH, jobject jdst) { + //struct SwsContext *ctx = NativeZ_getP(env, jctx); + return 0; +} + diff --git a/src/notzed.jjmpeg/jni/jj-avmisc.def b/src/notzed.jjmpeg/jni/jj-avmisc.def new file mode 100644 index 0000000..3e8d02e --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avmisc.def @@ -0,0 +1,57 @@ +header avformat libavformat/avformat.h { + av_register_input_format + av_find_input_format + av_iformat_next + + av_register_output_format + av_oformat_next + av_guess_format + av_guess_codec +} + +header avcodec libavcodec/avcodec.h { + avcodec_get_type + avcodec_get_name +} + +header avutil libavutil/samplefmt.h { + av_get_sample_fmt_name + av_get_sample_fmt +} + +header avutil libavutil/channel_layout.h { + av_get_channel_name + av_get_channel_layout + av_get_channel_layout_nb_channels + av_get_channel_layout_string + av_get_default_channel_layout +} + +header avutil libavutil/imgutils.h { + av_get_pix_fmt + av_get_pix_fmt_name + + av_get_media_type_string + + av_rescale + av_d2q + av_mul_q + av_div_q + av_add_q + av_sub_q + +# TBD: for swscale + av_image_fill_linesizes + av_image_fill_pointers + + av_strerror +} + +header swscale libswscale/swscale.h { + swscale_version + swscale_configuration + swscale_license + sws_getContext + sws_freeContext + sws_scale +} diff --git a/src/notzed.jjmpeg/jni/jj-avoptions.c b/src/notzed.jjmpeg/jni/jj-avoptions.c new file mode 100644 index 0000000..f834ffb --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avoptions.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "au_notzed_jjmpeg_AVOptions.h" + +#include "jjmpeg.h" +#include "jj-avoptions.h" + +jint AVOptions_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveReferences(env, java_names, &java) + || jj_ResolveFunctions(env, fn_names, &fn); +} + +#define GETP(name) void *name = NativeZ_getP(env, j ## name) +#define STRING(name) const char *name = nativez_GetString(env, j ## name) +#define RSTRING(name) nativez_ReleaseString(env, j ## name, name) + +/* ********************************************************************** */ +/* Setters */ + +#define CHECKSET(env, name, res) checkSet(env, name, res) + +static void checkSet(JNIEnv *env, const char *name, int res) { + if (res != 0) { + char *msg = alloca(strlen(name) + 32); + + sprintf(msg, "set `%s' failed", name); + jjthrowAVIOException(env, res, msg); + } +} + +#if 1 +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_set +(JNIEnv *env, jobject jo, jstring jname, jstring jval, jint flags) { + GETP(o); + STRING(name); STRING(val); + CHECKSET(env, name, DLCALL(av_opt_set)(o, name, val, flags)); + RSTRING(name); RSTRING(val); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setInt +(JNIEnv *env, jobject jo, jstring jname, jlong val, jint flags) { + GETP(o); + STRING(name); + CHECKSET(env, name, DLCALL(av_opt_set_int)(o, name, val, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setDouble +(JNIEnv *env, jobject jo, jstring jname, jdouble val, jint flags) { + GETP(o); + STRING(name); + CHECKSET(env, name, DLCALL(av_opt_set_double)(o, name, val, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setQ +(JNIEnv *env, jobject jo, jstring jname, jobject jval, jint flags) { + GETP(o); + STRING(name); + AVRational val = AVRational_get(env, jval); + CHECKSET(env, name, DLCALL(av_opt_set_q)(o, name, val, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setBin +(JNIEnv *env, jobject jo, jstring jname, jbyteArray jval, jint offset, jint len, jint flags) { + // not implemented +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setImageSize +(JNIEnv *env, jobject jo, jstring jname, jobject jval, jint flags) { + GETP(o); + STRING(name); + AVSize val = AVSize_get(env, jval); + CHECKSET(env, name, DLCALL(av_opt_set_image_size)(o, name, val.width, val.height, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setPixelFormat +(JNIEnv *env, jobject jo, jstring jname, jint jval, jint flags) { + GETP(o); + STRING(name); + CHECKSET(env, name, DLCALL(av_opt_set_pixel_fmt)(o, name, jval, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setSampleFormat +(JNIEnv *env, jobject jo, jstring jname, jint jval, jint flags) { + GETP(o); + STRING(name); + CHECKSET(env, name, DLCALL(av_opt_set_sample_fmt)(o, name, jval, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setVideoRate +(JNIEnv *env, jobject jo, jstring jname, jobject jval, jint flags) { + GETP(o); + STRING(name); + AVRational val = AVRational_get(env, jval); + CHECKSET(env, name, DLCALL(av_opt_set_video_rate)(o, name, val, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setChannelLayout +(JNIEnv *env, jobject jo, jstring jname, jlong val, jint flags) { + GETP(o); + STRING(name); + CHECKSET(env, name, DLCALL(av_opt_set_channel_layout)(o, name, val, flags)); + RSTRING(name); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setDictionary +(JNIEnv *env, jobject jo, jstring jname, jobject jval, jint flags) { + GETP(o); + AVDictionary *val = AVDictionary_get(env, jval); + STRING(name); + CHECKSET(env, name, DLCALL(av_opt_set_dict_val)(o, name, val, flags)); + RSTRING(name); + DLCALL(av_dict_free)(&val); +} +#endif + +/* ********************************************************************** */ +/* Getters, guessed it didn't you. */ + +#define CHECKGET(env, name, res) checkGet(env, name, res) + +static void checkGet(JNIEnv *env, const char *name, int res) { + if (res != 0) { + char *msg = alloca(strlen(name) + 32); + + sprintf(msg, "set `%s' failed", name); + jjthrowAVIOException(env, res, msg); + } +} + +#if 1 +// Hmm, this also gets 'binary' values, assume the value is a string here +// and a binary in the other one + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVOptions_get +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + char *val; + CHECKGET(env, name, DLCALL(av_opt_get)(o, name, flags, (uint8_t **)&val)); + jstring jval = nativez_NewString(env, val); + DLCALL(av_free)(val); + RSTRING(name); + return jval; +} + +JNIEXPORT jlong JNICALL Java_au_notzed_jjmpeg_AVOptions_getInt +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + int64_t val; + CHECKGET(env, name, DLCALL(av_opt_get_int)(o, name, flags, &val)); + RSTRING(name); + return val; +} + +JNIEXPORT jdouble JNICALL Java_au_notzed_jjmpeg_AVOptions_getDouble +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + double val; + CHECKGET(env, name, DLCALL(av_opt_get_double)(o, name, flags, &val)); + RSTRING(name); + return val; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVOptions_getQ +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + AVRational val; + CHECKGET(env, name, DLCALL(av_opt_get_q)(o, name, flags, &val)); + RSTRING(name); + return AVRational_new(env, val); +} + +JNIEXPORT jintArray JNICALL Java_au_notzed_jjmpeg_AVOptions_getImageSize +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + AVSize val; + CHECKGET(env, name, DLCALL(av_opt_get_image_size)(o, name, flags, &val.width, &val.height)); + RSTRING(name); + return AVSize_new(env, val); +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVOptions_getPixelFormat +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + enum AVPixelFormat val; + CHECKGET(env, name, DLCALL(av_opt_get_pixel_fmt)(o, name, flags, &val)); + RSTRING(name); + return val; +} + +JNIEXPORT jint JNICALL Java_au_notzed_jjmpeg_AVOptions_getSampleFormat +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + enum AVSampleFormat val; + CHECKGET(env, name, DLCALL(av_opt_get_sample_fmt)(o, name, flags, &val)); + RSTRING(name); + return val; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVOptions_getVideoRate +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + AVRational val; + CHECKGET(env, name, DLCALL(av_opt_get_video_rate)(o, name, flags, &val)); + RSTRING(name); + return AVRational_new(env, val); +} + +JNIEXPORT jlong JNICALL Java_au_notzed_jjmpeg_AVOptions_getChannelLayout +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + int64_t val; + CHECKGET(env, name, DLCALL(av_opt_get_channel_layout)(o, name, flags, &val)); + RSTRING(name); + return val; +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVOptions_getDictionary +(JNIEnv *env, jobject jo, jstring jname, jint flags) { + GETP(o); + STRING(name); + AVDictionary *val; + CHECKGET(env, name, DLCALL(av_opt_get_dict_val)(o, name, flags, &val)); + jobject jval = AVDictionary_new(env); + AVDictionary_set(env, jval, val, 1); + RSTRING(name); + return jval; +} +#endif + +/* ********************************************************************** */ +/* Other methods */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_setOptions +(JNIEnv *env, jobject jo, jstring joptions) { + GETP(o); + STRING(options); + int res = DLCALL(av_set_options_string)(o, options, "=", ","); + + RSTRING(options); + CHECKSET(env, "options", res); +} + +JNIEXPORT jstring JNICALL Java_au_notzed_jjmpeg_AVOptions_getOptions +(JNIEnv *env, jobject jo, jint opt_flags, jint ser_flags) { + GETP(o); + char *val = NULL; + int res = DLCALL(av_opt_serialize)(o, opt_flags, ser_flags, &val, '=', ','); + jstring jval = nativez_NewString(env, val); + + CHECKGET(env, "options", res); + + DLCALL(av_free)(val); + return jval; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVOptions_copyOptions +(JNIEnv *env, jobject jdst, jobject jsrc) { + GETP(dst); GETP(src); + DLCALL(av_opt_copy)(dst, src); +} + +static jobject AVOption_create(JNIEnv *env, const AVOption *o) { + if (o) { + jvalue args[] = { + { .j = (intptr_t)o }, + { .l = nativez_NewString(env, o->name) }, + { .l = nativez_NewString(env, o->help) }, + { .i = o->type }, + { .i = o->flags }, + { .l = nativez_NewString(env, o->unit) }, + }; + + return (*env)->NewObjectA(env, AVOption_classid, AVOption_new_jlliil, args); + } else { + return NULL; + } +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVOptions_nextOption +(JNIEnv *env, jobject jo, jobject jopt) { + GETP(o); + const AVOption *prev = jopt ? (void *)(intptr_t)(*env)->GetLongField(env, jopt, AVOption_p) : NULL; + + prev = DLCALL(av_opt_next)(o, prev); + return AVOption_create(env, prev); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVOptions_find +(JNIEnv *env, jobject jo, jstring jname, jstring junit, jint opt_flags, jint search_flags) { + GETP(o); STRING(name); STRING(unit); + const AVOption *opt = DLCALL(av_opt_find)(o, name, unit, opt_flags, search_flags); + RSTRING(unit); RSTRING(name); + return AVOption_create(env, opt); +} diff --git a/src/notzed.jjmpeg/jni/jj-avoptions.def b/src/notzed.jjmpeg/jni/jj-avoptions.def new file mode 100644 index 0000000..7712775 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avoptions.def @@ -0,0 +1,38 @@ +header avutil libavutil/opt.h { + av_free + av_dict_free + + av_set_options_string + av_opt_serialize + av_opt_copy + av_opt_next + av_opt_find + + av_opt_set + av_opt_set_int + av_opt_set_double + av_opt_set_q + av_opt_set_bin + av_opt_set_image_size + av_opt_set_pixel_fmt + av_opt_set_sample_fmt + av_opt_set_video_rate + av_opt_set_channel_layout + av_opt_set_dict_val + + av_opt_get + av_opt_get_int + av_opt_get_double + av_opt_get_q + av_opt_get_image_size + av_opt_get_pixel_fmt + av_opt_get_sample_fmt + av_opt_get_video_rate + av_opt_get_channel_layout + av_opt_get_dict_val +} + +java AVOption au/notzed/jjmpeg/AVOptions$AVOption { + , (JLjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V + p, J +} diff --git a/src/notzed.jjmpeg/jni/jj-avpacket.c b/src/notzed.jjmpeg/jni/jj-avpacket.c new file mode 100644 index 0000000..b43cb2d --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avpacket.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "au_notzed_jjmpeg_AVPacket.h" + +#include "jjmpeg.h" +#include "jj-avpacket.h" + +jint AVPacket_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveReferences(env, java_names, &java) + || jj_ResolveFunctions(env, fn_names, &fn); +} + +/* ********************************************************************** */ +/* Fields */ + +GS_long(AVPacket, PTS, pts) +GS_long(AVPacket, DTS, dts) +GET_prim(jint, AVPacket, Size, size) +GS_int(AVPacket, StreamIndex, stream_index) +GET_prim(jint, AVPacket, Flags, flags) + +JNIEXPORT void JNICALL MAKE_JJNAME(AVPacket, setFlags) + (JNIEnv *env, jobject jo, jint mask, jint flags) { + AVCodecContext *o = NativeZ_getP(env, jo); + + o->flags = (o->flags & ~mask) | (mask & flags); +} + +GET_prim(jlong, AVPacket, Position, pos) + +/* ********************************************************************** */ +/* Methods */ + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVPacket_release +(JNIEnv *env, jclass jc, jlong jpacket) { + AVPacket *packet = (void *)(intptr_t)jpacket; + + DLCALL(av_packet_free)(&packet); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVPacket_alloc +(JNIEnv *env, jclass jc) { + AVPacket *packet = DLCALL(av_packet_alloc)(); + + //if (!packet) // outofmemoryerror? + + return NativeZ_create(env, jc, packet); +} + +/* **************************************** */ + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVPacket_getData +(JNIEnv *env, jobject jpacket) { + AVPacket *packet = NativeZ_getP(env, jpacket); + + if (packet->data) + return nativez_NewDirectBuffer(env, packet->data, packet->size); + + return NULL; +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVPacket_clear +(JNIEnv *env, jobject jpacket) { + AVPacket *packet = NativeZ_getP(env, jpacket); + + DLCALL(av_packet_unref)(packet); +} + +JNIEXPORT jobject JNICALL Java_au_notzed_jjmpeg_AVPacket_clone +(JNIEnv *env, jobject jpacket) { + AVPacket *packet = NativeZ_getP(env, jpacket); + AVPacket *dst = DLCALL(av_packet_clone)(packet); + + // if !dst ... + return NativeZ_create(env, AVPacket_classid, dst); +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVPacket_rescaleTS +(JNIEnv *env, jobject jpacket, jobject jsrc, jobject jdst) { + AVPacket *packet = NativeZ_getP(env, jpacket); + AVRational src = AVRational_get(env, jsrc); + AVRational dst = AVRational_get(env, jdst); + + DLCALL(av_packet_rescale_ts)(packet, src, dst); +} diff --git a/src/notzed.jjmpeg/jni/jj-avpacket.def b/src/notzed.jjmpeg/jni/jj-avpacket.def new file mode 100644 index 0000000..3352b5a --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avpacket.def @@ -0,0 +1,10 @@ +header avcodec libavcodec/avcodec.h { + av_packet_alloc + av_packet_free + av_packet_unref + av_packet_clone + av_packet_rescale_ts +} + +java AVPacket au/notzed/jjmpeg/AVPacket { +} diff --git a/src/notzed.jjmpeg/jni/jj-avstream.c b/src/notzed.jjmpeg/jni/jj-avstream.c new file mode 100644 index 0000000..945488a --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avstream.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include "au_notzed_jjmpeg_AVStream.h" + +#include "jjmpeg.h" +#include "jj-avstream.h" + +jint AVStream_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveReferences(env, java_names, &java); +} + +/* ********************************************************************** */ +/* Fields */ + +GET_prim(jint, AVStream, Index, index) +GS_int(AVStream, ID, id) + +GS_value(AVRational_new, AVRational_get, AVStream, TimeBase, time_base) + +GS_long(AVStream, StartTime, start_time) +GS_long(AVStream, Duration, duration) +GS_long(AVStream, NumFrames, nb_frames) + +GS_int(AVStream, Discard, discard) + +GS_value(AVRational_new, AVRational_get, AVStream, AverageFrameRate, avg_frame_rate) +GS_value(AVRational_new, AVRational_get, AVStream, SampleAspectRatio, sample_aspect_ratio) +GS_value(AVRational_new, AVRational_get, AVStream, DisplayAspectRatio, display_aspect_ratio) + +REF_object(AVCodecParameters, AVStream, CodecParameters, codecpar) diff --git a/src/notzed.jjmpeg/jni/jj-avstream.def b/src/notzed.jjmpeg/jni/jj-avstream.def new file mode 100644 index 0000000..785ed2c --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avstream.def @@ -0,0 +1,4 @@ + +java AVCodecParameters au/notzed/jjmpeg/AVCodecParameters { +} + diff --git a/src/notzed.jjmpeg/jni/jj-avutil.c b/src/notzed.jjmpeg/jni/jj-avutil.c new file mode 100644 index 0000000..fc3add1 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avutil.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "au_notzed_jjmpeg_AVUtil.h" + +#include "jjmpeg.h" +#include "jj-avutil.h" + +jint AVUtil_OnLoad(JavaVM *vmi, JNIEnv *env) { + return jj_ResolveReferences(env, java_names, &java) + || jj_ResolveFunctions(env, fn_names, &fn); +} + +static jobject jlogger_ref; + +static void log_callback(void*ptr, int level, const char*fmt, va_list vl) { + JNIEnv *env = nativez_AttachCurrentThread(); + + if (env) { + char log[1024]; + int prefix = 1; + int res; + + res = DLCALL(av_log_format_line2)(ptr, level, fmt, vl, log, sizeof(log), &prefix); + if (res > 1) { + jvalue args[2]; + + // Strip trailing \r\n + if (log[--res] == '\n') + log[res] = 0; + if (log[--res] == '\r') + log[res] = 0; + + args[0].i = level; + args[1].l = nativez_NewString(env, log); + + (*env)->CallVoidMethodA(env, jlogger_ref, AVLogger_log_il, args); + } + } +} + +JNIEXPORT void JNICALL Java_au_notzed_jjmpeg_AVUtil_setLogger +(JNIEnv *env, jclass jc, jobject jlogger) { + if (jlogger_ref) { + (*env)->DeleteGlobalRef(env, jlogger_ref); + jlogger_ref = NULL; + } + + if (jlogger) { + jlogger_ref = (*env)->NewGlobalRef(env, jlogger); + DLCALL(av_log_set_callback)(log_callback); + } else { + DLCALL(av_log_set_callback)(DLCALL(av_log_default_callback)); + } +} diff --git a/src/notzed.jjmpeg/jni/jj-avutil.def b/src/notzed.jjmpeg/jni/jj-avutil.def new file mode 100644 index 0000000..55f3458 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jj-avutil.def @@ -0,0 +1,10 @@ +header avutil libavutil/log.h { + av_log_set_callback + av_log_default_callback + av_log_format_line2 + av_default_item_name +} + +java AVLogger au/notzed/jjmpeg/AVLogger { + log, (ILjava/lang/String;)V +} diff --git a/src/notzed.jjmpeg/jni/jjmpeg-jni.c b/src/notzed.jjmpeg/jni/jjmpeg-jni.c new file mode 100644 index 0000000..16d397e --- /dev/null +++ b/src/notzed.jjmpeg/jni/jjmpeg-jni.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "jjmpeg.h" +#include "jjmpeg-jni.h" + +#define D(x) + +static int fail(const char *ctx, const char *what) __attribute__ ((noinline)); + +#undef DLSO +#define DLSO(name, v, flags) { #name, v, flags } + +static NZLibTable libtable[] = { + DLSO(avutil, LIBAVUTIL_VERSION_MAJOR, 0), + DLSO(avcodec, LIBAVCODEC_VERSION_MAJOR, 0), + DLSO(avformat, LIBAVFORMAT_VERSION_MAJOR, 0), + DLSO(swscale, LIBSWSCALE_VERSION_MAJOR, 0), // noncore? + DLSO(swresample, LIBSWRESAMPLE_VERSION_MAJOR, 0), // noncore? + DLSO(avdevice, LIBAVDEVICE_VERSION_MAJOR, DLSO_NONCORE), + + { 0 } +}; + +typedef jint (*onLoad)(JavaVM *, JNIEnv *); + +static onLoad ctors[] = { + nativez_OnLoad, + AVOptions_OnLoad, + + AVUtil_OnLoad, + + AVCodec_OnLoad, + AVCodecContext_OnLoad, + AVCodecParameters_OnLoad, + AVDevice_OnLoad, + AVFormatContext_OnLoad, + AVFrame_OnLoad, + AVIOContext_OnLoad, + AVMisc_OnLoad, + AVPacket_OnLoad, + AVStream_OnLoad, +}; + +jint JNI_OnLoad(JavaVM *vmi, void *reserved) { + JNIEnv *env; + + D(printf("libjjmpeg.so: OnLoad()\n")); + + if ((*vmi)->GetEnv(vmi, (void *)&env, JNI_VERSION_1_4) < 0) + return 0; + + if (jj_ResolveReferences(env, java_names, &java) != 0 + || jj_ResolveLibraries(env, libtable) != 0 + || jj_ResolveFunctions(env, fn_names, &fn) != 0) + return -1; + + /* Call other onload functions */ + for (int i=0;i + +int jj_ResolveLibraries(JNIEnv *env, NZLibTable *table) { + char name[64]; + + for (int i=0;table[i].name;i++) { + void *lib; + + sprintf(name, "lib%s.so", table[i].name); + + lib = dlopen(name, RTLD_LAZY | RTLD_GLOBAL); + if (!lib) { + return fail("open library", name); + } + + table[i].ptr = lib; + } + return 0; +} + +static int jj_ResolveFunctionsX(JNIEnv *env, const NZLibTable *table, const char *fn_names, void *fnp) { + void *lib = NULL; + const char *name = fn_names; + int index = 0; + const char *lib_name = ""; + void **fn = fnp; + + while (*name) { + const char *next = name + strlen(name) + 1; + + if (*name == '#') { + lib = NULL; + lib_name = name+1; + for (int i=0;table[i].name;i++) { + if (strcmp(table[i].name, lib_name) == 0) { + lib = table[i].ptr; + break; + } + } + } else if (lib) { + void *entry = dlsym(lib, name); + + fn[index++] = entry; + if (!entry) { + return fail("resolve function", name); + } + } else { + return fail("find function library", lib_name); + } + + name = next; + } + + return 0; +} + +int jj_ResolveFunctions(JNIEnv *env, const char *fn_names, void *fnp) { + return jj_ResolveFunctionsX(env, libtable, fn_names, fnp); +} + +int jj_ResolveReferences(JNIEnv *env, const char *jn_names, void *jnp) { + jclass jc = NULL; + + const char *name = jn_names; + int index = 0; + void **jn = jnp; + const char *cname = "?"; + + while (*name) { + const char *next = name + strlen(name) + 1; + + switch (*name) { + case '#': // class + jc = (*env)->FindClass(env, name + 1); + jn[index] = jc = (*env)->NewGlobalRef(env, jc); + cname = name + 1; + break; + case ',': // field + jn[index] = (*env)->GetFieldID(env, jc, name+1, next); + break; + case ';': // static field + jn[index] = (*env)->GetStaticFieldID(env, jc, name+1, next); + break; + case '.': // method + jn[index] = (*env)->GetMethodID(env, jc, name+1, next); + break; + case ':': // static method + jn[index] = (*env)->GetStaticMethodID(env, jc, name+1, next); + break; + default: + return fail("Invalid table", name); + } + + if (!jn[index]) + return fail(cname, name+1); + + if (*name != '#') + next = next + strlen(next) + 1; + name = next; + index += 1; + } + + return 0; +} + +/* ********************************************************************** */ + +void jjthrowAVIOException(JNIEnv *env, int error, const char *msg) { + jvalue jargs[2]; + jthrowable jex; + + jargs[0].i = error; + jargs[1].l = nativez_NewString(env, msg); + + jex = (*env)->NewObjectA(env, AVIOException_classid, AVIOException_new_il, jargs); + if (jex) + (*env)->Throw(env, jex); +} + +/* ********************************************************************** */ + +jstring jjnewIntArrayT(JNIEnv *env, const int *ap, int terminal) { + jintArray ret = NULL; + + if (ap) { + int len = 0; + + while (ap[len] != terminal) + len++; + ret = (*env)->NewIntArray(env, len); + (*env)->SetIntArrayRegion(env, ret, 0, len, (const jint *)ap); + } + return ret; +} + +/* ********************************************************************** */ + +jobject AVRational_new(JNIEnv *env, const AVRational cr) { + jvalue args[] = { + { .i = cr.num }, + { .i = cr.den } + }; + + return (*env)->NewObjectA(env, AVRational_classid, AVRational_new_ii, args); +} + +jobjectArray AVRational_newArray(JNIEnv *env, int len) { + return (*env)->NewObjectArray(env, len, AVRational_classid, NULL); +} + +AVRational AVRational_get(JNIEnv *env, jobject jr) { + AVRational cr; + + cr.num = (*env)->GetIntField(env, jr, AVRational_num); + cr.den = (*env)->GetIntField(env, jr, AVRational_den); + + return cr; +} + +/* ********************************************************************** */ + +jobject AVSize_new(JNIEnv *env, const AVSize cr) { + jvalue args[] = { + { .i = cr.width }, + { .i = cr.height } + }; + + return (*env)->NewObjectA(env, AVSize_classid, AVSize_new_ii, args); +} + +AVSize AVSize_get(JNIEnv *env, jobject jr) { + AVSize cr; + + cr.width = (*env)->GetIntField(env, jr, AVSize_width); + cr.height = (*env)->GetIntField(env, jr, AVSize_height); + + return cr; +} + +/* ********************************************************************** */ + +/* Peek inside this snot interface */ +struct AVDictionary { + int count; + AVDictionaryEntry *elems; +}; + +jobject AVDictionary_new(JNIEnv *env) { + return (*env)->NewObjectA(env, AVDictionary_classid, AVDictionary_new_, NULL); +} + +jobject AVDictionary_newArray(JNIEnv *env, int len) { + return (*env)->NewObjectArray(env, len, AVDictionary_classid, NULL); +} + +AVDictionary *AVDictionary_get(JNIEnv *env, jobject jdict) { + AVDictionary *m = NULL; + if (jdict) { + jobjectArray jlist = (*env)->CallObjectMethodA(env, jdict, AVDictionary_toArray_, NULL); + jsize size = (*env)->GetArrayLength(env, jlist); + + if (size) { + m = DLCALL(av_malloc)(sizeof(*m)); + m->count = size; + m->elems = DLCALL(av_malloc)(sizeof(*m->elems) * size); + + for (int i=0;iGetObjectArrayElement(env, jlist, i); + jstring jkey = (*env)->CallObjectMethodA(env, jentry, Entry_getKey_, NULL); + jstring jvalue = (*env)->CallObjectMethodA(env, jentry, Entry_getValue_, NULL); + const char *key = nativez_GetString(env, jkey); + const char *value = nativez_GetString(env, jvalue); + + m->elems[i].key = DLCALL(av_strdup)(key); + m->elems[i].value = DLCALL(av_strdup)(value); + + nativez_ReleaseString(env, jkey, key); + nativez_ReleaseString(env, jvalue, value); + } + } + } + + return m; +} + +void AVDictionary_set(JNIEnv *env, jobject jdict, AVDictionary *dict, int free) { + if (jdict) { + (*env)->CallVoidMethodA(env, jdict, AVDictionary_clear_, NULL); + if (dict && dict->count) { + jvalue args[2]; + + for (int i=0;icount;i++) { + args[0].l = nativez_NewString(env, dict->elems[i].key); + args[1].l = nativez_NewString(env, dict->elems[i].value); + + (*env)->CallObjectMethodA(env, jdict, AVDictionary_put_ll, args); + } + } + } + if (free) + DLCALL(av_dict_free)(&dict); +} + diff --git a/src/notzed.jjmpeg/jni/jjmpeg-jni.def b/src/notzed.jjmpeg/jni/jjmpeg-jni.def new file mode 100644 index 0000000..a7f7b66 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jjmpeg-jni.def @@ -0,0 +1,58 @@ +header avutil libavutil/opt.h { + av_malloc + av_mallocz + av_dict_free + av_strdup + + av_strerror +} + +java AVIOException au/notzed/jjmpeg/AVIOException { + ,(I)V + ,(ILjava/lang/String;)V +} + +java AVRational au/notzed/jjmpeg/AVRational { + ,(II)V + num,I + den,I +} + +java AVSize au/notzed/jjmpeg/AVSize { + , (II)V + width, I + height, I +} + +java AVDictionary au/notzed/jjmpeg/AVDictionary { + ,()V + toArray, ()[Ljava/lang/Object; + clear, ()V + put, (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +} + +java Buffer java/nio/Buffer { + hasArray,()Z + array,()Ljava/lang/Object; + arrayOffset, ()I + position,()I + position, (I)Ljava/nio/Buffer; + limit, (I)Ljava/nio/Buffer; +} + +#java ByteBuffer java/nio/ByteBuffer { +# order, (Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer; +#} + +java Entry java/util/Map$Entry { + getKey, ()Ljava/lang/Object; + getValue, ()Ljava/lang/Object; +} + +#java NativeZ au/notzed/nativez/NativeZ { +# static create, (Ljava/lang/Class;J)Lau/notzed/nativez/NativeZ; +# static register, (Lau/notzed/nativez/NativeZ;)Lau/notzed/nativez/NativeZ; +# static resolve, (Ljava/lang/Class;J)Lau/notzed/nativez/NativeZ; +# static refer, (Ljava/lang/Class;J)Lau/notzed/nativez/NativeZ; +# p, J +#} diff --git a/src/notzed.jjmpeg/jni/jjmpeg.h b/src/notzed.jjmpeg/jni/jjmpeg.h new file mode 100644 index 0000000..433b32b --- /dev/null +++ b/src/notzed.jjmpeg/jni/jjmpeg.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2017 Michael Zucchi + * + * This file is part of jjmpeg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _JJMPEG_H +#define _JJMPEG_H + +#include "nativez.h" + +/* Macros for accessor creation */ + +#define MAKE_JJNAME(jclass, method) \ + Java_au_notzed_jjmpeg_ ## jclass ## _ ## method + +#define GET_prim(jprim, jclass, jname, cname) \ + JNIEXPORT jprim JNICALL \ + MAKE_JJNAME(jclass, get ## jname) \ + (JNIEnv *env, jobject jo) { \ + jclass *o = NativeZ_getP(env, jo); \ + return o->cname; \ + } + +#define SET_prim(jprim, jclass, jname, cname) \ + JNIEXPORT void JNICALL \ + MAKE_JJNAME(jclass, set ## jname) \ + (JNIEnv *env, jobject jo, jprim jval) { \ + jclass *o = NativeZ_getP(env, jo); \ + o->cname = jval; \ + } + +#define GS_int(jclass, jname, cname) \ + GET_prim(jint, jclass, jname, cname) \ + SET_prim(jint, jclass, jname, cname) + +#define GS_float(jclass, jname, cname) \ + GET_prim(jfloat, jclass, jname, cname) \ + SET_prim(jfloat, jclass, jname, cname) + +#define GS_long(jclass, jname, cname) \ + GET_prim(jlong, jclass, jname, cname) \ + SET_prim(jlong, jclass, jname, cname) + +#define GET_bool(jclass, jname, cname) \ + JNIEXPORT jboolean JNICALL \ + MAKE_JJNAME(jclass, jname) \ + (JNIEnv *env, jobject jo) { \ + jclass *o = NativeZ_getP(env, jo); \ + return o->cname; \ + } + +#define GET_object(oclass, jclass, jname, cname) \ + JNIEXPORT jobject JNICALL \ + MAKE_JJNAME(jclass, get ## jname) \ + (JNIEnv *env, jobject jo) { \ + jclass *o = NativeZ_getP(env, jo); \ + return NativeZ_resolve(env, oclass ## _classid, o->cname); \ + } + +#define SET_object(jclass, jname, cname) \ + JNIEXPORT void JNICALL \ + MAKE_JJNAME(jclass, set ## jname) \ + (JNIEnv *env, jobject jo, jobject jval) { \ + jclass *o = NativeZ_getP(env, jo); \ + o->cname = NativeZ_getP(env, jval); \ + } + +#define GS_object(oclass, jclass, jname, cname) \ + GET_object(oclass, jclass, jname, cname) \ + SET_object(jclass, jname, cname) + +#define REF_object(oclass, jclass, jname, cname) \ + JNIEXPORT jobject JNICALL \ + MAKE_JJNAME(jclass, get ## jname) \ + (JNIEnv *env, jobject jo) { \ + jclass *o = NativeZ_getP(env, jo); \ + return NativeZ_refer(env, oclass ## _classid, o->cname); \ + } + +#define GET_enum(eclass, jclass, jname, cname) \ + JNIEXPORT jobject JNICALL \ + MAKE_JJNAME(jclass, get ## jname) \ + (JNIEnv *env, jobject jo) { \ + jclass *o = NativeZ_getP(env, jo); \ + return eclass ## _resolve(env, o->cname); \ + } + +#define SET_enum(eclass, jclass, jname, cname) \ + JNIEXPORT void JNICALL \ + MAKE_JJNAME(jclass, set ## jname) \ + (JNIEnv *env, jobject jo, jobject jval) { \ + jclass *o = NativeZ_getP(env, jo); \ + o->cname = eclass ## _value(env, jval); \ + } + +#define GS_enum(eclass, jclass, jname, cname) \ + GET_enum(eclass, jclass, jname, cname) \ + SET_enum(eclass, jclass, jname, cname) + +#define GET_string(jclass, jname, cname) \ + JNIEXPORT jstring JNICALL \ + MAKE_JJNAME(jclass, get ## jname) \ + (JNIEnv *env, jobject jo) { \ + jclass *o = NativeZ_getP(env, jo); \ + return nativez_NewString(env, o->cname); \ + } + +// Not sure about this +/* +#define SET_string(jclass, jname, cname) \ + JNIEXPORT void JNICALL \ + MAKE_JJNAME(jclass, set ## jname) \ + (JNIEnv *env, jobject jo, jobject jval) { \ + jclass *o = NativeZ_getP(env, jo); \ + const char *val = jjgetString(env, jval); \ + c->cname = av_strdup(val); \ + jjreleaseString(env, jval, val); \ + } +*/ + +#define GET_value(rnew, jclass, jname, cname) \ + JNIEXPORT jobject JNICALL \ + MAKE_JJNAME(jclass, get ## jname) \ + (JNIEnv *env, jobject jo) { \ + jclass *o = NativeZ_getP(env, jo); \ + return rnew(env, o->cname); \ + } + +#define SET_value(rget, jclass, jname, cname) \ + JNIEXPORT void JNICALL \ + MAKE_JJNAME(jclass, set ## jname) \ + (JNIEnv *env, jobject jo, jobject jval) { \ + jclass *o = NativeZ_getP(env, jo); \ + o->cname = rget(env, jval); \ +} + +#define GS_value(rnew, rget, jclass, jname, cname) \ + GET_value(rnew, jclass, jname, cname) \ + SET_value(rget, jclass, jname, cname) + +/* jni table helpers */ +#define JJCLASS(name) JCLASS( name ## _classid, "au/notzed/jjmpeg/" #name ) + +/** + * Create a new array from a terminated integer (/ enum) array + * + * @param ap Array pointer, may be NULL. + * @param terminal Terminating value. + */ +jstring jjnewIntArrayT(JNIEnv *env, const int *ap, int terminal); + +void jjthrowAVIOException(JNIEnv *env, int error, const char *msg); + +/* forward references */ +typedef struct AVRational AVRational; +typedef struct AVDictionary AVDictionary; +enum AVPixelFormat; +enum AVSampleFormat; +enum AVMediaType; + +/* Type Helpers */ + +// Nasty stuff +jobject AVDictionary_new(JNIEnv *env); +jobjectArray AVDictionary_newArray(JNIEnv *env, int len); +AVDictionary *AVDictionary_get(JNIEnv *env, jobject jdict); +void AVDictionary_set(JNIEnv *env, jobject jdict, AVDictionary *dict, int free); + +// Pass by value +jobject AVRational_new(JNIEnv *env, AVRational cr); +jobjectArray AVRational_newArray(JNIEnv *env, int len); +AVRational AVRational_get(JNIEnv *env, jobject jr); + +typedef struct AVSize AVSize; +struct AVSize { + int width; + int height; +}; + +jobject AVSize_new(JNIEnv *env, AVSize size); +AVSize AVSize_get(JNIEnv *env, jobject jr); + +/* Constructors */ +jint AVOptions_OnLoad(JavaVM *vmi, JNIEnv *env); + +jint AVCodecContext_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVCodecParameters_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVCodec_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVDevice_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVFormatContext_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVFrame_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVIOContext_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVMisc_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVPacket_OnLoad(JavaVM *vmi, JNIEnv *env); +jint AVStream_OnLoad(JavaVM *vmi, JNIEnv *env); + +jint AVUtil_OnLoad(JavaVM *vmi, JNIEnv *env); + +/* temporary */ +int jj_ResolveFunctions(JNIEnv *env, const char *fn_names, void *fnp); +int jj_ResolveLibraries(JNIEnv *env, NZLibTable *table); +int jj_ResolveReferences(JNIEnv *env, const char *jn_names, void *jnp); +#undef DLCALL +#define DLCALL(x) (fn.x) +#undef DLCHECK_RET +#define DLCHECK_RET(env, x, ret) do { if (!nativez_NonNull(env, #x, (void *)fn.x)) { return ret; } } while (0) + +#endif diff --git a/src/notzed.jjmpeg/jni/jni.make b/src/notzed.jjmpeg/jni/jni.make new file mode 100644 index 0000000..acea673 --- /dev/null +++ b/src/notzed.jjmpeg/jni/jni.make @@ -0,0 +1,64 @@ +# +# Copyright (C) 2017,2019 Michael Zucchi +# +# This file is part of jjmpeg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +notzed.jjmpeg_JNI_LIBRARIES = jjmpeg + +jjmpeg_SOURCES = \ + jjmpeg-jni.c \ + jj-avcodec.c \ + jj-avcodeccontext.c \ + jj-avcodecparameters.c \ + jj-avdevice.c \ + jj-avformatcontext.c \ + jj-avframe.c \ + jj-aviocontext.c \ + jj-avmisc.c \ + jj-avoptions.c \ + jj-avpacket.c \ + jj-avstream.c \ + jj-avutil.c + +linux-amd64_jjmpeg_LDFLAGS = -ldl + +jjmpeg_LDLIBS=-L$(NATIVEZ_HOME)/lib -lnativez $($(TARGET)_jjmpeg_LDFLAGS) +jjmpeg_CPPFLAGS=-I$(FFMPEG_HOME)/include -I$(NATIVEZ_HOME)/include -I$(notzed.jjmpeg_jnidir) +jjmpeg_CFLAGS=-Wall -Wmissing-prototypes -Wno-pointer-sign + +jjmpeg_DEFS = \ + jjmpeg-jni.def \ + jj-avcodeccontext.def \ + jj-avcodec.def \ + jj-avcodecparameters.def \ + jj-avdevice.def \ + jj-avformatcontext.def \ + jj-avframe.def \ + jj-aviocontext.def \ + jj-avmisc.def \ + jj-avoptions.def \ + jj-avpacket.def \ + jj-avstream.def \ + jj-avutil.def + +jjmpeg_makedep=$(notzed.jjmpeg_objdir)/$(1).o: $(notzed.jjmpeg_jnidir)/$(1).h + +$(foreach def,$(jjmpeg_DEFS),$(eval $(call jjmpeg_makedep,$(def:.def=)))) + +$(notzed.jjmpeg_jnidir)/%.h: src/notzed.jjmpeg/jni/%.def + @install -d $(@D) + src/notzed.jjmpeg/jni/extract-proto.pl -f "$(FFMPEG_HOME)" $< > $@ || ( rm $@ ; exit 1) diff --git a/src/notzed.jjmpeg/legal/LICENSE b/src/notzed.jjmpeg/legal/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/src/notzed.jjmpeg/legal/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. -- 2.39.2