diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..de460881 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: neilcsmith-net + diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..e5b2aa16 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,45 @@ +name: Test on Maven + +on: + [push, pull_request] + +jobs: + test-1-24-jdk25: + + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up JDK 25 + uses: actions/setup-java@v5 + with: + java-version: '25' + distribution: 'temurin' + + - name: Install GStreamer + run: sudo apt-get update && sudo apt-get install gstreamer1.0-plugins-good gstreamer1.0-plugins-bad + + - name: Build with Maven + run: ./mvnw --batch-mode verify + + test-1-20-jdk8: + + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up JDK 8 + uses: actions/setup-java@v5 + with: + java-version: '8' + distribution: 'temurin' + + - name: Install GStreamer + run: sudo apt-get update && sudo apt-get install gstreamer1.0-plugins-good gstreamer1.0-plugins-bad + + - name: Build with Maven + run: ./mvnw --batch-mode verify diff --git a/.gitignore b/.gitignore index 59b5fa5b..1219377d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,6 @@ # NetBeans Files # /nbproject/ -nb-configuration.xml \ No newline at end of file +nb-configuration.xml + +.idea diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..222cba3b --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,4 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip +distributionSha256Sum=0d7125e8c91097b36edb990ea5934e6c68b4440eef4ea96510a0f6815e7eeadb diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2ab1814b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -dist: xenial -language: java -jdk: openjdk8 -addons: - apt: - packages: - - gstreamer1.0-plugins-good diff --git a/README.md b/README.md index 7158f2d8..4c05b8dd 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,100 @@ GStreamer 1.x Java Core (gst1-java-core) ======================================== -This is a set of Java bindings for [GStreamer 1.x][gstreamer], an open-source -multimedia framework. The bindings are a fork of the original [GStreamer-Java][gstreamer-java] -bindings for GStreamer 0.10. +gst1-java-core is a set of Java bindings for [GStreamer 1.x][gstreamer]. GStreamer +is an open-source, pipeline-based multimedia framework written in C. It allows a +programmer to create a wide variety of media-handling pipelines inside +applications, from simple media playback, to encoding, live-streaming, analysis, +machine learning, WebRTC and more. + +GStreamer is designed to be cross-platform, and binaries are provided for a range +of operating systems. gst1-java-core is actively tested on Linux (x86 and Arm), +Windows and macOS, but should work on any OS with Java, JNA and GStreamer support. +The bindings are in use in a wide variety of commercial and open-source projects, +across desktop, server and embedded. -## Status and support +## Usage + +See the [examples repository][gst1-examples] for some self-contained projects to +get you started. Use the [Javadoc][gst1-javadoc]! - all classes are documented, +and include links to the relevant native documentation where appropriate. Please use the +[GStreamer-Java mailing list][gstreamer-java-group] to ask questions. + +To try the examples you will need [GStreamer installed][gstreamer-download] on your +system. Other options for deployment are possible - see requirements below. + +Please note, this is not an easy-to-use multimedia framework for beginners. It currently +requires people to know the Java language and be familiar with the GStreamer framework +(and possibly be prepared to apply things from tutorials on GStreamer programming in +other languages to the Java bindings). + +## History and status -Releases are available via Maven Central (under the `org.freedesktop.gstreamer` group ID), -or can be downloaded from the GitHub [release page][gst1-releases]. +Releases are available via Maven Central (under the `org.freedesktop.gstreamer` +group ID), or can be downloaded from the GitHub [release page][gst1-releases]. -The bindings are currently at version 0.9.x as we progress to a 1.0 release. However, -they are actively in use in a number of open-source and commercial projects, and -should generally be considered stable (they are *more* stable than the 0.10 bindings). -The version number reflects the fact that some work remains to be done to complete the -fork from the 0.10 bindings, which might result in minimal breaking changes. +Since v1.0.0 the bindings are functionally and API stable, but note that the lowlevel +packages are _effectively_ non-public and subject to change at any time. -The current lead maintainer of the bindings is Neil C Smith on behalf of the [Praxis LIVE][praxislive] -project. The original 0.10 project was started by Wayne Meissner, and numerous other -people have made valuable contributions to the original project and the 1.x fork over the years. +The lead maintainer of the bindings is Neil C Smith at [Codelerity Ltd.][codelerity]. +The project began in 2015 as a fork of the original [GStreamer-Java][gstreamer-java] +bindings for GStreamer 0.10 started by Wayne Meissner. Numerous other people have made +valuable contributions to the original project and the 1.x fork over the years. -Initial help on getting started, and support for open-source projects, can be obtained from the -[GStreamer-Java Google Group][gstreamer-java-group]. +## Help and support -Commercial support is available, and sponsorship of features is welcome (it all -helps us get to 1.0!) - please email support@praxislive.org for more information. +Help on getting started, and support for open-source projects, can be obtained +from the [GStreamer-Java mailing list][gstreamer-java-group]. + +Commercial support and custom development is available, and sponsorship of additional +features is also welcome - please email info@codelerity.com to discuss. ## Requirements -The bindings have been tested on Linux, Windows and OSX. You will need to have GStreamer 1.x -available in your path in order to use the bindings. Windows and OSX installers for GStreamer are -available from the [GStreamer project itself][gstreamer-download]. Linux users should be -able to get GStreamer from their distribution repository if it isn't already installed. +The bindings are tested on Linux, Windows and macOS. Windows and macOS installers +for GStreamer are available from the [GStreamer project itself][gstreamer-download]. +Linux users should be able to get GStreamer from their distribution repository if it +isn't already installed. -It is possible to ship GStreamer with your application should you not wish your users -to have to install it separately. +You will need to have the GStreamer 1.x native libraries available in your system path +in order to use the bindings, and may also need to set up environment variables +depending on how GStreamer is installed. See the `Utils` class in each example project +for one possible way to configure this inside your Java code. -The minimum supported version of GStreamer is 1.8.x. +It is possible to ship GStreamer with your application should you want your users not +to have to install it separately. There are various ways to achieve this - see the +[upstream documentation][gstreamer-deploy]. Advice is also available via the support +options above. -You will also need the [JNA (Java Native Access)][jna] library, minimum version 4.4.0. +The minimum supported version of GStreamer is 1.8.x. If you require access to features +related to later GStreamer versions (eg. WebRTC support), make sure to request the +version you need when calling `Gst.init(..)` -## Usage +You will also need the [JNA (Java Native Access)][jna] library, minimum version 5.2.0. + +The minimum required Java version is Java 8. + +## Contributions + +Contributions to the library are welcomed, either to fix / enhance current features, +or bring in new ones. There is also ongoing work to rework the lowlevel bindings. -See the examples repository for usage. More documentation will follow. Please use the -[GStreamer-Java Google Group][gstreamer-java-group] to discuss usage or ask questions. +**Before opening a Pull Request** please raise an issue or discuss your contribution on +the mailing list. New features must have tests, selectively applied if targeting +features in versions of GStreamer above 1.8. All Pull Requests will be automatically +tested via CI, and all tests must pass before merging will be considered. -Please note: this is not an easy-to-use multimedia framework for beginners. It currently -requires people to both know the Java language and be familiar with the GStreamer framework -(or be prepared to apply things from tutorials on GStreamer programming in other languages -(e.g. python or C#) to the Java bindings). +If you are making a large contribution to benefit a commercial project, sponsorship +of integration and support time would be appreciated. [gstreamer]: https://gstreamer.freedesktop.org/ [gstreamer-download]: https://gstreamer.freedesktop.org/download/ +[gstreamer-deploy]: https://gstreamer.freedesktop.org/documentation/deploying/index.html [gstreamer-java]: https://github.com/gstreamer-java/gstreamer-java +[gst1-examples]: https://github.com/gstreamer-java/gst1-java-examples +[gst1-javadoc]: https://javadoc.io/doc/org.freedesktop.gstreamer/gst1-java-core [gst1-releases]: https://github.com/gstreamer-java/gst1-java-core/releases [gstreamer-java-group]: https://groups.google.com/forum/#!forum/gstreamer-java [jna]: https://github.com/java-native-access/jna -[praxislive]: http://www.praxislive.org \ No newline at end of file +[codelerity]: https://www.codelerity.com diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..bd8896bf --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..5761d948 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index 71b4d721..e917e0a5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,11 +5,11 @@ org.freedesktop.gstreamer gst1-java-core - 0.9.5-SNAPSHOT + 1.5.0-SNAPSHOT jar GStreamer 1.x Java Core - Unofficial Java binding for the Gstreamer framework + Unofficial Java binding for the GStreamer framework https://github.com/gstreamer-java/gst1-java-core @@ -19,7 +19,7 @@ - GNU Lesser General Public License + LGPL-3.0-only http://www.gnu.org/licenses/lgpl.html repo @@ -33,12 +33,12 @@ - ossrh - https://oss.sonatype.org/content/repositories/snapshots + sonatype-central + https://central.sonatype.com/repository/maven-snapshots - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + sonatype-central + https://repo.maven.apache.org/maven2 @@ -46,74 +46,41 @@ neilcsmith-net Neil C Smith - neilcsmith.net[at]googlemail[dot]com + Codelerity Ltd. + neil@codelerity.com - lead maintainer - developer + Lead maintainer + Developer - http://www.neilcsmith.net + https://www.codelerity.com wmeissner Wayne Meissner - wmeissner[at]gmail[dot]com - founder - developer - - - - lfarkas - Levante Farkas - lfarkas[at]gmail[dot]com - - developer - - - - andres.colubri - Andres Colubri - andres.colubri[at]gmail[dot]com - - developer - - - - tshalif - Tal Shalif - tal[at]shalif[dot]com - - developer - - - - dhoyt - David Hoyt - david.g.hoyt[at]gmail[dot]com - - developer + Founder of GStreamer 0.10 bindings - UTF-8 + 8 - 1.6 - 1.6 + 0.8.5 + UTF-8 junit junit - 4.8.1 + 4.13.2 test net.java.dev.jna jna - [4.4.0,5.0) + 5.18.1 @@ -136,24 +103,76 @@ + + + eu.maveniverse.maven.njord + extension3 + ${njord.version} + + org.apache.maven.plugins maven-surefire-plugin - 2.20 + 3.5.4 - - pertest - -Djna.nosys=true ${maven.test.jvmargs} + 1 + none + false + -XX:+IgnoreUnrecognizedVMOptions --enable-native-access=ALL-UNNAMED -Djna.nosys=true ${maven.test.jvmargs} **/Test*.java + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + 8 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.2 + + + + org.freedesktop.gstreamer + + + + + + windows-gstreamer-path + + + windows + + + gstreamer.path + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${gstreamer.path};${java.library.path} + + + + + + release @@ -161,7 +180,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.3.1 attach-sources @@ -174,7 +193,12 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 3.12.0 + + none + org.freedesktop.gstreamer.lowlevel + 8 + attach-javadocs @@ -187,7 +211,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 3.2.8 sign-artifacts diff --git a/src/org/freedesktop/gstreamer/Bin.java b/src/org/freedesktop/gstreamer/Bin.java index 1dbf50c5..f2e29cd0 100644 --- a/src/org/freedesktop/gstreamer/Bin.java +++ b/src/org/freedesktop/gstreamer/Bin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Neil C Smith + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2009 Levente Farkas * Copyright (C) 2007 Wayne Meissner @@ -20,113 +20,97 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import static org.freedesktop.gstreamer.lowlevel.GstBinAPI.GSTBIN_API; -import static org.freedesktop.gstreamer.lowlevel.GstParseAPI.GSTPARSE_API; +import java.util.EnumSet; import java.util.List; - -import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; -import org.freedesktop.gstreamer.lowlevel.GstTypes; - -import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.lowlevel.GstIteratorPtr; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; /** * Base class and element that can contain other elements. - * - * Bin is an element that can contain other {@link Element}s, allowing them to be - * managed as a group. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstBin.html + *

+ * Bin is an element that can contain other {@link Element}s, allowing them to + * be managed as a group. *

* Pads from the child elements can be ghosted to the bin, see {@link GhostPad}. * This makes the bin look like any other elements and enables creation of * higher-level abstraction elements. *

- * A new {@link Bin} is created with {@link Bin#Bin(String)}. Use a {@link Pipeline} instead if you - * want to create a toplevel bin because a normal bin doesn't have a bus or - * handle clock distribution of its own. + * A new {@link Bin} is created with {@link Bin#Bin(String)}. Use a + * {@link Pipeline} instead if you want to create a toplevel bin because a + * normal bin doesn't have a bus or handle clock distribution of its own. *

* After the bin has been created you will typically add elements to it with - * {@link Bin#add(Element)}. Elements can be removed with {@link Bin#remove(Element)} + * {@link Bin#add(Element)}. Elements can be removed with + * {@link Bin#remove(Element)} *

- * An element can be retrieved from a bin with {@link Bin#getElementByName(String)}. + * An element can be retrieved from a bin with + * {@link Bin#getElementByName(String)}. *

- * A list of elements contained in a bin can be retrieved with {@link Bin#getElements} + * A list of elements contained in a bin can be retrieved with + * {@link Bin#getElements} * - * The {@link ELEMENT_ADDED} signal is fired whenever a new element is added - * to the bin. Likewise the {@link ELEMENT_REMOVED} signal is fired - * whenever an element is removed from the bin. + * The {@link ELEMENT_ADDED} signal is fired whenever a new element is added to + * the bin. Likewise the {@link ELEMENT_REMOVED} signal is fired whenever an + * element is removed from the bin. * */ public class Bin extends Element { + public static final String GST_NAME = "bin"; public static final String GTYPE_NAME = "GstBin"; - - public static final int DEBUG_GRAPH_SHOW_MEDIA_TYPE = (1<<0); - public static final int DEBUG_GRAPH_SHOW_CAPS_DETAILS = (1<<1); - public static final int DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS = (1<<2); - public static final int DEBUG_GRAPH_SHOW_STATES = (1<<3); - public static final int DEBUG_GRAPH_SHOW_ALL = ((1<<4)-1); - - public Bin(Initializer init) { + + protected Bin(Initializer init) { super(init); } + + Bin(Handle handle, boolean needRef) { + super(handle, needRef); + } /** * Creates a new Bin with a unique name. */ public Bin() { - this(initializer(GSTBIN_API.ptr_gst_bin_new(null), false)); + this(Natives.initializer(GSTBIN_API.ptr_gst_bin_new(null), false, true)); } - + /** * Creates a new Bin with the given name. + * * @param name The Name to assign to the new Bin */ public Bin(String name) { - this(initializer(GSTBIN_API.ptr_gst_bin_new(name), false)); - } - - /** - * Creates a bin from a text bin description. - * - * This function allows creation of a bin based on the syntax used in the - * gst-launch utillity. - * - * @param binDecription the command line describing the bin - * @param ghostUnlinkedPads whether to create ghost pads for the bin from - * any unlinked pads - * @return The new Bin. - */ - @Deprecated - public static Bin launch(String binDecription, boolean ghostUnlinkedPads) { - Pointer[] err = {null}; - Bin bin = GSTPARSE_API.gst_parse_bin_from_description(binDecription, ghostUnlinkedPads, err); - if (bin == null) { - throw new GstException(new GError(new GErrorStruct(err[0]))); - } - return bin; + this(Natives.initializer(GSTBIN_API.ptr_gst_bin_new(name), false, true)); } - + /** * Adds an Element to this Bin. *

- * Sets the element's parent, and thus takes ownership of the element. - * An element can only be added to one bin. + * Sets the element's parent, and thus takes ownership of the element. An + * element can only be added to one bin. *

* If the element's pads are linked to other pads, the pads will be unlinked * before the element is added to the bin. * * @param element The {@link Element} to add to this Bin. - * @return true if the element was successfully added, false if the Bin - * will not accept the element. + * @return true if the element was successfully added, false if the Bin will + * not accept the element. */ public boolean add(Element element) { return GSTBIN_API.gst_bin_add(this, element); } - + /** * Adds an array of Element objects to this Bin * @@ -136,7 +120,7 @@ public boolean add(Element element) { public void addMany(Element... elements) { GSTBIN_API.gst_bin_add_many(this, elements); } - + /** * Removes a Element from this Bin *

@@ -151,7 +135,7 @@ public void addMany(Element... elements) { public boolean remove(Element element) { return GSTBIN_API.gst_bin_remove(this, element); } - + /** * Removes an array of {@link Element} objects from this Bin * @@ -160,59 +144,66 @@ public boolean remove(Element element) { public void removeMany(Element... elements) { GSTBIN_API.gst_bin_remove_many(this, elements); } - - private List elementList(Pointer iter) { - return new GstIterator(iter, Element.class).asList(); + + private List elementList(GstIteratorPtr iter) { + return GstIterator.asList(iter, Element.class); } + /** * Retrieve a list of the {@link Element}s contained in the Bin. - * + * * @return The List of {@link Element}s. */ public List getElements() { return elementList(GSTBIN_API.gst_bin_iterate_elements(this)); } + /** - * Gets an a list of the elements in this bin in topologically - * sorted order. This means that the elements are returned from - * the most downstream elements (sinks) to the sources. + * Gets an a list of the elements in this bin in topologically sorted order. + * This means that the elements are returned from the most downstream + * elements (sinks) to the sources. + * * @return The List of {@link Element}s. */ public List getElementsSorted() { return elementList(GSTBIN_API.gst_bin_iterate_sorted(this)); } - + /** - * Retrieve a list of the {@link Element}s contained in the Bin and its Bin children. - * - * This differs from {@link #getElements()} as it will also return {@link Element}s - * that are in any Bin elements contained in this Bin, also recursing down those Bins. - * + * Retrieve a list of the {@link Element}s contained in the Bin and its Bin + * children. + * + * This differs from {@link #getElements()} as it will also return + * {@link Element}s that are in any Bin elements contained in this Bin, also + * recursing down those Bins. + * * @return The List of {@link Element}s. */ public List getElementsRecursive() { return elementList(GSTBIN_API.gst_bin_iterate_recurse(this)); } - + /** * Retrieve a list of the sink {@link Element}s contained in the Bin. + * * @return The List of sink {@link Element}s. */ public List getSinks() { return elementList(GSTBIN_API.gst_bin_iterate_sinks(this)); } - + /** * Retrieve a list of the source {@link Element}s contained in the Bin. + * * @return The List of source {@link Element}s. */ public List getSources() { return elementList(GSTBIN_API.gst_bin_iterate_sources(this)); } - + /** - * Gets the {@link Element} with the given name from the bin. This - * function recurses into child bins. + * Gets the {@link Element} with the given name from the bin. This function + * recurses into child bins. * * @param name The name of the {@link Element} to find. * @return The {@link Element} if found, else null. @@ -220,75 +211,90 @@ public List getSources() { public Element getElementByName(String name) { return GSTBIN_API.gst_bin_get_by_name(this, name); } - + /** - * Gets the element with the given name from this bin. If the - * element is not found, a recursion is performed on the parent bin. + * Gets the element with the given name from this bin. If the element is not + * found, a recursion is performed on the parent bin. + * * @param name The name of the {@link Element} to find. * @return The {@link Element} if found, else null. */ public Element getElementByNameRecurseUp(String name) { return GSTBIN_API.gst_bin_get_by_name_recurse_up(this, name); } - - /** - * Looks for an element inside the bin that implements the given - * interface. If such an element is found, it returns the element. - * @param iface The class of the {@link Element} to search for. - * @return The {@link Element} that implements the interface. - */ - public T getElementByInterface(Class iface) { - return iface.cast(GSTBIN_API.gst_bin_get_by_interface(this, GstTypes.typeFor(iface))); - } - - /** - * Calls {@link #debugToDotFile(int, String, boolean)} without timestamping + +// /** +// * Looks for an element inside the bin that implements the given interface. +// * If such an element is found, it returns the element. +// * +// * @param iface The class of the {@link Element} to search for. +// * @return The {@link Element} that implements the interface. +// */ +// public T getElementByInterface(Class iface) { +// return iface.cast(GSTBIN_API.gst_bin_get_by_interface(this, GstTypes.typeFor(iface))); +// } + /** + * To aid debugging applications one can use this method to write out the + * whole network of gstreamer elements that form the pipeline into a dot + * file. This file can be processed with graphviz to get an image. e.g. dot + * -Tpng -oimage.png graph_lowlevel.dot + *

+ * The function is only active if gstreamer is configured with + * "--gst-enable-gst-debug" and the environment variable + * GST_DEBUG_DUMP_DOT_DIR is set to a basepath (e.g. /tmp). + * + * @param details to show in the graph + * @param fileName output base filename (e.g. "myplayer") */ - public void debugToDotFile(int details, String fileName) { - debugToDotFile(details, fileName, false); + public void debugToDotFile(EnumSet details, String fileName) { + GSTBIN_API.gst_debug_bin_to_dot_file( + this, NativeFlags.toInt(details), fileName); } - + /** - * To aid debugging applications one can use this method to write out the whole - * network of gstreamer elements that form the pipeline into an dot file. - * This file can be processed with graphviz to get an image. - * e.g. dot -Tpng -oimage.png graph_lowlevel.dot - * + * To aid debugging applications one can use this method to write out the + * whole network of gstreamer elements that form the pipeline into a dot + * file. This file can be processed with graphviz to get an image. e.g. dot + * -Tpng -oimage.png graph_lowlevel.dot + *

* The function is only active if gstreamer is configured with * "--gst-enable-gst-debug" and the environment variable * GST_DEBUG_DUMP_DOT_DIR is set to a basepath (e.g. /tmp). - * - * @param details to show in the graph, e.g. DEBUG_GRAPH_SHOW_ALL + *

+ * Unlike {@link #debugToDotFile(java.util.EnumSet, java.lang.String)} this + * method adds the current timestamp to the filename, so that it can be + * used to take multiple snapshots. + * + * @param details to show in the graph * @param fileName output base filename (e.g. "myplayer") - * @param timestampFileName if true it adds the current timestamp - * to the filename, so that it can be used to take multiple snapshots. */ - public void debugToDotFile(int details, String fileName, boolean timestampFileName) { - if (timestampFileName) - GSTBIN_API.gst_debug_bin_to_dot_file_with_ts(this, details, fileName); - else - GSTBIN_API.gst_debug_bin_to_dot_file(this, details, fileName); + public void debugToDotFileWithTS(EnumSet details, String fileName) { + GSTBIN_API.gst_debug_bin_to_dot_file_with_ts( + this, NativeFlags.toInt(details), fileName); } - + /** * Signal emitted when an {@link Element} is added to this Bin - * + * * @see #connect(ELEMENT_ADDED) * @see #disconnect(ELEMENT_ADDED) */ public static interface ELEMENT_ADDED { + /** * Called when an {@link Element} is added to a {@link Bin} - * + * * @param bin the Bin the element was added to. * @param element the {@link Element} that was added. */ public void elementAdded(Bin bin, Element element); } + /** * Add a listener for the element-added signal on this Bin - * - * @param listener The listener to be called when an {@link Element} is added. + * + * @param listener The listener to be called when an {@link Element} is + * added. */ public void connect(final ELEMENT_ADDED listener) { connect(ELEMENT_ADDED.class, listener, new GstCallback() { @@ -298,35 +304,38 @@ public void callback(Bin bin, Element elem) { } }); } - + /** * Disconnect the listener for the element-added signal - * + * * @param listener The listener that was registered to receive the signal. */ public void disconnect(ELEMENT_ADDED listener) { disconnect(ELEMENT_ADDED.class, listener); } - + /** * Signal emitted when an {@link Element} is removed from this Bin - * + * * @see #connect(ELEMENT_REMOVED) * @see #disconnect(ELEMENT_REMOVED) */ public static interface ELEMENT_REMOVED { + /** * Called when an {@link Element} is removed from a {@link Bin} - * + * * @param bin the Bin the element was removed from. * @param element the {@link Element} that was removed. */ public void elementRemoved(Bin bin, Element element); } + /** * Add a listener for the element-removed signal on this Bin - * - * @param listener The listener to be called when an {@link Element} is removed. + * + * @param listener The listener to be called when an {@link Element} is + * removed. */ public void connect(final ELEMENT_REMOVED listener) { connect(ELEMENT_REMOVED.class, listener, new GstCallback() { @@ -336,39 +345,47 @@ public void callback(Bin bin, Element elem) { } }); } + /** * Disconnect the listener for the element-removed signal - * + * * @param listener The listener that was registered to receive the signal. */ public void disconnect(ELEMENT_REMOVED listener) { disconnect(ELEMENT_REMOVED.class, listener); } - + /** - * Signal emitted when an {@link Element} is added to sub-bin of this {@link Bin} - * + * Signal emitted when an {@link Element} is added to sub-bin of this + * {@link Bin} + * * @see #connect(DEEP_ELEMENT_ADDED) * @see #disconnect(DEEP_ELEMENT_ADDED) */ + @Gst.Since(minor = 10) public static interface DEEP_ELEMENT_ADDED { + /** * Called when an {@link Element} is added to a {@link Bin} - * + * * Since GStreamer 1.10 - * - * @param bin the Bin - * @param sub_bin the Bin the element was added to. + * + * @param bin the Bin + * @param sub_bin the Bin the element was added to. * @param element the {@link Element} that was added. */ public void elementAdded(Bin bin, Bin sub_bin, Element element); } + /** * Add a listener for the deep-element-added signal on this Bin - * - * @param listener The listener to be called when an {@link Element} is added. + * + * @param listener The listener to be called when an {@link Element} is + * added. */ + @Gst.Since(minor = 10) public void connect(final DEEP_ELEMENT_ADDED listener) { + Gst.checkVersion(1, 10); connect(DEEP_ELEMENT_ADDED.class, listener, new GstCallback() { @SuppressWarnings("unused") public void callback(Bin bin, Bin sub_bin, Element elem) { @@ -376,40 +393,49 @@ public void callback(Bin bin, Bin sub_bin, Element elem) { } }); } - + /** * Disconnect the listener for the deep-element-added signal - * + * * @param listener The listener that was registered to receive the signal. */ + @Gst.Since(minor = 10) public void disconnect(DEEP_ELEMENT_ADDED listener) { disconnect(DEEP_ELEMENT_ADDED.class, listener); } /** - * Signal emitted when an {@link Element} is removed from sub-bin of this {@link Bin} - * + * Signal emitted when an {@link Element} is removed from sub-bin of this + * {@link Bin} + * * @see #connect(ELEMENT_REMOVED) * @see #disconnect(ELEMENT_REMOVED) */ + @Gst.Since(minor = 10) public static interface DEEP_ELEMENT_REMOVED { + /** * Called when an {@link Element} is removed from a {@link Bin} - * + * * Since GStreamer 1.10 - * - * @param bin the Bin + * + * @param bin the Bin * @param sub_bin the Bin the element was removed from. * @param element the {@link Element} that was removed. */ public void elementRemoved(Bin bin, Bin sub_bin, Element element); } + /** - * Add a listener for the deep-element-removed signal on this Bin - * - * @param listener The listener to be called when an {@link Element} is removed. + * Add a listener for the deep-element-removed signal on this + * Bin + * + * @param listener The listener to be called when an {@link Element} is + * removed. */ + @Gst.Since(minor = 10) public void connect(final DEEP_ELEMENT_REMOVED listener) { + Gst.checkVersion(1, 10); connect(DEEP_ELEMENT_REMOVED.class, listener, new GstCallback() { @SuppressWarnings("unused") public void callback(Bin bin, Bin sub_bin, Element elem) { @@ -417,33 +443,38 @@ public void callback(Bin bin, Bin sub_bin, Element elem) { } }); } + /** * Disconnect the listener for the deep-element-removed signal - * + * * @param listener The listener that was registered to receive the signal. */ + @Gst.Since(minor = 10) public void disconnect(DEEP_ELEMENT_REMOVED listener) { disconnect(DEEP_ELEMENT_REMOVED.class, listener); } /** * Signal emitted when an {@link Element} has latency - * + * * @see #connect(DO_LATENCY) * @see #disconnect(DO_LATENCY) */ public static interface DO_LATENCY { + /** * Called when an {@link Element} is removed from a {@link Bin} - * + * * @param bin the Bin the element was removed from. */ public void doLatency(Bin bin); } + /** * Add a listener for the do-latency signal on this Bin - * - * @param listener The listener to be called when an {@link Element} is removed. + * + * @param listener The listener to be called when an {@link Element} is + * removed. */ public void connect(final DO_LATENCY listener) { connect(DO_LATENCY.class, listener, new GstCallback() { @@ -453,12 +484,64 @@ public void callback(Bin bin) { } }); } + /** * Disconnect the listener for the do-latency signal - * + * * @param listener The listener that was registered to receive the signal. */ public void disconnect(DO_LATENCY listener) { disconnect(DO_LATENCY.class, listener); } + + /** + * Available details for pipeline graphs produced by + * {@link #debugToDotFile(java.util.EnumSet, java.lang.String)} + */ + public static enum DebugGraphDetails implements NativeFlags { + + /** + * Show caps-name on edges. + */ + SHOW_MEDIA_TYPE(1 << 0), + /** + * Show caps-details on edges. + */ + SHOW_CAPS_DETAILS(1 << 1), + /** + * Show modified parameters on elements. + */ + SHOW_NON_DEFAULT_PARAMS(1 << 2), + /** + * Show element states. + */ + SHOW_STATES(1 << 3); + + /** + * A convenience EnumSet with all values. + */ + public final static EnumSet SHOW_ALL + = EnumSet.allOf(DebugGraphDetails.class); + + private final int value; + + private DebugGraphDetails(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + + } + + static class Handle extends Element.Handle { + + public Handle(GstObjectPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + } + } diff --git a/src/org/freedesktop/gstreamer/Buffer.java b/src/org/freedesktop/gstreamer/Buffer.java index b87cd1c3..3576c8c5 100644 --- a/src/org/freedesktop/gstreamer/Buffer.java +++ b/src/org/freedesktop/gstreamer/Buffer.java @@ -1,25 +1,25 @@ /* - * Copyright (c) 2015 Neil C Smith + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2019 Christophe Lafolet * Copyright (C) 2014 Tom Greenwood * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans - * + * * This file is part of gstreamer-java. * - * This code is free software: you can redistribute it and/or modify it under + * This code is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3 only, as * published by the Free Software Foundation. * - * This code 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 + * This code 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 * version 3 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import static org.freedesktop.gstreamer.lowlevel.GstBufferAPI.GSTBUFFER_API; @@ -31,263 +31,367 @@ import org.freedesktop.gstreamer.lowlevel.GstBufferAPI.MapInfoStruct; import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GstMetaPtr; /** - * Data-passing buffer type, supporting sub-buffers. - * Ssee {@link Pad}, {@link MiniObject} - *

- * Buffers are the basic unit of data transfer in GStreamer. The Buffer - * type provides all the state necessary to define a region of memory as part - * of a stream. Sub-buffers are also supported, allowing a smaller region of a - * buffer to become its own buffer, with mechanisms in place to ensure that - * neither memory space goes away prematurely. - *

- * Non-plugins will usually not need to allocate buffers, but they can be allocated - * using new {@link #Buffer(int)} to create a buffer with preallocated data of a given size. - *

- * The data pointed to by the buffer can be accessed with the {@link #getByteBuffer} - * method. For buffers of size 0, the data pointer is undefined (usually NULL) - * and should never be used. - *

- * If an element knows what pad you will push the buffer out on, it should use - * gst_pad_alloc_buffer() instead to create a buffer. This allows downstream - * elements to provide special buffers to write in, like hardware buffers. - *

- * A buffer has a pointer to a {@link Caps} describing the media type of the data - * in the buffer. Attach caps to the buffer with {@link #setCaps}; this - * is typically done before pushing out a buffer using gst_pad_push() so that - * the downstream element knows the type of the buffer. - *

- * A buffer will usually have a timestamp, and a duration, but neither of these - * are guaranteed (they may be set to -1). Whenever a - * meaningful value can be given for these, they should be set. The timestamp - * and duration are measured in nanoseconds (they are long values). - *

- * A buffer can also have one or both of a start and an end offset. These are - * media-type specific. For video buffers, the start offset will generally be - * the frame number. For audio buffers, it will be the number of samples - * produced so far. For compressed data, it could be the byte offset in a - * source or destination file. Likewise, the end offset will be the offset of - * the end of the buffer. These can only be meaningfully interpreted if you - * know the media type of the buffer (the #GstCaps set on it). Either or both - * can be set to -1. - *

- * To efficiently create a smaller buffer out of an existing one, you can - * use {@link #createSubBuffer}. - *

- * If a plug-in wants to modify the buffer data in-place, it should first obtain - * a buffer that is safe to modify by using {@link #makeWritable}. This - * function is optimized so that a copy will only be made when it is necessary. - *

- * A plugin that only wishes to modify the metadata of a buffer, such as the - * offset, timestamp or caps, should use gst_buffer_make_metadata_writable(), - * which will create a subbuffer of the original buffer to ensure the caller - * has sole ownership, and not copy the buffer data. - *

- * Buffers can be efficiently merged into a larger buffer with - * gst_buffer_merge() and gst_buffer_span() if the gst_buffer_is_span_fast() - * function returns TRUE. + * Buffers are the basic unit of data transfer in GStreamer. They contain the + * timing and offset along with other arbitrary metadata that is associated with + * the GstMemory blocks that the buffer contains. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstBuffer.html */ public class Buffer extends MiniObject { + public static final String GTYPE_NAME = "GstBuffer"; private final MapInfoStruct mapInfo; private final BufferStruct struct; - - public Buffer(Initializer init) { - super(init); - mapInfo = new MapInfoStruct(); - struct = new BufferStruct(handle()); - } - /** * Creates a newly allocated buffer without any data. */ public Buffer() { - this(initializer(GSTBUFFER_API.ptr_gst_buffer_new())); + this(Natives.initializer(GSTBUFFER_API.ptr_gst_buffer_new())); } - + /** - * Creates a newly allocated buffer with data of the given size. - * The buffer memory is not cleared. If the requested amount of - * memory cannot be allocated, an exception will be thrown. - * + * Creates a newly allocated buffer with data of the given size. The buffer + * memory is not cleared. If the requested amount of memory cannot be + * allocated, an exception will be thrown. + *

* Note that when size == 0, the buffer data pointer will be NULL. * * @param size */ public Buffer(int size) { - this(initializer(allocBuffer(size))); + this(Natives.initializer(allocBuffer(size))); } - + + Buffer(Initializer init) { + super(init); + mapInfo = new MapInfoStruct(); + struct = new BufferStruct(getRawPointer()); + } + private static Pointer allocBuffer(int size) { Pointer ptr = GSTBUFFER_API.ptr_gst_buffer_new_allocate(null, size, null); if (ptr == null) { - throw new OutOfMemoryError("Could not allocate Buffer of size "+ size); + throw new OutOfMemoryError("Could not allocate Buffer of size " + size); } return ptr; } - -// /** -// * Gets the size of the buffer data -// * -// * @return the size of the buffer data in bytes. -// */ -// public int getSize() { -// return GstBufferAPI.GSTBUFFER_API.gst_buffer_get_size(this).intValue(); -// } - + /** * Gets a {@link java.nio.ByteBuffer} that can access the native memory - * associated with this Buffer. - * + * associated with this Buffer, with the option of ensuring the memory is + * writable. + *

+ * When requesting a writable buffer, if the buffer is writable but the + * underlying memory isn't, a writable copy will automatically be created + * and returned. The readonly copy of the buffer memory will then also be + * replaced with this writable copy. + *

+ * The Buffer should be unmapped with {@link #unmap()} after usage. + * + * @param writable * @return A {@link java.nio.ByteBuffer} that can access this Buffer's data. */ - public ByteBuffer map(boolean writeable) { + public ByteBuffer map(boolean writable) { final boolean ok = GSTBUFFER_API.gst_buffer_map(this, mapInfo, - writeable ? GstBufferAPI.GST_MAP_WRITE : GstBufferAPI.GST_MAP_READ); + writable ? GstBufferAPI.GST_MAP_WRITE : GstBufferAPI.GST_MAP_READ); if (ok && mapInfo.data != null) { return mapInfo.data.getByteBuffer(0, mapInfo.size.intValue()); } return null; } - + + /** + * Release the memory previously mapped with {@link #map(boolean)} + */ public void unmap() { GSTBUFFER_API.gst_buffer_unmap(this, mapInfo); } - + + /** + * Get the amount of memory blocks that this buffer has. This amount is never + * larger than what {@code gst_buffer_get_max_memory()} returns. + * + * @return the number of memory blocks this buffer is made of. + */ + public int getMemoryCount() { + return GSTBUFFER_API.gst_buffer_n_memory(this); + } + /** - * Gets the timestamps of this buffer. - * The buffer DTS refers to the timestamp when the buffer should be decoded and is usually monotonically increasing. + * Gets the timestamps of this buffer. The buffer DTS refers to the + * timestamp when the buffer should be decoded and is usually monotonically + * increasing. * - * @return a ClockTime representing the timestamp or {@link ClockTime#NONE} when the timestamp is not known or relevant. + * @return a long representing the timestamp or {@link ClockTime#NONE} + * when the timestamp is not known or relevant. */ - public ClockTime getDecodeTimestamp() { - return (ClockTime)this.struct.readField("dts"); + public long getDecodeTimestamp() { + return (long) this.struct.readField("dts"); } /** * Set the decode timestamp of the Buffer - * @param val a ClockTime representing the timestamp or {@link ClockTime#NONE} when the timestamp is not known or relevant. + * + * @param val a long representing the timestamp or + * {@link ClockTime#NONE} when the timestamp is not known or relevant. */ - public void setDecodeTimestamp(ClockTime val) - { + public void setDecodeTimestamp(long val) { this.struct.writeField("dts", val); } /** - * Gets the timestamps of this buffer. - * The buffer PTS refers to the timestamp when the buffer content should be presented to the user and is not always monotonically increasing. + * Gets the timestamps of this buffer. The buffer PTS refers to the + * timestamp when the buffer content should be presented to the user and is + * not always monotonically increasing. * - * @return a ClockTime representing the timestamp or {@link ClockTime#NONE} when the timestamp is not known or relevant. + * @return a long representing the timestamp or {@link ClockTime#NONE} + * when the timestamp is not known or relevant. */ - public ClockTime getPresentationTimestamp() { - return (ClockTime)this.struct.readField("pts"); + public long getPresentationTimestamp() { + return (long) this.struct.readField("pts"); } /** * Set the presentation timestamp of the Buffer - * @param val a ClockTime representing the timestamp or {@link ClockTime#NONE} when the timestamp is not known or relevant. + * + * @param val a long representing the timestamp or + * {@link ClockTime#NONE} when the timestamp is not known or relevant. */ - public void setPresentationTimestamp(ClockTime val) - { + public void setPresentationTimestamp(long val) { this.struct.writeField("pts", val); } /** * Gets the duration of this buffer. * - * @return a ClockTime representing the timestamp or {@link ClockTime#NONE} when the timestamp is not known or relevant. + * @return a ClockTime representing the timestamp or {@link ClockTime#NONE} + * when the timestamp is not known or relevant. */ - public ClockTime getDuration() { - return (ClockTime)this.struct.readField("duration"); + public long getDuration() { + return (long) this.struct.readField("duration"); } /** * Set the duration of this buffer. - * @param val a ClockTime representing the duration or {@link ClockTime#NONE} when the timestamp is not known or relevant. + * + * @param val a long representing the duration or + * {@link ClockTime#NONE} when the timestamp is not known or relevant. */ - public void setDuration(ClockTime val) - { + public void setDuration(long val) { this.struct.writeField("duration", val); } /** * Get the offset (media-specific) of this buffer - * @return a media specific offset for the buffer data. For video frames, this is the frame number of this buffer. For audio samples, this is the offset of the first sample in this buffer. For file data or compressed data this is the byte offset of the first byte in this buffer. + * + * @return a media specific offset for the buffer data. For video frames, + * this is the frame number of this buffer. For audio samples, this is the + * offset of the first sample in this buffer. For file data or compressed + * data this is the byte offset of the first byte in this buffer. */ - public long getOffset() - { - return (Long)this.struct.readField("offset"); + public long getOffset() { + return (Long) this.struct.readField("offset"); } /** * Set the offset (media-specific) of this buffer - * @param val a media specific offset for the buffer data. For video frames, this is the frame number of this buffer. For audio samples, this is the offset of the first sample in this buffer. For file data or compressed data this is the byte offset of the first byte in this buffer. + * + * @param val a media specific offset for the buffer data. For video frames, + * this is the frame number of this buffer. For audio samples, this is the + * offset of the first sample in this buffer. For file data or compressed + * data this is the byte offset of the first byte in this buffer. */ - public void setOffset(long val) - { + public void setOffset(long val) { this.struct.writeField("offset", val); } /** * Get the offset (media-specific) of this buffer - * @return a media specific offset for the buffer data. For video frames, this is the frame number of this buffer. For audio samples, this is the offset of the first sample in this buffer. For file data or compressed data this is the byte offset of the first byte in this buffer. + * + * @return a media specific offset for the buffer data. For video frames, + * this is the frame number of this buffer. For audio samples, this is the + * offset of the first sample in this buffer. For file data or compressed + * data this is the byte offset of the first byte in this buffer. */ - public long getOffsetEnd() - { - return (Long)this.struct.readField("offset_end"); + public long getOffsetEnd() { + return (Long) this.struct.readField("offset_end"); } /** * Set the offset (media-specific) of this buffer - * @param val a media specific offset for the buffer data. For video frames, this is the frame number of this buffer. For audio samples, this is the offset of the first sample in this buffer. For file data or compressed data this is the byte offset of the first byte in this buffer. + * + * @param val a media specific offset for the buffer data. For video frames, + * this is the frame number of this buffer. For audio samples, this is the + * offset of the first sample in this buffer. For file data or compressed + * data this is the byte offset of the first byte in this buffer. */ - public void setOffsetEnd(long val) - { + public void setOffsetEnd(long val) { this.struct.writeField("offset_end", val); } /** - * get the GstBufferFlags describing this buffer. - * - * Since GStreamer 1.10 - * - * @return a bit mask whose values can be interpreted by comparing them with {@link BufferFlag} + * Get the GstBufferFlags describing this buffer. + *

+ * Since GStreamer 1.10 + * + * @return an EnumSet of {@link BufferFlags} */ - public int getFlags() - { - return GstBufferAPI.GSTBUFFER_API.gst_buffer_get_flags(this); + @Gst.Since(minor = 10) + public EnumSet getFlags() { + Gst.checkVersion(1, 10); + int nativeInt = GstBufferAPI.GSTBUFFER_API.gst_buffer_get_flags(this); + return NativeFlags.fromInt(BufferFlags.class, nativeInt); } /** - * set some of the GstBufferFlags describing this buffer. This is a union operation and does not clear flags that are not mentioned in val + * Get the metadata for api on buffer. When there is no such metadata, NULL + * is returned. + * + * @param implementation type of metadata + * @param api api type of metadata + * @return meta or null + */ + public T getMeta(Meta.API api) { + GType apiType = api.getAPIGType(); + if (apiType == GType.INVALID) { + return null; + } + GstMetaPtr ptr = GSTBUFFER_API.gst_buffer_get_meta(this, apiType); + // can not create metadata class from null pointer + if (ptr == null) { + return null; + } + return Natives.objectFor(ptr, api.getImplClass(), false, false); + } + + /** + * Iterate all Meta on buffer. * + * @return iterator of meta + */ + public Iterator iterateMeta() { + return new MetaIterator(this); + } + + /** + * Check if buffer contains metadata for api. + *

+ * Since GStreamer 1.14 + * + * @param implementation type of metadata + * @param api type of metadata + * @return return true only if buffer contains selected type of metadata + */ + @Gst.Since(minor = 14) + public boolean hasMeta(Meta.API api) { + return getMetaCount(api) > 0; + } + + /** + * Check number of metadata for api. There can be more than one metadata in + * case of multiple video/audio layer. + *

+ * Since GStreamer 1.14 + * + * @param implementation type of metadata + * @param api type of metadata + * @return count of metadata of provided api type + */ + @Gst.Since(minor = 14) + public int getMetaCount(Meta.API api) { + Gst.checkVersion(1, 14); + GType apiType = api.getAPIGType(); + if (apiType == GType.INVALID) { + return 0; + } + return GSTBUFFER_API.gst_buffer_get_n_meta(this, apiType); + } + + + /** + * Set some of the GstBufferFlags describing this buffer. This is a union + * operation and does not clear flags that are not mentioned. + *

* Since GStreamer 1.10 - * - * @param val a bit mask of flags to be set on the buffer. bits which are zero in val do not get cleared in this buffer. + * + * @param flags an EnumSet of {@link BufferFlags} to be set on the buffer. * @return true if flags were successfully set on this buffer - * @see BufferFlag */ - public boolean setFlags(int val) - { - return GstBufferAPI.GSTBUFFER_API.gst_buffer_set_flags(this, val); + @Gst.Since(minor = 10) + public boolean setFlags(EnumSet flags) { + Gst.checkVersion(1, 10); + return GstBufferAPI.GSTBUFFER_API.gst_buffer_set_flags(this, NativeFlags.toInt(flags)); } /** - * unset the GstBufferFlags describing this buffer. This is a difference operation and does not clear flags that are not mentioned in val - * + * unset the GstBufferFlags describing this buffer. This is a difference + * operation and does not clear flags that are not mentioned. + *

* Since GStreamer 1.10 - * - * @param val a bit mask of flags to be cleared on the buffer. bits which are zero in val do not get cleared in this buffer. + * + * @param flags an EnumSet of {@link BufferFlags} to be cleared on the buffer. * @return true if flags were successfully cleared on this buffer - * @see BufferFlag - * */ - public boolean unsetFlags(int val) - { - return GstBufferAPI.GSTBUFFER_API.gst_buffer_unset_flags(this, val); + * + */ + @Gst.Since(minor = 10) + public boolean unsetFlags(EnumSet flags) { + Gst.checkVersion(1, 10); + return GstBufferAPI.GSTBUFFER_API.gst_buffer_unset_flags(this, NativeFlags.toInt(flags)); + } + + private static class MetaIterator implements Iterator { + + private final PointerByReference state; + private final Buffer buffer; + private Meta next; + + MetaIterator(final Buffer buffer) { + state = new PointerByReference(); + this.buffer = buffer; + } + + @Override + public boolean hasNext() { + if (next == null) { + next = getNext(); + } + return next != null; + } + + @Override + public Meta next() { + if (!hasNext() || next == null) { + throw new NoSuchElementException(); + } + Meta m = next; + next = null; + return m; + } + + private Meta getNext() { + return Natives.objectFor( + GSTBUFFER_API.gst_buffer_iterate_meta(this.buffer, this.state), + Meta.class, + false, + false + ); + } + + } } diff --git a/src/org/freedesktop/gstreamer/BufferFlag.java b/src/org/freedesktop/gstreamer/BufferFlags.java similarity index 83% rename from src/org/freedesktop/gstreamer/BufferFlag.java rename to src/org/freedesktop/gstreamer/BufferFlags.java index f1b5e993..944858dd 100644 --- a/src/org/freedesktop/gstreamer/BufferFlag.java +++ b/src/org/freedesktop/gstreamer/BufferFlags.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -20,13 +21,12 @@ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; -import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; +import org.freedesktop.gstreamer.glib.NativeFlags; /** * A set of buffer flags used to describe properties of a {@link Buffer}. */ -public enum BufferFlag implements IntegerEnum { +public enum BufferFlags implements NativeFlags { /** * the {@link Buffer} is live data and should be discarded in the PAUSED state. @@ -93,14 +93,23 @@ public enum BufferFlag implements IntegerEnum { */ SYNC_AFTER(MiniObjectFlags.LAST.intValue() << 11), + /** + * This buffer is important and should not be dropped. This can be used to + * mark important buffers, e.g. to flag RTP packets carrying keyframes or + * codec setup data for RTP Forward Error Correction purposes, or to prevent + * still video frames from being dropped by elements due to QoS. (Since + * 1.14) + + */ + @Gst.Since(minor = 14) + NON_DROPPABLE(MiniObjectFlags.LAST.intValue() << 12), + /* padding */ - LAST(MiniObjectFlags.LAST.intValue() << 16), + LAST(MiniObjectFlags.LAST.intValue() << 16); - /** The value used for unknown native values */ - @DefaultEnumValue - UNKNOWN(~0); + private final int value; - private BufferFlag(int value) { + private BufferFlags(int value) { this.value = value; } /** @@ -111,5 +120,4 @@ public final int intValue() { return value; } - private final int value; } diff --git a/src/org/freedesktop/gstreamer/BufferPool.java b/src/org/freedesktop/gstreamer/BufferPool.java index 23994758..b61d03ca 100644 --- a/src/org/freedesktop/gstreamer/BufferPool.java +++ b/src/org/freedesktop/gstreamer/BufferPool.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * * This file is part of gstreamer-java. @@ -20,36 +21,58 @@ import org.freedesktop.gstreamer.lowlevel.GstBufferPoolAPI; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.Natives; +/** + * A BufferPool is an object that can be used to pre-allocate and recycle + * buffers of the same size and with the same properties. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstBufferPool.html + */ public class BufferPool extends GstObject { public static final String GTYPE_NAME = "GstBufferPool"; + /** + * Creates a new instance of BufferPool + */ + public BufferPool() { + this(Natives.initializer(GstBufferPoolAPI.GSTBUFFERPOOL_API.ptr_gst_buffer_pool_new())); + } + /** * This constructor is for internal use only. * @param init initialization data. */ - public BufferPool(final Initializer init) { + BufferPool(final Initializer init) { super(init); } /** - * Creates a new instance of BufferPool + * Configure the BufferPool with the given parameters. + * + * @param caps the {@link Caps} for the buffers + * @param size the size of each buffer, not including prefix and padding + * @param min_buffers the minimum amount of buffers to allocate + * @param max_buffers the maximum amount of buffers to allocate or 0 for unlimited */ - public BufferPool() { - this(initializer(GstBufferPoolAPI.GSTBUFFERPOOL_API.ptr_gst_buffer_pool_new())); - } - public void setParams(Caps caps, int size, int min_buffers, int max_buffers) { Structure config = GstBufferPoolAPI.GSTBUFFERPOOL_API.gst_buffer_pool_get_config(this); GstBufferPoolAPI.GSTBUFFERPOOL_API.gst_buffer_pool_config_set_params(config, caps, size, min_buffers, max_buffers); } + /** + * Query the {@link Caps} configured on the BufferPool. + * + * @return Caps configured on the BufferPool + */ public Caps getCaps() { Structure config = GstBufferPoolAPI.GSTBUFFERPOOL_API.gst_buffer_pool_get_config(this); Pointer[] ptr = new Pointer[1]; GstBufferPoolAPI.GSTBUFFERPOOL_API.gst_buffer_pool_config_get_params(config, ptr, null, null, null); - return new Caps(new Initializer(ptr[0], false, true)); + return new Caps(Natives.initializer(ptr[0], false, true)); } } diff --git a/src/org/freedesktop/gstreamer/Bus.java b/src/org/freedesktop/gstreamer/Bus.java index f7a266d7..2520e6e0 100644 --- a/src/org/freedesktop/gstreamer/Bus.java +++ b/src/org/freedesktop/gstreamer/Bus.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Neil C Smith + * Copyright (c) 2025 Neil C Smith * Copyright (C) 2014 Tom Greenwood * Copyright (C) 2007 Wayne Meissner * Copyright (C) 2004 Wim Taymans @@ -18,13 +18,10 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Locale; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,12 +30,20 @@ import com.sun.jna.CallbackThreadInitializer; import com.sun.jna.Native; import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; import com.sun.jna.ptr.PointerByReference; +import org.freedesktop.gstreamer.glib.GObject; +import org.freedesktop.gstreamer.glib.NativeEnum; -import org.freedesktop.gstreamer.event.BusSyncHandler; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; import org.freedesktop.gstreamer.lowlevel.GstBusAPI; import org.freedesktop.gstreamer.lowlevel.GstBusAPI.BusCallback; +import org.freedesktop.gstreamer.lowlevel.GstBusPtr; +import org.freedesktop.gstreamer.lowlevel.GstMessagePtr; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.message.MessageType; import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; import static org.freedesktop.gstreamer.lowlevel.GstBusAPI.GSTBUS_API; @@ -46,574 +51,362 @@ import static org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.GSTMINIOBJECT_API; /** - * The {@link Bus} is an object responsible for delivering {@link Message}s in - * a first-in first-out way from the streaming threads to the application. - *

- * Since the application typically only wants to deal with delivery of these - * messages from one thread, the Bus will marshall the messages between - * different threads. This is important since the actual streaming of media - * is done in another thread than the application. + * The {@link Bus} is an object responsible for delivering {@link Message}s in a + * first-in first-out way from the streaming threads to the application. *

- * A message is posted on the bus with the gst_bus_post() method. With the - * gst_bus_peek() and gst_bus_pop() methods one can look at or retrieve a - * previously posted message. + * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstbus.html *

- * The bus can be polled with the gst_bus_poll() method. This methods blocks - * up to the specified timeout value until one of the specified messages types - * is posted on the bus. The application can then _pop() the messages from the - * bus to handle them. + * Since the application typically only wants to deal with delivery of these + * messages from one thread, the Bus will marshal the messages between different + * threads. This is important since the actual streaming of media is done in + * another thread than the application. *

* It is also possible to get messages from the bus without any thread - * marshalling with the {@link #setSyncHandler} method. This makes it - * possible to react to a message in the same thread that posted the - * message on the bus. This should only be used if the application is able - * to deal with messages from different threads. + * marshalling with the {@link #setSyncHandler} method. This makes it possible + * to react to a message in the same thread that posted the message on the bus. + * This should only be used if the application is able to deal with messages + * from different threads. *

* Every {@link Pipeline} has one bus. *

- * Note that a Pipeline will set its bus into flushing state when changing - * from READY to NULL state. + * Note that a Pipeline will set its bus into flushing state when changing from + * READY to NULL state. */ public class Bus extends GstObject { - static final Logger log = Logger.getLogger(Bus.class.getName()); - static final Level LOG_DEBUG = Level.FINE; public static final String GTYPE_NAME = "GstBus"; + private static final Logger LOG = Logger.getLogger(Bus.class.getName()); + private static final SyncCallback SYNC_CALLBACK = new SyncCallback(); + + private volatile BusSyncHandler syncHandler = null; + private final Object lock = new Object(); + private final List> messageProxies = new CopyOnWriteArrayList<>(); private boolean watchAdded = false; /** * This constructor is used internally by gstreamer-java - * + * * @param init internal initialization data */ - public Bus(Initializer init) { - super(init); + Bus(Initializer init) { + super(init); GSTBUS_API.gst_bus_set_sync_handler(this, null, null, null); - GSTBUS_API.gst_bus_set_sync_handler(this, syncCallback, null, null); + GSTBUS_API.gst_bus_set_sync_handler(this, SYNC_CALLBACK, null, null); } - + /** * Instructs the bus to flush out any queued messages. - * - * If flushing, flush out any messages queued in the bus. Will flush future + * + * If flushing, flush out any messages queued in the bus. Will flush future * messages until {@link #setFlushing} is called with false. - * + * * @param flushing true if flushing is desired. */ public void setFlushing(boolean flushing) { GSTBUS_API.gst_bus_set_flushing(this, flushing ? 1 : 0); } - - /** - * Signal emitted when end-of-stream is reached in a pipeline. - * - * The application will only receive this message in the PLAYING state and - * every time it sets a pipeline to PLAYING that is in the EOS state. - * The application can perform a flushing seek in the pipeline, which will - * undo the EOS state again. - * - * @see #connect(EOS) - * @see #disconnect(EOS) - */ - public static interface EOS { - /** - * Called when a {@link Pipeline} element posts a end-of-stream message. - * - * @param source the element which posted the message. - */ - public void endOfStream(GstObject source); - } - - /** - * Signal emitted when an error occurs. - *

- * When the application receives an error message it should stop playback - * of the pipeline and not assume that more data will be played. - * - * @see #connect(ERROR) - * @see #disconnect(ERROR) - */ - public static interface ERROR { - /** - * Called when a {@link Pipeline} element posts an error message. - * - * @param source the element which posted the message. - * @param code a numeric code representing the error. - * @param message a string representation of the error. - */ - public void errorMessage(GstObject source, int code, String message); - } - - /** - * Signal emitted when a warning message is delivered. - * - * @see #connect(WARNING) - * @see #disconnect(WARNING) - */ - public static interface WARNING { - /** - * Called when a {@link Pipeline} element posts an warning message. - * - * @param source the element which posted the message. - * @param code a numeric code representing the warning. - * @param message a string representation of the warning. - */ - public void warningMessage(GstObject source, int code, String message); - } - - /** - * Signal emitted when an informational message is delivered. - * - * @see #connect(INFO) - * @see #disconnect(INFO) - */ - public static interface INFO { - /** - * Called when a {@link Pipeline} element posts an informational message. - * - * @param source the element which posted the message. - * @param code a numeric code representing the informational message. - * @param message a string representation of the informational message. - */ - public void infoMessage(GstObject source, int code, String message); - } - - /** - * Signal emitted when a new tag is identified on the stream. - * - * @see #connect(TAG) - * @see #disconnect(TAG) - */ - public static interface TAG { - /** - * Called when a {@link Pipeline} element finds media meta-data. - * - * @param source the element which posted the message. - * @param tagList a list of media meta-data. - */ - public void tagsFound(GstObject source, TagList tagList); - } - - /** - * Signal emitted when a state change happens. - * - * @see #connect(STATE_CHANGED) - * @see #disconnect(STATE_CHANGED) - */ - public static interface STATE_CHANGED { - /** - * Called when a {@link Pipeline} element executes a {@link State} change. - * - * @param source the element which posted the message. - * @param old the old state that the element is changing from. - * @param current the new (current) state the element is changing to. - * @param pending the pending (target) state. - */ - public void stateChanged(GstObject source, State old, State current, State pending); - } - - /** - * Signal emitted when the pipeline is buffering data. - * - * @see #connect(BUFFERING) - * @see #disconnect(BUFFERING) - */ - public static interface BUFFERING { - /** - * Called when a {@link Pipeline} element needs to buffer data before - * it can continue processing. - *

- * {@code percent} is a value between 0 and 100. A value of 100 means that - * the buffering completed. - *

- * When {@code percent} is < 100 the application should PAUSE a PLAYING pipeline. - * When {@code percent} is 100, the application can set the pipeline (back) to PLAYING. - *

- * The application must be prepared to receive BUFFERING messages in the - * PREROLLING state and may only set the pipeline to PLAYING after receiving a - * message with {@code percent} set to 100, which can happen after the pipeline - * completed prerolling. - * - * @param source the element which posted the message. - * @param percent the percentage of buffering that has completed. - */ - public void bufferingData(GstObject source, int percent); - } - - /** - * Signal sent when a new duration message is posted by an element that - * know the duration of a stream in a specific format. - *

- * This message is received by bins and is used to calculate the total - * duration of a pipeline. - *

- * Elements may post a duration message with a duration of - * {@link ClockTime#NONE} to indicate that the duration has changed and the - * cached duration should be discarded. The new duration can then be - * retrieved via a query. - * The application can get the new duration with a duration query. - * - * @see #connect(DURATION) - * @see #disconnect(DURATION) - */ - public static interface DURATION { - /** - * Called when a new duration message is posted on the Bus. - * - * @param source the element which posted the message. - * @param format the format of the duration - * @param duration the new duration. - */ - public void durationChanged(GstObject source, Format format, long duration); - } - - /** - * This message is posted by elements that start playback of a segment as a - * result of a segment seek. - *

- * This message is not received by the application - * but is used for maintenance reasons in container elements. - */ - public static interface SEGMENT_START { - public void segmentStart(GstObject source, Format format, long position); - } - - /** - * Signal emitted when the pipeline has completed playback of a segment. - *

- * This message is posted by elements that finish playback of a segment as a - * result of a segment seek. This message is received by the application - * after all elements that posted a {@link SEGMENT_START} have posted segment-done. - * - * @see #connect(SEGMENT_DONE) - * @see #disconnect(SEGMENT_DONE) - */ - public static interface SEGMENT_DONE { - /** - * Called when a segment-done message has been posted. - * - * @param source the element which posted the message. - * @param format the format of the position being done. - * @param position the position of the segment being done. - */ - public void segmentDone(GstObject source, Format format, long position); - } - - /** - * Signal emitted by elements when they complete an ASYNC state change. - *

- * Applications will only receive this message from the top level pipeline. - *

- * - * @see #connect(ASYNC_DONE) - * @see #disconnect(ASYNC_DONE) - */ - public static interface ASYNC_DONE { - /** - * Called when a segment-done message has been posted. - * - * @param source the element which posted the message. - */ - public void asyncDone(GstObject source); - } - - /** - * Catch all signals emitted on the Bus. - *

- * The signal handler will be called asynchronously from the thread that posted - * the message on the Bus. - * - * @see #connect(MESSAGE) - * @see #disconnect(MESSAGE) - */ - public static interface MESSAGE { - /** - * Called when a {@link Element} posts a {@link Message} on the Bus. - * - * @param bus the Bus the message was posted on. - * @param message the message that was posted. - */ - public void busMessage(Bus bus, Message message); - } /** * Add a listener for end-of-stream messages. - * - * @param listener The listener to be called when end-of-stream is encountered. + * + * @param listener The listener to be called when end-of-stream is + * encountered. */ public void connect(final EOS listener) { connect(EOS.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - listener.endOfStream(msg.getSource()); + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + listener.endOfStream(Natives.objectFor(msg.getSource(), GstObject.class, true, true)); return true; } }); } - + /** * Disconnect the listener for end-of-stream messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(EOS listener) { disconnect(EOS.class, listener); } - + /** * Add a listener for error messages. - * - * @param listener The listener to be called when an error in the stream is encountered. + * + * @param listener The listener to be called when an error in the stream is + * encountered. */ public void connect(final ERROR listener) { connect(ERROR.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { PointerByReference err = new PointerByReference(); GSTMESSAGE_API.gst_message_parse_error(msg, err, null); GErrorStruct error = new GErrorStruct(err.getValue()); - listener.errorMessage(msg.getSource(), error.getCode(), error.getMessage()); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.errorMessage(source, error.getCode(), error.getMessage()); GLIB_API.g_error_free(err.getValue()); return true; } }); } - + /** * Disconnect the listener for error messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(ERROR listener) { disconnect(ERROR.class, listener); } - + /** * Add a listener for warning messages. - * - * @param listener The listener to be called when an {@link Element} emits a warning. + * + * @param listener The listener to be called when an {@link Element} emits a + * warning. */ public void connect(final WARNING listener) { connect(WARNING.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { PointerByReference err = new PointerByReference(); - GSTMESSAGE_API.gst_message_parse_warning(msg, err, null); + GSTMESSAGE_API.gst_message_parse_warning(msg, err, null); GErrorStruct error = new GErrorStruct(err.getValue()); - listener.warningMessage(msg.getSource(), error.getCode(), error.getMessage()); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.warningMessage(source, error.getCode(), error.getMessage()); GLIB_API.g_error_free(err.getValue()); return true; } }); } - + /** * Disconnect the listener for warning messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(WARNING listener) { disconnect(WARNING.class, listener); } - + /** * Add a listener for informational messages. - * - * @param listener The listener to be called when an {@link Element} emits a an informational message. + * + * @param listener The listener to be called when an {@link Element} emits a + * an informational message. */ public void connect(final INFO listener) { connect(INFO.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { PointerByReference err = new PointerByReference(); - GSTMESSAGE_API.gst_message_parse_info(msg, err, null); + GSTMESSAGE_API.gst_message_parse_info(msg, err, null); GErrorStruct error = new GErrorStruct(err.getValue()); - listener.infoMessage(msg.getSource(), error.getCode(), error.getMessage()); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.infoMessage(source, error.getCode(), error.getMessage()); GLIB_API.g_error_free(err.getValue()); return true; } }); } - + /** * Disconnect the listener for informational messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(INFO listener) { disconnect(INFO.class, listener); } - + /** * Add a listener for {@link State} changes in the Pipeline. - * - * @param listener The listener to be called when the Pipeline changes state. + * + * @param listener The listener to be called when the Pipeline changes + * state. */ public void connect(final STATE_CHANGED listener) { connect(STATE_CHANGED.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - State[] o = new State[1]; - State[] n = new State[1]; - State[] p = new State[1]; - GSTMESSAGE_API.gst_message_parse_state_changed(msg, o, n, p); - listener.stateChanged(msg.getSource(), o[0], n[0], p[0]); + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + IntByReference oldPtr = new IntByReference(); + IntByReference currentPtr = new IntByReference(); + IntByReference pendingPtr = new IntByReference(); + GSTMESSAGE_API.gst_message_parse_state_changed(msg, oldPtr, currentPtr, pendingPtr); + State old = NativeEnum.fromInt(State.class, oldPtr.getValue()); + State current = NativeEnum.fromInt(State.class, currentPtr.getValue()); + State pending = NativeEnum.fromInt(State.class, pendingPtr.getValue()); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.stateChanged(source, old, current, pending); return true; } }); } + /** * Disconnect the listener for {@link State} change messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(STATE_CHANGED listener) { disconnect(STATE_CHANGED.class, listener); } + /** * Add a listener for new media tags. - * + * * @param listener The listener to be called when new media tags are found. */ public void connect(final TAG listener) { connect(TAG.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { PointerByReference list = new PointerByReference(); GSTMESSAGE_API.gst_message_parse_tag(msg, list); - TagList tl = new TagList(TagList.initializer(list.getValue())); - listener.tagsFound(msg.getSource(), tl); + TagList tl = new TagList(Natives.initializer(list.getValue())); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.tagsFound(source, tl); return true; } }); } - + /** * Disconnect the listener for tag messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(TAG listener) { disconnect(TAG.class, listener); } - + /** * Add a listener for {@link BUFFERING} messages in the Pipeline. - * + * * @param listener The listener to be called when the Pipeline buffers data. */ public void connect(final BUFFERING listener) { connect(BUFFERING.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - int[] percent = { 0 }; + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + IntByReference percent = new IntByReference(); GSTMESSAGE_API.gst_message_parse_buffering(msg, percent); - listener.bufferingData(msg.getSource(), percent[0]); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.bufferingData(source, percent.getValue()); return true; } }); } - + /** * Disconnect the listener for buffering messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(BUFFERING listener) { disconnect(BUFFERING.class, listener); } - + /** * Add a listener for duration changes. - * + * * @param listener The listener to be called when the duration changes. */ - public void connect(final DURATION listener) { - connect(DURATION.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - System.out.println("duration update"); - Format[] format = new Format[1]; - long[] duration = { 0 }; - GSTMESSAGE_API.gst_message_parse_duration(msg, format, duration); - listener.durationChanged(msg.getSource(), format[0], duration[0]); + public void connect(final DURATION_CHANGED listener) { + connect(DURATION_CHANGED.class, listener, new BusCallback() { + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.durationChanged(source); return true; } }); } + /** * Disconnect the listener for duration change messages. - * + * * @param listener The listener that was registered to receive the message. */ - public void disconnect(DURATION listener) { - disconnect(DURATION.class, listener); + public void disconnect(DURATION_CHANGED listener) { + disconnect(DURATION_CHANGED.class, listener); } - + /** * Add a listener for {@link SEGMENT_START} messages in the Pipeline. - * - * @param listener The listener to be called when the Pipeline has started a segment. + * + * @param listener The listener to be called when the Pipeline has started a + * segment. */ public void connect(final SEGMENT_START listener) { connect(SEGMENT_START.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - Format[] format = new Format[1]; - long[] position = { 0 }; - GSTMESSAGE_API.gst_message_parse_segment_start(msg, format, position); - listener.segmentStart(msg.getSource(), format[0], position[0]); + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + IntByReference formatPtr = new IntByReference(); + LongByReference positionPtr = new LongByReference(); + GSTMESSAGE_API.gst_message_parse_segment_start(msg, formatPtr, positionPtr); + Format format = NativeEnum.fromInt(Format.class, formatPtr.getValue()); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.segmentStart(source, format, positionPtr.getValue()); return true; } }); } - + /** * Disconnect the listener for segment-start messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(SEGMENT_START listener) { disconnect(SEGMENT_START.class, listener); } - + /** * Add a listener for {@link SEGMENT_DONE} messages in the Pipeline. - * - * @param listener The listener to be called when the Pipeline has finished a segment. + * + * @param listener The listener to be called when the Pipeline has finished + * a segment. */ public void connect(final SEGMENT_DONE listener) { connect(SEGMENT_DONE.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - Format[] format = new Format[1]; - long[] position = { 0 }; - GSTMESSAGE_API.gst_message_parse_segment_done(msg, format, position); - listener.segmentDone(msg.getSource(), format[0], position[0]); + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + IntByReference formatPtr = new IntByReference(); + LongByReference positionPtr = new LongByReference(); + GSTMESSAGE_API.gst_message_parse_segment_done(msg, formatPtr, positionPtr); + Format format = NativeEnum.fromInt(Format.class, formatPtr.getValue()); + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.segmentDone(source, format, positionPtr.getValue()); return true; } }); } - + /** * Disconnect the listener for segment-done messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(SEGMENT_DONE listener) { disconnect(SEGMENT_DONE.class, listener); } - + /** * Add a listener for {@link ASYNC_DONE} messages in the Pipeline. - * - * @param listener The listener to be called when the an element has finished - * an async state change. + * + * @param listener The listener to be called when the an element has + * finished an async state change. */ public void connect(final ASYNC_DONE listener) { connect(ASYNC_DONE.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - listener.asyncDone(msg.getSource()); + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + GstObject source = Natives.objectFor(msg.getSource(), GstObject.class, true, true); + listener.asyncDone(source); return true; } }); } - + /** * Disconnect the listener for async-done messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(ASYNC_DONE listener) { @@ -622,23 +415,25 @@ public void disconnect(ASYNC_DONE listener) { /** * Add a listener for all messages posted on the Bus. - * - * @param listener The listener to be called when a {@link Message} is posted. + * + * @param listener The listener to be called when a {@link Message} is + * posted. */ public void connect(final MESSAGE listener) { connect(MESSAGE.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - listener.busMessage(bus, msg); + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + listener.busMessage(Bus.this, Natives.objectFor(msg, Message.class, true, true)); return true; } }); } - + /** * Add a listener for messages of type {@code signal} posted on the Bus. - * + * * @param signal the signal to connect to. - * @param listener The listener to be called when a {@link Message} is posted. + * @param listener The listener to be called when a {@link Message} is + * posted. */ public void connect(String signal, final MESSAGE listener) { // @@ -648,25 +443,25 @@ public void connect(String signal, final MESSAGE listener) { signal = signal.substring(signal.lastIndexOf("::") + 2); } connect(signal, MESSAGE.class, listener, new BusCallback() { - public boolean callback(Bus bus, Message msg, Pointer user_data) { - listener.busMessage(bus, msg); + public boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer user_data) { + listener.busMessage(Bus.this, Natives.objectFor(msg, Message.class, true, true)); return true; } }); } - + /** * Disconnect the listener for segment-done messages. - * + * * @param listener The listener that was registered to receive the message. */ public void disconnect(MESSAGE listener) { disconnect(MESSAGE.class, listener); } - + /** * Posts a {@link Message} on this Bus. - * + * * @param message the message to post. * @return true if the message could be posted, false if * the bus is flushing. @@ -674,156 +469,139 @@ public void disconnect(MESSAGE listener) { public boolean post(Message message) { return GSTBUS_API.gst_bus_post(this, message); } - - private BusSyncHandler syncHandler = new BusSyncHandler() { - public BusSyncReply syncMessage(Message msg) { - return BusSyncReply.PASS; - } - }; - public BusSyncHandler getSyncHandler() { - return syncHandler; - } + + /** + * Sets the synchronous handler (message listener) on the bus. The handler + * will be called every time a new message is posted on the bus. Note that + * the handler will be called in the same thread context as the posting + * object. Applications should generally handle messages asynchronously + * using the other message listeners. + *

+ * Only one handler may be attached to the bus at any one time. An attached + * sync handler forces creation of {@link Message} objects for all messages + * on the bus, so the handler should be removed if no longer required. + *

+ * A single native sync handler is used at all times, with synchronous and + * asynchronous dispatch handled on the Java side, so the bindings do not + * inherit issues in clearing or replacing the sync handler with versions of + * GStreamer prior to 1.16.3. + * + * @param handler bus sync handler, or null to remove + */ public void setSyncHandler(BusSyncHandler handler) { syncHandler = handler; } - private static org.freedesktop.gstreamer.lowlevel.GstBusAPI.BusSyncHandler syncCallback = new GstBusAPI.BusSyncHandler() { - - { - Native.setCallbackThreadInitializer(this, - new CallbackThreadInitializer(true, false, "GstBus")); - - } - - public BusSyncReply callback(final Bus bus, final Message msg, Pointer userData) { - if (bus.syncHandler != null) { - BusSyncReply reply = bus.syncHandler.syncMessage(msg); - - if (reply != BusSyncReply.DROP) { - Gst.getExecutor().execute(new Runnable() { - public void run() { - bus.dispatchMessage(msg); - } - }); - } - } - // - // Unref the message, since we are dropping it. - // (the normal GC will drop other refs to it) - // - GSTMINIOBJECT_API.gst_mini_object_unref(msg); - return BusSyncReply.DROP; - } - }; - + + /** + * Clear the synchronous handler. + *

+ * This is a convenience method equivalent to {@code setSyncHandler(null)} + */ + public void clearSyncHandler() { + setSyncHandler(null); + } + + /** + * Get the current synchronous handler. + * + * @return current sync handler, or null + */ + public BusSyncHandler getSyncHandler() { + return syncHandler; + } + /** * Connects to a signal. - * + * * The signal name is deduced from the listenerClass name. - * + * * @param listenerClass the class of the listener. * @param listener the listener to associate with the {@code callback} * @param callback The callback to call when the signal is emitted. */ private void connect(Class listenerClass, T listener, BusCallback callback) { - final String signal = listenerClass.getSimpleName().toLowerCase().replaceAll("_", "-"); - connect(signal, listenerClass, listener, callback); + String className = listenerClass.getSimpleName(); + MessageType type; + if ("MESSAGE".equals(className)) { + type = MessageType.ANY; + } else { + type = MessageType.valueOf(listenerClass.getSimpleName()); + } + addMessageProxy(type, listenerClass, listener, callback); } - + /** * Connects a callback to a signal. *

* This differs to {@link GObject#connect} in that it hooks up Bus signals * to the sync callback, not the generic GObject signal mechanism. - * + * + * @param listener type * @param signal the name of the signal to connect to. * @param listenerClass the class of the {@code listener} * @param listener the listener to associate with the {@code callback} * @param callback the callback to call when the signal is emitted. */ @Override - public synchronized void connect(String signal, Class listenerClass, T listener, + public void connect(String signal, Class listenerClass, T listener, final Callback callback) { if (listenerClass.getEnclosingClass() != Bus.class) { super.connect(signal, listenerClass, listener, callback); - return; - } - MessageType type = MessageType.forName(signal); - if (type == MessageType.UNKNOWN && "message".equals(signal)) { - type = MessageType.ANY; - } - if (type == MessageType.UNKNOWN) { - throw new IllegalArgumentException("Illegal signal: " + signal); - } - final Map, Map> signals = getListenerMap(); - Map m = signals.get(type); - if (m == null) { - m = new HashMap(); - signals.put(listenerClass, m); + } else { + MessageType type; + if ("message".equals(signal)) { + type = MessageType.ANY; + } else { + type = MessageType.valueOf(signal.toUpperCase(Locale.ROOT).replace('-', '_')); + } + addMessageProxy(type, listenerClass, listener, (BusCallback) callback); } - MessageProxy proxy = new MessageProxy(type, (BusCallback) callback); - m.put(listener, proxy); - messageProxies.add(proxy); - + } + + private synchronized void addMessageProxy(MessageType type, + Class listenerClass, + T listener, + BusCallback callback) { + messageProxies.add(new MessageProxy(type, listenerClass, listener, callback)); addWatch(); } - + @Override - public synchronized void disconnect(Class listenerClass, T listener) { + public void disconnect(Class listenerClass, T listener) { if (listenerClass.getEnclosingClass() != Bus.class) { super.disconnect(listenerClass, listener); - return; + } else { + removeMessageProxy(listenerClass, listener); } - final Map, Map> signals = getListenerMap(); - Map m = signals.get(listenerClass); - if (m != null) { - MessageProxy proxy = m.remove(listener); - if (proxy != null) { - messageProxies.remove(proxy); - } - if (m.isEmpty()) { - removeWatch(); - signals.remove(listenerClass); - } + } + + private synchronized void removeMessageProxy(Class listenerClass, T listener) { + messageProxies.removeIf(p -> p.listener == listener); + if (messageProxies.isEmpty()) { + removeWatch(); } } - + /** * Dispatches a message to all interested listeners. - * - *

+ *

* We do this here from a sync callback, because the default gstbus dispatch - * uses the default main context to signal that there are messages waiting on - * the bus. Since that is used by the GTK L&F under swing, we never get those - * notifications, and the messages just queue up. - * @param message - */ - private void dispatchMessage(Message msg) { - // Dispatch to listeners - for (Object listener : messageProxies) { - ((MessageProxy) listener).busMessage(this, msg); - } - } - private static class MessageProxy implements MESSAGE { - private final MessageType type; - private final BusCallback callback; - public MessageProxy(MessageType type, BusCallback callback) { - this.type = type; - this.callback = callback; - } - public void busMessage(final Bus bus, final Message msg) { - if ((type.intValue() & msg.getType().intValue()) != 0) { - callback.callback(bus, msg, null); + * uses the default main context to signal that there are messages waiting + * on the bus. Since that is used by the GTK L&F under swing, we never get + * those notifications, and the messages just queue up. + * + */ + private void dispatchMessage(GstBusPtr busPtr, GstMessagePtr msgPtr) { + messageProxies.forEach(p -> { + try { + p.busMessage(busPtr, msgPtr); + } catch (Throwable t) { + LOG.log(Level.SEVERE, "Exception thrown by bus message handler", t); } - } - } - - private final Map, Map> getListenerMap() { - if (signalListeners == null) { - signalListeners = new ConcurrentHashMap, Map>(); - } - return signalListeners; + }); + GSTMINIOBJECT_API.gst_mini_object_unref(msgPtr); } - + @Override public void dispose() { removeWatch(); @@ -831,40 +609,328 @@ public void dispose() { } /** - * Adds the bus signal watch. - * This will reference the bus until the signal watch is removed - * and so will stop the Bus being GC'd and disposed. + * Adds the bus signal watch. This will reference the bus until the signal + * watch is removed and so will stop the Bus being GC'd and disposed. */ - private void addWatch() - { - synchronized (lock) - { - if (!watchAdded) - { - log.fine("Add watch"); + private void addWatch() { + synchronized (lock) { + if (!watchAdded) { + LOG.fine("Add watch"); GSTBUS_API.gst_bus_add_signal_watch(this); watchAdded = true; } } } - + /** - * Removes the bus signal watch (which will remove the bus reference - * held by the signal watch). + * Removes the bus signal watch (which will remove the bus reference held by + * the signal watch). */ - private void removeWatch() - { - synchronized (lock) - { - if (watchAdded) - { - log.fine("Remove watch"); + private void removeWatch() { + synchronized (lock) { + if (watchAdded) { + LOG.fine("Remove watch"); GSTBUS_API.gst_bus_remove_signal_watch(this); watchAdded = false; } } } - - private Map, Map> signalListeners; - private List messageProxies = new CopyOnWriteArrayList(); + + /** + * Signal emitted when end-of-stream is reached in a pipeline. + * + * The application will only receive this message in the PLAYING state and + * every time it sets a pipeline to PLAYING that is in the EOS state. The + * application can perform a flushing seek in the pipeline, which will undo + * the EOS state again. + * + * @see #connect(EOS) + * @see #disconnect(EOS) + */ + public static interface EOS { + + /** + * Called when a {@link Pipeline} element posts a end-of-stream message. + * + * @param source the element which posted the message. + */ + public void endOfStream(GstObject source); + } + + /** + * Signal emitted when an error occurs. + *

+ * When the application receives an error message it should stop playback of + * the pipeline and not assume that more data will be played. + * + * @see #connect(ERROR) + * @see #disconnect(ERROR) + */ + public static interface ERROR { + + /** + * Called when a {@link Pipeline} element posts an error message. + * + * @param source the element which posted the message. + * @param code a numeric code representing the error. + * @param message a string representation of the error. + */ + public void errorMessage(GstObject source, int code, String message); + } + + /** + * Signal emitted when a warning message is delivered. + * + * @see #connect(WARNING) + * @see #disconnect(WARNING) + */ + public static interface WARNING { + + /** + * Called when a {@link Pipeline} element posts an warning message. + * + * @param source the element which posted the message. + * @param code a numeric code representing the warning. + * @param message a string representation of the warning. + */ + public void warningMessage(GstObject source, int code, String message); + } + + /** + * Signal emitted when an informational message is delivered. + * + * @see #connect(INFO) + * @see #disconnect(INFO) + */ + public static interface INFO { + + /** + * Called when a {@link Pipeline} element posts an informational + * message. + * + * @param source the element which posted the message. + * @param code a numeric code representing the informational message. + * @param message a string representation of the informational message. + */ + public void infoMessage(GstObject source, int code, String message); + } + + /** + * Signal emitted when a new tag is identified on the stream. + * + * @see #connect(TAG) + * @see #disconnect(TAG) + */ + public static interface TAG { + + /** + * Called when a {@link Pipeline} element finds media meta-data. + * + * @param source the element which posted the message. + * @param tagList a list of media meta-data. + */ + public void tagsFound(GstObject source, TagList tagList); + } + + /** + * Signal emitted when a state change happens. + * + * @see #connect(STATE_CHANGED) + * @see #disconnect(STATE_CHANGED) + */ + public static interface STATE_CHANGED { + + /** + * Called when a {@link Pipeline} element executes a {@link State} + * change. + * + * @param source the element which posted the message. + * @param old the old state that the element is changing from. + * @param current the new (current) state the element is changing to. + * @param pending the pending (target) state. + */ + public void stateChanged(GstObject source, State old, State current, State pending); + } + + /** + * Signal emitted when the pipeline is buffering data. + * + * @see #connect(BUFFERING) + * @see #disconnect(BUFFERING) + */ + public static interface BUFFERING { + + /** + * Called when a {@link Pipeline} element needs to buffer data before it + * can continue processing. + *

+ * {@code percent} is a value between 0 and 100. A value of 100 means + * that the buffering completed. + *

+ * When {@code percent} is less than 100 the application should PAUSE a + * PLAYING pipeline. When {@code percent} is 100, the application can + * set the pipeline (back) to PLAYING. + *

+ * The application must be prepared to receive BUFFERING messages in the + * PREROLLING state and may only set the pipeline to PLAYING after + * receiving a message with {@code percent} set to 100, which can happen + * after the pipeline completed prerolling. + * + * @param source the element which posted the message. + * @param percent the percentage of buffering that has completed. + */ + public void bufferingData(GstObject source, int percent); + } + + /** + * Signal sent when a new duration message is posted by an element that know + * the duration of a stream in a specific format. + *

+ * This message is received by bins and is used to calculate the total + * duration of a pipeline. + *

+ * Elements may post a duration message with a duration of + * {@link ClockTime#NONE} to indicate that the duration has changed and the + * cached duration should be discarded. The new duration can then be + * retrieved via a query. The application can get the new duration with a + * duration query. + * + * @see #connect(DURATION) + * @see #disconnect(DURATION) + */ + public static interface DURATION_CHANGED { + + /** + * Called when a new duration message is posted on the Bus. + * + * @param source the element which posted the message. + */ + public void durationChanged(GstObject source); + } + + /** + * This message is posted by elements that start playback of a segment as a + * result of a segment seek. + *

+ * This message is not received by the application but is used for + * maintenance reasons in container elements. + */ + public static interface SEGMENT_START { + + public void segmentStart(GstObject source, Format format, long position); + } + + /** + * Signal emitted when the pipeline has completed playback of a segment. + *

+ * This message is posted by elements that finish playback of a segment as a + * result of a segment seek. This message is received by the application + * after all elements that posted a {@link SEGMENT_START} have posted + * segment-done. + * + * @see #connect(SEGMENT_DONE) + * @see #disconnect(SEGMENT_DONE) + */ + public static interface SEGMENT_DONE { + + /** + * Called when a segment-done message has been posted. + * + * @param source the element which posted the message. + * @param format the format of the position being done. + * @param position the position of the segment being done. + */ + public void segmentDone(GstObject source, Format format, long position); + } + + /** + * Signal emitted by elements when they complete an ASYNC state change. + *

+ * Applications will only receive this message from the top level pipeline. + *

+ * + * @see #connect(ASYNC_DONE) + * @see #disconnect(ASYNC_DONE) + */ + public static interface ASYNC_DONE { + + /** + * Called when a segment-done message has been posted. + * + * @param source the element which posted the message. + */ + public void asyncDone(GstObject source); + } + + /** + * Catch all signals emitted on the Bus. + *

+ * The signal handler will be called asynchronously from the thread that + * posted the message on the Bus. + * + * @see #connect(MESSAGE) + * @see #disconnect(MESSAGE) + */ + public static interface MESSAGE { + + /** + * Called when a {@link Element} posts a {@link Message} on the Bus. + * + * @param bus the Bus the message was posted on. + * @param message the message that was posted. + */ + public void busMessage(Bus bus, Message message); + } + + private static class MessageProxy { + + private final MessageType type; + private final Class listenerClass; + private final Object listener; + private final BusCallback callback; + + MessageProxy(MessageType type, Class listenerClass, T listener, BusCallback callback) { + this.type = type; + this.listenerClass = listenerClass; + this.listener = listener; + this.callback = callback; + } + + void busMessage(final GstBusPtr bus, final GstMessagePtr msg) { + if (type == MessageType.ANY || type.intValue() == msg.getMessageType()) { + callback.callback(bus, msg, null); + } + } + } + + private static class SyncCallback implements GstBusAPI.BusSyncHandler { + + { + Native.setCallbackThreadInitializer(this, + new CallbackThreadInitializer(true, + Boolean.getBoolean("glib.detachCallbackThreads"), + "GstBus")); + } + + @Override + public BusSyncReply callback(final GstBusPtr busPtr, final GstMessagePtr msgPtr, Pointer userData) { + Bus bus = Natives.objectFor(busPtr, Bus.class, true, true); + // volatile - use local reference + BusSyncHandler syncHandler = bus.syncHandler; + if (syncHandler != null) { + Message msg = Natives.objectFor(msgPtr, Message.class, true, true); + BusSyncReply reply = syncHandler.syncMessage(msg); + if (reply != BusSyncReply.DROP) { + Gst.getExecutor().execute(() -> bus.dispatchMessage(busPtr, msgPtr)); + } else { + // not calling dispatch message so unref here + GSTMINIOBJECT_API.gst_mini_object_unref(msgPtr); + } + } else { + Gst.getExecutor().execute(() -> bus.dispatchMessage(busPtr, msgPtr)); + } + return BusSyncReply.DROP; + } + } + } diff --git a/src/org/freedesktop/gstreamer/event/BusSyncHandler.java b/src/org/freedesktop/gstreamer/BusSyncHandler.java similarity index 66% rename from src/org/freedesktop/gstreamer/event/BusSyncHandler.java rename to src/org/freedesktop/gstreamer/BusSyncHandler.java index 996f3c0f..2d682169 100644 --- a/src/org/freedesktop/gstreamer/event/BusSyncHandler.java +++ b/src/org/freedesktop/gstreamer/BusSyncHandler.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,12 +16,18 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ +package org.freedesktop.gstreamer; -package org.freedesktop.gstreamer.event; - -import org.freedesktop.gstreamer.BusSyncReply; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.message.Message; +/** + * A BusSyncHandler will be invoked synchronously, when a new message has been + * injected into a {@link Bus}. This function is mostly used internally. Only + * one sync handler can be attached to a given bus. + * + * @see Bus#setSyncHandler(org.freedesktop.gstreamer.BusSyncHandler) + */ public interface BusSyncHandler { + public BusSyncReply syncMessage(Message message); } diff --git a/src/org/freedesktop/gstreamer/BusSyncReply.java b/src/org/freedesktop/gstreamer/BusSyncReply.java index 95a8cda1..24ba56f1 100644 --- a/src/org/freedesktop/gstreamer/BusSyncReply.java +++ b/src/org/freedesktop/gstreamer/BusSyncReply.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -18,23 +19,22 @@ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; -import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.message.Message; /** * The result values for a GstBusSyncHandler. */ -public enum BusSyncReply implements IntegerEnum { +public enum BusSyncReply implements NativeEnum { + /** Drop the {@link Message} */ DROP(0), /** Pass the {@link Message} to the async queue */ PASS(1), /** Pass {@link Message} to async queue, continue if message is handled */ - ASYNC(2), + ASYNC(2); - /** Unknown reply value */ - @DefaultEnumValue - UNKNOWN(~0); + private final int value; BusSyncReply(int value) { this.value = value; @@ -44,8 +44,8 @@ public enum BusSyncReply implements IntegerEnum { * Gets the integer value of the enum. * @return The integer value for this enum. */ + @Override public int intValue() { return value; } - private final int value; } diff --git a/src/org/freedesktop/gstreamer/Caps.java b/src/org/freedesktop/gstreamer/Caps.java index 8ab39142..381cb49f 100644 --- a/src/org/freedesktop/gstreamer/Caps.java +++ b/src/org/freedesktop/gstreamer/Caps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Neil C Smith + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (C) 2007 Wayne Meissner * Copyright (C) <2003> David A. Schleef @@ -18,142 +18,153 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstCapsAPI.GSTCAPS_API; /** * Structure describing sets of media formats *

- * Caps (capabilities) are lightweight objects describing media types. - * They are composed of an array of {@link Structure}. + * Caps (capabilities) are lightweight objects describing media types. They are + * composed of an array of {@link Structure}. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstCaps.html *

* Caps are exposed on {@link PadTemplate} to describe all possible types a * given pad can handle. They are also stored in the {@link Registry} along with * a description of the {@link Element}. *

- * Caps are exposed on the element pads using the {@link Pad#getCaps} method. - * This method describes the possible types that the pad can handle or produce at runtime. + * Caps are exposed on the element pads using the {@link Pad#getAllowedCaps() } + * method. This method describes the possible types that the pad can handle or + * produce at runtime. *

- * Caps are also attached to buffers to describe the content of the data - * pointed to by the buffer with {@link Buffer#setCaps}. Caps attached to - * a {@link Buffer} allow for format negotiation upstream and downstream. + * Caps are also attached to buffers to describe the content of the data pointed + * to by the buffer with {@link Sample#setCaps}. Caps attached to a + * {@link Buffer} allow for format negotiation upstream and downstream. *

* A Caps can be constructed with the following code fragment: *

- * + * * Caps caps = Caps.fromString("video/x-raw-rgb, bpp=32, depth=24, width=640, height=480"); - * + * *

* A Caps is fixed when it has no properties with ranges or lists. Use - * {@link #isFixed} to test for fixed caps. Only fixed caps can be - * set on a {@link Pad} or {@link Buffer}. + * {@link #isFixed} to test for fixed caps. Only fixed caps can be set on a + * {@link Pad} or {@link Buffer}. *

- * Various methods exist to work with the media types such as subtracting - * or intersecting. + * Various methods exist to work with the media types such as subtracting or + * intersecting. * * @see Structure */ public class Caps extends MiniObject { + public static final String GTYPE_NAME = "GstCaps"; - - /** - * Creates a new Caps that is empty. - * That is, the returned Caps contains no media formats. - * - * @return The new Caps. - */ - public static Caps emptyCaps() { - return new Caps(initializer(GSTCAPS_API.ptr_gst_caps_new_empty())); - } - + /** - * Creates a new Caps that indicates that it is compatible with - * any media format. + * Creates a new Caps that is empty. That is, the returned Caps contains no + * media formats. * - * @return The new Caps. - */ - public static Caps anyCaps() { - return new Caps(initializer(GSTCAPS_API.ptr_gst_caps_new_any())); - } - /** - * Construct a new Caps from a string representation. - * Example: - *

- * - * Caps caps = Caps.fromString("video/x-raw, format=RGB, bpp=32, depth=24, width=640, height=480"); - * - * @param caps The string representation of the caps. - * @return The new Caps. - */ - public static Caps fromString(String caps) { - return new Caps(initializer(GSTCAPS_API.ptr_gst_caps_from_string(caps))); - } - - /** - * Creates a new Caps that is empty. - * That is, the returned Caps contains no media formats. * @see #emptyCaps */ public Caps() { - this(initializer(GSTCAPS_API.ptr_gst_caps_new_empty())); + this(Natives.initializer(GSTCAPS_API.ptr_gst_caps_new_empty())); } - + /** * Construct a new Caps from a string representation. - * + * * @param caps The string representation of the caps. * @see #fromString */ public Caps(String caps) { - this(initializer(GSTCAPS_API.ptr_gst_caps_from_string(caps))); + this(Natives.initializer(GSTCAPS_API.ptr_gst_caps_from_string(caps))); } + /** * Create a caps that is a copy of another caps. - * + * * @param caps The caps to copy. * @see #copy */ public Caps(Caps caps) { - this(initializer(GSTCAPS_API.ptr_gst_caps_copy(caps))); + this(Natives.initializer(GSTCAPS_API.ptr_gst_caps_copy(caps))); } - - protected static Initializer initializer(Pointer ptr) { - return new Initializer(ptr, false, true); - } - - public Caps(Initializer init) { + + Caps(Initializer init) { super(init); } - + /** - * Get the number of structures contained in this caps. + * Append the structures contained in caps to this caps object. The + * structures in caps are not copied -- they are transferred to this caps. + *

+ * If either caps is ANY, the resulting caps will be ANY. * - * @return the number of structures that this caps contains + * @param caps The Caps to append */ - public int size() { - return GSTCAPS_API.gst_caps_get_size(this); + public void append(Caps caps) { + GSTCAPS_API.gst_caps_append(this, caps); } - + + /** + * Append structure to this caps. The structure is not copied; this caps + * takes ownership, so do not use struct after calling this method. + * + * @param struct The structure to append. + */ + public void append(Structure struct) { + GSTCAPS_API.gst_caps_append_structure(this, struct); + } + /** * Create a new Caps as a copy of the this caps. - * - * The new Caps will be a copy of this caps, with all the internal structures - * copied as well. + * + * The new Caps will be a copy of this caps, with all the internal + * structures copied as well. * * @return The new Caps. */ public Caps copy() { return GSTCAPS_API.gst_caps_copy(this); } - + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof Caps)) { + return false; + } + return other == this || isEqual((Caps) other); + } + + /** + * Get a {@link Structure} contained in this caps. + * + * Finds the structure in @caps that has the index @index, and returns it. + * + * @param index The index of the structure to get. + * @return The Structure corresponding to index. + */ + public Structure getStructure(int index) { + /* + * WARNING: This function takes a const GstCaps *, but returns a + * non-const GstStructure *. This is for programming convenience -- + * the caller should be aware that structures inside a constant + * #GstCaps should not be modified. + */ + // The above means we return a Structure proxy which does not own the pointer. + // gst_caps_get_structure is not marked as CallerOwnsReturn, so it should work + return GSTCAPS_API.gst_caps_get_structure(this, index); + } + /** * Creates a new {@link Caps} that contains all the formats that are common * to both this Caps and the other Caps. - * + * * @param other The {@link Caps} to intersect with this one. * * @return The new {@link Caps} @@ -161,239 +172,238 @@ public Caps copy() { public Caps intersect(Caps other) { return GSTCAPS_API.gst_caps_intersect(this, other); } - + /** - * Subtracts the subtrahend Caps from this Caps. - * - * This function does not work reliably if optional properties for caps - * are included on one caps and omitted on the other. - * @param subtrahend The {@link Caps} to subtract. - * @return The resulting caps. + * Check if this caps is always compatible with another caps. + *

+ * A given Caps structure is always compatible with another if every media + * format that is in the first is also contained in the second. That is, + * this caps1 is a subset of other. + * + * @param other The caps to test against. + * @return true if other is always compatible with this caps. */ - public Caps subtract(Caps subtrahend) { - return GSTCAPS_API.gst_caps_subtract(this, subtrahend); + public boolean isAlwaysCompatible(Caps other) { + return GSTCAPS_API.gst_caps_is_always_compatible(this, other); } /** - * Normalize the Caps. - * - * Creates a new {@link Caps} that represents the same set of formats as - * this Caps, but contains no lists. Each list is expanded into separate - * {@link Structure}s - * - * @return The new {@link Caps} - * @see Structure + * Determine if this caps represents any media format. + * + * @return true if this caps represents any format. */ - public Caps normalize() { - this.ref(); // gst_caps_normalize copies "this" and drops one reference - return GSTCAPS_API.gst_caps_normalize(this); + public boolean isAny() { + return GSTCAPS_API.gst_caps_is_any(this); } - + /** - * Modifies this caps inplace into a representation that represents the - * same set of formats, but in a simpler form. Component structures that are - * identical are merged. Component structures that have values that can be - * merged are also merged. + * Determine if this caps represents no media formats. * - * @return The new {@link Caps} + * @return true if this caps represents no formats. */ - public Caps simplify() { - this.ref(); // gst_caps_simplify copies "this" and drops one reference - return GSTCAPS_API.gst_caps_simplify(this); + public boolean isEmpty() { + return GSTCAPS_API.gst_caps_is_empty(this); } - + /** - * Append the structures contained in caps to this caps object. - * The structures in caps are not copied -- they are transferred to this caps. + * Checks if the given caps represent the same set of caps. *

- * If either caps is ANY, the resulting caps will be ANY. - * - * @param caps The Caps to append + * This function does not work reliably if optional properties for caps are + * included on one caps and omitted on the other. + * + * @param other The caps to compare this caps to. + * @return true if other caps equals this one. */ - public void append(Caps caps) { - GSTCAPS_API.gst_caps_append(this, caps); + public boolean isEqual(Caps other) { + return GSTCAPS_API.gst_caps_is_equal(this, other); } - + /** - * Append structure to this caps. - * The structure is not copied; this caps takes ownership, so do not use struct - * after calling this method. - * - * @param struct The structure to append. + * Tests if two Caps are equal. This function only works on fixed Caps. + * + * @param other The other caps to test against. + * @return true if the other caps is equal to this one. */ - public void append(Structure struct) { - GSTCAPS_API.gst_caps_append_structure(this, struct); + public boolean isEqualFixed(Caps other) { + return GSTCAPS_API.gst_caps_is_equal_fixed(this, other); } - + /** - * Remove a struture from the caps. - * Removes the stucture with the given index from the list of structures - * contained in this caps. - * - * @param index Index of the structure to remove. + * Determine if this caps is fixed. + *

+ * Fixed Caps describe exactly one format, that is, they have exactly one + * structure, and each field in the structure describes a fixed type. + * Examples of non-fixed types are GST_TYPE_INT_RANGE and GST_TYPE_LIST. + * + * @return true if this caps is fixed */ - public void removeStructure(int index) { - GSTCAPS_API.gst_caps_remove_structure(this, index); + public boolean isFixed() { + return GSTCAPS_API.gst_caps_is_fixed(this); } - + /** - * Merge two {@link Caps} together. + * Checks if all caps represented by this caps are also represented by + * superset. *

- * Appends the structures contained in caps2 to caps1 if they are not yet - * expressed by caps1 . The structures in caps2 are not copied -- they are - * transferred to a writable copy of caps1 , and then caps2 is freed. If - * either caps is ANY, the resulting caps will be ANY. - *

+ * This function does not work reliably if optional properties for caps are + * included on one caps and omitted on the other. * - * @param caps1 the {@link Caps} that will take the new entries. - * @param caps2 the {@link Caps} to merge in - * @return merged Caps + * @param superset The potentially greater Caps + * @return true if this caps is a subset of superset */ - public static Caps merge(Caps caps1, Caps caps2) { - return GSTCAPS_API.gst_caps_merge(caps1, caps2); + public boolean isSubset(Caps superset) { + return GSTCAPS_API.gst_caps_is_subset(this, superset); } /** * * Returns a writable copy of this caps. *

- * This method will invalidate the native side of this caps object, so it should - * not be used after calling this method, and only the returned Caps object should be used. + * This method will invalidate the native side of this caps object, so it + * should not be used after calling this method, and only the returned Caps + * object should be used. *

- * + * * @return A writable version of this caps object. */ + // @TODO should this take a ref ? public Caps makeWritable() { return GSTCAPS_API.gst_caps_make_writable(this); } - - public void setInteger(String field, Integer value) { - GSTCAPS_API.gst_caps_set_simple(this, field, value, null); - } - + /** - * Get a {@link Structure} contained in this caps. - * - * Finds the structure in @caps that has the index @index, and - * returns it. + * Normalize the Caps. * - * @param index The index of the structure to get. - * @return The Structure corresponding to index. + * Creates a new {@link Caps} that represents the same set of formats as + * this Caps, but contains no lists. Each list is expanded into separate + * {@link Structure}s + * + * @return The new {@link Caps} + * @see Structure */ - public Structure getStructure(int index) { - /* - * WARNING: This function takes a const GstCaps *, but returns a - * non-const GstStructure *. This is for programming convenience -- - * the caller should be aware that structures inside a constant - * #GstCaps should not be modified. - */ - // The above means we return a Structure proxy which does not own the pointer. - // gst_caps_get_structure is not marked as CallerOwnsReturn, so it should work - return GSTCAPS_API.gst_caps_get_structure(this, index); + public Caps normalize() { +// this.ref(); // gst_caps_normalize copies "this" and drops one reference + Natives.ref(this); + return GSTCAPS_API.gst_caps_normalize(this); } - + /** - * Destructively discard all but the first structure from this caps. - * - * Useful when fixating. This caps must be writable. - * - * @return truncated copy of the Caps + * Remove a structure from the caps. Removes the structure with the given + * index from the list of structures contained in this caps. + * + * @param index Index of the structure to remove. */ - public Caps truncate() { - this.ref(); - return GSTCAPS_API.gst_caps_truncate(this); + public void removeStructure(int index) { + GSTCAPS_API.gst_caps_remove_structure(this, index); } - - @Override - public String toString() { - return GSTCAPS_API.gst_caps_to_string(this); + + public void setInteger(String field, Integer value) { + GSTCAPS_API.gst_caps_set_simple(this, field, value, null); } - + /** - * Determine if this caps represents any media format. + * Modifies this caps inplace into a representation that represents the same + * set of formats, but in a simpler form. Component structures that are + * identical are merged. Component structures that have values that can be + * merged are also merged. * - * @return true if this caps represents any format. + * @return The new {@link Caps} */ - public boolean isAny() { - return GSTCAPS_API.gst_caps_is_any(this); + public Caps simplify() { +// this.ref(); // gst_caps_simplify copies "this" and drops one reference + Natives.ref(this); + return GSTCAPS_API.gst_caps_simplify(this); } - + /** - * Determine if this caps represents no media formats. + * Get the number of structures contained in this caps. * - * @return true if this caps represents no formats. + * @return the number of structures that this caps contains */ - public boolean isEmpty() { - return GSTCAPS_API.gst_caps_is_empty(this); + public int size() { + return GSTCAPS_API.gst_caps_get_size(this); } - + /** - * Determine if this caps is fixed. - *

- * Fixed Caps describe exactly one format, that is, they have exactly - * one structure, and each field in the structure describes a fixed type. - * Examples of non-fixed types are GST_TYPE_INT_RANGE and GST_TYPE_LIST. + * Subtracts the subtrahend Caps from this Caps. * - * @return true if this caps is fixed + * This function does not work reliably if optional properties for + * caps are included on one caps and omitted on the other. + * + * @param subtrahend The {@link Caps} to subtract. + * @return The resulting caps. */ - public boolean isFixed() { - return GSTCAPS_API.gst_caps_is_fixed(this); + public Caps subtract(Caps subtrahend) { + return GSTCAPS_API.gst_caps_subtract(this, subtrahend); } - + + @Override + public String toString() { + return GSTCAPS_API.gst_caps_to_string(this); + } + /** - * Checks if the given caps represent the same set of caps. - *

- * This function does not work reliably if optional properties for caps - * are included on one caps and omitted on the other. + * Destructively discard all but the first structure from this caps. * - * @param other The caps to compare this caps to. - * @return true if other caps equals this one. + * Useful when fixating. This caps must be writable. + * + * @return truncated copy of the Caps */ - public boolean isEqual(Caps other) { - return GSTCAPS_API.gst_caps_is_equal(this, other); + public Caps truncate() { +// this.ref(); + Natives.ref(this); + return GSTCAPS_API.gst_caps_truncate(this); } - + /** - * Tests if two Caps are equal. This function only works on fixed Caps. + * Creates a new Caps that indicates that it is compatible with any media + * format. * - * @param other The other caps to test against. - * @return true if the other caps is equal to this one. + * @return The new Caps. */ - public boolean isEqualFixed(Caps other) { - return GSTCAPS_API.gst_caps_is_equal_fixed(this, other); + public static Caps anyCaps() { + return new Caps(Natives.initializer(GSTCAPS_API.ptr_gst_caps_new_any())); } - + /** - * Checks if all caps represented by this caps are also represented by superset. - *

- * This function does not work reliably if optional properties for caps - * are included on one caps and omitted on the other. + * Creates a new Caps that is empty. That is, the returned Caps contains no + * media formats. * - * @param superset The potentially greater Caps - * @return true if this caps is a subset of superset + * @return The new Caps. */ - public boolean isSubset(Caps superset) { - return GSTCAPS_API.gst_caps_is_subset(this, superset); + public static Caps emptyCaps() { + return new Caps(Natives.initializer(GSTCAPS_API.ptr_gst_caps_new_empty())); } - + /** - * Check if this caps is always compatible with another caps. + * Construct a new Caps from a string representation. Example: *

- * A given Caps structure is always compatible with another if every media - * format that is in the first is also contained in the second. That is, - * this caps1 is a subset of other. + * + * Caps caps = Caps.fromString("video/x-raw, format=RGB, bpp=32, depth=24, width=640, height=480"); + * * - * @param other The caps to test against. - * @return true if other is always compatible with this caps. + * @param caps The string representation of the caps. + * @return The new Caps. */ - public boolean isAlwaysCompatible(Caps other) { - return GSTCAPS_API.gst_caps_is_always_compatible(this, other); + public static Caps fromString(String caps) { + return new Caps(Natives.initializer(GSTCAPS_API.ptr_gst_caps_from_string(caps))); } - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof Caps)) { - return false; - } - return other == this || isEqual((Caps) other); + + /** + * Merge two {@link Caps} together. + *

+ * Appends the structures contained in caps2 to caps1 if they are not yet + * expressed by caps1 . The structures in caps2 are not copied -- they are + * transferred to a writable copy of caps1 , and then caps2 is freed. If + * either caps is ANY, the resulting caps will be ANY. + *

+ * + * @param caps1 the {@link Caps} that will take the new entries. + * @param caps2 the {@link Caps} to merge in + * @return merged Caps + */ + public static Caps merge(Caps caps1, Caps caps2) { + return GSTCAPS_API.gst_caps_merge(caps1, caps2); } + } diff --git a/src/org/freedesktop/gstreamer/Clock.java b/src/org/freedesktop/gstreamer/Clock.java index 25fc0e65..b0f51e67 100644 --- a/src/org/freedesktop/gstreamer/Clock.java +++ b/src/org/freedesktop/gstreamer/Clock.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen @@ -92,6 +93,7 @@ public class Clock extends GstObject { public static final String GTYPE_NAME = "GstClock"; + @Deprecated // should be package private public Clock(Initializer init) { super(init); } @@ -107,7 +109,7 @@ public Clock(Initializer init) { * @param resolution the new resolution of the clock. * @return the new resolution of the clock. */ - public ClockTime setResolution(ClockTime resolution) { + public long setResolution(long resolution) { return GSTCLOCK_API.gst_clock_set_resolution(this, resolution); } @@ -117,7 +119,7 @@ public ClockTime setResolution(ClockTime resolution) { * * @return the resolution of the clock in nanoseconds. */ - public ClockTime getResolution() { + public long getResolution() { return GSTCLOCK_API.gst_clock_get_resolution(this); } @@ -131,7 +133,7 @@ public ClockTime getResolution() { * @return the time of the clock. Or {@link ClockTime#NONE} when * given incorrect input. */ - public ClockTime getTime() { + public long getTime() { return GSTCLOCK_API.gst_clock_get_time(this); } /** @@ -142,7 +144,7 @@ public ClockTime getTime() { * * @return the internal time of the clock. Or {@link ClockTime#NONE} when given wrong input. */ - public ClockTime getInternalTime() { + public long getInternalTime() { return GSTCLOCK_API.gst_clock_get_internal_time(this); } @@ -189,10 +191,28 @@ public boolean setMaster(Clock master) { * @param rateNumerator the numerator of the rate of the clock relative to its internal time * @param rateDenominator the denominator of the rate of the clock */ - public void getCalibration(ClockTime internal, ClockTime external, ClockTime rateNumerator, ClockTime rateDenominator) { + @Deprecated + public void getCalibration(long internal, long external, long rateNumerator, long rateDenominator) { GSTCLOCK_API.gst_clock_set_calibration(this, internal, external, rateNumerator, rateDenominator); } + /** + * Gets the internal rate and reference time of clock. See + * {@link #setCalibration} for more information. + * + * @return calibration + */ + public Calibration getCalibration() { + long[] internalPtr = new long[1]; + long[] externalPtr = new long[1]; + long[] rateNumPtr = new long[1]; + long[] rateDenomPtr = new long[1]; + GSTCLOCK_API.gst_clock_get_calibration(this, internalPtr, + externalPtr, rateNumPtr, rateDenomPtr); + return new Calibration(internalPtr[0], externalPtr[0], + rateNumPtr[0], rateDenomPtr[0]); + } + /** * Adjusts the rate and time of this clock. A rate of 1/1 is the normal speed of * the clock. Values bigger than 1/1 make the clock go faster. @@ -222,7 +242,7 @@ public void getCalibration(ClockTime internal, ClockTime external, ClockTime rat * @param rateNumerator the numerator of the rate of the clock relative to its internal time * @param rateDenominator the denominator of the rate of the clock */ - public void setCalibration(ClockTime internal, ClockTime external, ClockTime rateNumerator, ClockTime rateDenominator) { + public void setCalibration(long internal, long external, long rateNumerator, long rateDenominator) { GSTCLOCK_API.gst_clock_set_calibration(this, internal, external, rateNumerator, rateDenominator); } @@ -235,7 +255,7 @@ public void setCalibration(ClockTime internal, ClockTime external, ClockTime rat * @param time The requested time * @return A {@link ClockID} that can be used to request the time notification. */ - public ClockID newSingleShotID(ClockTime time) { + public ClockID newSingleShotID(long time) { return GSTCLOCK_API.gst_clock_new_single_shot_id(this, time); } @@ -250,7 +270,108 @@ public ClockID newSingleShotID(ClockTime time) { * @param interval The requested interval. * @return A {@link ClockID} that can be used to request the time notification. */ - public ClockID newPeriodicID(ClockTime startTime, ClockTime interval) { + public ClockID newPeriodicID(long startTime, long interval) { return GSTCLOCK_API.gst_clock_new_periodic_id(this, startTime, interval); } + + /** + * Data storage for clock calibration. + */ + public static final class Calibration { + + private final long internal; + private final long external; + private final long rateNum; + private final long rateDenom; + + private Calibration(long internal, long external, long rateNum, long rateDenom) { + this.internal = internal; + this.external = external; + this.rateNum = rateNum; + this.rateDenom = rateDenom; + } + + /** + * The internal time. + * + * @return internal time + */ + public long internal() { + return internal; + } + + /** + * The external time. + * + * @return external time + */ + public long external() { + return external; + } + + /** + * The rate numerator. + * + * @return rate numerator + */ + public long rateNum() { + return rateNum; + } + + /** + * The rate denominator. + * + * @return rate denominator + */ + public long rateDenom() { + return rateDenom; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 59 * hash + (int) (this.internal ^ (this.internal >>> 32)); + hash = 59 * hash + (int) (this.external ^ (this.external >>> 32)); + hash = 59 * hash + (int) (this.rateNum ^ (this.rateNum >>> 32)); + hash = 59 * hash + (int) (this.rateDenom ^ (this.rateDenom >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Calibration other = (Calibration) obj; + if (this.internal != other.internal) { + return false; + } + if (this.external != other.external) { + return false; + } + if (this.rateNum != other.rateNum) { + return false; + } + if (this.rateDenom != other.rateDenom) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Calibration{" + "internal=" + internal + ", external=" + external + + ", rateNum=" + rateNum + ", rateDenom=" + rateDenom + '}'; + } + + + } + + } diff --git a/src/org/freedesktop/gstreamer/ClockEntryType.java b/src/org/freedesktop/gstreamer/ClockEntryType.java deleted file mode 100644 index 9616550a..00000000 --- a/src/org/freedesktop/gstreamer/ClockEntryType.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008 Wayne Meissner - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * 2005 Wim Taymans - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -/** - * The type of the clock entry. - */ -public enum ClockEntryType { - - /** A single shot timeout */ - SINGLE, - /** A periodic timeout request */ - PERIODIC -} diff --git a/src/org/freedesktop/gstreamer/ClockID.java b/src/org/freedesktop/gstreamer/ClockID.java index 7144cf2d..ab25ebf8 100644 --- a/src/org/freedesktop/gstreamer/ClockID.java +++ b/src/org/freedesktop/gstreamer/ClockID.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,12 +16,10 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import com.sun.jna.Pointer; - -import org.freedesktop.gstreamer.lowlevel.RefCountedObject; +import org.freedesktop.gstreamer.glib.RefCountedObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; import static org.freedesktop.gstreamer.lowlevel.GstClockAPI.GSTCLOCK_API; @@ -28,53 +27,65 @@ * A datatype to hold the handle to an outstanding sync or async clock callback. */ public class ClockID extends RefCountedObject implements Comparable { - public ClockID(Initializer init) { - super(init); - } - - @Override - protected void disposeNativeHandle(Pointer ptr) { - GSTCLOCK_API.gst_clock_id_unref(ptr); - } - @Override - protected void ref() { - GSTCLOCK_API.gst_clock_id_ref(this); + ClockID(Initializer init) { + super(new Handle(init.ptr, init.ownsHandle), init.needRef); } - @Override - protected void unref() { - GSTCLOCK_API.gst_clock_id_unref(this); - } - /** - * Cancel an outstanding request. This can either - * be an outstanding async notification or a pending sync notification. - * After this call, @id cannot be used anymore to receive sync or - * async notifications, you need to create a new #GstClockID. + * Cancel an outstanding request. This can either be an outstanding async + * notification or a pending sync notification. After this call, @id cannot + * be used anymore to receive sync or async notifications, you need to + * create a new #GstClockID. */ public void unschedule() { GSTCLOCK_API.gst_clock_id_unschedule(this); } - + /** * Gets the time of the clock ID *

* Thread safe. - * + * * @return The time of this clock id. */ - public ClockTime getTime() { + public long getTime() { return GSTCLOCK_API.gst_clock_id_get_time(this); } - + /** - * Compares this ClockID to another. + * Compares this ClockID to another. * * @param other The other ClockID to compare to - * @return negative value if a < b; zero if a = b; positive value if a > b + * @return negative value if + * {@code a < b; zero if a = b; positive value if a > b} */ + @Override public int compareTo(ClockID other) { return GSTCLOCK_API.gst_clock_id_compare_func(this, other); } + + private static final class Handle extends RefCountedObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GSTCLOCK_API.gst_clock_id_unref(ptr); + } + + @Override + protected void ref() { + GSTCLOCK_API.gst_clock_id_ref(getPointer()); + } + + @Override + protected void unref() { + GSTCLOCK_API.gst_clock_id_unref(getPointer()); + } + + } + } diff --git a/src/org/freedesktop/gstreamer/ClockReturn.java b/src/org/freedesktop/gstreamer/ClockReturn.java index 8d75c252..055ad4b7 100644 --- a/src/org/freedesktop/gstreamer/ClockReturn.java +++ b/src/org/freedesktop/gstreamer/ClockReturn.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -27,7 +28,7 @@ * The return value of a clock operation. */ public enum ClockReturn { - /** The operation succeded. */ + /** The operation succeeded. */ OK, /** The operation was scheduled too late. */ EARLY, @@ -41,5 +42,7 @@ public enum ClockReturn { ERROR, /** Operation is not supported */ @DefaultEnumValue - UNSUPPORTED + UNSUPPORTED, + /** The ClockID is done waiting */ + DONE } diff --git a/src/org/freedesktop/gstreamer/ClockTime.java b/src/org/freedesktop/gstreamer/ClockTime.java index 20b2c138..b0b0e711 100644 --- a/src/org/freedesktop/gstreamer/ClockTime.java +++ b/src/org/freedesktop/gstreamer/ClockTime.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,242 +16,132 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import java.util.concurrent.TimeUnit; /** - * A representation of time in the gstreamer framework. + * Utility methods for working with clock time (ns) in GStreamer. */ -public final class ClockTime implements Comparable { - //-------------------------------------------------------------------------- - // Static variables - // - public final static ClockTime NONE = new ClockTime(-1, TimeUnit.NANOSECONDS); - public final static ClockTime ZERO = new ClockTime(0, TimeUnit.NANOSECONDS); +public final class ClockTime { + + public final static long NONE = -1; + public final static long ZERO = 0; + - //-------------------------------------------------------------------------- - // Instance variables - // - private final long value; + private ClockTime() {} /** - * Creates a new instance of Time - * - * @param time the length of time this object represents. - * @param units the units time is expressed in. + * Convert time in microseconds to GStreamer clocktime (nanoseconds) + * + * @param microseconds the microsecond value to represent. + * @return time in nanoseconds */ - private ClockTime(long time, TimeUnit units) { - this.value = units.toNanos(time); + public static long fromMicros(long microseconds) { + return TimeUnit.MICROSECONDS.toNanos(microseconds); } - + /** - * Creates a new ClockTime object for a microsecond value. - * @param microseconds The microsecond value to represent. - * @return The new ClockTime object. + * Convert GStreamer clocktime (nanoseconds) to microseconds. + * + * @param clocktime nanosecond time + * @return time in microseconds */ - public static ClockTime fromMicros(long microseconds) { - return valueOf(microseconds, TimeUnit.MICROSECONDS); + public static long toMicros(long clocktime) { + return TimeUnit.NANOSECONDS.toMicros(clocktime); } /** - * Creates a new ClockTime object for a millisecond value. - * @param milliseconds The millisecond value to represent. - * @return The new ClockTime object. + * Convert time in milliseconds to GStreamer clocktime (nanoseconds) + * + * @param milliseconds the millisecond value to represent. + * @return time in nanoseconds */ - public static ClockTime fromMillis(long milliseconds) { - return valueOf(milliseconds, TimeUnit.MILLISECONDS); + public static long fromMillis(long milliseconds) { + return TimeUnit.MILLISECONDS.toNanos(milliseconds); } - + /** - * Creates a new ClockTime object for a nanosecond value. - * @param nanoseconds The nanosecond value to represent. - * @return The new ClockTime object. + * Convert GStreamer clocktime (nanoseconds) to milliseconds. + * + * @param clocktime nanosecond time + * @return time in milliseconds */ - public static ClockTime fromNanos(long nanoseconds) { - return valueOf(nanoseconds, TimeUnit.NANOSECONDS); + public static long toMillis(long clocktime) { + return TimeUnit.NANOSECONDS.toMillis(clocktime); } - + /** - * Creates a new ClockTime object for a second value. - * @param seconds The second value to represent. - * @return The new ClockTime object. + * Convert time in milliseconds to GStreamer clocktime (nanoseconds) + * + * @param seconds the seconds value to represent. + * @return time in nanoseconds */ - public static ClockTime fromSeconds(long seconds) { - return valueOf(seconds, TimeUnit.SECONDS); + public static long fromSeconds(long seconds) { + return TimeUnit.SECONDS.toNanos(seconds); } - + /** - * Returns a new ClockTime object that represents the time value. - * - * @param time the length of time this object represents, in value. - * @param units the units time is expressed in. - * @return The new ClockTime object. + * Convert GStreamer clocktime (nanoseconds) to seconds. + * + * @param clocktime nanosecond time + * @return time in milliseconds */ - public static ClockTime valueOf(long time, TimeUnit units) { - long nanoseconds = units.toNanos(time); - if (nanoseconds == -1L) { - return NONE; - } else if (nanoseconds == 0L) { - return ZERO; - } - return new ClockTime(time, units); + public static long toSeconds(long clocktime) { + return TimeUnit.NANOSECONDS.toSeconds(clocktime); } - + /** * Get the hours component of the total time. - * + * + * @param clocktime GStreamer time in nanoseconds * @return The hours component of the total time. */ - public long getHours() { - return (toSeconds() / 3600) % 24; + public static long getHoursComponent(long clocktime) { + return (toSeconds(clocktime) / 3600) % 24; } - + /** * Get the minutes component of the total time. - * + * + * @param clocktime GStreamer time in nanoseconds * @return The minutes component of the total time. */ - public long getMinutes() { - return (toSeconds() / 60) % 60; + public static long getMinutesComponent(long clocktime) { + return (toSeconds(clocktime) / 60) % 60; } - + /** * Get the seconds component of the total time. - * + * + * @param clocktime GStreamer time in nanoseconds * @return The seconds component of the total time. */ - public long getSeconds() { - return toSeconds() % 60; - } - - /** - * Get the nanosecond component of the total time. - * - * @return The value component of the total time. - */ - public long getNanoSeconds() { - return value % TimeUnit.SECONDS.toNanos(1); - } - - /** - * Converts this ClockTime to a time value of unit units. - * - * @param unit the {@link TimeUnit} to convertTo this time to. - * @return the total time represented by this ClockTime. - * @see TimeUnit#convert - */ - public long convertTo(TimeUnit unit) { - return unit.convert(value, TimeUnit.NANOSECONDS); - } - - /** - * Gets the total number of microseconds represented by this {@code ClockTime}. - *

This is a convenience wrapper, equivalent to: - *

{@code convertTo(TimeUnit.MICROSECONDS) } - * - * @return The total microseconds represented by this {@code ClockTime}. - */ - public long toMicros() { - return convertTo(TimeUnit.MICROSECONDS); + public static long getSecondsComponent(long clocktime) { + return toSeconds(clocktime) % 60; } - - /** - * Gets the total number of milliseconds represented by this {@code ClockTime}. - *

This is a convenience wrapper, equivalent to: - *

{@code convertTo(TimeUnit.MILLISECONDS) } - * - * @return The total milliseconds represented by this {@code ClockTime}. - */ - public long toMillis() { - return convertTo(TimeUnit.MILLISECONDS); - } - - /** - * Gets the total number of value represented by this {@code ClockTime}. - *

This is a convenience wrapper, equivalent to: - *

{@code convertTo(TimeUnit.NANOSECONDS) } - * - * @return The total value represented by this {@code ClockTime}. - */ - public long toNanos() { - return convertTo(TimeUnit.NANOSECONDS); - } - - /** - * Gets the total number of seconds represented by this {@code ClockTime}. - *

This is a convenience wrapper, equivalent to: - *

{@code convertTo(TimeUnit.SECONDS) } - * - * @return The total seconds represented by this {@code ClockTime}. - */ - public long toSeconds() { - return convertTo(TimeUnit.SECONDS); - } - + /** * Determines if this ClockTime represents a valid time value. - * + * + * @param clocktime GStreamer time in nanoseconds * @return true if valid, else false */ - public boolean isValid() { - return value != NONE.value; + public static boolean isValid(long clocktime) { + return clocktime != NONE; } - + /** * Returns a {@code String} representation of this {@code ClockTime}. - * + * + * @param clocktime GStreamer time in nanoseconds * @return a string representation of this {@code ClockTime} */ - @Override - public String toString() { - return String.format("%02d:%02d:%02d", getHours(), getMinutes(), getSeconds()); - } - - /** - * Compares this {@code ClockTime} to the specified object. - *

The result is {@code true} if and only if the argument is not - * {@code null} and is a {@code ClockTime} object equivalent to this - * {@code ClockTime} - * - * @param obj - * @return true if the specified object is equivalent to this - * {@code ClockTime} - */ - @Override - public boolean equals(Object obj) { - return obj instanceof ClockTime && ((ClockTime) obj).value == value; - } - - /** - * Returns a hash code for this {@code ClockTime}. - * - * @return a hash code value for this ClockTime. - * @see java.lang.Long#hashCode - */ - @Override - public int hashCode() { - return (int)(value ^ (value >>> 32)); + public static String toString(long clocktime) { + return String.format("%02d:%02d:%02d", + getHoursComponent(clocktime), + getMinutesComponent(clocktime), + getSecondsComponent(clocktime)); } - - /** - * Compares this ClockTime to another. - * - * @param time the other ClockTime to compare to. - * @return {@code 0} if this {@code ClockTime} is equal to time. - * A value less than zero if this {@code ClockTime} is numerically less than - * time. - * A value greater than zero if this {@code ClockTime} is numerically - * greater than time. - */ - public int compareTo(ClockTime time) { - if (value < time.value) { - return -1; - } else if (value > time.value) { - return 1; - } - return 0; - } } diff --git a/src/org/freedesktop/gstreamer/Context.java b/src/org/freedesktop/gstreamer/Context.java new file mode 100644 index 00000000..f77e713e --- /dev/null +++ b/src/org/freedesktop/gstreamer/Context.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2025 Neil C Smith + * Copyright (c) 2019 Christophe Lafolet + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import org.freedesktop.gstreamer.lowlevel.GstContextAPI; +import org.freedesktop.gstreamer.lowlevel.GstContextPtr; + +/** + * Lightweight objects to represent element contexts. + *

+ * See upstream documentation at https://gstreamer.freedesktop.org/documentation/gstreamer/gstcontext.html + *

+ * Context is a container object used to store contexts like a device context, a + * display server connection and similar concepts that should be shared between + * multiple elements. + *

+ * Applications can set a context on a complete pipeline by using + * {@link Element#setContext(Context)}, which will then be propagated to all + * child elements. Elements can handle these in + * {@link Element#setContext(Context)} and merge them with the context + * information they already have. + *

+ * When an element needs a context it will do the following actions in this + * order until one step succeeds: + *

    + *
  1. Check if the element already has a context
  2. + *
  3. Query downstream with GST_QUERY_CONTEXT for the context
  4. + *
  5. Query upstream* with GST_QUERY_CONTEXT for the context
  6. + *
  7. Post a GST_MESSAGE_NEED_CONTEXT message on the bus with the required + * context types and afterwards check if a usable context was set now
  8. + *
  9. Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT message on + * the bus.
  10. + *
+ *

+ * Bins will catch GST_MESSAGE_NEED_CONTEXT messages and will set any previously + * known context on the element that asks for it if possible. Otherwise the + * application should provide one if it can. + *

+ * Contexts can be persistent. A persistent context is kept in elements when + * they reach {@link State#NULL}, non-persistent ones will be removed. Also, a + * non-persistent context won't override a previous persistent context set to an + * element. + */ +public class Context extends MiniObject { + + public static final String GTYPE_NAME = "GstContext"; + + private final Handle handle; + + public Context(String contextType) { + this(contextType, true); + } + + /** + * Create a new context. + */ + public Context(String context_type, boolean persistent) { + this(new Handle(GstContextAPI.GSTCONTEXT_API.gst_context_new(context_type, persistent), true), false); + } + + Context(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; + } + + Context(Initializer init) { + this(new Handle(init.ptr.as(GstContextPtr.class, GstContextPtr::new), init.ownsHandle), init.needRef); + } + + /** + * Access the structure of the context. + * + * @return The structure of this context. The structure is still owned by this + * context, which means that you should not modify it and not dispose of + * it. + */ + public Structure getStructure() { + return GstContextAPI.GSTCONTEXT_API.gst_context_get_structure(handle.getPointer()); + } + + /** + * Get a writable version of the structure. + * + * @return The structure of this context. The structure is still owned by the + * context, which means that you should not dispose of it. + */ + public Structure getWritableStructure() { + return GstContextAPI.GSTCONTEXT_API.gst_context_writable_structure(handle.getPointer()); + } + + /** + * Get the type of this context. + * + * @return The type of this context. + */ + public String getContextType() { + return GstContextAPI.GSTCONTEXT_API.gst_context_get_context_type(handle.getPointer()); + } + + protected static class Handle extends MiniObject.Handle { + + public Handle(GstContextPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected GstContextPtr getPointer() { + return (GstContextPtr) super.getPointer(); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/ControlBinding.java b/src/org/freedesktop/gstreamer/ControlBinding.java new file mode 100644 index 00000000..a1ff1e68 --- /dev/null +++ b/src/org/freedesktop/gstreamer/ControlBinding.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import org.freedesktop.gstreamer.lowlevel.GValueAPI; +import org.freedesktop.gstreamer.lowlevel.GstControlBindingPtr; + +import static org.freedesktop.gstreamer.lowlevel.GstControlBindingAPI.GSTCONTROLBINDING_API; + +/** + * Attachment for control source sources. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstControlBinding.html + *

+ */ +public class ControlBinding extends GstObject { + + public static final String GTYPE_NAME = "GstControlBinding"; + + private final Handle handle; + + protected ControlBinding(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; + } + + ControlBinding(Initializer init) { + this(new Handle( + init.ptr.as(GstControlBindingPtr.class, GstControlBindingPtr::new), + init.ownsHandle), + init.needRef); + } + + /** + * Gets the value for the given controlled property at the requested time. + * + * @param timestamp the time the control-change should be read from + * @return the value of the property at the given time, or NULL if the + * property isn't controlled + */ + public Object getValue(long timestamp) { + GValueAPI.GValue gValue = GSTCONTROLBINDING_API.gst_control_binding_get_value( + handle.getPointer(), timestamp); + return gValue == null ? null : gValue.getValue(); + } + + /** + * Gets a number of values for the given controlled property starting at + * the requested time. + *

+ * This function is useful if one wants to e.g. draw a graph of the control + * curve or apply a control curve sample by sample. + * + * @param timestamp the time that should be processed + * @param interval the time spacing between subsequent values + * @param values array to fill with control values + * @return false if the given array could not be filled + */ + public boolean getValueArray(long timestamp, long interval, Object[] values) { + GValueAPI.GValue[] gValues = new GValueAPI.GValue[values.length]; + boolean ok = GSTCONTROLBINDING_API.gst_control_binding_get_g_value_array( + handle.getPointer(), + timestamp, + interval, + gValues.length, + gValues); + if (ok) { + for (int i = 0; i < values.length; i++) { + values[i] = gValues[i].getValue(); + } + } + return ok; + } + + /** + * This function is used to disable a control binding for some time, i.e. + * GstObject.syncValues() will do nothing. + * + * @param disabled whether to disable the controller or not + */ + public void setDisabled(boolean disabled) { + GSTCONTROLBINDING_API.gst_control_binding_set_disabled(handle.getPointer(), disabled); + } + + /** + * Check if the control binding is disabled. + * + * @return TRUE if the binding is inactive + */ + public boolean isDisabled() { + return GSTCONTROLBINDING_API.gst_control_binding_is_disabled(handle.getPointer()); + + } + + + protected static class Handle extends GstObject.Handle { + + public Handle(GstControlBindingPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected GstControlBindingPtr getPointer() { + return (GstControlBindingPtr) super.getPointer(); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/ControlSource.java b/src/org/freedesktop/gstreamer/ControlSource.java new file mode 100644 index 00000000..0375543b --- /dev/null +++ b/src/org/freedesktop/gstreamer/ControlSource.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import org.freedesktop.gstreamer.lowlevel.GstControlSourcePtr; + +import static org.freedesktop.gstreamer.lowlevel.GstControlSourceAPI.GSTCONTROLSOURCE_API; + +/** + * Base class for control source sources. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstControlSource.html + *

+ */ +public class ControlSource extends GstObject { + + public static final String GTYPE_NAME = "GstControlSource"; + + private final Handle handle; + + protected ControlSource(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; + } + + ControlSource(Initializer init) { + this(new Handle( + init.ptr.as(GstControlSourcePtr.class, GstControlSourcePtr::new), + init.ownsHandle), + init.needRef); + } + + /** + * Gets the value for this ControlSource at a given timestamp. + * + * @param timestamp the time for which the value should be returned + * @return value + * @throws IllegalStateException if the value could not be calculated + */ + public double getValue(long timestamp) { + double[] out = new double[1]; + boolean ok = GSTCONTROLSOURCE_API.gst_control_source_get_value(handle.getPointer(), timestamp, out); + if (ok) { + return out[0]; + } else { + throw new IllegalStateException(); + } + } + + /** + * Gets an array of values for for this ControlSource. Values that are + * undefined contain NANs. + * + * @param timestamp the first timestamp + * @param interval the time steps + * @param values array to put control-values in + * @return true if the values were successfully calculated + */ + public boolean getValueArray(long timestamp, long interval, double[] values) { + return GSTCONTROLSOURCE_API.gst_control_source_get_value_array( + handle.getPointer(), + timestamp, + interval, + values.length, + values); + } + + /** + * A simple structure for saving a timestamp and a value. + *

+ * Equivalent to GstTimedValue. + *

+ * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstControlSource.html#GstTimedValue + * + */ + public static final class TimedValue { + + public final long timestamp; + public final double value; + + /** + * Create a TimedValue wrapping the timestamp (see {@link ClockTime}) + * and corresponding value. + * + * @param timestamp the timestamp (GstClockTime) of the value change + * @param value the corresponding value + */ + public TimedValue(long timestamp, double value) { + this.timestamp = timestamp; + this.value = value; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + (int) (this.timestamp ^ (this.timestamp >>> 32)); + hash = 37 * hash + (int) (Double.doubleToLongBits(this.value) ^ (Double.doubleToLongBits(this.value) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TimedValue other = (TimedValue) obj; + if (this.timestamp != other.timestamp) { + return false; + } + if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "TimedValue{" + "timestamp=" + timestamp + ", value=" + value + '}'; + } + + } + + protected static class Handle extends GstObject.Handle { + + public Handle(GstControlSourcePtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected GstControlSourcePtr getPointer() { + return (GstControlSourcePtr) super.getPointer(); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/DateTime.java b/src/org/freedesktop/gstreamer/DateTime.java index 97d1c6ec..7d9b7cb3 100644 --- a/src/org/freedesktop/gstreamer/DateTime.java +++ b/src/org/freedesktop/gstreamer/DateTime.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2010 Levente Farkas * @@ -14,67 +15,63 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import static org.freedesktop.gstreamer.lowlevel.GstDateTimeAPI.GSTDATETIME_API; -import org.freedesktop.gstreamer.lowlevel.GType; -import org.freedesktop.gstreamer.lowlevel.NativeObject; +import org.freedesktop.gstreamer.glib.NativeObject; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.lowlevel.GPointer; /** */ public class DateTime extends NativeObject { - - public static final String GTYPE_NAME = "GstDateTime"; - public static final GType GTYPE = GSTDATETIME_API.gst_date_time_get_type(); + public static final String GTYPE_NAME = "GstDateTime"; public static DateTime createInstanceLocalEpoch(long secs) { - return new DateTime(GSTDATETIME_API.gst_date_time_new_from_unix_epoch_local_time(secs), false, true); + return new DateTime(GSTDATETIME_API.gst_date_time_new_from_unix_epoch_local_time(secs), false, true); } - public DateTime(Initializer init) { - super(init); - } - - public DateTime(Pointer ptr, boolean needRef, boolean ownsHandle) { - this(initializer(ptr, needRef, ownsHandle)); + DateTime(Initializer init) { + this(new Handle(init.ptr, init.ownsHandle)); } - @Override - protected void disposeNativeHandle(Pointer ptr) { - GSTDATETIME_API.gst_date_time_unref(ptr); + DateTime(Pointer ptr, boolean needRef, boolean ownsHandle) { + this(new Handle(new GPointer(ptr), ownsHandle)); } + DateTime(Handle handle) { + super(handle); + } + public int getYear() { - return GSTDATETIME_API.gst_date_time_get_year(handle()); + return GSTDATETIME_API.gst_date_time_get_year(getRawPointer()); } - + public int getMonth() { - return GSTDATETIME_API.gst_date_time_get_month(handle()); + return GSTDATETIME_API.gst_date_time_get_month(getRawPointer()); } - + public int getDay() { - return GSTDATETIME_API.gst_date_time_get_day(handle()); + return GSTDATETIME_API.gst_date_time_get_day(getRawPointer()); } public int getHour() { - return GSTDATETIME_API.gst_date_time_get_hour(handle()); + return GSTDATETIME_API.gst_date_time_get_hour(getRawPointer()); } - + public int getMinute() { - return GSTDATETIME_API.gst_date_time_get_minute(handle()); + return GSTDATETIME_API.gst_date_time_get_minute(getRawPointer()); } public int getSecond() { - return GSTDATETIME_API.gst_date_time_get_second(handle()); + return GSTDATETIME_API.gst_date_time_get_second(getRawPointer()); } public int getMicrosecond() { - return GSTDATETIME_API.gst_date_time_get_microsecond(handle()); + return GSTDATETIME_API.gst_date_time_get_microsecond(getRawPointer()); } @Override @@ -82,4 +79,17 @@ public String toString() { return "" + getYear() + "-" + getMonth() + "-" + getDay() + " " + getHour() + ":" + getMinute() + ":" + getSecond() + "." + getMicrosecond(); } + private static final class Handle extends NativeObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GSTDATETIME_API.gst_date_time_unref(ptr.getPointer()); + } + + } + } diff --git a/src/org/freedesktop/gstreamer/Element.java b/src/org/freedesktop/gstreamer/Element.java index 1ba847ef..56d86194 100644 --- a/src/org/freedesktop/gstreamer/Element.java +++ b/src/org/freedesktop/gstreamer/Element.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018 Neil C Smith + * Copyright (c) 2025 Neil C Smith + * Copyright (c) 2019 Christophe Lafolet * Copyright (c) 2009 Levente Farkas * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen @@ -19,13 +20,23 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; - +import org.freedesktop.gstreamer.event.Event; +import org.freedesktop.gstreamer.event.SeekEvent; +import org.freedesktop.gstreamer.event.SeekFlags; +import org.freedesktop.gstreamer.event.SeekType; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; +import org.freedesktop.gstreamer.lowlevel.GstContextPtr; +import org.freedesktop.gstreamer.lowlevel.GstIteratorPtr; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.query.Query; import static org.freedesktop.gstreamer.lowlevel.GstElementAPI.GSTELEMENT_API; import static org.freedesktop.gstreamer.lowlevel.GObjectAPI.GOBJECT_API; @@ -33,157 +44,186 @@ /** * Abstract base class for all pipeline elements. *

- * Element is the abstract base class needed to construct an element that - * can be used in a GStreamer pipeline. Please refer to the plugin writers - * guide for more information on creating Element subclasses. + * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstelement.html + *

+ * Element is the abstract base class needed to construct an element that can be + * used in a GStreamer pipeline. Please refer to the plugin writers guide for + * more information on creating Element subclasses. *

* The name of a Element can be retrieved with {@link #getName} and set with * {@link #setName}. *

- * All elements have pads (of the type {@link Pad}). These pads link to pads on - * other elements. {@link Buffer}s flow between these linked pads. - * An Element has a list of {@link Pad} structures for all their input (or sink) - * and output (or source) pads. - * Core and plug-in writers can add and remove pads with {@link #addPad} - * and {@link #removePad}. + * All elements have pads (of the type {@link Pad}). These pads link to pads on + * other elements. {@link Buffer}s flow between these linked pads. An Element + * has a list of {@link Pad} structures for all their input (or sink) and output + * (or source) pads. Core and plug-in writers can add and remove pads with + * {@link #addPad} and {@link #removePad}. *

- * A pad of an element can be retrieved by name with {@link #getPad}. - * An list of all pads can be retrieved with {@link #getPads}. + * Elements can be linked through their pads. If the link is straightforward, + * use the {@link #link} convenience function to link two elements, or + * {@link #linkMany} for more elements in a row. *

- * Elements can be linked through their pads. - * If the link is straightforward, use the {@link #link} - * convenience function to link two elements, or {@link #linkMany} - * for more elements in a row. + * For finer control, use {@link #linkPads} and {@link #linkPadsFiltered} to + * specify the pads to link on each element by name. *

- * For finer control, use {@link #linkPads} and {@link #linkPadsFiltered} - * to specify the pads to link on each element by name. - *

- * Each element has a state (see {@link State}). You can get and set the state + * Each element has a state (see {@link State}). You can get and set the state * of an element with {@link #getState} and {@link #setState}. * */ public class Element extends GstObject { + public static final String GTYPE_NAME = "GstElement"; - /** - * Creates a new instance of Element. This constructor is used internally. - * + /** + * Creates a new instance of Element. This constructor is used internally. + * * @param init internal initialization data. */ - public Element(Initializer init) { + protected Element(Initializer init) { super(init); } - + + Element(Handle handle, boolean needRef) { + super(handle, needRef); + } + /** - * Creates an instance of the required element type, but does not wrap it in + * Creates an instance of the required element type, but does not wrap it in * a proxy. - * + * * @param factoryName The name of the factory to use to produce the Element * @param elementName The name to assign to the created Element * @return a raw element. */ protected static Initializer makeRawElement(String factoryName, String elementName) { - return initializer(ElementFactory.makeRawElement(factoryName, elementName)); + return Natives.initializer(ElementFactory.makeRawElement(factoryName, elementName)); } - + /** - * Links this element to another element. - * The link must be from source to destination; the other direction will not - * be tried. + * Links this element to another element. The link must be from source to + * destination; the other direction will not be tried. + *

+ * The function looks for existing pads that aren't linked yet. It will + * request new pads if necessary. Such pads need to be released manually + * when unlinking. If multiple links are possible, only one is established. *

- * The function looks for existing pads that aren't linked yet. - * It will request new pads if necessary. Such pads need to be released manualy when unlinking. - * If multiple links are possible, only one is established. - *

* Make sure you have added your elements to a bin or pipeline with * {@link Bin#add} or {@link Bin#addMany} before trying to link them. + *

+ * See {@link #linkPadsFiltered} if you need more control about which pads + * are selected for linking. * - * @param dest The {@link Element} containing the destination pad. + * @param dest The element containing the destination pad. * @return true if the elements could be linked, false otherwise. */ public boolean link(Element dest) { return GSTELEMENT_API.gst_element_link(this, dest); } - + /** - * Chain together a series of elements, with this element as the first in the list. + * Links this element to another element using the given caps as filtercaps. + * The link must be from source to destination; the other direction will not + * be tried. + *

+ * The function looks for existing pads that aren't linked yet. It will + * request new pads if necessary. Such pads need to be released manually + * when unlinking. If multiple links are possible, only one is established. *

* Make sure you have added your elements to a bin or pipeline with * {@link Bin#add} or {@link Bin#addMany} before trying to link them. + *

+ * See {@link #linkPadsFiltered} if you need more control about which pads + * are selected for linking. * - * @param elems The list of elements to be linked. + * @param dest The element containing the destination pad. + * @param filter The {@link Caps} to filter the link, or Null for no filter. * @return true if the elements could be linked, false otherwise. */ - public boolean link(Element... elems) { - // Its much more efficient to copy the array and let the native code do the linking - Element[] list = new Element[elems.length + 1]; - list[0] = this; - System.arraycopy(elems, 0, list, 1, elems.length); - return linkMany(list); + public boolean linkFiltered(Element dest, Caps filter) { + return GSTELEMENT_API.gst_element_link_filtered(this, dest, filter); } /** - * Unlinks all source pads of this source element with all sink pads - * of the sink element to which they are linked. - *

+ * Unlinks all source pads of this source element with all sink pads of the + * sink element to which they are linked. + *

* If the link has been made using {@link #link}, it could have created an - * requestpad, which has to be released using gst_element_release_request_pad(). - * + * requestpad, which has to be released using + * gst_element_release_request_pad(). + * * @param dest The sink Element to unlink. */ public void unlink(Element dest) { GSTELEMENT_API.gst_element_unlink(this, dest); } - + /** * Tests if the Element is currently playing. - * + * * @return true if the Element is currently playing */ public boolean isPlaying() { return getState() == State.PLAYING; } - + /** - * Tells the Element to start playing the media stream. + * Tells the Element to start playing the media stream. Equivalent to + * calling {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#PLAYING}. + * + * @return the status of the element's state change. */ public StateChangeReturn play() { return setState(State.PLAYING); } - + /** - * Tells the Element to set ready the media stream. + * Tells the Element to set ready the media stream. Equivalent to calling + * {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#READY}. + * + * @return the status of the element's state change. */ public StateChangeReturn ready() { return setState(State.READY); } - + /** - * Tells the Element to pause playing the media stream. + * Tells the Element to pause playing the media stream. Equivalent to + * calling {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#PAUSED}. + * + * @return the status of the element's state change. */ public StateChangeReturn pause() { - return setState(State.PAUSED); + return setState(State.PAUSED); } - + /** - * Tells the Element to pause playing the media stream. + * Tells the Element to stop playing the media stream. Equivalent to calling + * {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#NULL}. + * + * @return the status of the element's state change. */ public StateChangeReturn stop() { - return setState(State.NULL); + return setState(State.NULL); } /** - * Sets the state of the element. + * Sets the state of the element. *

- * This method will try to set the requested state by going through all the + * This method will try to set the requested state by going through all the * intermediary states. *

- * This function can return {@link StateChangeReturn#ASYNC}, in which case the - * element will perform the remainder of the state change asynchronously in - * another thread. + * This function can return {@link StateChangeReturn#ASYNC}, in which case + * the element will perform the remainder of the state change asynchronously + * in another thread. *

- * An application can use {@link #getState} to wait for the completion - * of the state change or it can wait for a state change message on the bus. + * An application can use {@link #getState} to wait for the completion of + * the state change or it can wait for a state change message on the bus. * * @param state the element's new {@link State}. * @return the status of the element's state change. @@ -191,36 +231,37 @@ public StateChangeReturn stop() { public StateChangeReturn setState(State state) { return GSTELEMENT_API.gst_element_set_state(this, state); } - + /** - * Locks the state of an element, so state changes of the parent don't affect this element anymore. + * Locks the state of an element, so state changes of the parent don't + * affect this element anymore. * * @param locked_state true to lock the element's {@link State}. - * @return true if the state was changed, false if bad parameters were - * given or the elements state-locking needed no change. + * @return true if the state was changed, false if bad parameters were given + * or the elements state-locking needed no change. */ public boolean setLockedState(boolean locked_state) { return GSTELEMENT_API.gst_element_set_locked_state(this, locked_state); } - + /** * Gets the state of the element. *

* This method will wait until any async state change has completed. - * + * * @return The {@link State} the Element is currently in. */ public State getState() { return getState(-1); } - + /** * Gets the state of the element. *

* For elements that performed an ASYNC state change, as reported by - * {@link #setState}, this function will block up to the - * specified timeout value for the state change to complete. - * + * {@link #setState(org.freedesktop.gstreamer.State)}, this function will + * block up to the specified timeout value for the state change to complete. + * * @param timeout the amount of time to wait. * @param units the units of the timeout. * @return The {@link State} the Element is currently in. @@ -231,14 +272,14 @@ public State getState(long timeout, TimeUnit units) { GSTELEMENT_API.gst_element_get_state(this, state, null, units.toNanos(timeout)); return state[0]; } - + /** * Gets the state of the element. *

* For elements that performed an ASYNC state change, as reported by - * {@link #setState}, this function will block up to the - * specified timeout value for the state change to complete. - * + * {@link #setState(org.freedesktop.gstreamer.State)}, this function will + * block up to the specified timeout value for the state change to complete. + * * @param timeout The amount of time in nanoseconds to wait. * @return The {@link State} the Element is currently in. * @@ -248,18 +289,17 @@ public State getState(long timeout) { GSTELEMENT_API.gst_element_get_state(this, state, null, timeout); return state[0]; } - + /** * Gets the state of the element. *

* For elements that performed an ASYNC state change, as reported by - * {@link #setState}, this function will block up to the - * specified timeout value for the state change to complete. - * + * {@link #setState(org.freedesktop.gstreamer.State)}, this function will + * block up to the specified timeout value for the state change to complete. + * * @param timeout The amount of time in nanoseconds to wait. - * @param states an array to store the states in. Must be of sufficient size + * @param states an array to store the states in. Must be of sufficient size * to hold two elements. - * */ public void getState(long timeout, State[] states) { State[] state = new State[1]; @@ -268,149 +308,157 @@ public void getState(long timeout, State[] states) { states[0] = state[0]; states[1] = pending[0]; } - + /** - * Tries to change the state of the element to the same as its parent. - * If this function returns false, the state of element is undefined. - * @return true, if the element's state could be synced to the parent's state. MT safe. + * Tries to change the state of the element to the same as its parent. If + * this function returns false, the state of element is undefined. + * + * @return true, if the element's state could be synced to the parent's + * state. MT safe. */ public boolean syncStateWithParent() { - return GSTELEMENT_API.gst_element_sync_state_with_parent(this); + return GSTELEMENT_API.gst_element_sync_state_with_parent(this); } - + /** * Sets the {@link Caps} on this Element. - * + * * @param caps the new Caps to set. */ public void setCaps(Caps caps) { GOBJECT_API.g_object_set(this, "caps", caps); } - + /** * Retrieves a pad from the element by name. This version only retrieves * already-existing (i.e. 'static') pads. - * + * * @param padname The name of the {@link Pad} to get. * @return The requested {@link Pad} if found, otherwise null. */ public Pad getStaticPad(String padname) { return GSTELEMENT_API.gst_element_get_static_pad(this, padname); } - + /** - * Retrieves a list of the element's pads. + * Retrieves a list of the element's pads. * * @return the List of {@link Pad}s. */ public List getPads() { - return new GstIterator(GSTELEMENT_API.gst_element_iterate_pads(this), Pad.class).asList(); + return padList(GSTELEMENT_API.gst_element_iterate_pads(this)); } - + /** - * Retrieves a list of the element's source pads. + * Retrieves a list of the element's source pads. * * @return the List of {@link Pad}s. */ public List getSrcPads() { - return new GstIterator(GSTELEMENT_API.gst_element_iterate_src_pads(this), Pad.class).asList(); + return padList(GSTELEMENT_API.gst_element_iterate_src_pads(this)); } - + /** - * Retrieves a list of the element's sink pads. + * Retrieves a list of the element's sink pads. * * @return the List of {@link Pad}s. */ public List getSinkPads() { - return new GstIterator(GSTELEMENT_API.gst_element_iterate_sink_pads(this), Pad.class).asList(); + return padList(GSTELEMENT_API.gst_element_iterate_sink_pads(this)); } + + private List padList(GstIteratorPtr iter) { + return GstIterator.asList(iter, Pad.class); + } + /** - * Adds a {@link Pad} (link point) to the Element. - * The Pad's parent will be set to this element. - *

- * Pads are not automatically activated so elements should perform the needed - * steps to activate the pad in case this pad is added in the PAUSED or PLAYING - * state. See {@link Pad#setActive} for more information about activating pads. - *

+ * Adds a {@link Pad} (link point) to the Element. The Pad's parent will be + * set to this element. + *

+ * Pads are not automatically activated so elements should perform the + * needed steps to activate the pad in case this pad is added in the PAUSED + * or PLAYING state. See {@link Pad#setActive} for more information about + * activating pads. + *

* This function will emit the {@link PAD_ADDED} signal on the element. * * @param pad The {@link Pad} to add. - * @return true if the pad could be added. This function can fail when - * a pad with the same name already existed or the pad already had another - * parent. + * @return true if the pad could be added. This function can fail when a pad + * with the same name already existed or the pad already had another parent. */ public boolean addPad(Pad pad) { return GSTELEMENT_API.gst_element_add_pad(this, pad); } - + /** * Retrieves a pad from the element by name. This version only retrieves * request pads. The pad must be released with {@link #releaseRequestPad}. - * + * * @param name the name of the request {@link Pad} to retrieve. - * @return the requested Pad if found, otherwise null. - * Release using {@link #releaseRequestPad} after usage. + * @return the requested Pad if found, otherwise null. Release + * using {@link #releaseRequestPad} after usage. */ public Pad getRequestPad(String name) { return GSTELEMENT_API.gst_element_get_request_pad(this, name); } - + /** * Frees the previously requested pad obtained via {@link #getRequestPad}. - * + * * @param pad the pad to release. */ public void releaseRequestPad(Pad pad) { GSTELEMENT_API.gst_element_release_request_pad(this, pad); } - + /** * Remove a {@link Pad} from the element. *

- * This method is used by plugin developers and should not be used - * by applications. Pads that were dynamically requested from elements - * with {@link #getRequestPad} should be released with the + * This method is used by plugin developers and should not be used by + * applications. Pads that were dynamically requested from elements with + * {@link #getRequestPad} should be released with the * {@link #releaseRequestPad} function instead. - *

- * Pads are not automatically deactivated so elements should perform the needed - * steps to deactivate the pad in case this pad is removed in the PAUSED or - * PLAYING state. See {@link Pad#setActive} for more information about - * deactivating pads. - *

+ *

+ * Pads are not automatically deactivated so elements should perform the + * needed steps to deactivate the pad in case this pad is removed in the + * PAUSED or PLAYING state. See {@link Pad#setActive} for more information + * about deactivating pads. + *

* This function will emit the {@link PAD_REMOVED} signal on the element. * * @param pad The {@link Pad} to remove. - * @return true if the pad could be removed. Can return false if the - * pad does not belong to the provided element. + * @return true if the pad could be removed. Can return false if the pad + * does not belong to the provided element. */ public boolean removePad(Pad pad) { return GSTELEMENT_API.gst_element_remove_pad(this, pad); } - + /** * Retrieves the factory that was used to create this element. + * * @return the {@link ElementFactory} used for creating this element. */ public ElementFactory getFactory() { return GSTELEMENT_API.gst_element_get_factory(this); } - + /** - * Get the bus of the element. Note that only a {@link Pipeline} will provide a - * bus for the application. + * Get the bus of the element. Note that only a {@link Pipeline} will + * provide a bus for the application. * * @return the element's {@link Bus} */ public Bus getBus() { return GSTELEMENT_API.gst_element_get_bus(this); } - + /** * Sends an event to an element. *

- * If the element doesn't implement an event handler, the event will be - * pushed on a random linked sink pad for upstream events or a random - * linked source pad for downstream events. + * If the element doesn't implement an event handler, the event will be + * pushed on a random linked sink pad for upstream events or a random linked + * source pad for downstream events. * * @param ev The {@link Event} to send. * @return true if the event was handled. @@ -418,58 +466,62 @@ public Bus getBus() { public boolean sendEvent(Event ev) { return GSTELEMENT_API.gst_element_send_event(this, ev); } - + /** - * Signal emitted when an {@link Pad} is added to this {@link Element} - * + * Signal emitted when an {@link Pad} is added to this element. + * * @see #connect(PAD_ADDED) * @see #disconnect(PAD_ADDED) */ public static interface PAD_ADDED { + /** * Called when a new {@link Pad} is added to an Element. - * + * * @param element the element the pad was added to. * @param pad the pad which was added. */ public void padAdded(Element element, Pad pad); } - + /** - * Signal emitted when an {@link Pad} is removed from this {@link Element} - * + * Signal emitted when an {@link Pad} is removed from this element. + * * @see #connect(PAD_REMOVED) * @see #disconnect(PAD_REMOVED) */ public static interface PAD_REMOVED { + /** * Called when a new {@link Pad} is removed from an Element. - * + * * @param element the element the pad was removed from. * @param pad the pad which was removed. */ public void padRemoved(Element element, Pad pad); } - + /** - * Signal emitted when this {@link Element} ceases to generated dynamic pads. - * + * Signal emitted when this element ceases to generated dynamic pads. + * * @see #connect(NO_MORE_PADS) * @see #disconnect(NO_MORE_PADS) */ public static interface NO_MORE_PADS { + /** - * Called when an {@link Element} ceases to generated dynamic pads. - * + * Called when an element ceases to generated dynamic pads. + * * @param element the element which posted this message. */ public void noMorePads(Element element); } - + /** * Add a listener for the pad-added signal - * - * @param listener Listener to be called when a {@link Pad} is added to the {@link Element}. + * + * @param listener Listener to be called when a {@link Pad} is added to the + * element. */ public void connect(final PAD_ADDED listener) { connect(PAD_ADDED.class, listener, new GstCallback() { @@ -479,43 +531,45 @@ public void callback(Element elem, Pad pad) { } }); } - + /** * Remove a listener for the pad-added signal - * + * * @param listener The listener that was previously added. */ public void disconnect(PAD_ADDED listener) { disconnect(PAD_ADDED.class, listener); } + /** * Add a listener for the pad-added signal - * - * @param listener Listener to be called when a {@link Pad} is removed from the {@link Element}. + * + * @param listener Listener to be called when a {@link Pad} is removed from + * the element. */ public void connect(final PAD_REMOVED listener) { - connect(PAD_REMOVED.class, listener,new GstCallback() { + connect(PAD_REMOVED.class, listener, new GstCallback() { @SuppressWarnings("unused") public void callback(Element elem, Pad pad) { listener.padRemoved(elem, pad); } }); } - + /** * Remove a listener for the pad-removed signal - * + * * @param listener The listener that was previously added. */ public void disconnect(PAD_REMOVED listener) { disconnect(PAD_REMOVED.class, listener); } - + /** * Add a listener for the no-more-pads signal - * - * @param listener Listener to be called when the {@link Element} will has - * finished generating dynamic pads. + * + * @param listener Listener to be called when the element has finished + * generating dynamic pads. */ public void connect(final NO_MORE_PADS listener) { connect(NO_MORE_PADS.class, listener, new GstCallback() { @@ -525,174 +579,303 @@ public void callback(Element elem) { } }); } - + /** * Remove a listener for the no-more-pads signal - * + * * @param listener The listener that was previously added. */ public void disconnect(NO_MORE_PADS listener) { disconnect(NO_MORE_PADS.class, listener); } - + /** * Link together a list of elements. *

* Make sure you have added your elements to a bin or pipeline with * {@link Bin#add} or {@link Bin#addMany} before trying to link them. - + * * @param elements The list of elements to link together. * @return true if all elements successfully linked. */ public static boolean linkMany(Element... elements) { return GSTELEMENT_API.gst_element_link_many(elements); } - + /** * Unlink a list of elements. - * + * * @param elements The list of elements to link together - * + * */ public static void unlinkMany(Element... elements) { GSTELEMENT_API.gst_element_unlink_many(elements); } - + /** * Link together source and destination pads of two elements. - * + * * A side effect is that if one of the pads has no parent, it becomes a - * child of the parent of the other element. If they have different - * parents, the link fails. - * - * @param src The {@link Element} containing the source {@link Pad}. - * @param srcPadName The name of the source {@link Pad}. Can be null for any pad. - * @param dest The {@link Element} containing the destination {@link Pad}. - * @param destPadName The name of the destination {@link Pad}. Can be null for any pad. - * + * child of the parent of the other element. If they have different parents, + * the link fails. + * + * @param src The element containing the source {@link Pad}. + * @param srcPadName The name of the source {@link Pad}. Can be null for any + * pad. + * @param dest The element containing the destination {@link Pad}. + * @param destPadName The name of the destination {@link Pad}. Can be null + * for any pad. + * * @return true if the pads were successfully linked. */ public static boolean linkPads(Element src, String srcPadName, Element dest, String destPadName) { return GSTELEMENT_API.gst_element_link_pads(src, srcPadName, dest, destPadName); } - + /** - * Link together source and destination pads of two elements. - * A side effect is that if one of the pads has no parent, it becomes a child of the parent of - * the other element. If they have different parents, the link fails. If caps - * is not null, makes sure that the caps of the link is a subset of caps. - * - * @param src The {@link Element} containing the source {@link Pad}. - * @param srcPadName The name of the source {@link Pad}. Can be null for any pad. - * @param dest The {@link Element} containing the destination {@link Pad}. - * @param destPadName The name of the destination {@link Pad}. Can be null for any pad. + * Link together source and destination pads of two elements. A side effect + * is that if one of the pads has no parent, it becomes a child of the + * parent of the other element. If they have different parents, the link + * fails. If caps is not null, makes sure that the caps of the link is a + * subset of caps. + * + * @param src The element containing the source {@link Pad}. + * @param srcPadName The name of the source {@link Pad}. Can be null for any + * pad. + * @param dest The element containing the destination {@link Pad}. + * @param destPadName The name of the destination {@link Pad}. Can be null + * for any pad. * @param caps The {@link Caps} to use to filter the link. - * + * * @return true if the pads were successfully linked. */ - public static boolean linkPadsFiltered(Element src, String srcPadName, + public static boolean linkPadsFiltered(Element src, String srcPadName, Element dest, String destPadName, Caps caps) { return GSTELEMENT_API.gst_element_link_pads_filtered(src, srcPadName, dest, destPadName, caps); } - + /** * Unlink source and destination pads of two elements. - * - * @param src The {@link Element} containing the source {@link Pad}. + * + * @param src The element containing the source {@link Pad}. * @param srcPadName The name of the source {@link Pad}. - * @param dest The {@link Element} containing the destination {@link Pad}. + * @param dest The element containing the destination {@link Pad}. * @param destPadName The name of the destination {@link Pad}. - * + * */ public static void unlinkPads(Element src, String srcPadName, Element dest, String destPadName) { GSTELEMENT_API.gst_element_unlink_pads(src, srcPadName, dest, destPadName); } - + /** - * Posts a {@link Message} on the element's {@link Bus}. + * Posts a {@link Message} on the element's {@link Bus}. * * @param message the Message to post. - * @return true if the message was posted, false if the + * @return true if the message was posted, false if the * element does not have a {@link Bus}. */ public boolean postMessage(Message message) { return GSTELEMENT_API.gst_element_post_message(this, message); } - + /** - * Gets the currently configured clock of the element. - * + * Gets the currently configured clock of the element. + * * @return the clock of the element. */ public Clock getClock() { return GSTELEMENT_API.gst_element_get_clock(this); } - + /** - * Returns the base time of the element. The base time is the - * absolute time of the clock when this element was last put to - * PLAYING. Subtracting the base time from the clock time gives - * the stream time of the element. - * + * Returns the base time of the element. The base time is the absolute time + * of the clock when this element was last put to PLAYING. Subtracting the + * base time from the clock time gives the stream time of the element. + * * @return the base time of the element */ - public ClockTime getBaseTime() { + public long getBaseTime() { return GSTELEMENT_API.gst_element_get_base_time(this); } /** * Set the base time of an element. - * + * * @param time the base time to set * @see #getBaseTime() */ - public void setBaseTime(ClockTime time) { - GSTELEMENT_API.gst_element_set_base_time(this, time); + public void setBaseTime(long time) { + GSTELEMENT_API.gst_element_set_base_time(this, time); } - + /** - * Returns the start time of this element. - * - * The start time is the running time of the clock when this element was last put to {@link State#PAUSED}. Usually the start_time is managed by a toplevel element such as {@link Pipeline}. - * + * Returns the start time of this element. + * + * The start time is the running time of the clock when this element was + * last put to {@link State#PAUSED}. Usually the start_time is managed by a + * toplevel element such as {@link Pipeline}. + * * MT safe. - * + * * @return the start time of this element. */ - public ClockTime getStartTime() { - return GSTELEMENT_API.gst_element_get_start_time(this); + public long getStartTime() { + return GSTELEMENT_API.gst_element_get_start_time(this); } - + /** - * Set the start time of an element. The start time of the element is the running time of the element when it last went to the {@link State#PAUSED} state. In {@link State#READY} or after a flushing seek, it is set to 0. - * - * Toplevel elements like GstPipeline will manage the start_time and base_time on its children. Setting the start_time to {@link ClockTime#NONE} on such a toplevel element will disable the distribution - * of the base_time to the children and can be useful if the application manages the base_time itself, for example if you want to synchronize capture from multiple pipelines, and you can also ensure - * that the pipelines have the same clock. - * + * Set the start time of an element. The start time of the element is the + * running time of the element when it last went to the {@link State#PAUSED} + * state. In {@link State#READY} or after a flushing seek, it is set to 0. + * + * Toplevel elements like GstPipeline will manage the start_time and + * base_time on its children. Setting the start_time to + * {@link ClockTime#NONE} on such a toplevel element will disable the + * distribution of the base_time to the children and can be useful if the + * application manages the base_time itself, for example if you want to + * synchronize capture from multiple pipelines, and you can also ensure that + * the pipelines have the same clock. + * * MT safe. - * + * * @param time the start time to set * @see #getStartTime() */ - public void setStartTime(ClockTime time) { - GSTELEMENT_API.gst_element_set_start_time(this, time); + public void setStartTime(long time) { + GSTELEMENT_API.gst_element_set_start_time(this, time); } - + /** * Performs a query on the element. - * + * * For elements that don't implement a query handler, this function forwards * the query to a random srcpad or to the peer of a random linked sinkpad of * this element. - * + * * Please note that some queries might need a running pipeline to work. - * + * * @param query the Query to perform * @return true if the query could be performed */ public boolean query(Query query) { return GSTELEMENT_API.gst_element_query(this, query); } - + + /** + * Sets the context of the element. + * + * @param context the Context to set. + */ + public void setContext(Context context) { + GstContextPtr gstContextPtr = Natives.getPointer(context).as(GstContextPtr.class, GstContextPtr::new); + GSTELEMENT_API.gst_element_set_context(this, gstContextPtr); + } + + /** + * Gets the context with the context_type set on the element or NULL. + * + * @param context_type + * @return a context or NULL + */ + public Context getContext(String context_type) { + GstContextPtr gstContextPtr = GSTELEMENT_API.gst_element_get_context(this, context_type); + return gstContextPtr != null ? Natives.callerOwnsReturn(gstContextPtr, Context.class) : null; + } + + /** + * Queries an element (usually top-level pipeline or playbin element) for + * the total stream duration in nanoseconds. This query will only work once + * the pipeline is prerolled (i.e. reached PAUSED or PLAYING state). The + * application will receive an ASYNC_DONE message on the pipeline bus when + * that is the case. + *

+ * If the duration changes for some reason, you will get a DURATION_CHANGED + * message on the pipeline bus, in which case you should re-query the + * duration using this function. + * + * @param format the {@code Format} to return the duration in + * @return the total duration of the current media stream, or -1 if the + * query failed + */ + public long queryDuration(Format format) { + long[] dur = {0}; + return GSTELEMENT_API.gst_element_query_duration(this, format, dur) ? dur[0] : -1L; + } + + /** + * Queries an element (usually top-level pipeline or playbin element) for + * the stream position in nanoseconds. This will be a value between 0 and + * the stream duration (if the stream duration is known). This query will + * usually only work once the pipeline is prerolled (i.e. reached PAUSED or + * PLAYING state). The application will receive an ASYNC_DONE message on the + * pipeline bus when that is the case. + * + * @param format The {@link Format} to return the position in + * @return the current position or -1 if the query failed. + */ + public long queryPosition(Format format) { + long[] pos = {0}; + return GSTELEMENT_API.gst_element_query_position(this, format, pos) ? pos[0] : -1L; + } + + /** + * Sends a seek event to an element. See {@link SeekEvent} for the details + * of the parameters. The seek event is sent to the element using the native + * equivalent of {@link #sendEvent(org.freedesktop.gstreamer.event.Event)}. + * + * @param rate the new playback rate + * @param format the format of the seek values + * @param seekFlags the seek flags + * @param startType the type and flags for the new start position + * @param start the value of the new start position + * @param stopType the type and flags for the new stop position + * @param stop the value of the new stop position + * @return true if seek operation succeeded. Flushing seeks will trigger a + * preroll, which will emit an ASYNC_DONE message + */ + public boolean seek(double rate, Format format, Set seekFlags, + SeekType startType, long start, SeekType stopType, long stop) { + + return GSTELEMENT_API.gst_element_seek(this, rate, format, + NativeFlags.toInt(seekFlags), startType, start, stopType, stop); + } + + /** + * Simple API to perform a seek on the given element, meaning it just seeks + * to the given position relative to the start of the stream. For more + * complex operations like segment seeks (e.g. for looping) or changing the + * playback rate or seeking relative to the last configured playback segment + * you should use + * {@link #seek(double, org.freedesktop.gstreamer.Format, java.util.Set, org.freedesktop.gstreamer.event.SeekType, long, org.freedesktop.gstreamer.event.SeekType, long)}. + *

+ * In a completely prerolled PAUSED or PLAYING pipeline, seeking is always + * guaranteed to return TRUE on a seekable media type or FALSE when the + * media type is certainly not seekable (such as a live stream). + *

+ * Some elements allow for seeking in the READY state, in this case they + * will store the seek event and execute it when they are put to PAUSED. If + * the element supports seek in READY, it will always return TRUE when it + * receives the event in the READY state. + * + * @param format a {@link Format} to seek in, such as {@link Format#TIME} + * @param seekFlags seek options; playback applications will usually want to + * use {@link SeekFlags#FLUSH} and {@link SeekFlags#KEY_UNIT} here + * @param seekPosition position to seek to (relative to the start); if you + * are doing a seek in format TIME this value is in nanoseconds + * @return true if seek operation succeeded. Flushing seeks will trigger a + * preroll, which will emit an ASYNC_DONE message + */ + public boolean seekSimple(Format format, Set seekFlags, long seekPosition) { + return GSTELEMENT_API.gst_element_seek_simple(this, format, + NativeFlags.toInt(seekFlags), seekPosition); + } + + static class Handle extends GstObject.Handle { + + public Handle(GstObjectPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + } + } diff --git a/src/org/freedesktop/gstreamer/ElementFactory.java b/src/org/freedesktop/gstreamer/ElementFactory.java index 3a479a69..f461bbd9 100644 --- a/src/org/freedesktop/gstreamer/ElementFactory.java +++ b/src/org/freedesktop/gstreamer/ElementFactory.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,9 +16,8 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; + import static org.freedesktop.gstreamer.lowlevel.GstElementFactoryAPI.GSTELEMENTFACTORY_API; import static org.freedesktop.gstreamer.lowlevel.GstPadTemplateAPI.GSTPADTEMPLATE_API; import static org.freedesktop.gstreamer.lowlevel.GstPluginAPI.GSTPLUGIN_API; @@ -31,175 +31,247 @@ import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; import org.freedesktop.gstreamer.lowlevel.GstPadTemplateAPI.GstStaticPadTemplate; -import org.freedesktop.gstreamer.lowlevel.GstTypes; -import org.freedesktop.gstreamer.lowlevel.NativeObject; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.Natives; /** * ElementFactory is used to create instances of elements. - * - * Use the {@link #find} and {@link #create} methods to create element instances + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstElementFactory.html + *

+ * Use the {@link #find} and {@link #create} methods to create element instances * or use {@link #make} as a convenient shortcut. * */ public class ElementFactory extends PluginFeature { - private static Logger logger = Logger.getLogger(ElementFactory.class.getName()); - private static Level DEBUG = Level.FINE; - - private static final Map> typeMap - = new HashMap>(); public static final String GTYPE_NAME = "GstElementFactory"; + + private static final Level DEBUG = Level.FINE; + private static final Logger LOG = Logger.getLogger(ElementFactory.class.getName()); /** - * Register a new class into the typeMap. - */ - public static void registerElement(Class klass, String name) { - typeMap.put(name, klass); + * Creates a new instance of ElementFactory + * + * @param init internal initialization data. + */ + ElementFactory(Initializer init) { + super(init); + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "", new Object[]{init}); + } } - + /** - * Retrieve an instance of a factory that can produce {@link Element}s - * - * @param name The type of {@link Element} to produce. - * @return An ElementFactory that will produce {@link Element}s of the - * desired type. + * Creates a new element from the factory. + * + * @param name the name to assign to the created Element + * @return A new {@link Element} */ - public static ElementFactory find(String name) { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "find", name); - } - ElementFactory factory = GSTELEMENTFACTORY_API.gst_element_factory_find(name); - if (factory == null) { - throw new IllegalArgumentException("No such Gstreamer factory: " + name); - } - return factory; + public Element create(String name) { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "create", name); + } + Pointer elem = GSTELEMENTFACTORY_API.ptr_gst_element_factory_create(this, name); + LOG.log(DEBUG, "gst_element_factory_create returned: " + elem); + if (elem == null) { + throw new IllegalArgumentException("Cannot create GstElement"); + } + return elementFor(elem, getName()); } - + /** - * Creates a new Element from the specified factory. + * Returns the name of the person who wrote the factory. * - * @param factoryName The name of the factory to use to produce the Element - * @param name The name to assign to the created Element - * @return A new GstElemElement + * @return The name of the author */ - public static Element make(String factoryName, String name) { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "make", new Object[] { factoryName, name}); - } - return elementFor(makeRawElement(factoryName, name), factoryName); + public String getAuthor() { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "getAuthor"); + } + return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "author"); } /** - * Get a list of factories that match the given type. Only elements with a - * rank greater or equal to minrank will be returned. The list of factories - * is returned by decreasing rank. - * - * @param type - * a {@link ElementFactoryListType} - * @param minrank - * Minimum rank - * @return a List of ElementFactory elements. + * Returns a description of the factory. + * + * @return A brief description of the factory. */ - public static List listGetElement(ElementFactoryListType type, Rank minrank) { - GList glist = GSTELEMENTFACTORY_API.gst_element_factory_list_get_elements(type.getValue(), minrank.getValue()); - List list = new ArrayList(); + public String getDescription() { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "getDescription"); + } + return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "description"); + } + + /** + * Returns a string describing the type of factory. This is an unordered + * list separated with slashes ('/'). + * + * @return The description of the type of factory. + */ + public String getKlass() { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "getKlass"); + } + return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "klass"); + } + /** + * Returns the long, English name for the factory. + * + * @return The long, English name for the factory. + */ + public String getLongName() { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "getLongName"); + } + return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "long-name"); + } + + /** + * Gets the list of {@link StaticPadTemplate} for this factory. + * + * @return The list of {@link StaticPadTemplate} + */ + public List getStaticPadTemplates() { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "getStaticPadTemplates"); + } + GList glist = GSTELEMENTFACTORY_API.gst_element_factory_get_static_pad_templates(this); + LOG.log(DEBUG, "GSTELEMENTFACTORY_API.gst_element_factory_get_static_pad_templates returned: " + glist); + List templates = new ArrayList(); GList next = glist; while (next != null) { if (next.data != null) { - ElementFactory fact = new ElementFactory(initializer(next.data, true, true)); - list.add(fact); + GstStaticPadTemplate temp = new GstStaticPadTemplate(next.data); + templates.add(new StaticPadTemplate(temp.getName(), temp.getPadDirection(), + temp.getPadPresence(), GSTPADTEMPLATE_API.gst_static_pad_template_get_caps(temp))); } next = next.next(); } - - GSTPLUGIN_API.gst_plugin_list_free(glist); - - return list; + return templates; } /** - * Filter out all the elementfactories in list that can handle caps in the - * given direction. - * - * If subsetonly is true, then only the elements whose pads templates are a - * complete superset of caps will be returned. Else any element whose pad - * templates caps can intersect with caps will be returned. - * - * @param list - * a {@link List} of {@link ElementFactory} to filter - * @param caps - * a {@link Caps} - * @param direction - * a {@link PadDirection} to filter on - * @param subsetonly - * whether to filter on caps subsets or not. - * @return a {@link List} of {@link ElementFactory} elements that match the - * given requisits. + * Retrieve an instance of a factory that can produce {@link Element}s + * + * @param name The type of {@link Element} to produce. + * @return An ElementFactory that will produce {@link Element}s of the + * desired type. */ - public static List listFilter(List list, Caps caps, - PadDirection direction, boolean subsetonly) { - GList glist = null; - List filterList = new ArrayList(); - - for (ElementFactory fact : list) { - fact.ref(); - glist = GLIB_API.g_list_append(glist, fact.handle()); + public static ElementFactory find(String name) { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "find", name); } + ElementFactory factory = GSTELEMENTFACTORY_API.gst_element_factory_find(name); + if (factory == null) { + throw new IllegalArgumentException("No such Gstreamer factory: " + name); + } + return factory; + } - GList gFilterList = GSTELEMENTFACTORY_API.gst_element_factory_list_filter(glist, caps, direction, subsetonly); +// /** +// * Filter out all the elementfactories in list that can handle caps in the +// * given direction. +// * +// * If subsetonly is true, then only the elements whose pads templates are a +// * complete superset of caps will be returned. Else any element whose pad +// * templates caps can intersect with caps will be returned. +// * +// * @param list a {@link List} of {@link ElementFactory} to filter +// * @param caps a {@link Caps} +// * @param direction a {@link PadDirection} to filter on +// * @param subsetonly whether to filter on caps subsets or not. +// * @return a {@link List} of {@link ElementFactory} elements that match the +// * given requisits. +// */ +// public static List listFilter(List list, Caps caps, +// PadDirection direction, boolean subsetonly) { +// GList glist = null; +// List filterList = new ArrayList(); +// +// for (ElementFactory fact : list) { +// fact.ref(); +// glist = GLIB_API.g_list_append(glist, fact.handle()); +// } +// +// GList gFilterList = GSTELEMENTFACTORY_API.gst_element_factory_list_filter(glist, caps, direction, subsetonly); +// +// GList next = gFilterList; +// while (next != null) { +// if (next.data != null) { +// ElementFactory fact = new ElementFactory(initializer(next.data, true, true)); +// filterList.add(fact); +// } +// next = next.next(); +// } +// +// GSTPLUGIN_API.gst_plugin_list_free(glist); +// GSTPLUGIN_API.gst_plugin_list_free(gFilterList); +// +// return filterList; +// } - GList next = gFilterList; + /** + * Get a list of factories that match the given type. Only elements with a + * rank greater or equal to minrank will be returned. The list of factories + * is returned by decreasing rank. + * + * @param type a {@link ListType} + * @param minrank Minimum rank + * @return a List of ElementFactory elements. + */ + public static List listGetElements(ListType type, Rank minrank) { + GList glist = GSTELEMENTFACTORY_API.gst_element_factory_list_get_elements(type.getValue(), minrank.intValue()); + List list = new ArrayList(); + + GList next = glist; while (next != null) { if (next.data != null) { - ElementFactory fact = new ElementFactory(initializer(next.data, true, true)); - filterList.add(fact); + ElementFactory fact = new ElementFactory(Natives.initializer(next.data, true, true)); + list.add(fact); } next = next.next(); } GSTPLUGIN_API.gst_plugin_list_free(glist); - GSTPLUGIN_API.gst_plugin_list_free(gFilterList); - return filterList; + return list; } /** * Get a list of factories that match the given parameter. * - * It is a combination of listGetElement and listFilter - * passing all the results of the first call to the second. + * It is a combination of listGetElement and listFilter passing all the + * results of the first call to the second. * * This method improves performance because there is no need to map to java * list the elements returned by the first call. * - * @param type - * a {@link ElementFactoryListType} - * @param minrank - * Minimum rank - * @param caps - * a {@link Caps} - * @param direction - * a {@link PadDirection} to filter on - * @param subsetonly - * whether to filter on caps subsets or not. + * @param type a {@link ListType} + * @param minrank Minimum rank + * @param caps a {@link Caps} + * @param direction a {@link PadDirection} to filter on + * @param subsetonly whether to filter on caps subsets or not. * @return a {@link List} of {@link ElementFactory} elements that match the - * given requisits. + * given requisits. */ - public static List listGetElementFilter(ElementFactoryListType type, Rank minrank, + public static List listGetElementsFilter(ListType type, Rank minrank, Caps caps, PadDirection direction, boolean subsetonly) { List filterList = new ArrayList(); - GList glist = GSTELEMENTFACTORY_API.gst_element_factory_list_get_elements(type.getValue(), minrank.getValue()); + GList glist = GSTELEMENTFACTORY_API.gst_element_factory_list_get_elements(type.getValue(), minrank.intValue()); GList gFilterList = GSTELEMENTFACTORY_API.gst_element_factory_list_filter(glist, caps, direction, subsetonly); GList next = gFilterList; while (next != null) { if (next.data != null) { - ElementFactory fact = new ElementFactory(initializer(next.data, true, true)); + ElementFactory fact = new ElementFactory(Natives.initializer(next.data, true, true)); filterList.add(fact); } next = next.next(); @@ -211,161 +283,94 @@ public static List listGetElementFilter(ElementFactoryListType t return filterList; } - public static Pointer makeRawElement(String factoryName, String name) { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "makeRawElement", new Object[] { factoryName, name}); - } - Pointer elem = GSTELEMENTFACTORY_API.ptr_gst_element_factory_make(factoryName, name); - logger.log(DEBUG, "Return from gst_element_factory_make=" + elem); - if (elem == null) { - throw new IllegalArgumentException("No such Gstreamer factory: " - + factoryName); + /** + * Creates a new Element from the specified factory. + * + * @param factoryName The name of the factory to use to produce the Element + * @param name The name to assign to the created Element + * @return A new GstElemElement + */ + public static Element make(String factoryName, String name) { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "make", new Object[]{factoryName, name}); } - return elem; + return elementFor(makeRawElement(factoryName, name), factoryName); } - - @SuppressWarnings("unchecked") - private static Element elementFor(Pointer ptr, String factoryName) { - Class cls = ElementFactory.typeMap.get(factoryName); - if (cls == null) { - cls = (Class)GstTypes.classFor(Element.getType(ptr)); - if (cls == null) cls = Element.class; - ElementFactory.typeMap.put(factoryName, cls); + static Pointer makeRawElement(String factoryName, String name) { + if (LOG.isLoggable(Level.FINER)) { + LOG.entering("ElementFactory", "makeRawElement", new Object[]{factoryName, name}); } - return NativeObject.objectFor(ptr, cls, false); - } - - /** - * Creates a new instance of ElementFactory - * @param init internal initialization data. - */ - public ElementFactory(Initializer init) { - super(init); - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "", new Object[] { init }); - } - } - - /** - * Creates a new element from the factory. - * - * @param name the name to assign to the created Element - * @return A new {@link Element} - */ - public Element create(String name) { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "create", name); - } - Pointer elem = GSTELEMENTFACTORY_API.ptr_gst_element_factory_create(this, name); - logger.log(DEBUG, "gst_element_factory_create returned: " + elem); + Pointer elem = GSTELEMENTFACTORY_API.ptr_gst_element_factory_make(factoryName, name); + LOG.log(DEBUG, "Return from gst_element_factory_make=" + elem); if (elem == null) { - throw new IllegalArgumentException("Cannot create GstElement"); + throw new IllegalArgumentException("No such Gstreamer factory: " + + factoryName); } - return elementFor(elem, getName()); - } - - /** - * Returns the name of the person who wrote the factory. - * - * @return The name of the author - */ - public String getAuthor() { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "getAuthor"); - } - return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "author"); + return elem; } - /** - * Returns a description of the factory. - * - * @return A brief description of the factory. - */ - public String getDescription() { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "getDescription"); - } - return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "description"); - } - - /** - * Returns the long, English name for the factory. - * - * @return The long, English name for the factory. - */ - public String getLongName() { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "getLongName"); - } - return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "long-name"); + private static Element elementFor(Pointer ptr, String factoryName) { + return Natives.objectFor(ptr, Element.class, false, true); } /** - * Returns a string describing the type of factory. - * This is an unordered list separated with slashes ('/'). - * - * @return The description of the type of factory. + * The type of ElementFactory to filter. */ - public String getKlass() { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "getKlass"); - } - return GSTELEMENTFACTORY_API.gst_element_factory_get_metadata(this, "klass"); - } - - /** - * Gets the list of {@link StaticPadTemplate} for this factory. - * - * @return The list of {@link StaticPadTemplate} - */ - public List getStaticPadTemplates() { - if (logger.isLoggable(Level.FINER)) { - logger.entering("ElementFactory", "getStaticPadTemplates"); - } - GList glist = GSTELEMENTFACTORY_API.gst_element_factory_get_static_pad_templates(this); - logger.log(DEBUG, "GSTELEMENTFACTORY_API.gst_element_factory_get_static_pad_templates returned: " + glist); - List templates = new ArrayList(); - GList next = glist; - while (next != null) { - if (next.data != null) { - GstStaticPadTemplate temp = new GstStaticPadTemplate(next.data); - templates.add(new StaticPadTemplate(temp.getName(), temp.getPadDirection(), - temp.getPadPresence(), GSTPADTEMPLATE_API.gst_static_pad_template_get_caps(temp))); - } - next = next.next(); - } - return templates; - } + public enum ListType { - public enum ElementFactoryListType { + /** Decoder elements */ DECODER((long) 1 << 0), + /** Encoder elements */ ENCODER((long) 1 << 1), - SINK((long)1 << 2), - SRC((long)1 << 3), - MUXER((long)1 << 4), - DEMUXER((long)1 << 5), - PARSER((long)1 << 6), - PAYLOADER((long)1 << 7), - DEPAYLOADER((long)1 << 8), - FORMATTER((long)1 << 9), - MAX_ELEMENTS((long)1 << 48), - ANY((((long)1) << 49) - 1), - + /** Sink elements */ + SINK((long) 1 << 2), + /** Source elements */ + SRC((long) 1 << 3), + /** Muxer elements */ + MUXER((long) 1 << 4), + /** Demuxer elements */ + DEMUXER((long) 1 << 5), + /** Parser elements */ + PARSER((long) 1 << 6), + /** Payloader elements */ + PAYLOADER((long) 1 << 7), + /** Depayloader elements */ + DEPAYLOADER((long) 1 << 8), + /** Formatter elements */ + FORMATTER((long) 1 << 9), + /** Decryptor elements */ + DECRYPTOR((long) 1 << 10), + /** Encryptor elements */ + ENCRYPTOR((long) 1 << 11), +// /** Private, do not use */ +// MAX_ELEMENTS((long) 1 << 48), + /** Any elements */ + ANY((((long) 1) << 49) - 1), + /** Any of the defined media element types */ MEDIA_ANY(~((long) 0) << 48), - MEDIA_VIDEO((long)1 << 49), - MEDIA_AUDIO((long)1 << 50), - MEDIA_IMAGE((long)1 << 51), - MEDIA_SUBTITLE((long)1 << 52), - MEDIA_METADATA((long)1 << 53), + /** Elements handling video media types */ + MEDIA_VIDEO((long) 1 << 49), + /** Elements handling audio media types */ + MEDIA_AUDIO((long) 1 << 50), + /** Elements handling image media types */ + MEDIA_IMAGE((long) 1 << 51), + /** Elements handling subtitle media types */ + MEDIA_SUBTITLE((long) 1 << 52), + /** Elements handling metadata media types */ + MEDIA_METADATA((long) 1 << 53), + /** All encoders handling video or image media types */ VIDEO_ENCODER(ENCODER.getValue() | MEDIA_VIDEO.getValue() | MEDIA_IMAGE.getValue()), + /** All encoders handling audio media types */ AUDIO_ENCODER(ENCODER.getValue() | MEDIA_AUDIO.getValue()), + /** All sinks handling audio, video or image media types */ AUDIOVIDEO_SINKS(SINK.getValue() | MEDIA_AUDIO.getValue() | MEDIA_VIDEO.getValue() | MEDIA_IMAGE.getValue()), - DECODABLE(ENCODER.getValue() | DEMUXER.getValue() | DEPAYLOADER.getValue() | PARSER.getValue()); + /** All elements used to 'decode' streams (decoders, demuxers, parsers, depayloaders) */ + DECODABLE(DECODER.getValue() | DEMUXER.getValue() | DEPAYLOADER.getValue() | PARSER.getValue()); - private long value; + private final long value; - private ElementFactoryListType(long value) { + private ListType(long value) { this.value = value; } diff --git a/src/org/freedesktop/gstreamer/Event.java b/src/org/freedesktop/gstreamer/Event.java deleted file mode 100644 index f7a271e7..00000000 --- a/src/org/freedesktop/gstreamer/Event.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2007, 2008 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import org.freedesktop.gstreamer.lowlevel.ReferenceManager; -import org.freedesktop.gstreamer.lowlevel.annotations.HasSubtype; - -import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; - -/** - * Base type of all events. - * - *

Events are passed between elements in parallel to the data stream. Some events - * are serialized with buffers, others are not. Some events only travel downstream, - * others only upstream. Some events can travel both upstream and downstream. - * - *

The events are used to signal special conditions in the datastream such as - * EOS (end of stream) or the start of a new stream-segment. - * - *

Events are also used to flush the pipeline of any pending data. - * - * @see Pad#pushEvent - * @see Pad#sendEvent - * @see Element#sendEvent - */ -@HasSubtype -public class Event extends MiniObject { - public static final String GTYPE_NAME = "GstEvent"; - - /** - * This constructor is for internal use only. - * @param init initialization data. - */ - public Event(Initializer init) { - super(init); - } - - /** - * Gets the structure containing the data in this event. - * - * @return a structure. - */ - public Structure getStructure() { - return ReferenceManager.addKeepAliveReference(GSTEVENT_API.gst_event_get_structure(this), this); - } -} diff --git a/src/org/freedesktop/gstreamer/FlowReturn.java b/src/org/freedesktop/gstreamer/FlowReturn.java index e93183f7..35655c0f 100644 --- a/src/org/freedesktop/gstreamer/FlowReturn.java +++ b/src/org/freedesktop/gstreamer/FlowReturn.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -20,24 +21,30 @@ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.glib.NativeEnum; import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; /** * The result of passing data to a pad. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPad.html#GstFlowReturn + *

*/ -public enum FlowReturn implements IntegerEnum { - /** Resend buffer, possibly with new caps (not send yet). */ - RESEND(1), +public enum FlowReturn implements NativeEnum { + /** Data passing was ok. */ + // @TODO need to map custom success and custom error OK and ERROR? + @DefaultEnumValue OK(0), /** {@link Pad} is not linked. */ NOT_LINKED(-1), /** {@link Pad} is in wrong state. */ - WRONG_STATE(-2), + FLUSHING(-2), /** Did not expect anything, like after EOS. */ - UNEXPECTED(-3), + EOS(-3), /** {@link Pad} is in not negotiated. */ NOT_NEGOTIATED(-4), @@ -48,12 +55,10 @@ public enum FlowReturn implements IntegerEnum { ERROR(-5), /** This operation is not supported. */ - NOT_SUPPORTED(-6), + NOT_SUPPORTED(-6); + + private final int value; - /** The default enum value used when no other value matches the native value */ - @DefaultEnumValue - __UNKNOWN_NATIVE_VALUE(~0); - FlowReturn(int value) { this.value = value; } @@ -62,8 +67,8 @@ public enum FlowReturn implements IntegerEnum { * Gets the integer value of the enum. * @return The integer value for this enum. */ + @Override public int intValue() { return value; } - private int value; } diff --git a/src/org/freedesktop/gstreamer/Format.java b/src/org/freedesktop/gstreamer/Format.java index 5903d059..d38ef097 100644 --- a/src/org/freedesktop/gstreamer/Format.java +++ b/src/org/freedesktop/gstreamer/Format.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -18,52 +19,47 @@ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.EnumMapper; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.glib.NativeEnum; import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; /** * Standard predefined formats. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/gstreamer-GstFormat.html#GstFormat + *

*/ -public enum Format implements IntegerEnum { +// @TODO this won't handle custom registered formats - do we need to? +public enum Format implements NativeEnum { /** Undefined format */ + @DefaultEnumValue UNDEFINED(0), /** * The default format of the pad/element. This can be samples for raw audio, - * frames/fields for raw video. + * frames/fields for raw video (some, but not all, elements support this; + * use {@link #TIME } if you don't have a good reason to query for + * samples/frames) */ DEFAULT(1), - /** bytes */ + /** Bytes */ BYTES(2), /** Time in nanoseconds */ TIME(3), - /** {@link Buffer}s */ + /** {@link Buffer}s (few, if any, elements implement this as of May 2009) */ BUFFERS(4), - /** Percentage of stream */ - PERCENT(5), + /** Percentage of stream (few, if any, elements implement this as of May 2009) */ + PERCENT(5); + + private final int value; - /** The default enum value used when no other value matches the native value */ - @DefaultEnumValue - __UNKNOWN_NATIVE_VALUE(~0); Format(int value) { this.value = value; } - /** - * Gets the integer value of the enum. - * @return The integer value for this enum. - */ - public final int intValue() { - return value; - } - /** - * Returns the enum constant of this type with the specified integer value. - * @param format integer value. - * @return Enum constant. - */ - public final static Format valueOf(int format) { - return EnumMapper.getInstance().valueOf(format, Format.class); + @Override + public int intValue() { + return value; } - public final int value; } diff --git a/src/org/freedesktop/gstreamer/Fraction.java b/src/org/freedesktop/gstreamer/Fraction.java index 542b3407..ea05f978 100644 --- a/src/org/freedesktop/gstreamer/Fraction.java +++ b/src/org/freedesktop/gstreamer/Fraction.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007,2008 Wayne Meissner * * This file is part of gstreamer-java. @@ -18,9 +19,6 @@ package org.freedesktop.gstreamer; -import static org.freedesktop.gstreamer.lowlevel.GstValueAPI.GSTVALUE_API; -import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; - /** * Represents a fraction value. */ @@ -65,10 +63,4 @@ public double toDouble() { return denominator != 0 ? ((double) numerator / denominator) : Double.NaN; } - public static Fraction objectFor(GValue value) { - if (GSTVALUE_API.gst_fraction_get_type().equals(value.getType())) { - return new Fraction(GSTVALUE_API.gst_value_get_fraction_numerator(value), GSTVALUE_API.gst_value_get_fraction_denominator(value)); - } - return null; - } } diff --git a/src/org/freedesktop/gstreamer/GInetSocketAddress.java b/src/org/freedesktop/gstreamer/GInetSocketAddress.java deleted file mode 100644 index 2a11e153..00000000 --- a/src/org/freedesktop/gstreamer/GInetSocketAddress.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.freedesktop.gstreamer; - -import org.freedesktop.gstreamer.glib.GInetAddress; -import org.freedesktop.gstreamer.glib.GSocketAddress; -import org.freedesktop.gstreamer.lowlevel.GioAPI; - -import com.sun.jna.Pointer; - -public class GInetSocketAddress extends GSocketAddress{ - - public static final String GTYPE_NAME = "GInetSocketAddress"; - - - protected static Initializer createRawAddress(String address, int port) { - Pointer nativePointer = GioAPI.g_inet_socket_address_new_from_string(address, port); - if (nativePointer == null) { - throw new GstException("Can not create "+GInetSocketAddress.class.getSimpleName()+" for "+address+":"+port+", please check that the IP address is valid, with format x.x.x.x"); - } - return initializer(nativePointer); - } - - public GInetSocketAddress(String address, int port) { - this(createRawAddress(address, port)); - } - - public GInetSocketAddress(Initializer init) { - super(init); - } - - public int getPort() { - return (Integer) get("port"); - } - - public GInetAddress getAddress() { - return (GInetAddress) get("address"); - } -} diff --git a/src/org/freedesktop/gstreamer/GSocket.java b/src/org/freedesktop/gstreamer/GSocket.java deleted file mode 100644 index f57c22da..00000000 --- a/src/org/freedesktop/gstreamer/GSocket.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.freedesktop.gstreamer; - -import org.freedesktop.gstreamer.glib.GCancellable; -import org.freedesktop.gstreamer.glib.GSocketFamily; -import org.freedesktop.gstreamer.glib.GSocketProtocol; -import org.freedesktop.gstreamer.glib.GSocketType; -import org.freedesktop.gstreamer.lowlevel.GioAPI; -import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; - -import com.sun.jna.Pointer; - -public class GSocket extends GObject{ - - public static final String GTYPE_NAME = "GSocket"; - - public static Initializer makeRawSocket(GSocketFamily family, GSocketType type, GSocketProtocol protocol) throws GstException { - GErrorStruct reference = new GErrorStruct(); - GErrorStruct[] errorArray = (GErrorStruct[]) reference.toArray(1); - Pointer socketPointer = GioAPI.g_socket_new(family.toGioValue(), type.toGioValue(), protocol.toGioValue(), reference.getPointer()); - if (socketPointer == null) { - errorArray[0].read(); - throw new GstException(new GError(errorArray[0])); - } - return initializer(socketPointer); - } - - public GSocket(GSocketFamily family, GSocketType type, GSocketProtocol protocol) throws GstException { - this(makeRawSocket(family, type, protocol)); - } - - public GSocket(Initializer init) { - super(init); - } - - public GSocket bind(String address, int port) { - GInetSocketAddress boundAddress = new GInetSocketAddress(address, port); - GErrorStruct reference = new GErrorStruct(); - GErrorStruct[] errorArray = (GErrorStruct[]) reference.toArray(1); - if ( ! GioAPI.g_socket_bind(getNativeAddress(), boundAddress.getNativeAddress(), true, reference.getPointer()) ) { - errorArray[0].read(); - throw new GstException(new GError(errorArray[0])); - } - return this; - } - - public void connect(String address, int port) { - GInetSocketAddress connectedAddress = new GInetSocketAddress(address, port); - GErrorStruct reference = new GErrorStruct(); - GErrorStruct[] errorArray = (GErrorStruct[]) reference.toArray(1); - if ( ! GioAPI.g_socket_connect(getNativeAddress(), connectedAddress.getNativeAddress(), new GCancellable().getNativeAddress(), reference.getPointer()) ) { - errorArray[0].read(); - throw new GstException(new GError(errorArray[0])); - } - } - - public GInetSocketAddress getLocalAddress() { - return (GInetSocketAddress) get("local-address"); - } - - public GInetSocketAddress getRemoteAddress() { - return (GInetSocketAddress) get("remote-address"); - } - - public GSocketType getSocketType() { - return GSocketType.fromGioValue((Integer) get("type")); - } - - public GSocketFamily getSocketFamily() { - return GSocketFamily.fromGioValue((Integer) get("family")); - } - - public GSocketProtocol getSocketProtocol() { - return GSocketProtocol.fromGioValue((Integer) get("protocol")); - } - - public boolean isBlocking() { - return (Boolean) get("blocking"); - } - - public int getFD() { - return (Integer) get("fd"); - } - - -} diff --git a/src/org/freedesktop/gstreamer/GhostPad.java b/src/org/freedesktop/gstreamer/GhostPad.java index 30dde9c1..ca5d79a1 100644 --- a/src/org/freedesktop/gstreamer/GhostPad.java +++ b/src/org/freedesktop/gstreamer/GhostPad.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen @@ -20,67 +21,73 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstGhostPadAPI.GSTGHOSTPAD_API; /** * Pseudo link pads. - * *

- * GhostPads are useful when organizing pipelines with {@link Bin} like elements. - * The idea here is to create hierarchical element graphs. The bin element - * contains a sub-graph. Now one would like to treat the bin-element like any other - * {@link Element}. This is where GhostPads come into play. A GhostPad acts as a - * proxy for another pad. Thus the bin can have sink and source ghost-pads that - * are associated with sink and source pads of the child elements. - * + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstGhostPad.html + *

+ * GhostPads are useful when organizing pipelines with {@link Bin} like + * elements. The idea here is to create hierarchical element graphs. The bin + * element contains a sub-graph. Now one would like to treat the bin-element + * like any other {@link Element}. This is where GhostPads come into play. A + * GhostPad acts as a proxy for another pad. Thus the bin can have sink and + * source ghost-pads that are associated with sink and source pads of the child + * elements. *

- * If the target pad is known at creation time, {@link #GhostPad(String, Pad)} is the - * function to use to get a ghost-pad. Otherwise one can use {@link #GhostPad(String, PadDirection)} - * to create the ghost-pad and use {@link #setTarget} to establish the association later on. + * If the target pad is known at creation time, {@link #GhostPad(String, Pad)} + * is the function to use to get a ghost-pad. Otherwise one can use + * {@link #GhostPad(String, PadDirection)} to create the ghost-pad and use + * {@link #setTarget} to establish the association later on. + *

+ * Note that GhostPads add overhead to the data processing of a pipeline. * - *

Note that GhostPads add overhead to the data processing of a pipeline. - * * @see Pad */ public class GhostPad extends Pad { + public static final String GTYPE_NAME = "GstGhostPad"; /** * Creates a new instance of GhostPad */ - public GhostPad(Initializer init) { - super(init); + GhostPad(Initializer init) { + super(init); } - + /** - * Create a new ghostpad with target as the target. The direction will be taken - * from the target pad. The target pad must be unlinked. - * + * Create a new ghostpad with target as the target. The direction will be + * taken from the target pad. The target pad must be unlinked. + * * @param name The name of the new pad, or null to assign a default name. * @param target The {@link Pad} to ghost. */ public GhostPad(String name, Pad target) { - this(initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new(name, target))); + this(Natives.initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new(name, target))); } - + /** - * Create a new ghostpad with target as the target. The direction will be taken - * from the target pad. The template used on the ghostpad will be template. + * Create a new ghostpad with target as the target. The direction will be + * taken from the target pad. The template used on the ghostpad will be + * template. * * @param name The name of the new pad, or null to assign a default name. * @param target The {@link Pad} to ghost. * @param template The {@link PadTemplate} to use on the ghostpad. */ public GhostPad(String name, Pad target, PadTemplate template) { - this(initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new_from_template(name, target, template))); + this(Natives.initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new_from_template(name, target, template))); } - + /** - * Create a new ghostpad without a target with the given direction. - * A target can be set on the ghostpad later with the {@link #setTarget} method. + * Create a new ghostpad without a target with the given direction. A target + * can be set on the ghostpad later with the {@link #setTarget} method. *

* The created ghostpad will not have a padtemplate. * @@ -88,9 +95,9 @@ public GhostPad(String name, Pad target, PadTemplate template) { * @param direction The direction of the ghostpad. */ public GhostPad(String name, PadDirection direction) { - this(initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new_no_target(name, direction.ordinal()))); + this(Natives.initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new_no_target(name, direction.ordinal()))); } - + /** * Create a new ghostpad based on template, without setting a target. The * direction will be taken from the template. @@ -99,25 +106,26 @@ public GhostPad(String name, PadDirection direction) { * @param template The {@link PadTemplate} to use on the ghostpad. */ public GhostPad(String name, PadTemplate template) { - this(initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new_no_target_from_template(name, template))); + this(Natives.initializer(GSTGHOSTPAD_API.ptr_gst_ghost_pad_new_no_target_from_template(name, template))); } - + /** * Get the target pad of this ghostpad. * - * @return the target {@link Pad}, can be null if the ghostpad has no target set + * @return the target {@link Pad}, can be null if the ghostpad has no target + * set */ public Pad getTarget() { return GSTGHOSTPAD_API.gst_ghost_pad_get_target(this); } - + /** - * Set the new target of the ghostpad. Any existing target - * is unlinked and links to the new target are established. + * Set the new target of the ghostpad. Any existing target is unlinked and + * links to the new target are established. * * @param pad The new pad target. - * @return true if the new target could be set. This function can return false - * when the internal pads could not be linked. + * @return true if the new target could be set. This function can return + * false when the internal pads could not be linked. */ public boolean setTarget(Pad pad) { return GSTGHOSTPAD_API.gst_ghost_pad_set_target(this, pad); diff --git a/src/org/freedesktop/gstreamer/Gst.java b/src/org/freedesktop/gstreamer/Gst.java index f55b0f14..8835c929 100644 --- a/src/org/freedesktop/gstreamer/Gst.java +++ b/src/org/freedesktop/gstreamer/Gst.java @@ -1,6 +1,6 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2018 Antonio Morales - * Copyright (c) 2018 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -17,19 +17,19 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.query.Query; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.event.Event; +import org.freedesktop.gstreamer.glib.GError; + import static org.freedesktop.gstreamer.lowlevel.GstAPI.GST_API; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; @@ -37,60 +37,66 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; -import org.freedesktop.gstreamer.elements.AppSink; -import org.freedesktop.gstreamer.elements.AppSrc; -import org.freedesktop.gstreamer.elements.BaseSink; -import org.freedesktop.gstreamer.elements.BaseSrc; -import org.freedesktop.gstreamer.elements.BaseTransform; -import org.freedesktop.gstreamer.elements.DecodeBin; -import org.freedesktop.gstreamer.elements.PlayBin; -import org.freedesktop.gstreamer.elements.URIDecodeBin; -import org.freedesktop.gstreamer.elements.WebRTCBin; -import org.freedesktop.gstreamer.glib.GDate; -import org.freedesktop.gstreamer.glib.GInetAddress; -import org.freedesktop.gstreamer.glib.GSocketAddress; import org.freedesktop.gstreamer.glib.MainContextExecutorService; -import org.freedesktop.gstreamer.lowlevel.GMainContext; import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; import org.freedesktop.gstreamer.lowlevel.GstTypes; -import org.freedesktop.gstreamer.lowlevel.NativeObject; +import org.freedesktop.gstreamer.glib.NativeObject; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; -import java.util.Arrays; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ServiceLoader; import java.util.logging.Level; +import java.util.stream.Stream; +import org.freedesktop.gstreamer.controller.Controllers; +import org.freedesktop.gstreamer.elements.Elements; +import org.freedesktop.gstreamer.glib.GLib; +import org.freedesktop.gstreamer.glib.GMainContext; +import org.freedesktop.gstreamer.video.Video; + import static org.freedesktop.gstreamer.lowlevel.GstParseAPI.GSTPARSE_API; +import static org.freedesktop.gstreamer.glib.Natives.registration; +import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; + +import org.freedesktop.gstreamer.webrtc.WebRTC; /** * Media library supporting arbitrary formats and filter graphs. - * */ -@SuppressWarnings("deprecation") public final class Gst { - private static Logger logger = Logger.getLogger(Gst.class.getName()); + + private final static Logger LOG = Logger.getLogger(Gst.class.getName()); + private final static AtomicInteger INIT_COUNT = new AtomicInteger(0); + private final static boolean CHECK_VERSIONS = !Boolean.getBoolean("gstreamer.suppressVersionChecks"); + private final static boolean DISABLE_EXTERNAL = Boolean.getBoolean("gstreamer.disableExternalTypes"); private static ScheduledExecutorService executorService; private static volatile CountDownLatch quit = new CountDownLatch(1); private static GMainContext mainContext; private static boolean useDefaultContext = false; - private static final AtomicInteger initCount = new AtomicInteger(0); - private static List shutdownTasks = Collections.synchronizedList(new ArrayList()); + private static List shutdownTasks = Collections.synchronizedList(new ArrayList()); + // set minorVersion to a value guaranteed to be >= anything else unless set in init() + private static int minorVersion = Integer.MAX_VALUE; - public static class NativeArgs { + private static class NativeArgs { + public IntByReference argcRef; public PointerByReference argvRef; Memory[] argsCopy; Memory argvMemory; + public NativeArgs(String progname, String[] args) { // // Allocate some native memory to pass the args down to the native layer // argsCopy = new Memory[args.length + 2]; argvMemory = new Memory(argsCopy.length * Native.POINTER_SIZE); - + // // Insert the program name as argv[0] // @@ -107,6 +113,7 @@ public NativeArgs(String progname, String[] args) { argvRef = new PointerByReference(argvMemory); argcRef = new IntByReference(args.length + 1); } + String[] toStringArray() { // // Unpack the native arguments back into a String array @@ -120,243 +127,246 @@ String[] toStringArray() { } } return args.toArray(new String[args.size()]); - } + } } - /** Creates a new instance of Gst */ private Gst() { } - + /** - * Gets the version of gstreamer currently in use. - * + * Gets the version of gstreamer currently available. This function can be + * used prior to calling {@link #init(org.freedesktop.gstreamer.Version) } + * * @return the version of gstreamer */ public static Version getVersion() { - long[] major = { 0 }, minor = { 0 }, micro = { 0 }, nano = { 0 }; + long[] major = {0}, minor = {0}, micro = {0}, nano = {0}; GST_API.gst_version(major, minor, micro, nano); - return new Version(major[0], minor[0], micro[0], nano[0]); + return new Version((int) major[0], (int) minor[0], (int) micro[0], (int) nano[0]); } - + /** * Gets the the version of gstreamer currently in use, as a String. - * + * * @return a string representation of the version. */ public static String getVersionString() { return GST_API.gst_version_string(); } + /** - * Get Segmentation Trap status. + * Some functions in the GStreamer core might install a custom SIGSEGV + * handler to better catch and report errors to the application. Currently + * this feature is enabled by default when loading plugins. + *

+ * Applications might want to disable this behaviour with the + * {@link #setSegTrap(boolean) } function. This is typically done if the + * application wants to install its own handler without GStreamer + * interfering. + * * @return Segmentation Trap status. */ public static boolean isSegTrapEnabled() { - return GST_API.gst_segtrap_is_enabled(); + return GST_API.gst_segtrap_is_enabled(); } + /** - * Set Segmentation Trap status. + * Applications might want to disable/enable the SIGSEGV handling of the + * GStreamer core. See {@link #isSegTrapEnabled() } for more information. + * * @param enabled */ public static void setSegTrap(boolean enabled) { - GST_API.gst_segtrap_set_enabled(enabled); + GST_API.gst_segtrap_set_enabled(enabled); } /** * Test whether the GStreamer library already initialized. - * + * * @return true if the GStreamer library already initialized. */ public static synchronized final boolean isInitialized() { - return initCount.get() > 0; + return INIT_COUNT.get() > 0; } - - /** - * Gets the common {@code Executor} used to execute background tasks. - * - * @return an executor that can be used for background tasks. - */ - public static Executor getExecutor() { - return getScheduledExecutorService(); - } - - /** - * Gets the common {@code ExecutorService} used to execute background tasks. - * - * @return an executor that can be used for background tasks. - */ - public static ExecutorService getExecutorService() { - return getScheduledExecutorService(); - } - + /** - * Gets the common {@code ScheduledExecutorService} used to execute + * Gets the common {@link ScheduledExecutorService} used to execute * background tasks and schedule timeouts. - * + * * @return an executor that can be used for background tasks. */ - public static ScheduledExecutorService getScheduledExecutorService() { + public static ScheduledExecutorService getExecutor() { return executorService; } - + /** * Signals the thread that called {@link #init} to return. */ public static void quit() { quit.countDown(); } - - + /** * Create a new pipeline based on command line syntax. - * - * Please note that you might get a return value that is not NULL even - * though the error is set. - * In this case there was a recoverable parsing error and you can try - * to play the pipeline. - * - * @param pipelineDescription the command line describing the pipeline + * + * Please note that you might get a return value that is not NULL even + * though the error is set. In this case there was a recoverable parsing + * error and you can try to play the pipeline. + * + * @param pipelineDescription the command line describing the pipeline * @param errors a list that any errors will be added to - * @return a new element on success. - * If more than one top-level element is specified by - * the pipeline_description , all elements are put into a Pipeline, - * which then is returned. + * @return a new element on success. If more than one top-level element is + * specified by the pipeline_description , all elements are put into a + * Pipeline, which then is returned. * @throws GstException if the pipeline / element could not be created */ public static Element parseLaunch(String pipelineDescription, List errors) { - Pointer[] err = { null }; + Pointer[] err = {null}; Element pipeline = GSTPARSE_API.gst_parse_launch(pipelineDescription, err); if (pipeline == null) { - throw new GstException(new GError(new GErrorStruct(err[0]))); + throw new GstException(extractError(err[0])); } - + // check for error - if (err[0] != null) { + if (err[0] != null) { if (errors != null) { - errors.add(new GError(new GErrorStruct(err[0]))); + errors.add(extractError(err[0])); } else { - logger.log(Level.WARNING, new GError(new GErrorStruct(err[0])).getMessage()); + LOG.log(Level.WARNING, extractError(err[0]).getMessage()); } } - + return pipeline; } - + /** * Create a new pipeline based on command line syntax. - * - * Please note that you might get a return value that is not NULL even - * though the error is set. - * In this case there was a recoverable parsing error and you can try - * to play the pipeline. - * - * @param pipelineDescription the command line describing the pipeline - * @return a new element on success. - * If more than one top-level element is specified by - * the pipeline_description , all elements are put into a Pipeline, - * which then is returned. + * + * Please note that you might get a return value that is not NULL even + * though the error is set. In this case there was a recoverable parsing + * error and you can try to play the pipeline. + * + * @param pipelineDescription the command line describing the pipeline + * @return a new element on success. If more than one top-level element is + * specified by the pipeline_description , all elements are put into a + * Pipeline, which then is returned. * @throws GstException if the pipeline / element could not be created */ public static Element parseLaunch(String pipelineDescription) { return parseLaunch(pipelineDescription, null); } - + /** - * Create a new element based on command line syntax. - * - * error will contain an error message if an erroneous pipeline is specified. - * An error does not mean that the pipeline could not be constructed. - * - * @param pipelineDescription An array of strings containing the command line describing the pipeline. + * Create a new element based on command line syntax. + * + * error will contain an error message if an erroneous pipeline is + * specified. An error does not mean that the pipeline could not be + * constructed. + * + * @param pipelineDescription An array of strings containing the command + * line describing the pipeline. * @param errors a list that any errors will be added to - * @return a new element on success. + * @return a new element on success. * @throws GstException if the pipeline / element could not be created */ public static Element parseLaunch(String[] pipelineDescription, List errors) { - Pointer[] err = { null }; + Pointer[] err = {null}; Element pipeline = GSTPARSE_API.gst_parse_launchv(pipelineDescription, err); if (pipeline == null) { - throw new GstException(new GError(new GErrorStruct(err[0]))); + throw new GstException(extractError(err[0])); } - + // check for error - if (err[0] != null) { + if (err[0] != null) { if (errors != null) { - errors.add(new GError(new GErrorStruct(err[0]))); + errors.add(extractError(err[0])); } else { - logger.log(Level.WARNING, new GError(new GErrorStruct(err[0])).getMessage()); + LOG.log(Level.WARNING, extractError(err[0]).getMessage()); } } - + return pipeline; } - + /** - * Create a new element based on command line syntax. - * - * error will contain an error message if an erroneous pipeline is specified. - * An error does not mean that the pipeline could not be constructed. - * - * @param pipelineDescription An array of strings containing the command line describing the pipeline. - * @return a new element on success. + * Create a new element based on command line syntax. + * + * error will contain an error message if an erroneous pipeline is + * specified. An error does not mean that the pipeline could not be + * constructed. + * + * @param pipelineDescription An array of strings containing the command + * line describing the pipeline. + * @return a new element on success. * @throws GstException if the pipeline / element could not be created */ public static Element parseLaunch(String[] pipelineDescription) { return parseLaunch(pipelineDescription, null); } - + /** - * Creates a bin from a text bin description. - * - * This function allows creation of a bin based on the syntax used in the - * gst-launch utillity. - * - * @param binDescription the command line describing the bin - * @param ghostUnlinkedPads whether to create ghost pads for the bin from any unlinked pads - * @param errors list that any errors will be added to - * @return The new Bin. - * @throws GstException if the bin could not be created - */ - public static Bin parseBinFromDescription(String binDescription, boolean ghostUnlinkedPads, List errors) { + * Creates a bin from a text bin description. + * + * This function allows creation of a bin based on the syntax used in the + * gst-launch utillity. + * + * @param binDescription the command line describing the bin + * @param ghostUnlinkedPads whether to create ghost pads for the bin from + * any unlinked pads + * @param errors list that any errors will be added to + * @return The new Bin. + * @throws GstException if the bin could not be created + */ + public static Bin parseBinFromDescription(String binDescription, boolean ghostUnlinkedPads, List errors) { - Pointer[] err = { null }; + Pointer[] err = {null}; Bin bin = GSTPARSE_API.gst_parse_bin_from_description(binDescription, ghostUnlinkedPads, err); - - if (bin == null) { - throw new GstException(new GError(new GErrorStruct(err[0]))); + + if (bin == null) { + throw new GstException(extractError(err[0])); } // check for error - if (err[0] != null) { + if (err[0] != null) { if (errors != null) { - errors.add(new GError(new GErrorStruct(err[0]))); + errors.add(extractError(err[0])); } else { - logger.log(Level.WARNING, new GError(new GErrorStruct(err[0])).getMessage()); + LOG.log(Level.WARNING, extractError(err[0]).getMessage()); } } return bin; - } - - /** - * Creates a bin from a text bin description. - * - * This function allows creation of a bin based on the syntax used in the - * gst-launch utillity. - * - * @param binDescription the command line describing the bin - * @param ghostUnlinkedPads whether to create ghost pads for the bin from any unlinked pads - * @return The new Bin. - * @throws GstException if the bin could not be created - */ - public static Bin parseBinFromDescription(String binDescription, boolean ghostUnlinkedPads) { - return parseBinFromDescription(binDescription, ghostUnlinkedPads, null); - } - + } + + /** + * Creates a bin from a text bin description. + * + * This function allows creation of a bin based on the syntax used in the + * gst-launch utillity. + * + * @param binDescription the command line describing the bin + * @param ghostUnlinkedPads whether to create ghost pads for the bin from + * any unlinked pads + * @return The new Bin. + * @throws GstException if the bin could not be created + */ + public static Bin parseBinFromDescription(String binDescription, boolean ghostUnlinkedPads) { + return parseBinFromDescription(binDescription, ghostUnlinkedPads, null); + } + private static GError extractError(Pointer errorPtr) { + GErrorStruct struct = new GErrorStruct(errorPtr); + struct.read(); + GError err = new GError(struct.getCode(), struct.getMessage()); + GLIB_API.g_error_free(errorPtr); + return err; + } + /** * Waits for the gstreamer system to shutdown via a call to {@link #quit}. - *

For most gui programs, this is of little use. However, it can be - * a convenient way of keeping the main thread alive whilst gstreamer + *

+ * For most gui programs, this is of little use. However, it can be a + * convenient way of keeping the main thread alive whilst gstreamer * processing on other threads continues. */ public static void main() { @@ -370,7 +380,7 @@ public static void main() { quit = new CountDownLatch(1); } } - + /** * Schedules a task for execution on the gstreamer background * {@link java.util.concurrent.Executor}. @@ -380,78 +390,127 @@ public static void main() { public static void invokeLater(final Runnable task) { getExecutor().execute(task); } - - /** - * Executes a task on the gstreamer background - * {@link java.util.concurrent.Executor}, waiting until the task completes - * before returning. - * - * @param task the task to execute. - */ - public static void invokeAndWait(Runnable task) { - try { - getExecutorService().submit(task).get(); - } catch (Exception ex) { - throw new RuntimeException(ex.getCause()); - } - } - + + /** * Gets the current main context used (if any). - * + * * @return a main context. */ + // @TODO leaking lowlevel public static GMainContext getMainContext() { return mainContext; } - + /** * Initializes the GStreamer library. - *

This is a shortcut if no arguments are to be passed to gstreamer. + *

+ * This is a shortcut if no arguments are to be passed to gstreamer. + *

+ * This is equivalent to calling + * {@link #init(org.freedesktop.gstreamer.Version) } with + * {@link Version#BASELINE}, currently GStreamer 1.8. If you require + * features from a later version of GStreamer you should specify the + * required version. * * @throws org.freedesktop.gstreamer.GstException */ public static final void init() throws GstException { - init("unknown", new String[] {}); + init(Version.BASELINE, "gst1-java-core"); } - + /** * Initializes the GStreamer library. - *

This sets up internal path lists, registers built-in elements, and - * loads standard plugins. + *

+ * This is a shortcut if no arguments are to be passed to gstreamer. * + * @param requiredVersion + * @throws org.freedesktop.gstreamer.GstException + */ + public static final void init(Version requiredVersion) throws GstException { + init(requiredVersion, "gst1-java-core"); + } + + /** + * Initializes the GStreamer library. + *

+ * This sets up internal path lists, registers built-in elements, and loads + * standard plugins. *

- * This method should be called before calling any other GLib functions. If - * this is not an option, your program must initialise the GLib thread system - * using g_thread_init() before any other GLib functions are called. + * This is equivalent to calling + * {@link #init(org.freedesktop.gstreamer.Version, java.lang.String, java.lang.String...) } + * with {@link Version#BASELINE}, currently GStreamer 1.8. If you + * require features from a later version of GStreamer you should specify the + * required version. * + * @param progname the java program name. + * @param args the java argument list. + * @return the list of arguments with any gstreamer specific options + * stripped out. + * @throws org.freedesktop.gstreamer.GstException + */ + public static synchronized final String[] init(String progname, String... args) throws GstException { + return init(Version.BASELINE, progname, args); + } + + /** + * Initializes the GStreamer library. *

- * Note:

- * This method will throw a GstException if it fails. - * + * This sets up internal path lists, registers built-in elements, and loads + * standard plugins. + * + * @param requestedVersion the minimum requested GStreamer version. * @param progname the java program name. * @param args the java argument list. - * @return the list of arguments with any gstreamer specific options stripped - * out. + * @return the list of arguments with any gstreamer specific options + * stripped out. * @throws org.freedesktop.gstreamer.GstException */ - public static synchronized final String[] init(String progname, String[] args) throws GstException { + public static synchronized final String[] init(Version requestedVersion, + String progname, String... args) throws GstException { + + if (CHECK_VERSIONS) { + Version availableVersion = getVersion(); + if (requestedVersion.getMajor() != 1 || availableVersion.getMajor() != 1) { + throw new GstException("gst1-java-core only supports GStreamer 1.x"); + } + if (requestedVersion.getMinor() < 8) { + requestedVersion = new Version(1, 8); + } + if (!availableVersion.checkSatisfies(requestedVersion)) { + throw new GstException(String.format( + "The requested version of GStreamer is not available\nRequested : %s\nAvailable : %s\n", + requestedVersion, availableVersion)); + } + } + // // Only do real init the first time through // - if (initCount.getAndIncrement() > 0) { + if (INIT_COUNT.getAndIncrement() > 0) { + if (CHECK_VERSIONS) { + if (requestedVersion.getMinor() > minorVersion) { + minorVersion = (int) requestedVersion.getMinor(); + } + } return args; } + NativeArgs argv = new NativeArgs(progname, args); - Pointer[] error = { null }; + Pointer[] error = {null}; if (!GST_API.gst_init_check(argv.argcRef, argv.argvRef, error)) { - initCount.decrementAndGet(); - throw new GstException(new GError(new GErrorStruct(error[0]))); + INIT_COUNT.decrementAndGet(); + throw new GstException(extractError(error[0])); + } + + LOG.fine("after gst_init, argc=" + argv.argcRef.getValue()); + + Version runningVersion = getVersion(); + if (runningVersion.getMajor() != 1) { + LOG.warning("gst1-java-core only supports GStreamer 1.x"); } - logger.fine("after gst_init, argc=" + argv.argcRef.getValue()); - if (useDefaultContext) { mainContext = GMainContext.getDefaultContext(); executorService = new MainContextExecutorService(mainContext); @@ -461,183 +520,198 @@ public static synchronized final String[] init(String progname, String[] args) t } quit = new CountDownLatch(1); loadAllClasses(); + + if (CHECK_VERSIONS) { + minorVersion = requestedVersion.getMinor(); + } + return argv.toStringArray(); } - + /** - * Undoes all the initialization done in {@link #init}. - *

This will run any cleanup tasks, terminate any timers and other - * asynchronous tasks, and de-initialize the gstreamer library. + * Clean up any resources created by GStreamer in + * {@link #init(org.freedesktop.gstreamer.Version)}. + * + * It is normally not needed to call this function in a normal + * application as the resources will automatically be freed when the program + * terminates. This function is therefore mostly used by testsuites and + * other memory profiling tools. + * + * After this call GStreamer (including this method) should not be used + * anymore. */ public static synchronized final void deinit() { // // Only perform real shutdown if called as many times as Gst.init() is // - if (initCount.decrementAndGet() > 0) { + if (INIT_COUNT.decrementAndGet() > 0) { return; } // Perform any cleanup tasks for (Object task : shutdownTasks.toArray()) { ((Runnable) task).run(); } - + // Stop any more tasks/timers from being scheduled executorService.shutdown(); - + // Wake up the run thread. - quit(); - + quit(); + // Wait for tasks to complete. try { if (!executorService.awaitTermination(100, TimeUnit.MILLISECONDS)) { // Force-kill everything executorService.shutdownNow(); } - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + } mainContext = null; System.gc(); // Make sure any dangling objects are unreffed before calling deinit(). GST_API.gst_deinit(); } - + /** * Adds a task to be called when {@link Gst#deinit} is called. - *

This is used internally, and is not recommended for other uses. - * + *

+ * This is used internally, and is not recommended for other uses. + * * @param task the task to execute. */ - public static void addStaticShutdownTask(Runnable task) { + static void addStaticShutdownTask(Runnable task) { shutdownTasks.add(task); } - + /** * Instructs gstreamer-java to use the default main context. *

- * This may be useful if integration with the GTK main loop is desirable, - * as all {@link Bus} signals and timers will be executed in the context - * of the GTK main loop. + * This may be useful if integration with the GTK main loop is desirable, as + * all {@link Bus} signals and timers will be executed in the context of the + * GTK main loop. *

* For the majority of programs though, it is better to wrap the individual - * listeners in a proxy which executes the listener in the appropriate + * listeners in a proxy which executes the listener in the appropriate * context. - * + * * @param useDefault if true, use the default glib main context. */ public static void setUseDefaultContext(boolean useDefault) { useDefaultContext = useDefault; } - + + /** + * Checks that the version of GStreamer requested in init() satisfies the + * given version or throws an exception. + * + * @param major major version, only 1 is supported + * @param minor minor version required + * @throws GstException if the requested version support was not requested + */ + public static void checkVersion(int major, int minor) { + if (CHECK_VERSIONS && (major != 1 || minor > minorVersion)) { + throw new GstException("Not supported by requested GStreamer version"); + } + } + + /** + * Tests that the version of GStreamer requested in init() satisfies the + * given version. + * + * @param major major version, only 1 is supported + * @param minor minor version required + * @return boolean whether the version requirement can be satisfied + */ + public static boolean testVersion(int major, int minor) { + if (CHECK_VERSIONS && (major != 1 || minor > minorVersion)) { + return false; + } + return true; + } + // Make the gstreamer executor threads daemon, so they don't stop the main // program from exiting private static final ThreadFactory threadFactory = new ThreadFactory() { private final AtomicInteger counter = new AtomicInteger(0); - /** - * Determines if Gst has been started from an applet and returns - * it's parent group. - * - * This is to avoid a problem where the service thread is killed when - * an applet is destroyed. If multiple applets are active simultaneously, - * this could be a problem. - * - * @return Applet's parent ("main") thread group or null, if not - * running inside an applet - */ - private ThreadGroup getThreadGroup() { - ThreadGroup tg = Thread.currentThread().getThreadGroup(); - try { - Class atgClass = Class.forName("sun.applet.AppletThreadGroup"); - return atgClass.isInstance(tg) ? tg.getParent() : null; - } catch (ClassNotFoundException ex) { - return null; - } - } + + @Override public Thread newThread(Runnable task) { final String name = "gstreamer service thread " + counter.incrementAndGet(); - Thread t = new Thread(getThreadGroup(), task, name); + Thread t = new Thread(task, name); t.setDaemon(true); t.setPriority(Thread.NORM_PRIORITY); return t; } }; - - private static String getField(Class cls, String name) - throws SecurityException, IllegalArgumentException { - try { - Field f = cls.getDeclaredField(name); - int mod = f.getModifiers(); - if (Modifier.isStatic(mod) && Modifier.isFinal(mod) && f.getType().equals(String.class)) { - f.setAccessible(true); - return (String)f.get(null); + + private static synchronized void loadAllClasses() { + Stream.of(new GLib.Types(), + new Types(), + new Event.Types(), + new Message.Types(), + new Query.Types(), + new Controllers(), + new Elements(), + new WebRTC.Types(), + new Video.Types()) + .flatMap(NativeObject.TypeProvider::types) + .forEachOrdered(GstTypes::register); + if (!DISABLE_EXTERNAL) { + try { + ServiceLoader extProviders + = ServiceLoader.load(NativeObject.TypeProvider.class); + extProviders.iterator().forEachRemaining(prov + -> prov.types().forEachOrdered(GstTypes::register)); + } catch (Throwable t) { + LOG.log(Level.SEVERE, "Error during external types registration", t); } - } catch (NoSuchFieldException e) { - } catch (IllegalAccessException e) { - e.printStackTrace(); } - return null; } - - @SuppressWarnings("unchecked") - public static synchronized void registerClass(Class cls) { - String value = null; - value = getField(cls, "GTYPE_NAME"); - if (value != null) - GstTypes.registerType(cls, value); - value = getField(cls, "GST_NAME"); - if (Element.class.isAssignableFrom(cls) && value != null) - ElementFactory.registerElement((Class)cls, value); + + public static class Types implements NativeObject.TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + registration(Bin.class, Bin.GTYPE_NAME, Bin::new), + registration(Buffer.class, Buffer.GTYPE_NAME, Buffer::new), + registration(BufferPool.class, BufferPool.GTYPE_NAME, BufferPool::new), + registration(Bus.class, Bus.GTYPE_NAME, Bus::new), + registration(Caps.class, Caps.GTYPE_NAME, Caps::new), + registration(Clock.class, Clock.GTYPE_NAME, Clock::new), + registration(Context.class, Context.GTYPE_NAME, Context::new), + registration(DateTime.class, DateTime.GTYPE_NAME, DateTime::new), + registration(Element.class, Element.GTYPE_NAME, Element::new), + registration(ElementFactory.class, ElementFactory.GTYPE_NAME, ElementFactory::new), + registration(GhostPad.class, GhostPad.GTYPE_NAME, GhostPad::new), + registration(Pad.class, Pad.GTYPE_NAME, Pad::new), + registration(PadTemplate.class, PadTemplate.GTYPE_NAME, PadTemplate::new), + registration(Pipeline.class, Pipeline.GTYPE_NAME, Pipeline::new), + registration(Plugin.class, Plugin.GTYPE_NAME, Plugin::new), + registration(PluginFeature.class, PluginFeature.GTYPE_NAME, PluginFeature::new), + registration(Promise.class, Promise.GTYPE_NAME, Promise::new), + registration(Registry.class, Registry.GTYPE_NAME, Registry::new), + registration(SDPMessage.class, SDPMessage.GTYPE_NAME, SDPMessage::new), + registration(Sample.class, Sample.GTYPE_NAME, Sample::new), + registration(Structure.class, Structure.GTYPE_NAME, Structure::new), + registration(TagList.class, TagList.GTYPE_NAME, TagList::new) + ); + } + } - @SuppressWarnings("unchecked") - private static synchronized void loadAllClasses() { - for(Class cls : nativeClasses) { - registerClass(cls); - } + /** + * Annotation on classes, methods or fields to show the required GStreamer + * version. This should particularly be used where the version required is + * higher than the current baseline supported version (GStreamer 1.8) + */ + @Retention(RetentionPolicy.RUNTIME) + @Documented + public static @interface Since { + + public int major() default 1; + + public int minor(); } - // to generate the list we use: - // egrep -rl "GST_NAME|GTYPE_NAME" src 2>/dev/null | egrep -v ".svn|Gst.java" | sort - // even though the best would be all subclasses of NativeObject - @SuppressWarnings("rawtypes") - private static List> nativeClasses - = Arrays.>asList( - GDate.class, - GInetAddress.class, - GSocket.class, - GSocketAddress.class, - GInetSocketAddress.class, - TagList.class, - // ----------- Base ------------- - Buffer.class, - BufferPool.class, - Bus.class, - Caps.class, - Clock.class, - DateTime.class, - Element.class, - ElementFactory.class, - Event.class, - GhostPad.class, - Message.class, - Pad.class, - PadTemplate.class, - Plugin.class, - PluginFeature.class, - Promise.class, - Query.class, - Registry.class, - SDPMessage.class, - Sample.class, - WebRTCSessionDescription.class, - // ----------- Elements ------------- - AppSink.class, - AppSrc.class, - BaseSrc.class, - BaseSink.class, - BaseTransform.class, - Bin.class, - DecodeBin.class, - Pipeline.class, - PlayBin.class, - URIDecodeBin.class, - WebRTCBin.class - ); + } diff --git a/src/org/freedesktop/gstreamer/GstException.java b/src/org/freedesktop/gstreamer/GstException.java index 6c9213a3..300f3fa0 100644 --- a/src/org/freedesktop/gstreamer/GstException.java +++ b/src/org/freedesktop/gstreamer/GstException.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -19,10 +20,13 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.GError; +import org.freedesktop.gstreamer.glib.GLibException; + /** - * Thrown when a gstreamer error occurs. + * Thrown when a GStreamer error occurs. */ -public class GstException extends RuntimeException { +public class GstException extends GLibException { private static final long serialVersionUID = -7413580400835548033L; @@ -41,7 +45,8 @@ public GstException() { public GstException(String msg) { super(msg); } + public GstException(GError error) { - super(error.getMessage()); + super(error); } } diff --git a/src/org/freedesktop/gstreamer/GstIterator.java b/src/org/freedesktop/gstreamer/GstIterator.java index fbacb101..222500c3 100644 --- a/src/org/freedesktop/gstreamer/GstIterator.java +++ b/src/org/freedesktop/gstreamer/GstIterator.java @@ -1,7 +1,5 @@ /* - * Copyright (c) 2016 Neil C Smith - * Copyright (c) 2009 Levente Farkas - * Copyright (c) 2007 Wayne Meissner + * Copyright (c) 2021 Neil C Smith * * This file is part of gstreamer-java. * @@ -17,89 +15,34 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import com.sun.jna.Pointer; - -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.lowlevel.GstIteratorPtr; +import org.freedesktop.gstreamer.lowlevel.GstTypes; import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GValueAPI; -import org.freedesktop.gstreamer.lowlevel.GstTypes; -import org.freedesktop.gstreamer.lowlevel.NativeObject; import static org.freedesktop.gstreamer.lowlevel.GstIteratorAPI.GSTITERATOR_API; /** - * + * Utility class for working with gstiterator. */ -class GstIterator extends NativeObject implements java.lang.Iterable { - private final GType gtype; - - GstIterator(Pointer ptr, Class cls) { - super(initializer(ptr)); - gtype = GstTypes.typeFor(cls); - } - - @Override - public Iterator iterator() { - return new IteratorImpl(); - } +class GstIterator { - @Override - protected void disposeNativeHandle(Pointer ptr) { - GSTITERATOR_API.gst_iterator_free(ptr); - } - - public List asList() { - List list = new LinkedList(); - for (T t : this) { - list.add(t); + static List asList(GstIteratorPtr iter, Class type) { + final GType gtype = GstTypes.typeFor(type); + final GValueAPI.GValue gValue = new GValueAPI.GValue(gtype); + List list = new ArrayList<>(); + while (GSTITERATOR_API.gst_iterator_next(iter, gValue) == 1) { + list.add((T) gValue.getValue()); } - return Collections.unmodifiableList(list); + gValue.reset(); + GSTITERATOR_API.gst_iterator_free(iter); + return list; } - class IteratorImpl implements java.util.Iterator { - - final GValueAPI.GValue gValue; - - T next; - - IteratorImpl() { - gValue = new GValueAPI.GValue(gtype); - next = getNext(); - } - - private T getNext() { - if (GSTITERATOR_API.gst_iterator_next(handle(), gValue) == 1) { - T result = (T) gValue.getValue(); - // reset cached structure or we get a memory leak - gValue.reset(); - return result; - } else { - return null; - } - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public T next() { - T result = next; - next = getNext(); - return result; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Items cannot be removed."); - } - } } diff --git a/src/org/freedesktop/gstreamer/GstObject.java b/src/org/freedesktop/gstreamer/GstObject.java index e1ea02ae..972576c5 100644 --- a/src/org/freedesktop/gstreamer/GstObject.java +++ b/src/org/freedesktop/gstreamer/GstObject.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen @@ -19,164 +20,302 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import java.util.EventListener; -import java.util.EventListenerProxy; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Logger; import com.sun.jna.Pointer; +import java.util.logging.Logger; +import org.freedesktop.gstreamer.glib.GObject; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GObjectAPI; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GstControlBindingPtr; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; +import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; +import static org.freedesktop.gstreamer.lowlevel.GObjectAPI.GOBJECT_API; +import static org.freedesktop.gstreamer.lowlevel.GValueAPI.GVALUE_API; +import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; import static org.freedesktop.gstreamer.lowlevel.GstObjectAPI.GSTOBJECT_API; +import static org.freedesktop.gstreamer.lowlevel.GstValueAPI.GSTVALUE_API; /** * Base class for the GStreamer object hierarchy - * + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstObject.html + *

* GstObject provides a root for the object hierarchy tree filed in by the - * GStreamer library. It is currently a thin wrapper on top of - * {@link GObject}. It is an abstract class that is not very usable on its own. + * GStreamer library. It is currently a thin wrapper on top of {@link GObject}. + * It is an abstract class that is not very usable on its own. * */ public class GstObject extends GObject { - private static Logger logger = Logger.getLogger(GstObject.class.getName()); + + private static Logger LOG = Logger.getLogger(GstObject.class.getName()); + + private final Handle handle; /** * Wraps an underlying C GstObject with a Java proxy - * + * * @param init Initialization data */ - public GstObject(Initializer init) { - super(init); + protected GstObject(Initializer init) { + this(new Handle(init.ptr.as(GstObjectPtr.class, GstObjectPtr::new), init.ownsHandle), init.needRef); } - protected static Initializer initializer(Pointer ptr) { - return initializer(ptr, true, true); + + protected GstObject(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; } - protected static Initializer initializer(Pointer ptr, boolean needRef) { - return initializer(ptr, needRef, true); + + /** + * Set the value of a GstObject property from a String representation. + *

+ * The data value is deserialized using gst_value_deserialize. + * + * @param property the property to set + * @param data the value as a valid String representation + * @throws IllegalArgumentException if the data cannot be deserialized to + * the required type. + */ + public void setAsString(String property, String data) { + GObjectAPI.GParamSpec propertySpec = findProperty(property); + if (propertySpec == null) { + throw new IllegalArgumentException("Unknown property: " + property); + } + final GType propType = propertySpec.value_type; + + GValue propValue = new GValue(); + GVALUE_API.g_value_init(propValue, propType); + + boolean success = GSTVALUE_API.gst_value_deserialize(propValue, data); + + if (success) { + GOBJECT_API.g_param_value_validate(propertySpec, propValue); + GOBJECT_API.g_object_set_property(this, property, propValue); + } + + GVALUE_API.g_value_unset(propValue); // Release any memory + + if (!success) { + throw new IllegalArgumentException( + "Unable to deserialize data to required type: " + + propType.getTypeName()); + } + } - + /** - * Steal the native peer from another GstObject. - * After calling this, the victim object is disconnected from the native object - * and any attempt to use it will throw an exception. - * - * @param victim The GstObject to takeover. - * @return An Initializer that can be passed to {@link #GstObject} + * Get the value of a GstObject property as a serialized String + * representation of its value. + *

+ * The data value is serialized using gst_value_serialize. + * + * @param property the property to get + * @return serialized String value of property */ - protected static Initializer steal(GstObject victim) { - Initializer init = new Initializer(victim.handle(), false, true); - victim.invalidate(); - return init; + public String getAsString(String property) { + GObjectAPI.GParamSpec propertySpec = findProperty(property); + if (propertySpec == null) { + throw new IllegalArgumentException("Unknown property: " + property); + } + final GType propType = propertySpec.value_type; + GValue propValue = new GValue(); + GVALUE_API.g_value_init(propValue, propType); + GOBJECT_API.g_object_get_property(this, property, propValue); + Pointer ptr = GSTVALUE_API.gst_value_serialize(propValue); + String ret = ptr.getString(0); + GLIB_API.g_free(ptr); + return ret; + } + + private GObjectAPI.GParamSpec findProperty(String propertyName) { + Pointer ptr = GOBJECT_API.g_object_class_find_property( + getRawPointer().getPointer(0), propertyName); + if (ptr == null) { + return null; + } + return new GObjectAPI.GParamSpec(ptr); } + /** * Sets the name of this object, or gives this object a guaranteed unique * name (if name is null). - * - * Returns: TRUE if the name could be set. Since Objects that have - * a parent cannot be renamed, this function returns FALSE in those - * cases. + * + * Returns: TRUE if the name could be set. Since Objects that have a parent + * cannot be renamed, this function returns FALSE in those cases. * * MT safe. - * + * * @param name new name of object - * @return true if the name was set. Since Objects that have - * a parent cannot be renamed, this function returns false in those - * cases. + * @return true if the name was set. Since Objects that have a parent cannot + * be renamed, this function returns false in those cases. */ public boolean setName(String name) { - logger.entering("GstObject", "setName", name); + LOG.entering("GstObject", "setName", name); return GSTOBJECT_API.gst_object_set_name(this, name); } - + /** * Returns a copy of the name of this object. - * + * * For a nameless object, this returns null. * * @return the name of this object. */ public String getName() { - logger.entering("GstObject", "getName"); + LOG.entering("GstObject", "getName"); return GSTOBJECT_API.gst_object_get_name(this); } - - @Override - public String toString() { - return String.format("%s: [%s]", getClass().getSimpleName(), getName()); + + /** + * Returns this object's parent, if there is one. + * + * @return parent or null + */ + public GstObject getParent() { + return GSTOBJECT_API.gst_object_get_parent(this); + } + + /** + * Returns a suggestion for timestamps where buffers should be split to get + * best controller results. + * + * @return the suggested timestamp or {@link ClockTime#NONE} if no + * control-rate was set. + */ + public long suggestNextSync() { + return GSTOBJECT_API.gst_object_suggest_next_sync(handle.getPointer()); } - protected void ref() { - GSTOBJECT_API.gst_object_ref(this); + + /** + * Sets the properties of the object, according to the {@link ControlSource} + * that (maybe) handle them and for the given timestamp. + *

+ * If this function fails, it is most likely the application developers + * fault. Most probably the control sources are not setup correctly. + * + * @param timestamp the time that should be processed + * @return true if the controller values have been applied to the object + * properties + */ + public boolean syncValues(long timestamp) { + return GSTOBJECT_API.gst_object_sync_values(handle.getPointer(), timestamp); } - protected void unref() { - GSTOBJECT_API.gst_object_unref(this); + + /** + * Check if this object has active controlled properties. + * + * @return TRUE if the object has active controlled properties + */ + public boolean hasActiveControlBindings() { + return GSTOBJECT_API.gst_object_has_active_control_bindings(handle.getPointer()); } - - @Override - protected void sink() { - GSTOBJECT_API.gst_object_ref_sink(this); + + /** + * This function is used to disable all controlled properties of the object + * for some time, i.e. {@link #syncValues(long) } will do nothing. + * + * @param disabled whether to disable the controllers or not + */ + public void setControlBindingsDisabled(boolean disabled) { + GSTOBJECT_API.gst_object_set_control_bindings_disabled(handle.getPointer(), disabled); } - - public GstObject getParent() { - return GSTOBJECT_API.gst_object_get_parent(this); + + /** + * This function is used to disable the control bindings on a property for + * some time, i.e. {@link #syncValues(long) } will do nothing for the + * property. + * + * @param propertyName property to disable + * @param disabled whether to disable the controller or not + */ + public void setControlBindingDisabled(String propertyName, boolean disabled) { + GSTOBJECT_API.gst_object_set_control_binding_disabled(handle.getPointer(), propertyName, disabled); } - + /** - * Adds an {@link EventListenerProxy} on this object. - * This is used by subclasses that wish to map between java style event listeners - * and gstreamer signals. - * - * @param listenerClass Class of the listener being added. - * @param listener The listener being added. - * @param proxy Proxy for the event listener. + * Attach a {@link ControlBinding } to this object. If there was already a + * binding for this property it will be replaced. + * + * @param binding the ControlBinding that should be used + * @throws IllegalStateException if the binding has not been setup for this + * object */ - protected synchronized void addListenerProxy(Class listenerClass, EventListener listener, EventListenerProxy proxy) { - Map map = getListenerMap().get(listenerClass); - /* - * Create the map for this class if it doesn't exist - */ - if (map == null) { - map = new HashMap(); - getListenerMap().put(listenerClass, map); + public void addControlBinding(ControlBinding binding) { + GstControlBindingPtr bindingPtr = Natives.getPointer(binding) + .as(GstControlBindingPtr.class, GstControlBindingPtr::new); + boolean ok = GSTOBJECT_API.gst_object_add_control_binding(handle.getPointer(), bindingPtr); + if (!ok) { + throw new IllegalStateException(); } - map.put(listener, proxy); } - + /** - * Removes an {@link EventListenerProxy} from this object. - * This is used by subclasses that wish to map between java style event listeners - * and gstreamer signals. - * - * @param listenerClass The class of listener the proxy was added for. - * @param listener The listener the proxy was added for. - * @return The proxy that was removed, or null if no proxy was found. + * Gets the corresponding {@link ControlBinding} for the property. + * + * @param propertyName name of the property + * @return control binding for the property or NULL if the property is not + * controlled */ - protected synchronized EventListenerProxy removeListenerProxy(Class listenerClass, EventListener listener) { - Map map = getListenerMap().get(listenerClass); - if (map == null) { - return null; + public ControlBinding getControlBinding(String propertyName) { + GstControlBindingPtr ptr = GSTOBJECT_API.gst_object_get_control_binding( + handle.getPointer(), propertyName); + return ptr == null ? null : Natives.callerOwnsReturn(ptr, ControlBinding.class); + } + + /** + * Removes the corresponding {@link ControlBinding }. + * + * @param binding the binding to remove + * @return true if the binding could be removed + */ + public boolean removeControlBinding(ControlBinding binding) { + GstControlBindingPtr bindingPtr = Natives.getPointer(binding) + .as(GstControlBindingPtr.class, GstControlBindingPtr::new); + return GSTOBJECT_API.gst_object_remove_control_binding(handle.getPointer(), bindingPtr); + } + + @Override + public String toString() { + return String.format("%s: [%s]", getClass().getSimpleName(), getName()); + } +// protected static Initializer initializer(Pointer ptr) { +// return initializer(ptr, true, true); +// } +// +// protected static Initializer initializer(Pointer ptr, boolean needRef) { +// return initializer(ptr, needRef, true); +// } + + protected static class Handle extends GObject.Handle { + + public Handle(GstObjectPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); } - EventListenerProxy proxy = map.remove(listener); - - /* - * Reclaim memory if this listener class is no longer used - */ - if (map.isEmpty()) { - listenerMap.remove(listenerClass); - if (listenerMap.isEmpty()) { - listenerMap = null; - } + + @Override + protected void ref() { + GSTOBJECT_API.gst_object_ref(getPointer()); } - return proxy; - } - private Map, Map> getListenerMap() { - if (listenerMap == null) { - listenerMap = new HashMap, Map>(); + + @Override + protected void sink() { + GSTOBJECT_API.gst_object_ref_sink(getPointer()); } - return listenerMap; + + @Override + protected void unref() { + GSTOBJECT_API.gst_object_unref(getPointer()); + } + + @Override + protected GstObjectPtr getPointer() { + return (GstObjectPtr) super.getPointer(); + } + } - - private Map, Map> listenerMap; } diff --git a/src/org/freedesktop/gstreamer/Message.java b/src/org/freedesktop/gstreamer/Message.java deleted file mode 100644 index d7a8144c..00000000 --- a/src/org/freedesktop/gstreamer/Message.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2007, 2008 Wayne Meissner - * Copyright (C) 2004 Wim Taymans - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import org.freedesktop.gstreamer.lowlevel.GstMessageAPI; -import org.freedesktop.gstreamer.lowlevel.ReferenceManager; -import org.freedesktop.gstreamer.lowlevel.annotations.HasSubtype; - -import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; - -/** - * Lightweight objects to signal the occurrence of pipeline events. - * - *

Messages are implemented as a subclass of {@link MiniObject} with a generic - * {@link Structure} as the content. This allows for writing custom messages without - * requiring an API change while allowing a wide range of different types - * of messages. - * - *

Messages are posted by objects in the pipeline and are passed to the - * application using the {@link Bus}. - - * The basic use pattern of posting a message on a Bus is as follows: - * - * - * Posting a Message - * - * bus.post(new EOSMessage(source)); - * - * - * - * An {@link Element} usually posts messages on the bus provided by the parent - * container using {@link Element#postMessage postMessage()}. - */ -@HasSubtype -public class Message extends MiniObject { - public static final String GTYPE_NAME = "GstMessage"; - - protected GstMessageAPI.MessageStruct messageStruct; - - /** - * Creates a new instance of Message. - * - * @param init internal initialization data. - */ - public Message(Initializer init) { - super(init); - messageStruct = new GstMessageAPI.MessageStruct(handle()); - } - - /** - * Gets the Element that posted this message. - * - * @return the element that posted the message. - */ - public GstObject getSource() { - return (GstObject)messageStruct.readField("src"); - } - - /** - * Gets the structure containing the data in this message. - * - * @return a structure. - */ - public Structure getStructure() { - return ReferenceManager.addKeepAliveReference(GSTMESSAGE_API.gst_message_get_structure(this), this); - } - - /** - * Gets the type of this message. - * - * @return the message type. - */ - public MessageType getType() { - MessageType type = (MessageType)messageStruct.readField("type"); - return type; - } - -} diff --git a/src/org/freedesktop/gstreamer/Meta.java b/src/org/freedesktop/gstreamer/Meta.java new file mode 100644 index 00000000..e0493dad --- /dev/null +++ b/src/org/freedesktop/gstreamer/Meta.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import java.util.Objects; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GstMetaPtr; + +/** + * Base for all metadata types added to a Buffer. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstmeta.html + *

+ */ +public class Meta extends NativeObject { + + /** + * Create Meta from Initializer. + * + * @param init initializer. + */ + protected Meta(Initializer init) { + this(new Handle(init.ptr.as(GstMetaPtr.class, GstMetaPtr::new), + init.ownsHandle)); + } + + /** + * Create Meta from Handle. + * + * @param handle native object handle. + */ + protected Meta(Handle handle) { + super(handle); + } + + @Override + public String toString() { + GstMetaPtr pointer = (GstMetaPtr)this.getPointer(); + return "[meta : gType=" + pointer.getGType() + "]"; + } + + /** + * NativeObject.Handle implementation. + */ + protected static class Handle extends NativeObject.Handle { + + /** + * Create Handle. + * + * @param ptr pointer to underlying native GstMeta. + * @param ownsHandle + */ + public Handle(GstMetaPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + } + + } + + /** + * API for Meta subclass. Used for querying from Buffer. + *

+ * The relevant API will usually be available as a public static final field + * on the implementation class. + *

+ * The API type reflects two distinct types (api and implementation) used in + * the underlying GStreamer GstMetaInfo. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstmeta.html#GstMetaInfo + *

+ * + * @param implementation type + */ + public static final class API { + + private final Class implClass; + private final String apiTypeName; + + private GType apiType; + + /** + * Create an API for the given implementation type. + * + * @param impl class implementing the API, must be registered to the + * underlying implementation GType + * @param api name of the underlying API GType + */ + public API(Class impl, String api) { + this.implClass = Objects.requireNonNull(impl); + this.apiTypeName = Objects.requireNonNull(api); + apiType = GType.INVALID; + } + + Class getImplClass() { + return implClass; + } + + GType getAPIGType() { + GType type = apiType; + if (type == GType.INVALID) { + type = GType.valueOf(apiTypeName); + apiType = type; + } + return type; + } + + } +} diff --git a/src/org/freedesktop/gstreamer/MetaFlags.java b/src/org/freedesktop/gstreamer/MetaFlags.java new file mode 100644 index 00000000..35e0d244 --- /dev/null +++ b/src/org/freedesktop/gstreamer/MetaFlags.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import org.freedesktop.gstreamer.glib.NativeFlags; + + /** + * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstmeta.html#GstMetaFlags + */ +/*public*/ enum MetaFlags implements NativeFlags { + +// /** +// * no flags +// */ +// GST_META_FLAG_NONE(0), + /** + * metadata should not be modified + */ + GST_META_FLAG_READONLY(1 << 0), + /** + * metadata is managed by a bufferpool + */ + GST_META_FLAG_POOLED(1 << 1), + /** + * metadata should not be removed + */ + GST_META_FLAG_LOCKED(1 << 2), +// /** +// * additional flags can be added starting from this flag. +// */ +// GST_META_FLAG_LAST(1 << 16) +// + ; + + private final int value; + + MetaFlags(int value) { + this.value = value; + } + + /** + * Gets the integer value of the enum. + * + * @return The integer value for this enum. + */ + @Override + public int intValue() { + return value; + } + + +} diff --git a/src/org/freedesktop/gstreamer/MiniObject.java b/src/org/freedesktop/gstreamer/MiniObject.java index 94321857..e4945716 100644 --- a/src/org/freedesktop/gstreamer/MiniObject.java +++ b/src/org/freedesktop/gstreamer/MiniObject.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2009 Levente Farkas @@ -18,55 +19,52 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import com.sun.jna.Native; -import com.sun.jna.Pointer; - -import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.MiniObjectStruct; -import org.freedesktop.gstreamer.lowlevel.RefCountedObject; +import org.freedesktop.gstreamer.glib.RefCountedObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; import static org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.GSTMINIOBJECT_API; +import org.freedesktop.gstreamer.lowlevel.GstMiniObjectPtr; /** * Lightweight base class for the GStreamer object hierarchy - * - * MiniObject is a baseclass like {@link GObject}, but has been stripped down of - * features to be fast and small. - * It offers sub-classing and ref-counting in the same way as GObject does. - * It has no properties and no signal-support though. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/gstreamer-GstMiniObject.html + *

+ * MiniObject is a simple structure that can be used to implement refcounted + * types. */ -public class MiniObject extends RefCountedObject { +public abstract class MiniObject extends RefCountedObject { + /** * Creates a new instance of MiniObject */ - public MiniObject(Initializer init) { - super(init); + protected MiniObject(Initializer init) { + this(new Handle(init.ptr.as(GstMiniObjectPtr.class, GstMiniObjectPtr::new), + init.ownsHandle), init.needRef); } - - /** - * Gives the type value. - */ - public static GType getType(Pointer ptr) { - // Quick getter for GType without allocation - // same as : new MiniObjectStruct(ptr).type - if (Native.SIZE_T_SIZE == 8) { - return GType.valueOf(ptr.getLong(0)); - } else if (Native.SIZE_T_SIZE == 4) { - return GType.valueOf( ((long) ptr.getInt(0)) & 0xffffffffL ); - } else { - throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); - } + + protected MiniObject(Handle handle, boolean needRef) { + super(handle, needRef); } /** - * If mini_object has the LOCKABLE flag set, check if the current EXCLUSIVE lock on object is the only one, this means that changes to the object will not be visible to any other object. + * If mini_object has the LOCKABLE flag set, check if the current EXCLUSIVE + * lock on object is the only one, this means that changes to the object + * will not be visible to any other object. * - *

If the LOCKABLE flag is not set, check if the refcount of mini_object is exactly 1, meaning that no other reference exists to the object and that the object is therefore writable. + *

+ *

If the LOCKABLE flag is not set, check if the refcount of mini_object + * is exactly 1, meaning that no other reference exists to the object and + * that the object is therefore writable. * - *

Modification of a mini-object should only be done after verifying that it is writable. + *

+ *

Modification of a mini-object should only be done after verifying + * that it is writable. * * @return true if the object is writable. */ @@ -76,7 +74,8 @@ public boolean isWritable() { /** * Makes a writable instance of this MiniObject. - *

The result is cast to subclass. + *

+ * The result is cast to subclass. * * @return a writable version (possibly a duplicate) of this MiniObject. */ @@ -85,7 +84,7 @@ protected T makeWritable() { if (result == null) { throw new NullPointerException("Could not make " + this.getClass().getSimpleName() + " writable"); } - return (T)result; + return (T) result; } /** @@ -98,28 +97,40 @@ public T copy() { if (result == null) { throw new NullPointerException("Could not make a copy of " + this.getClass().getSimpleName()); } - return (T)result; - } - - @Override - protected void ref() { - GSTMINIOBJECT_API.gst_mini_object_ref(this); - } - - @Override - protected void unref() { - GSTMINIOBJECT_API.gst_mini_object_unref(this); + return (T) result; } public int getRefCount() { - final MiniObjectStruct struct = new MiniObjectStruct(handle()); + final MiniObjectStruct struct = new MiniObjectStruct(getRawPointer()); return (Integer) struct.readField("refcount"); } - @Override - protected void disposeNativeHandle(Pointer ptr) { - if (ownsHandle.get()) { - GSTMINIOBJECT_API.gst_mini_object_unref(ptr); + protected static class Handle extends RefCountedObject.Handle { + + public Handle(GstMiniObjectPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GSTMINIOBJECT_API.gst_mini_object_unref( + ptr.as(GstMiniObjectPtr.class, GstMiniObjectPtr::new)); + } + + @Override + protected void ref() { + GSTMINIOBJECT_API.gst_mini_object_ref(getPointer()); } + + @Override + protected void unref() { + GSTMINIOBJECT_API.gst_mini_object_unref(getPointer()); + } + + @Override + protected GstMiniObjectPtr getPointer() { + return (GstMiniObjectPtr) super.getPointer(); + } + } } diff --git a/src/org/freedesktop/gstreamer/MiniObjectFlags.java b/src/org/freedesktop/gstreamer/MiniObjectFlags.java index def52f76..ea6f24ba 100644 --- a/src/org/freedesktop/gstreamer/MiniObjectFlags.java +++ b/src/org/freedesktop/gstreamer/MiniObjectFlags.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,38 +16,41 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.glib.NativeFlags; /** - * + * Flags for {@link MiniObject } */ -public enum MiniObjectFlags implements IntegerEnum { +public enum MiniObjectFlags implements NativeFlags { /** - * the object can be locked and unlocked with gst_mini_object_lock() and gst_mini_object_unlock() - * */ + * the object can be locked and unlocked with gst_mini_object_lock() and + * gst_mini_object_unlock() + * + */ LOCKABLE(1 << 0), - /** - * the object is permanently locked in READONLY mode. Only read locks can be performed on the object. + * the object is permanently locked in READONLY mode. Only read locks can be + * performed on the object. */ - LOCK_READONLY(1<<1), - + LOCK_READONLY(1 << 1), /** - * the object is expected to stay alive even after gst_deinit() has been called and so should be ignored by leak detection tools. (Since 1.10) + * the object is expected to stay alive even after gst_deinit() has been + * called and so should be ignored by leak detection tools. (Since 1.10) + */ + MAY_BE_LEAKED(1 << 2), + /** + * The last valid MiniObject flag */ - MAY_BE_LEAKED(1<<2), - - /** The last valid MiniObject flag */ LAST(1 << 4); - + + private final int value; private MiniObjectFlags(int value) { this.value = value; } - private final int value; + public int intValue() { return value; } diff --git a/src/org/freedesktop/gstreamer/Pad.java b/src/org/freedesktop/gstreamer/Pad.java index 378c1d34..ab85a378 100644 --- a/src/org/freedesktop/gstreamer/Pad.java +++ b/src/org/freedesktop/gstreamer/Pad.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2020 Neil C Smith * Copyright (C) 2018 Antonio Morales * Copyright (C) 2014 Tom Greenwood * Copyright (C) 2009 Tamas Korodi @@ -20,193 +21,211 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; +import com.sun.jna.NativeLong; +import org.freedesktop.gstreamer.event.Event; import com.sun.jna.Pointer; +import java.util.HashSet; +import java.util.Set; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; import org.freedesktop.gstreamer.lowlevel.GstPadProbeInfo; import org.freedesktop.gstreamer.lowlevel.GstPadAPI; import static org.freedesktop.gstreamer.lowlevel.GstPadAPI.GSTPAD_API; +import org.freedesktop.gstreamer.lowlevel.GstPadPtr; /** * Object contained by elements that allows links to other elements. *

- * A {@link Element} is linked to other elements via "pads", which are extremely - * light-weight generic link points. - * After two pads are retrieved from an element with {@link Element#getPad}, - * the pads can be link with {@link #link}. (For quick links, - * you can also use {@link Element#link}, which will make the obvious - * link for you if it's straightforward.) + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPad.html + *

+ * An {@link Element} is linked to other elements via "pads", which are extremely + * light-weight generic link points. After two pads are retrieved from an + * element with {@link Element#getPad}, the pads can be link with {@link #link}. + * (For quick links, you can also use {@link Element#link}, which will make the + * obvious link for you if it's straightforward.) *

- * Pads are typically created from a {@link PadTemplate} with {@link #Pad(PadTemplate, String)}. + * Pads are typically created from a {@link PadTemplate} with + * {@link #Pad(PadTemplate, String)}. *

* Pads have {@link Caps} attached to it to describe the media type they are - * capable of dealing with. {@link #getCaps} and {@link #setCaps} are - * used to manipulate the caps of the pads. - * Pads created from a pad template cannot set capabilities that are - * incompatible with the pad template capabilities. + * capable of dealing with. {@link #queryCaps} and {@link #setCaps} are used to + * manipulate the caps of the pads. Pads created from a pad template cannot set + * capabilities that are incompatible with the pad template capabilities. *

- * Pads without pad templates can be created with gst_pad_new(), - * which takes a direction and a name as an argument. If the name is NULL, - * then a guaranteed unique name will be assigned to it. + * Pads without pad templates can be created with gst_pad_new(), which takes a + * direction and a name as an argument. If the name is NULL, then a guaranteed + * unique name will be assigned to it. *

* {@link #getParentElement} will retrieve the Element that owns the pad. *

* An Element creating a pad will typically use the various - * gst_pad_set_*_function() calls to register callbacks for various events - * on the pads. + * gst_pad_set_*_function() calls to register callbacks for various events on + * the pads. *

- * GstElements will use gst_pad_push() and gst_pad_pull_range() to push out - * or pull in a buffer. + * GstElements will use gst_pad_push() and gst_pad_pull_range() to push out or + * pull in a buffer. *

* To send an Event on a pad, use {@link #sendEvent} and {@link #pushEvent}. - * + * * @see PadTemplate * @see Element * @see Event */ public class Pad extends GstObject { + public static final String GTYPE_NAME = "GstPad"; + private static final int EVENT_HAS_INFO_MASK = GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_UPSTREAM; + private final Handle handle; + /** * Creates a new instance of Pad */ - public Pad(Initializer init) { - super(init); + Pad(Initializer init) { + this(new Handle(init.ptr.as(GstPadPtr.class, GstPadPtr::new), init.ownsHandle), init.needRef); } + + private Pad(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; + } + /** - * Creates a new pad with the given name in the given direction. - * If name is null, a guaranteed unique name (across all pads) - * will be assigned. - * + * Creates a new pad with the given name in the given direction. If name is + * null, a guaranteed unique name (across all pads) will be assigned. + * * @param name The name of the new pad. * @param direction The direction of the new pad. */ public Pad(String name, PadDirection direction) { - this(initializer(GSTPAD_API.ptr_gst_pad_new(name, direction))); + this(Natives.initializer(GSTPAD_API.ptr_gst_pad_new(name, direction), false)); } - + /** * Creates a new pad with the given name from the given template. - * - * If name is null, a guaranteed unique name (across all pads) - * will be assigned. + * + * If name is null, a guaranteed unique name (across all pads) will be + * assigned. * * @param template The pad template to use. * @param name The name of the new pad. */ public Pad(PadTemplate template, String name) { - this(initializer(GSTPAD_API.ptr_gst_pad_new_from_template(template, name))); + this(Natives.initializer(GSTPAD_API.ptr_gst_pad_new_from_template(template, name), false)); } - + /** - * Get the capabilities this pad can produce or consume. - * - * This method returns all possible caps a pad can operate with, using - * the pad's get_caps function; not just the Caps as set by {@link #setCaps}. - * - * This returns the pad template caps if not explicitly set. + * Gets the capabilities this pad can produce or consume. Note that this + * method doesn't necessarily return the caps set by sending a + * gst_event_new_caps() - use {@link #getCurrentCaps() } for that instead. + * queryCaps returns all possible caps a pad can operate with, + * using the pad's CAPS query function, If the query fails, this function + * will return filter, if not NULL, otherwise ANY. + *

+ * When called on sinkpads filter contains the caps that upstream could + * produce in the order preferred by upstream. When called on srcpads filter + * contains the caps accepted by downstream in the preferred order. filter + * might be NULL but if it is not NULL the returned caps will be a subset of + * filter . + *

+ * Note that this function does not return writable Caps. * - * MT safe. + * @param filter suggested Caps or null * @return a newly allocated copy of the {@link Caps} of this pad. */ - public Caps getCaps() { - return GSTPAD_API.gst_pad_query_caps(this, null); + public Caps queryCaps(Caps filter) { + return GSTPAD_API.gst_pad_query_caps(this, filter); } - + /** - * Sets the capabilities of this pad. - * - * The caps must be fixed. Any previous caps on the pad will be destroyed. - * - * It is possible to set null caps, which will make the pad unnegotiated - * again. - * + * Gets the capabilities of the allowed media types that can flow through + * this pad and its peer. + *

+ * The allowed capabilities is calculated as the intersection of the results + * of calling {@link #queryCaps} on this pad and its peer. + *

* MT safe. - * @param caps The {@link Caps} to set. - * @return true if the caps could be set. false if the caps were not fixed - * or bad parameters were provided to this function. - */ - public boolean setCaps(Caps caps) { - return GSTPAD_API.gst_pad_set_caps(this, caps); - } - - /** - * Gets the capabilities of the allowed media types that can flow through this pad and its peer. * - * The allowed capabilities is calculated as the intersection of the results of - * calling {@link #getCaps} on this pad and its peer. - * - * MT safe. - * @return The allowed {@link Caps} of the pad link, or null if this pad has no peer. + * @return The allowed {@link Caps} of the pad link, or null if this pad has + * no peer. */ public Caps getAllowedCaps() { return GSTPAD_API.gst_pad_get_allowed_caps(this); } - + /** - * Get the capabilities of the media type that currently flows through this pad - * and its peer. + * Gets the capabilities currently configured on pad with the last + * GST_EVENT_CAPS event. * - * This function can be used on both src and sink pads. Note that srcpads are - * always negotiated before sinkpads so it is possible that the negotiated caps - * on the srcpad do not match the negotiated caps of the peer. + * @return the negotiated #GstCaps or null if this pad has + * no current caps * - * MT safe. - * @return the negotiated #GstCaps of the pad link, or null if this pad has - * no peer, or is not negotiated yet - * */ - public Caps getNegotiatedCaps() { + public Caps getCurrentCaps() { return GSTPAD_API.gst_pad_get_current_caps(this); } - + /** * Get the peer of this pad. * * MT safe. + * * @return The peer Pad of this Pad. */ public Pad getPeer() { return GSTPAD_API.gst_pad_get_peer(this); } + /** * Get the capabilities of the peer connected to this pad. + *

+ * When called on srcpads filter contains the caps that upstream could + * produce in the order preferred by upstream. When called on sinkpads + * filter contains the caps accepted by downstream in the preferred order. + * filter might be NULL but if it is not NULL the returned caps will be a + * subset of filter . * - * @return the {@link Caps} of the peer pad, or null if there is no peer pad. + * @param filter Caps to filter by, or null + * @return the {@link Caps} of the peer pad, or null if there is no peer + * pad. */ - public Caps getPeerCaps() { - return GSTPAD_API.gst_pad_peer_get_caps(this); + public Caps peerQueryCaps(Caps filter) { + return GSTPAD_API.gst_pad_peer_query_caps(this, filter); } - + /** * Check if the pad accepts the given caps. * * @param caps a {@link Caps} to check on the pad. * @return true if the pad can accept the caps. */ - public boolean acceptCaps(Caps caps) { + public boolean queryAcceptCaps(Caps caps) { return GSTPAD_API.gst_pad_query_accept_caps(this, caps); } - + /** - * Check if the peer of this pad accepts the caps. - * If this pad has no peer, this method returns true. + * Check if the peer of this pad accepts the caps. If this pad has no peer, + * this method returns true. * * @param caps {@link Caps} to check on the pad * @return true if the peer pad can accept the caps or this pad no peer. */ - public boolean peerAcceptCaps(Caps caps) { - return GSTPAD_API.gst_pad_peer_accept_caps(this, caps); + public boolean peerQueryAcceptCaps(Caps caps) { + return GSTPAD_API.gst_pad_peer_query_accept_caps(this, caps); } - + /** * Links this source pad and a sink pad. * * MT Safe. + * * @param sink the sink Pad to link. * @throws PadLinkException if pads cannot be linked. */ @@ -219,11 +238,11 @@ public void link(Pad sink) throws PadLinkException { /** * - * Unlinks the source pad from the sink pad. - * Will emit the "unlinked" signal on both pads. + * Unlinks the source pad from the sink pad. Will emit the "unlinked" signal + * on both pads. * * MT safe. - * + * * @param pad the sink Pad to unlink. * @return true if the pads were unlinked. This function returns false if * the pads were not linked together. @@ -231,7 +250,7 @@ public void link(Pad sink) throws PadLinkException { public boolean unlink(Pad pad) { return GSTPAD_API.gst_pad_unlink(this, pad); } - + /** * Check if this pad is linked to another pad or not. * @@ -240,39 +259,38 @@ public boolean unlink(Pad pad) { public boolean isLinked() { return GSTPAD_API.gst_pad_is_linked(this); } - + /** - * Get the direction of the pad. - * The direction of the pad is decided at construction time so this function - * does not take the LOCK. + * Get the direction of the pad. The direction of the pad is decided at + * construction time so this function does not take the LOCK. * * @return The {@link PadDirection} of the pad. */ public PadDirection getDirection() { return GSTPAD_API.gst_pad_get_direction(this); } - + /** - * Get the parent of this pad, cast to a {@link Element}. - * If this pad has no parent or its parent is not an element, returns null. + * Get the parent of this pad, cast to an {@link Element}. If this pad has no + * parent or its parent is not an element, returns null. * * @return The parent of the pad. */ public Element getParentElement() { return GSTPAD_API.gst_pad_get_parent_element(this); } - + /** - * Activates or deactivates the given pad. - * Normally called from within core state change functions. - * - * If active is true, makes sure the pad is active. If it is already active, either in - * push or pull mode, just return. Otherwise dispatches to the pad's activate - * function to perform the actual activation. - * + * Activates or deactivates the given pad. Normally called from within core + * state change functions. + *

+ * If active is true, makes sure the pad is active. If it is already active, + * either in push or pull mode, just return. Otherwise dispatches to the + * pad's activate function to perform the actual activation. + *

* If not @active, checks the pad's current mode and calls - * gst_pad_activate_push() or gst_pad_activate_pull(), as appropriate, with a - * FALSE argument. + * gst_pad_activate_push() or gst_pad_activate_pull(), as appropriate, with + * a FALSE argument. * * @param active whether or not the pad should be active. * @return true if the operation was successful. @@ -280,157 +298,49 @@ public Element getParentElement() { public boolean setActive(boolean active) { return GSTPAD_API.gst_pad_set_active(this, active); } - + /** - * Checks if the pad is blocked or not. - * This function returns the last requested state of the pad. It is not - * certain that the pad is actually blocking at this point (see {@link #isBlocking}). + * Checks if the pad is blocked or not. This function returns the last + * requested state of the pad. It is not certain that the pad is actually + * blocking at this point (see {@link #isBlocking}). * * @return true if the pad is blocked. */ public boolean isBlocked() { return GSTPAD_API.gst_pad_is_blocked(this); } - + /** * Run a runnable under a blocked state + * * @param callback The code to run when pad is blocked */ public void block(final Runnable callback) { - addEventProbe(new EVENT_PROBE() - { - public PadProbeReturn eventReceived(Pad pad, Event event) - { + addEventProbe(new EVENT_PROBE() { + public PadProbeReturn eventReceived(Pad pad, Event event) { callback.run(); - pad.removeCallback(EVENT_PROBE.class, this); - return PadProbeReturn.DROP; + return PadProbeReturn.REMOVE; } - }, GstPadAPI.GST_PAD_PROBE_TYPE_BLOCKING | GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM); + }, GstPadAPI.GST_PAD_PROBE_TYPE_IDLE); } - + /** - * Checks if the pad is blocking or not. This is a guaranteed state - * of whether the pad is actually blocking on a {@link Buffer} or a {@link Event}. - * + * Checks if the pad is blocking or not. This is a guaranteed state of + * whether the pad is actually blocking on a {@link Buffer} or an + * {@link Event}. + * * @return true if the pad is blocking. */ public boolean isBlocking() { return GSTPAD_API.gst_pad_is_blocking(this); } - - /** - * Signal emitted when new data is available on the {@link Pad} - * - * @see #connect(HAVE_DATA) - * @see #disconnect(HAVE_DATA) - */ - public static interface HAVE_DATA { - /** - * Called when a {@link Pad} has data available. - * - * @param pad the pad which emitted the signal. - * @param data the new data. - */ - public void haveData(Pad pad, MiniObject data); - } - - /** - * Signal emitted when new this {@link Pad} is linked to another {@link Pad} - * - * @see #connect(LINKED) - * @see #disconnect(LINKED) - */ - public static interface LINKED { - /** - * Called when a {@link Pad} is linked to another Pad. - * - * @param pad the pad that emitted the signal. - * @param peer the peer pad that has been connected. - */ - public void linked(Pad pad, Pad peer); - } - - /** - * Signal emitted when new this {@link Pad} is disconnected from a peer {@link Pad} - * - * @see #connect(UNLINKED) - * @see #disconnect(UNLINKED) - */ - public static interface UNLINKED { - /** - * Called when a {@link Pad} is unlinked from another Pad. - * - * @param pad the pad that emitted the signal. - * @param peer the peer pad that has been connected. - */ - public void unlinked(Pad pad, Pad peer); - } - - /** - * Signal emitted when a connection to a peer {@link Pad} is requested. - * - * @see #connect(REQUEST_LINK) - * @see #disconnect(REQUEST_LINK) - */ - public static interface REQUEST_LINK { - /** - * Called when a pad connection has been requested. - * @param pad the pad that emitted the signal. - * @param peer the peer pad for which a connection is requested. - */ - public void requestLink(Pad pad, Pad peer); - } - - /** - * Signal emitted when an event passes through this Pad. - * - * @see #addEventProbe(EVENT_PROBE) - * @see #removeEventProbe(EVENT_PROBE) - */ - public static interface EVENT_PROBE { - public PadProbeReturn eventReceived(Pad pad, Event event); - } - - /** - * Signal emitted when new data is available on the {@link Pad} - * - * @see #addDataProbe(DATA_PROBE) - * @see #removeDataProbe(DATA_PROBE) - */ - public static interface DATA_PROBE { - public PadProbeReturn dataReceived(Pad pad, Buffer buffer); - } - - /** - * Add a listener for the have-data signal on this {@link Pad} - * - * @param listener The listener to be called when data is available. - */ - public void connect(final HAVE_DATA listener) { - connect(HAVE_DATA.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public boolean callback(Pad pad, Buffer buffer) { - listener.haveData(pad, buffer); - return true; - } - }); - } - - /** - * Remove a listener for the have-data signal on this {@link Pad} - * - * @param listener The listener previously added for this signal. - */ - public void disconnect(HAVE_DATA listener) { - disconnect(HAVE_DATA.class, listener); - } - /** * Add a listener for the linked signal on this {@link Pad} - * - * @param listener The listener to be called when a peer {@link Pad} is linked. + * + * @param listener The listener to be called when a peer {@link Pad} is + * linked. */ public void connect(final LINKED listener) { connect(LINKED.class, listener, new GstCallback() { @@ -441,20 +351,21 @@ public boolean callback(Pad pad, Pad peer) { } }); } - + /** * Remove a listener for the linked signal on this {@link Pad} - * + * * @param listener The listener previously added for this signal. */ public void disconnect(LINKED listener) { disconnect(LINKED.class, listener); } - + /** * Add a listener for the unlinked signal on this {@link Pad} - * - * @param listener The listener to be called when when a peer {@link Pad} is unlinked. + * + * @param listener The listener to be called when when a peer {@link Pad} is + * unlinked. */ public void connect(final UNLINKED listener) { connect(UNLINKED.class, listener, new GstCallback() { @@ -465,63 +376,133 @@ public boolean callback(Pad pad, Pad peer) { } }); } + /** - * Remove a listener for the unlinked signal on this {@link Pad} - * + * Remove a listener for the unlinked signal on this + * {@link Pad} + * * @param listener The listener previously added for this signal. */ public void disconnect(UNLINKED listener) { disconnect(UNLINKED.class, listener); } + + /** + * Be notified of different states of pads. The provided callback is called + * for every state that matches mask. + *

+ * Probes are called in groups: First {@link PadProbeType#BLOCK} probes are + * called, then others, then finally {@link PadProbeType#IDLE}. The only + * exception here are IDLE probes that are called immediately if the pad is + * already idle while calling addProbe(). In each of the groups, probes are + * called in the order in which they were added. + * + * @param mask set of mask flags for probe - common options are fields of + * {@link PadProbeType} + * @param callback callback that will be called with notifications of the + * pad state + */ + public void addProbe(final Set mask, PROBE callback) { + addProbe(NativeFlags.toInt(mask), callback); + } /** - * Add a listener for the request-link signal on this {@link Pad} - * - * @param listener The listener to be called when a peer {@link Pad} requests - * to be linked to this one. + * Be notified of different states of pads. The provided callback is called + * for every state that matches mask. + *

+ * Probes are called in groups: First {@link PadProbeType#BLOCK} probes are + * called, then others, then finally {@link PadProbeType#IDLE}. The only + * exception here are IDLE probes that are called immediately if the pad is + * already idle while calling addProbe(). In each of the groups, probes are + * called in the order in which they were added. + * + * @param mask mask flag for probe + * @param callback callback that will be called with notifications of the + * pad state */ - public void connect(final REQUEST_LINK listener) { - connect(REQUEST_LINK.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public boolean callback(Pad pad, Pad peer) { - listener.requestLink(pad, peer); - return true; + public void addProbe(PadProbeType mask, PROBE callback) { + addProbe(mask.intValue(), callback); + } + + synchronized void addProbe(int mask, PROBE callback) { + final GstPadAPI.PadProbeCallback probe = new GstPadAPI.PadProbeCallback() { + @Override + public PadProbeReturn callback(Pad pad, GstPadProbeInfo probeInfo, Pointer user_data) { + PadProbeInfo info = new PadProbeInfo(probeInfo); + PadProbeReturn ret = callback.probeCallback(pad, info); + info.invalidate(); + if (ret == PadProbeReturn.REMOVE) { + // don't want handle to try and remove in GCallback::disconnect + // @TODO move to Map of probes over callback + handle.probes.remove(probeInfo.id); + removeCallback(PROBE.class, callback); + } + return ret; } - }); + }; + + NativeLong id = handle.addProbe(mask, probe); + if (id.longValue() == 0) { + // the Probe was an IDLE-Probe and it was already handled synchronously in handle.addProbe, + // so no Callback needs to be registered + return; + } + + GCallback cb = new GCallback(id, probe) { + @Override + protected void disconnect() { + handle.removeProbe(id); + } + }; + addCallback(PROBE.class, callback, cb); } /** - * Remove a listener for the request-link signal on this {@link Pad} + * Remove the provided probe callback from the Pad. * - * @param listener The listener previously added for this signal. + * @param callback callback to remove */ - public void disconnect(REQUEST_LINK listener) { - disconnect(REQUEST_LINK.class, listener); + public synchronized void removeProbe(PROBE callback) { + removeCallback(PROBE.class, callback); } - + public void addEventProbe(final EVENT_PROBE listener) { final int mask = GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_BOTH | GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_FLUSH; addEventProbe(listener, mask); } - - public void addEventProbe(final EVENT_PROBE listener, final int mask) { + + synchronized void addEventProbe(final EVENT_PROBE listener, final int mask) { final GstPadAPI.PadProbeCallback probe = new GstPadAPI.PadProbeCallback() { - public PadProbeReturn callback(Pad pad, GstPadProbeInfo probeInfo, Pointer user_data) { -// System.out.println("CALLBACK " + probeInfo.padProbeType); - if ((probeInfo.padProbeType & mask) != 0) { - Event event = GSTPAD_API.gst_pad_probe_info_get_event(probeInfo); - return listener.eventReceived(pad, event); - } - - //We have to negate the return value to keep consistency with gstreamer's API + public PadProbeReturn callback(Pad pad, GstPadProbeInfo probeInfo, Pointer user_data) { + if ((probeInfo.padProbeType & mask) != 0) { + Event event = null; + if ((probeInfo.padProbeType & EVENT_HAS_INFO_MASK) != 0) { + event = GSTPAD_API.gst_pad_probe_info_get_event(probeInfo); + } + PadProbeReturn ret = listener.eventReceived(pad, event); + if (ret == PadProbeReturn.REMOVE) { + // don't want handle to try and remove in GCallback::disconnect + handle.probes.remove(probeInfo.id); + removeCallback(EVENT_PROBE.class, listener); + } + return ret; + } + return PadProbeReturn.OK; } }; - - GCallback cb = new GCallback(GSTPAD_API.gst_pad_add_probe(this, mask, probe, null, null), probe) { + + NativeLong id = handle.addProbe(mask, probe); + if (id.longValue() == 0) { + // the Probe was an IDLE-Probe and it was already handled synchronously in handle.addProbe, + // so no Callback needs to be registered + return; + } + + GCallback cb = new GCallback(id, probe) { @Override protected void disconnect() { - GSTPAD_API.gst_pad_remove_probe(Pad.this, id); + handle.removeProbe(id); } }; addCallback(EVENT_PROBE.class, listener, cb); @@ -530,50 +511,58 @@ protected void disconnect() { public void removeEventProbe(EVENT_PROBE listener) { removeCallback(EVENT_PROBE.class, listener); } - - + public synchronized void addDataProbe(final DATA_PROBE listener) { - + final GstPadAPI.PadProbeCallback probe = new GstPadAPI.PadProbeCallback() { public PadProbeReturn callback(Pad pad, GstPadProbeInfo probeInfo, Pointer user_data) { if ((probeInfo.padProbeType & GstPadAPI.GST_PAD_PROBE_TYPE_BUFFER) != 0) { Buffer buffer = GSTPAD_API.gst_pad_probe_info_get_buffer(probeInfo); - return listener.dataReceived(pad, buffer); + PadProbeReturn ret = listener.dataReceived(pad, buffer); + if (ret == PadProbeReturn.REMOVE) { + // don't want handle to try and remove in GCallback::disconnect + handle.probes.remove(probeInfo.id); + removeCallback(DATA_PROBE.class, listener); + } + return ret; } - //We have to negate the return value to keep consistency with gstreamer's API return PadProbeReturn.OK; } }; - - GCallback cb = new GCallback(GSTPAD_API.gst_pad_add_probe(this, GstPadAPI.GST_PAD_PROBE_TYPE_BUFFER, probe, null, null), probe) { + + GCallback cb = new GCallback(handle.addProbe(GstPadAPI.GST_PAD_PROBE_TYPE_BUFFER, probe), probe) { @Override protected void disconnect() { - GSTPAD_API.gst_pad_remove_probe(Pad.this, id); + handle.removeProbe(id); } - }; - - addCallback(DATA_PROBE.class, listener, cb); + }; + + addCallback(DATA_PROBE.class, listener, cb); } - + public void removeDataProbe(DATA_PROBE listener) { removeCallback(DATA_PROBE.class, listener); } - /** - * Sends the event to this pad. - *

This function can be used by applications to send events in the pipeline. + * Sends the event to this pad. + *

+ * This function can be used by applications to send events in the pipeline. * - *

If this pad is a source pad, event should be an upstream event. - * If this pad is a sink pad, event should be a downstream event. - *

For example, you would not send a {@link EventType#EOS} on a src pad; - * EOS events only propagate downstream. - * - *

Furthermore, some downstream events have to be serialized with data flow, - * like EOS, while some can travel out-of-band, like {@link EventType#FLUSH_START}. If - * the event needs to be serialized with data flow, this function will take the - * pad's stream lock while calling its event function. + *

+ * If this pad is a source pad, event should be an upstream event. + * If this pad is a sink pad, event should be a downstream event. + *

+ * For example, you would not send a {@link EventType#EOS} on a src pad; EOS + * events only propagate downstream. + * + *

+ * Furthermore, some downstream events have to be serialized with data flow, + * like EOS, while some can travel out-of-band, like + * {@link EventType#FLUSH_START}. If the event needs to be serialized with + * data flow, this function will take the pad's stream lock while calling + * its event function. * * @param event the event to send. * @return true if the event was handled. @@ -581,116 +570,126 @@ public void removeDataProbe(DATA_PROBE listener) { public boolean sendEvent(Event event) { return GSTPAD_API.gst_pad_send_event(this, event); } - + /** - * Sends the event to the peer of this pad. - * - *

This function is mainly used by elements to send events to their peer elements. - * + * Sends the event to the peer of this pad. + * + *

+ * This function is mainly used by elements to send events to their peer + * elements. + * * @param event the event to send * @return true if the event was handled */ public boolean pushEvent(Event event) { return GSTPAD_API.gst_pad_push_event(this, event); } - + /** * Chain a buffer to pad. *

- * The function returns {@link org.gstreamer.FlowReturn#WRONG_STATE} if the pad was flushing. + * The function returns {@link FlowReturn#FLUSHING} if the + * pad was flushing. *

- * If the caps on buffer are different from the current caps on pad, this function will call any - * function installed on pad (see gst_pad_set_setcaps_function()). - * If the new caps are not acceptable for pad, this function returns - * {@link org.gstreamer.FlowReturn#NOT_NEGOTIATED}. + * If the caps on buffer are different from the current caps on pad, this + * function will call any function installed on pad (see + * gst_pad_set_setcaps_function()). If the new caps are not acceptable for + * pad, this function returns + * {@link FlowReturn#NOT_NEGOTIATED}. *

- * The function proceeds calling the chain function installed on pad and the return value of that function is - * returned to the caller. {@link org.gstreamer.FlowReturn#NOT_SUPPORTED} is returned if pad has no chain function. + * The function proceeds calling the chain function installed on pad and the + * return value of that function is returned to the caller. + * {@link FlowReturn#NOT_SUPPORTED} is returned if pad has no + * chain function. *

- * In all cases, success or failure, the caller loses its reference to buffer after calling this function. - * - * @param buffer the Buffer, returns {@link org.gstreamer.FlowReturn#ERROR} if NULL. + * In all cases, success or failure, the caller loses its reference to + * buffer after calling this function. + * + * @param buffer the Buffer, returns {@link FlowReturn#ERROR} + * if NULL. * @return a org.gstreamer.FlowReturn */ public FlowReturn chain(Buffer buffer) { - return GSTPAD_API.gst_pad_chain(this, buffer); + return GSTPAD_API.gst_pad_chain(this, buffer); } - + /** - * When pad is flushing this function returns {@link org.gstreamer.FlowReturn#WRONG_STATE} immediatly. + * When pad is flushing this function returns + * {@link FlowReturn#FLUSHING} immediately. *

- * Calls the getRange function of pad, see GstPadGetRangeFunction for a description of a getRange function. - * If pad has no getRange function installed (see gst_pad_set_getrange_function()) this function returns + * Calls the getRange function of pad, see GstPadGetRangeFunction for a + * description of a getRange function. If pad has no getRange function + * installed (see gst_pad_set_getrange_function()) this function returns * {@link FlowReturn#NOT_SUPPORTED}. - * + *

* This is a lowlevel function. Usualy {@link Pad#pullRange} is used. - * + * * @param offset The start offset of the buffer * @param size The length of the buffer * @param buffer the Buffer, returns {@link FlowReturn#ERROR} if NULL. - * @return a FlowReturn from the peer pad. When this function returns OK, buffer will contain a valid Buffer. + * @return a FlowReturn from the peer pad. When this function returns OK, + * buffer will contain a valid Buffer. */ public FlowReturn getRange(long offset, int size, Buffer[] buffer) { - return GSTPAD_API.gst_pad_get_range(this, offset, size, buffer); + return GSTPAD_API.gst_pad_get_range(this, offset, size, buffer); } /** * Pulls a buffer from the peer pad. *

- * This function will first trigger the pad block signal if it was installed. + * This function will first trigger the pad block signal if it was + * installed. *

- * When pad is not linked {@link FlowReturn#NOT_LINKED} is returned else this function returns - * the result of {@link Pad#getRange} on the peer pad. See {@link Pad#getRange} for a list of return values and for - * the semantics of the arguments of this function. + * When pad is not linked {@link FlowReturn#NOT_LINKED} is returned else + * this function returns the result of {@link Pad#getRange} on the peer pad. + * See {@link Pad#getRange} for a list of return values and for the + * semantics of the arguments of this function. *

- * buffer's caps must either be unset or the same as what is already configured on pad. - * Renegotiation within a running pull-mode pipeline is not supported. + * buffer's caps must either be unset or the same as what is already + * configured on pad. Renegotiation within a running pull-mode pipeline is + * not supported. + * * @param offset The start offset of the buffer * @param size The length of the buffer * @param buffer the Buffer, returns {@link FlowReturn#ERROR} if NULL. - * @return a FlowReturn from the peer pad. When this function returns OK, buffer will contain a valid Buffer. - * MT safe. + * @return a FlowReturn from the peer pad. When this function returns OK, + * buffer will contain a valid Buffer. MT safe. */ public FlowReturn pullRange(long offset, int size, Buffer[] buffer) { - return GSTPAD_API.gst_pad_pull_range(this, offset, size, buffer); + return GSTPAD_API.gst_pad_pull_range(this, offset, size, buffer); } - - - + /** - * Pushes a buffer to the peer of pad . - * This function will call installed block probes before triggering any - * installed data probes. - * - * The function proceeds calling gst_pad_chain() on the peer pad and returns - * the value from that function. If pad has no peer, GST_FLOW_NOT_LINKED + * Pushes a buffer to the peer of pad . This function will call installed + * block probes before triggering any installed data probes. + *

+ * The function proceeds calling gst_pad_chain() on the peer pad and returns + * the value from that function. If pad has no peer, {@link FlowReturn#NOT_LINKED} * will be returned. - * - * In all cases, success or failure, the caller loses its reference to + *

+ * In all cases, success or failure, the caller loses its reference to * buffer after calling this function. * - * @param buffer - * the GstBuffer to push returns GST_FLOW_ERROR if not. [transfer full] - * @return - * a GstFlowReturn from the peer pad. - * + * @param buffer the GstBuffer to push returns GST_FLOW_ERROR if not. + * [transfer full] + * @return a GstFlowReturn from the peer pad. + * * MT safe. */ public FlowReturn push(final Buffer buffer) { return GSTPAD_API.gst_pad_push(this, buffer); } - + /** * Gets the template for pad. - * - * @return the GstPadTemplate from which this pad was instantiated, - * or NULL if this pad has no template. Unref after usage. + * + * @return the GstPadTemplate from which this pad was instantiated, or NULL + * if this pad has no template. */ public PadTemplate getTemplate() { return GSTPAD_API.gst_pad_get_pad_template(this); } - /** * Check if the pad has caps set on it with a GST_EVENT_CAPS events * @@ -699,4 +698,132 @@ public PadTemplate getTemplate() { public boolean hasCurrentCaps() { return GSTPAD_API.gst_pad_has_current_caps(this); } + + /** + * Signal emitted when new this {@link Pad} is linked to another {@link Pad} + * + * @see #connect(LINKED) + * @see #disconnect(LINKED) + */ + public static interface LINKED { + + /** + * Called when a {@link Pad} is linked to another Pad. + * + * @param pad the pad that emitted the signal. + * @param peer the peer pad that has been connected. + */ + public void linked(Pad pad, Pad peer); + } + + /** + * Signal emitted when new this {@link Pad} is disconnected from a peer + * {@link Pad} + * + * @see #connect(UNLINKED) + * @see #disconnect(UNLINKED) + */ + public static interface UNLINKED { + + /** + * Called when a {@link Pad} is unlinked from another Pad. + * + * @param pad the pad that emitted the signal. + * @param peer the peer pad that has been connected. + */ + public void unlinked(Pad pad, Pad peer); + } + + /** + * Callback used by + * {@link #addProbe(java.util.EnumSet, org.freedesktop.gstreamer.Pad.PROBE)} + */ + public static interface PROBE { + + /** + * Callback used by + * {@link #addProbe(java.util.EnumSet, org.freedesktop.gstreamer.Pad.PROBE)}. + * Gets called to notify about the current blocking type. + *

+ * The PadProbeInfo and any Buffer, Event or Query referenced from + * it, is only valid for the duration of the callback. + * + * @param pad Pad that is blocked + * @param info PadProbeInfo with access to underlying data + * @return PadProbeReturn value + */ + public PadProbeReturn probeCallback(Pad pad, PadProbeInfo info); + + } + + /** + * Probe for listening when an event passes through this Pad. + * + * @see #addEventProbe(EVENT_PROBE) + * @see #removeEventProbe(EVENT_PROBE) + */ + public static interface EVENT_PROBE { + + public PadProbeReturn eventReceived(Pad pad, Event event); + + } + + /** + * Probe for listening when new data is available on the Pad. + * + * @see #addDataProbe(DATA_PROBE) + * @see #removeDataProbe(DATA_PROBE) + */ + public static interface DATA_PROBE { + + public PadProbeReturn dataReceived(Pad pad, Buffer buffer); + + } + + private static class Handle extends GstObject.Handle { + + private final Set probes; + + private Handle(GstPadPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + probes = new HashSet<>(); + } + + @Override + protected GstPadPtr getPointer() { + return (GstPadPtr) super.getPointer(); + } + + private synchronized NativeLong addProbe(int mask, GstPadAPI.PadProbeCallback probe) { + NativeLong id = GSTPAD_API.gst_pad_add_probe(getPointer(), mask, probe, null, null); + if (id.longValue() != 0) { + probes.add(id); + } + return id; + } + + private synchronized void removeProbe(NativeLong id) { + if (probes.remove(id)) { + GSTPAD_API.gst_pad_remove_probe(getPointer(), id); + } + } + + private synchronized void clearProbes() { + probes.forEach(id -> GSTPAD_API.gst_pad_remove_probe(getPointer(), id)); + probes.clear(); + } + + @Override + public void invalidate() { + clearProbes(); + super.invalidate(); + } + + @Override + public void dispose() { + clearProbes(); + super.dispose(); + } + + } } diff --git a/src/org/freedesktop/gstreamer/PadDirection.java b/src/org/freedesktop/gstreamer/PadDirection.java index c83d9183..23c8f844 100644 --- a/src/org/freedesktop/gstreamer/PadDirection.java +++ b/src/org/freedesktop/gstreamer/PadDirection.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -18,14 +19,27 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.NativeEnum; + /** * The direction of a {@link Pad}. */ -public enum PadDirection { +public enum PadDirection implements NativeEnum { /** The direction is unknown. */ - UNKNOWN, + UNKNOWN(0), /** The {@link Pad} is a source pad. */ - SRC, + SRC(1), /** The {@link Pad} is a sink pad. */ - SINK; + SINK(2); + + private final int value; + + private PadDirection(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } } diff --git a/src/org/freedesktop/gstreamer/PadLinkException.java b/src/org/freedesktop/gstreamer/PadLinkException.java index 499ef170..1263e557 100644 --- a/src/org/freedesktop/gstreamer/PadLinkException.java +++ b/src/org/freedesktop/gstreamer/PadLinkException.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + package org.freedesktop.gstreamer; /** diff --git a/src/org/freedesktop/gstreamer/PadLinkReturn.java b/src/org/freedesktop/gstreamer/PadLinkReturn.java index 0766c65c..f155d22a 100644 --- a/src/org/freedesktop/gstreamer/PadLinkReturn.java +++ b/src/org/freedesktop/gstreamer/PadLinkReturn.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -18,13 +19,13 @@ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.glib.NativeEnum; import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; /** * Result values from {@link Pad#link(Pad)} and friends. */ -public enum PadLinkReturn implements IntegerEnum { +public enum PadLinkReturn implements NativeEnum { /** Link succeeded. */ OK(0), /** Pads have no common grandparent. */ @@ -38,10 +39,11 @@ public enum PadLinkReturn implements IntegerEnum { /** Pads cannot cooperate in scheduling. */ NOSCHED(-5), /** Refused for some reason. */ - REFUSED(-6), + @DefaultEnumValue + REFUSED(-6); + + private final int value; - /** The default enum value used when no other value matches the native value */ - @DefaultEnumValue __UNKNOWN_NATIVE_VALUE(~0); PadLinkReturn(int value) { this.value = value; } @@ -49,9 +51,9 @@ public enum PadLinkReturn implements IntegerEnum { * Gets the integer value of the enum. * @return The integer value for this enum. */ + @Override public int intValue() { return value; } - private final int value; } diff --git a/src/org/freedesktop/gstreamer/ActivateMode.java b/src/org/freedesktop/gstreamer/PadMode.java similarity index 90% rename from src/org/freedesktop/gstreamer/ActivateMode.java rename to src/org/freedesktop/gstreamer/PadMode.java index 5f0dbfff..2faa2b59 100644 --- a/src/org/freedesktop/gstreamer/ActivateMode.java +++ b/src/org/freedesktop/gstreamer/PadMode.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -23,9 +24,9 @@ /** * The status of a {@link Pad}. After activating a pad, which usually happens when * the parent element goes from {@link State#READY} to {@link State#PAUSED}, the - * ActivateMode defines if the {@link Pad} operates in push or pull mode. + * PadMode defines if the {@link Pad} operates in push or pull mode. */ -public enum ActivateMode { +public enum PadMode { /** * Pad will not handle dataflow */ diff --git a/src/org/freedesktop/gstreamer/PadPresence.java b/src/org/freedesktop/gstreamer/PadPresence.java index df72dda5..f403b3e0 100644 --- a/src/org/freedesktop/gstreamer/PadPresence.java +++ b/src/org/freedesktop/gstreamer/PadPresence.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -19,7 +20,7 @@ package org.freedesktop.gstreamer; /** - * Indicates when a {@link Pad} will become available. + * Indicates when a {@link Pad} will become available, for use in {@link PadTemplate} */ public enum PadPresence { /** @@ -27,11 +28,11 @@ public enum PadPresence { */ ALWAYS, /** - * The pad will become available depending on the media stream + * The pad will become available depending on the media stream. */ SOMETIMES, /** - * The pad is only available on request. + * The pad is only available on request with {@link Element#getRequestPad(java.lang.String) } */ REQUEST; } diff --git a/src/org/freedesktop/gstreamer/PadProbeInfo.java b/src/org/freedesktop/gstreamer/PadProbeInfo.java new file mode 100644 index 00000000..16e5ee11 --- /dev/null +++ b/src/org/freedesktop/gstreamer/PadProbeInfo.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import java.util.Set; +import org.freedesktop.gstreamer.event.Event; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.lowlevel.GstPadProbeInfo; +import org.freedesktop.gstreamer.query.Query; + +import static org.freedesktop.gstreamer.lowlevel.GstPadAPI.GSTPAD_API; + +/** + * Probe info passed in to Pad.PROBE callback. + */ +public final class PadProbeInfo { + + private GstPadProbeInfo info; + + PadProbeInfo(GstPadProbeInfo info) { + this.info = info; + } + + /** + * Get the set of flags for the current probe type. + * + * @return probe type + */ + public Set getType() { + return NativeFlags.fromInt(PadProbeType.class, info.padProbeType); + } + + /** + * Get the Buffer from the probe, or null. + * + * @return buffer or null + */ + public Buffer getBuffer() { + return GSTPAD_API.gst_pad_probe_info_get_buffer(info); + } + + /** + * Get the Event from the probe, or null. + * + * @return event or null + */ + public Event getEvent() { + return GSTPAD_API.gst_pad_probe_info_get_event(info); + } + + /** + * Get the query from the probe, or null. + * + * @return query or null + */ + public Query getQuery() { + return GSTPAD_API.gst_pad_probe_info_get_query(info); + } + + void invalidate() { + info = null; + } + +} diff --git a/src/org/freedesktop/gstreamer/PadProbeReturn.java b/src/org/freedesktop/gstreamer/PadProbeReturn.java index ffe75080..b81bcb0b 100644 --- a/src/org/freedesktop/gstreamer/PadProbeReturn.java +++ b/src/org/freedesktop/gstreamer/PadProbeReturn.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,19 +16,43 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; /** - * The direction of a {@link Pad}. + * Different return values for Pad probe callbacks */ public enum PadProbeReturn { - /** Drop. */ + /** + * Drop data in data probes. For push mode this means that the data item is + * not sent downstream. For pull mode, it means that the data item is not + * passed upstream. In both cases, no other probes are called for this item + * and %GST_FLOW_OK or %TRUE is returned to the caller. + */ DROP, - /** normal probe return value */ + /** + * Normal probe return value. This leaves the probe in place, and defers + * decisions about dropping or passing data to other probes, if any. If + * there are no other probes, the default behaviour for the probe type + * applies ('block' for blocking probes, and 'pass' for non-blocking + * probes). + */ OK, - /** Remove the probe */ + /** + * Remove the probe + */ REMOVE, - /** pass the data item in the block probe and block on the next item */ - PASS; + /** + * Pass the data item in the block probe and block on the next item. + */ + PASS, + /** + * Data has been handled in the probe and will not be forwarded further. For + * events and buffers this is the same behaviour as %GST_PAD_PROBE_DROP + * (except that in this case you need to unref the buffer or event + * yourself). For queries it will also return %TRUE to the caller. The probe + * can also modify the #GstFlowReturn value by using the + * #GST_PAD_PROBE_INFO_FLOW_RETURN() accessor. Note that the resulting query + * must contain valid entries. Since: 1.6 + */ + HANDLED; } diff --git a/src/org/freedesktop/gstreamer/PadProbeType.java b/src/org/freedesktop/gstreamer/PadProbeType.java new file mode 100644 index 00000000..895bc936 --- /dev/null +++ b/src/org/freedesktop/gstreamer/PadProbeType.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import java.util.Collections; +import java.util.Set; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.lowlevel.GstPadAPI; + +/** + * The different probing types that can occur. When either one of {@link #IDLE} + * or {@link #BLOCK} is used, the probe will be a blocking probe. + *

+ * For convenience, the various flag combinations are provided as immutable + * Sets. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstpad.html#GstPadProbeType + */ +public enum PadProbeType implements NativeFlags { + + /* flags to control blocking */ + /** + * Probe idle pads and block while the callback is called. + */ + IDLE(GstPadAPI.GST_PAD_PROBE_TYPE_IDLE), + /** + * Probe and block pads. + */ + BLOCK(GstPadAPI.GST_PAD_PROBE_TYPE_BLOCK), + /* flags to select datatypes */ + /** + * Probe buffers. + */ + BUFFER(GstPadAPI.GST_PAD_PROBE_TYPE_BUFFER), + /** + * Probe buffer lists. + */ + BUFFER_LIST(GstPadAPI.GST_PAD_PROBE_TYPE_BUFFER_LIST), + /** + * Probe downstream events. + */ + EVENT_DOWNSTREAM(GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM), + /** + * Probe upstream events. + */ + EVENT_UPSTREAM(GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_UPSTREAM), + /** + * Probe flush events. This probe has to be explicitly enabled and is not + * included in the DOWNSTREAM or UPSTREAM probe types. + */ + EVENT_FLUSH(GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_FLUSH), + /** + * Probe downstream queries. + */ + QUERY_DOWNSTREAM(GstPadAPI.GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM), + /** + * Probe upstream queries. + */ + QUERY_UPSTREAM(GstPadAPI.GST_PAD_PROBE_TYPE_QUERY_UPSTREAM), + /* flags to select scheduling mode */ + /** + * Probe push. + */ + PUSH(GstPadAPI.GST_PAD_PROBE_TYPE_PUSH), + /** + * Probe pull. + */ + PULL(GstPadAPI.GST_PAD_PROBE_TYPE_PULL); + + /** + * Probe and block at the next opportunity, at data flow or when idle. + */ + public static final Set BLOCKING + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_BLOCKING)); + + /** + * Probe downstream data (buffers, buffer lists and events). + */ + public static final Set DATA_DOWNSTREAM + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM)); + + /** + * Probe upstream data (events). + */ + public static final Set DATA_UPSTREAM + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_DATA_UPSTREAM)); + + /** + * Probe upstream and downstream data (buffers, buffer lists and events). + */ + public static final Set DATA_BOTH + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_DATA_BOTH)); + + /** + * Probe and block downstream data (buffers, buffer lists and events). + */ + public static final Set BLOCK_DOWNSTREAM + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM)); + + /** + * Probe and block upstream data (events). + */ + public static final Set BLOCK_UPSTREAM + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_BLOCK_UPSTREAM)); + + /** + * Probe upstream and downstream events. + */ + public static final Set EVENT_BOTH + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_BOTH)); + + /** + * Probe upstream and downstream queries. + */ + public static final Set QUERY_BOTH + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_QUERY_BOTH)); + + /** + * Probe upstream events and queries and downstream buffers, buffer lists, + * events and queries. + */ + public static final Set ALL_BOTH + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_ALL_BOTH)); + + /** + * Probe push and pull. + */ + public static final Set SCHEDULING + = Collections.unmodifiableSet(NativeFlags.fromInt(PadProbeType.class, + GstPadAPI.GST_PAD_PROBE_TYPE_SCHEDULING)); + + private final int value; + + private PadProbeType(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + +} diff --git a/src/org/freedesktop/gstreamer/PadTemplate.java b/src/org/freedesktop/gstreamer/PadTemplate.java index 834194e8..1d1f5e5a 100644 --- a/src/org/freedesktop/gstreamer/PadTemplate.java +++ b/src/org/freedesktop/gstreamer/PadTemplate.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (C) 2007, 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen @@ -18,94 +19,100 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.GstPadTemplateAPI; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstPadTemplateAPI.GSTPADTEMPLATE_API; /** - * Padtemplates describe the possible media types a {@link Pad} or an - * {@link ElementFactory} can handle. This allows for both inspection of - * handled types before loading the element plugin as well as identifying pads - * on elements that are not yet created (request or sometimes pads). - * - * Pad and PadTemplates have {@code Caps} attached to it to describe the media type - * they are capable of dealing with. {@link #getCaps} is used to get the caps of - * a padtemplate. It is not possible to modify the caps of a padtemplate after - * creation. + * Padtemplates describe the possible media types a {@link Pad} or an + * {@link ElementFactory} can handle. This allows for both inspection of handled + * types before loading the element plugin as well as identifying pads on + * elements that are not yet created (request or sometimes pads). + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPadTemplate.html + *

+ * Pad and PadTemplates have {@code Caps} attached to it to describe the media + * type they are capable of dealing with. {@link #getCaps} is used to get the + * caps of a PadTemplate. It is not possible to modify the caps of a PadTemplate + * after creation. */ public class PadTemplate extends GstObject { + public static final String GTYPE_NAME = "GstPadTemplate"; - /** + /** * Creates a new proxy for PadTemplate. - *

This is only for internal use. - * + *

+ * This is only for internal use. + * * @param init internal initialization data. */ - public PadTemplate(Initializer init) { + PadTemplate(Initializer init) { super(init); } - + /** - * Creates a new pad template with a name according to the given template - * and with the given arguments. - * + * Creates a new pad template with a name according to the given template, + * with the given arguments and {@link PadPresence#ALWAYS } + * * @param nameTemplate the name template. * @param direction the direction of the template. * @param caps a {@code Caps} set for the template. */ public PadTemplate(String nameTemplate, PadDirection direction, Caps caps) { - this(initializer(GSTPADTEMPLATE_API.ptr_gst_pad_template_new(nameTemplate, direction, PadPresence.ALWAYS, caps))); + this(Natives.initializer(GSTPADTEMPLATE_API.ptr_gst_pad_template_new(nameTemplate, direction, PadPresence.ALWAYS, caps))); } + /** * Creates a new pad template with a name according to the given template * and with the given arguments. - * + * * @param nameTemplate the name template. * @param direction the direction of the template. * @param presence the presence of the pad, which controls the lifetime. * @param caps a {@code Caps} set for the template. */ public PadTemplate(String nameTemplate, PadDirection direction, PadPresence presence, Caps caps) { - this(initializer(GSTPADTEMPLATE_API.ptr_gst_pad_template_new(nameTemplate, direction, presence, caps))); + this(Natives.initializer(GSTPADTEMPLATE_API.ptr_gst_pad_template_new(nameTemplate, direction, presence, caps))); } - + /** * Gets the {@link Caps} set on this {@code PadTemplate} - * + * * @return the media type on this template. */ public Caps getCaps() { return GSTPADTEMPLATE_API.gst_pad_template_get_caps(this); } - + /** * Gets the name of this template - * + * * @return the name of this template */ public String getTemplateName() { - return get("name-template").toString(); + return get("name-template").toString(); } - + /** * Gets the direction of this template - * + * * @return {@link PadDirection} */ public PadDirection getDirection() { Object d = get("direction"); if (d instanceof Number) { return PadDirection.values()[((Number) d).intValue()]; - } + } return null; } - + /** * Gets presence of this template - * + * * @return {@link PadPresence} */ public PadPresence getPresence() { diff --git a/src/org/freedesktop/gstreamer/Pipeline.java b/src/org/freedesktop/gstreamer/Pipeline.java index 45448481..49f13753 100644 --- a/src/org/freedesktop/gstreamer/Pipeline.java +++ b/src/org/freedesktop/gstreamer/Pipeline.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Neil C Smith + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 2000 Erik Walthinsen * 2005 Wim Taymans @@ -18,54 +18,57 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import java.util.concurrent.TimeUnit; import com.sun.jna.Pointer; -import java.util.logging.Level; +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; - -import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; +import org.freedesktop.gstreamer.event.SeekFlags; +import org.freedesktop.gstreamer.event.SeekType; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; +import org.freedesktop.gstreamer.query.Query; import static org.freedesktop.gstreamer.lowlevel.GstElementAPI.GSTELEMENT_API; -import static org.freedesktop.gstreamer.lowlevel.GstParseAPI.GSTPARSE_API; import static org.freedesktop.gstreamer.lowlevel.GstPipelineAPI.GSTPIPELINE_API; import static org.freedesktop.gstreamer.lowlevel.GstQueryAPI.GSTQUERY_API; /** - * A {@code Pipeline} is a special {@link Bin} used as the toplevel container for - * the filter graph. The Pipeline will manage the selection and - * distribution of a global {@link Clock} as well as provide a {@link Bus} to - * the application. It will also implement a default behavour for managing - * seek events (see {@link #seek seek}). - * + * A {@code Pipeline} is a special {@link Bin} used as the top level container + * for the filter graph. The Pipeline will manage the selection and distribution + * of a global {@link Clock} as well as provide a {@link Bus} to the + * application. It will also implement a default behaviour for managing seek + * events (see {@link #seek seek}). + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPipeline.html *

* Elements are added and removed from the pipeline using the {@code Bin} * methods like {@link Bin#add add} and {@link Bin#remove remove}. - * *

- * Before changing the state of the Pipeline (see {@link Element}) a - * {@link Bus} can be retrieved with {@link #getBus getBus}. This bus can then - * be used to receive {@link Message}s from the elements in the pipeline. - * + * Before changing the state of the Pipeline (see {@link Element}) a {@link Bus} + * can be retrieved with {@link #getBus getBus}. This bus can then be used to + * receive {@link Message}s from the elements in the pipeline. *

- * By default, a Pipeline will automatically flush the pending Bus - * messages when going to the NULL state to ensure that no circular - * references exist when no messages are read from the Bus. This - * behaviour can be changed with {@link #setAutoFlushBus setAutoFlushBus} - * + * By default, a Pipeline will automatically flush the pending Bus messages when + * going to the NULL state to ensure that no circular references exist when no + * messages are read from the Bus. This behaviour can be changed with + * {@link #setAutoFlushBus setAutoFlushBus} *

- * When the Pipeline performs the PAUSED to PLAYING state change it will - * select a clock for the elements. The clock selection algorithm will by - * default select a clock provided by an element that is most upstream - * (closest to the source). For live pipelines (ones that return - * {@link StateChangeReturn#NO_PREROLL} from the - * {@link Element#setState setState} call) this will select the clock provided - * by the live source. For normal pipelines this will select a clock provided - * by the sinks (most likely the audio sink). If no element provides a clock, - * a default SystemClock is used. - * + * When the Pipeline performs the PAUSED to PLAYING state change it will select + * a clock for the elements. The clock selection algorithm will by default + * select a clock provided by an element that is most upstream (closest to the + * source). For live pipelines (ones that return + * {@link StateChangeReturn#NO_PREROLL} from the + * {@link Element#setState setState} call) this will select the clock provided + * by the live source. For normal pipelines this will select a clock provided by + * the sinks (most likely the audio sink). If no element provides a clock, a + * default SystemClock is used. + * *

* The clock selection can be controlled with the gst_pipeline_use_clock() * method, which will enforce a given clock on the pipeline. With @@ -73,294 +76,244 @@ * restored. * *

- * A Pipeline maintains a stream time for the elements. The stream - * time is defined as the difference between the current clock time and - * the base time. When the pipeline goes to READY or a flushing seek is - * performed on it, the stream time is reset to 0. When the pipeline is - * set from PLAYING to PAUSED, the current clock time is sampled and used to - * configure the base time for the elements when the pipeline is set - * to PLAYING again. This default behaviour can be changed with the - * gst_pipeline_set_new_stream_time() method. + * A Pipeline maintains a stream time for the elements. The stream time is + * defined as the difference between the current clock time and the base time. + * When the pipeline goes to READY or a flushing seek is performed on it, the + * stream time is reset to 0. When the pipeline is set from PLAYING to PAUSED, + * the current clock time is sampled and used to configure the base time for the + * elements when the pipeline is set to PLAYING again. This default behaviour + * can be changed with the gst_pipeline_set_new_stream_time() method. * - * When sending a flushing seek event to a GstPipeline (see - * {@link #seek seek}), it will make sure that the pipeline is properly - * PAUSED and resumed as well as set the new stream time to 0 when the - * seek succeeded. + * When sending a flushing seek event to a GstPipeline (see {@link #seek seek}), + * it will make sure that the pipeline is properly PAUSED and resumed as well as + * set the new stream time to 0 when the seek succeeded. */ public class Pipeline extends Bin { + public static final String GST_NAME = "pipeline"; public static final String GTYPE_NAME = "GstPipeline"; - private static Logger logger = Logger.getLogger(Pipeline.class.getName()); - - public Pipeline(Initializer init) { - super(init); + private static Logger LOG = Logger.getLogger(Pipeline.class.getName()); + + private final Handle handle; + + protected Pipeline(Initializer init) { + this(new Handle(init.ptr.as(GstObjectPtr.class, GstObjectPtr::new), init.ownsHandle), init.needRef); } + Pipeline(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; + handle.busRef.set(GSTPIPELINE_API.gst_pipeline_get_bus(this)); + } + /** * Creates a new instance of Pipeline with a unique name. */ public Pipeline() { - this(initializer(GSTPIPELINE_API.ptr_gst_pipeline_new(null), false)); - initBus(); + this(Natives.initializer(GSTPIPELINE_API.ptr_gst_pipeline_new(null), false)); } - + /** * Creates a new instance of Pipeline with the given name. - * + * * @param name The name used to identify this pipeline. */ public Pipeline(String name) { this(initializer(name)); - initBus(); } - private static Initializer initializer(String name) { - Pointer new_pipeline = GSTPIPELINE_API.ptr_gst_pipeline_new(name); - return initializer(new_pipeline, false); - } - - /** - * Create a new pipeline based on command line syntax. - * - * Please note that you might get a return value that is not NULL even - * though the error is set. - * In this case there was a recoverable parsing error and you can try - * to play the pipeline. - * - * @param pipelineDecription the command line describing the pipeline - * @return The new Pipeline. - */ - @Deprecated - public static Pipeline launch(String pipelineDecription) { - Pointer[] err = { null }; - Pipeline pipeline = (Pipeline) GSTPARSE_API.gst_parse_launch(pipelineDecription, err); - if (pipeline == null) { - throw new GstException(new GError(new GErrorStruct(err[0]))); - } - - // check for error - if (err[0] != null) { - logger.log(Level.WARNING, new GError(new GErrorStruct(err[0])).getMessage()); - } - - pipeline.initBus(); - return pipeline; + private static Initializer initializer(String name) { + Pointer new_pipeline = GSTPIPELINE_API.ptr_gst_pipeline_new(name); + return Natives.initializer(new_pipeline, false); } - - /** - * Create a new element based on command line syntax. - * - * error will contain an error message if an erroneous pipeline is specified. - * An error does not mean that the pipeline could not be constructed. - * - * @param pipelineDecription An array of strings containing the command line describing the pipeline. - * @return The new Pipeline. - */ - @Deprecated - public static Pipeline launch(String... pipelineDecription) { - Pointer[] err = { null }; - Pipeline pipeline = (Pipeline) GSTPARSE_API.gst_parse_launchv(pipelineDecription, err); - if (pipeline == null) { - throw new GstException(new GError(new GErrorStruct(err[0]))); - } - - // check for error - if (err[0] != null) { - // log error! - logger.log(Level.WARNING, new GError(new GErrorStruct(err[0])).getMessage()); - } - pipeline.initBus(); - return pipeline; - } - private void initBus() { - Bus bus = getBus(); - // TODO started to work around with add_signal_watch - when working can - // remove this comment! - // This is mostly a hack to get the Bus to install a sync message handler - // -// bus.setFlushing(true); -// bus.setFlushing(false); - } /** - * Sets this pipeline to automatically flush {@link Bus} messages or not. + * Usually, when a pipeline goes from READY to NULL state, it automatically + * flushes all pending messages on the bus, which is done for refcounting + * purposes, to break circular references. + *

+ * This means that applications that update state using (async) bus messages + * (e.g. do certain things when a pipeline goes from PAUSED to READY) might + * not get to see messages when the pipeline is shut down, because they + * might be flushed before they can be dispatched in the main thread. This + * behaviour can be disabled using this function. + *

+ * It is important that all messages on the bus are handled when the + * automatic flushing is disabled else memory leaks will be introduced. + *

+ * MT safe. * - * @param flush true if automatic flushing is desired, else false. + * @param flush whether or not to automatically flush the bus when the + * pipeline goes from READY to NULL state */ public void setAutoFlushBus(boolean flush) { GSTPIPELINE_API.gst_pipeline_set_auto_flush_bus(this, flush); } - + /** - * Checks if the pipeline will automatically flush messages when going to the NULL state. - * + * Checks if the pipeline will automatically flush messages when going to + * the NULL state. + *

+ * MT safe. + * * @return true if the pipeline automatically flushes messages. - */ + */ public boolean getAutoFlushBus() { return GSTPIPELINE_API.gst_pipeline_get_auto_flush_bus(this); } - + /** - * Set the clock for pipeline. - * The clock will be distributed to all the elements managed by the pipeline. - *

MT safe - * + * Set the clock for pipeline. The clock will be distributed to all the + * elements managed by the pipeline. + *

+ * MT safe + * * @param clock The {@link Clock} to use - * @return true if the clock could be set on the pipeline, false if some - * element did not accept the clock. + * @return true if the clock could be set on the pipeline, false if some + * element did not accept the clock. * */ public boolean setClock(Clock clock) { return GSTPIPELINE_API.gst_pipeline_set_clock(this, clock); } - + /** * Return the current {@link Clock} used by the pipeline. - * + * * @return The {@link Clock} currently in use. */ + @Override public Clock getClock() { return GSTPIPELINE_API.gst_pipeline_get_clock(this); } - + /** - * Force the Pipeline to use the a specific clock. - * The pipeline will always use the given clock even if new clock - * providers are added to this pipeline. - *

MT safe - * - * @param clock The {@link Clock} to use. If clock is null, all clocking is + * Force the Pipeline to use the a specific clock. The pipeline will always + * use the given clock even if new clock providers are added to this + * pipeline. + *

+ * MT safe + * + * @param clock The {@link Clock} to use. If clock is null, all clocking is * disabled, and the pipeline will run as fast as possible. - * + * */ public void useClock(Clock clock) { GSTPIPELINE_API.gst_pipeline_use_clock(this, clock); } - + /** * Gets the {@link Bus} this pipeline uses for messages. - * + * * @return The {@link Bus} that this pipeline uses. */ @Override public Bus getBus() { - return GSTPIPELINE_API.gst_pipeline_get_bus(this); + return handle.busRef.get(); } - + /** - * Sets the position in the media stream to time. - * + * Sets the position in the media stream to time in nanoseconds. + *

+ * Prefer use of + * {@link Element#seekSimple(org.freedesktop.gstreamer.Format, java.util.Set, long)}. + * * @param time The time to change the position to. * @return true if seek is successful */ - public boolean seek(ClockTime time) { - return seek(time.convertTo(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS); + // @TODO move seek and query methods on to element? + public boolean seek(long time) { + return seek(time, TimeUnit.NANOSECONDS); } - + /** * Sets the current position in the media stream. - * + *

+ * Prefer use of + * {@link Element#seekSimple(org.freedesktop.gstreamer.Format, java.util.Set, long)}. + * * @param time the time to change the position to. - * @param unit the {@code TimeUnit} the time is expressed in. + * @param unit the {@code TimeUnit} the time is expressed in. * @return true if seek is successful */ public boolean seek(long time, TimeUnit unit) { - return seek(1.0, Format.TIME, SeekFlags.FLUSH | SeekFlags.KEY_UNIT, - SeekType.SET, TimeUnit.NANOSECONDS.convert(time, unit), - SeekType.NONE, -1); + return seek(1.0, Format.TIME, EnumSet.of(SeekFlags.FLUSH, SeekFlags.KEY_UNIT), + SeekType.SET, TimeUnit.NANOSECONDS.convert(time, unit), + SeekType.NONE, -1); } - + /** * Seeks to a new position in the media stream. - * + * *

- * The start and stop values are expressed in format. + * The start and stop values are expressed in format. *

- * A rate of 1.0 means normal playback rate, 2.0 means double speed. - * Negative values means backwards playback. A value of 0.0 for the - * rate is not allowed and should be accomplished instead by PAUSING the - * pipeline. + * A rate of 1.0 means normal playback rate, 2.0 means double speed. + * Negative values means backwards playback. A value of 0.0 for the rate is + * not allowed and should be accomplished instead by PAUSING the pipeline. *

* A pipeline has a default playback segment configured with a start * position of 0, a stop position of -1 and a rate of 1.0. The currently - * configured playback segment can be queried with #GST_QUERY_SEGMENT. + * configured playback segment can be queried with #GST_QUERY_SEGMENT. *

- * and stopType specify how to adjust the currently configured - * start and stop fields in segment. Adjustments can be made relative or - * absolute to the last configured values. A type of {@link SeekType#NONE} means - * that the position should not be updated. + * startType and stopType specify how to adjust the currently configured + * start and stop fields in segment. Adjustments can be made relative or + * absolute to the last configured values. A type of {@link SeekType#NONE} + * means that the position should not be updated. *

- * When the rate is positive and start has been updated, playback will start - * from the newly configured start position. + * When the rate is positive and start has been updated, playback will start + * from the newly configured start position. *

- * For negative rates, playback will start from the newly configured stop - * position (if any). If the stop position if updated, it must be different from - * -1 for negative rates. + * For negative rates, playback will start from the newly configured stop + * position (if any). If the stop position if updated, it must be different + * from -1 for negative rates. *

- * It is not possible to seek relative to the current playback position, to do - * this, PAUSE the pipeline, query the current playback position with - * {@link org.gstreamer.Pipeline#queryPosition queryPosition} and update the playback segment - * current position with a {@link SeekType#SET} to the desired position. - * + * It is not possible to seek relative to the current playback position, to + * do this, PAUSE the pipeline, query the current playback position with + * {@link org.gstreamer.Pipeline#queryPosition queryPosition} and update the + * playback segment current position with a {@link SeekType#SET} to the + * desired position. + * * @param rate the new playback rate * @param format the format of the seek values - * @param flags the optional seek flags + * @param seekFlags the seek flags * @param startType the type and flags for the new start position * @param start the value of the new start position * @param stopType the type and flags for the new stop position * @param stop the value of the new stop position * @return true if seek is successful */ - public boolean seek(double rate, Format format, int flags, + // for compatibility + public boolean seek(double rate, Format format, EnumSet seekFlags, SeekType startType, long start, SeekType stopType, long stop) { - - return GSTELEMENT_API.gst_element_seek(this, rate, format, flags, - startType, start, stopType, stop); + return super.seek(rate, format, seekFlags, startType, start, stopType, stop); } - /** - * Gets the current position in the media stream in units of time. - * - * @return the ClockTime representing the current position. - */ - public ClockTime queryPosition() { - return ClockTime.valueOf(queryPosition(Format.TIME), TimeUnit.NANOSECONDS); + @Override + public boolean seek(double rate, Format format, Set seekFlags, + SeekType startType, long start, SeekType stopType, long stop) { + return super.seek(rate, format, seekFlags, startType, start, stopType, stop); } /** * Gets the current position in the media stream. - * + * * @param unit the {@code TimeUnit} to return the position in terms of. - * @return a time value representing the current position in units of unit. + * @return a time value representing the current position in units of unit. */ public long queryPosition(TimeUnit unit) { return unit.convert(queryPosition(Format.TIME), TimeUnit.NANOSECONDS); } - - /** - * Gets the current position in terms of the specified {@link Format}. - * - * @param format The {@link Format} to return the position in. - * @return The current position or -1 if the query failed. - */ + + @Override public long queryPosition(Format format) { - long[] pos = { 0 }; - return GSTELEMENT_API.gst_element_query_position(this, format, pos) ? pos[0] : -1L; - } - - /** - * Gets the time duration of the current media stream. - * - * @return The total duration of the current media stream. - */ - public ClockTime queryDuration() { - return ClockTime.valueOf(queryDuration(Format.TIME), TimeUnit.NANOSECONDS); + return super.queryPosition(format); } - + /** * Gets the time duration of the current media stream. - * + * * @param unit the {@code TimeUnit} to return the position in. * @return The total duration of the current media stream. */ @@ -368,19 +321,13 @@ public long queryDuration(TimeUnit unit) { return unit.convert(queryDuration(Format.TIME), TimeUnit.NANOSECONDS); } - /** - * Gets the duration of the current media stream in terms of the specified {@link Format}. - * - * @param format the {@code Format} to return the duration in. - * @return The total duration of the current media stream. - */ + @Override public long queryDuration(Format format) { - long[] dur = { 0 }; - return GSTELEMENT_API.gst_element_query_duration(this, format, dur) ? dur[0] : -1L; + return super.queryDuration(format); } /** - * Gets the {@link Segment} for the current media stream in terms of the specified {@link Format}. + * Gets the {@link Segment} for the current media stream in terms of time. * * @return The information regarding the current {@code Segment}. */ @@ -389,7 +336,8 @@ public Segment querySegment() { } /** - * Gets the {@link Segment} for the current media stream in terms of the specified {@link Format}. + * Gets the {@link Segment} for the current media stream in terms of the + * specified {@link Format}. * * @param format the {@code Format} to return the segment in. * @return The information regarding the current {@code Segment}. @@ -397,11 +345,42 @@ public Segment querySegment() { public Segment querySegment(Format format) { Query qry = GSTQUERY_API.gst_query_new_segment(format); GSTELEMENT_API.gst_element_query(this, qry); - double[] rate = { 0.0D }; - Format[] fmt = { Format.UNDEFINED }; - long[] start_value = { 0 }; - long[] stop_value = { 0 }; + double[] rate = {0.0D}; + Format[] fmt = {Format.UNDEFINED}; + long[] start_value = {0}; + long[] stop_value = {0}; GSTQUERY_API.gst_query_parse_segment(qry, rate, fmt, start_value, stop_value); return new Segment(rate[0], fmt[0], start_value[0], stop_value[0]); } + + static class Handle extends Bin.Handle { + + private final AtomicReference busRef; + + public Handle(GstObjectPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + this.busRef = new AtomicReference<>(); + } + + @Override + public void invalidate() { + disposeBus(); + super.invalidate(); + } + + @Override + public void dispose() { + disposeBus(); + super.dispose(); + } + + private void disposeBus() { + Bus bus = busRef.getAndSet(null); + if (bus != null) { + bus.dispose(); + } + } + + } + } diff --git a/src/org/freedesktop/gstreamer/Plugin.java b/src/org/freedesktop/gstreamer/Plugin.java index fa50512d..486840bf 100644 --- a/src/org/freedesktop/gstreamer/Plugin.java +++ b/src/org/freedesktop/gstreamer/Plugin.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen @@ -18,7 +19,6 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import static org.freedesktop.gstreamer.lowlevel.GstPluginAPI.GSTPLUGIN_API; @@ -26,15 +26,18 @@ /** * Container for features loaded from a shared object module *

- * GStreamer is extensible, so {@link Element} instances can be loaded at runtime. - * A plugin system can provide one or more of the basic - * GStreamer {@link PluginFeature} subclasses. + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPlugin.html + *

+ * GStreamer is extensible, so {@link Element} instances can be loaded at + * runtime. A plugin system can provide one or more of the basic GStreamer + * {@link PluginFeature} subclasses. *

- * A plugin should export a symbol gst_plugin_desc that is a - * struct of type GstPluginDesc. - * the plugin loader will check the version of the core library the plugin was - * linked against and will create a new Plugin. It will then call the - * #GstPluginInitFunc function that was provided in the + * A plugin should export a symbol gst_plugin_desc that is a struct + * of type GstPluginDesc. the plugin loader will check the version of the core + * library the plugin was linked against and will create a new Plugin. It will + * then call the #GstPluginInitFunc function that was provided in the * gst_plugin_desc. *

* Once you have a handle to a #GstPlugin (e.g. from the #GstRegistryPool), you @@ -43,37 +46,38 @@ * Use gst_plugin_find_feature() and gst_plugin_get_feature_list() to find * features in a plugin. *

- * Usually plugins are always automaticlly loaded so you don't need to call - * {@link #load} explicitly to bring it into memory. There are options to + * Usually plugins are always automatically loaded so you don't need to call + * {@link #loadByName} explicitly to bring it into memory. There are options to * statically link plugins to an app or even use GStreamer without a plugin - * repository in which case {@link #load} can be needed to bring the plugin - * into memory. + * repository in which case {@link #loadByName} can be needed to bring the + * plugin into memory. *

* @see PluginFeature * @see ElementFactory */ public class Plugin extends GstObject { + public static final String GTYPE_NAME = "GstPlugin"; - - /** - * Creates a new instance of GstElement - * + + /** + * Creates a new instance of GstElement + * * @param init internal initialization data. */ - public Plugin(Initializer init) { + Plugin(Initializer init) { super(init); } - + /** * Load the named plugin. * * @param pluginName * @return A new Plugin reference if the plugin was loaded, else null. */ - public static Plugin load(String pluginName) { + public static Plugin loadByName(String pluginName) { return GSTPLUGIN_API.gst_plugin_load_by_name(pluginName); } - + /** * Get the short name of the plugin. * @@ -83,7 +87,7 @@ public static Plugin load(String pluginName) { public String getName() { return GSTPLUGIN_API.gst_plugin_get_name(this); } - + /** * Get the long descriptive name of the plugin. * @@ -92,7 +96,7 @@ public String getName() { public String getDescription() { return GSTPLUGIN_API.gst_plugin_get_description(this); } - + /** * Get the filename of the plugin. * @@ -101,7 +105,7 @@ public String getDescription() { public String getFilename() { return GSTPLUGIN_API.gst_plugin_get_filename(this); } - + /** * Get the version of the plugin. * @@ -110,16 +114,16 @@ public String getFilename() { public String getVersion() { return GSTPLUGIN_API.gst_plugin_get_version(this); } - + /** - * get the license of the plugin. + * Get the license of the plugin. * * @return The license of the plugin. */ public String getLicense() { return GSTPLUGIN_API.gst_plugin_get_license(this); } - + /** * Get the source module the plugin belongs to. * @@ -128,7 +132,7 @@ public String getLicense() { public String getSource() { return GSTPLUGIN_API.gst_plugin_get_source(this); } - + /** * Get the package the plugin belongs to. * @@ -137,7 +141,7 @@ public String getSource() { public String getPackage() { return GSTPLUGIN_API.gst_plugin_get_package(this); } - + /** * Get the URL where the plugin comes from. * @@ -146,7 +150,7 @@ public String getPackage() { public String getOrigin() { return GSTPLUGIN_API.gst_plugin_get_origin(this); } - + /** * Get the release date (and possibly time) in form of a string, if * available. @@ -154,12 +158,13 @@ public String getOrigin() { * For normal GStreamer plugin releases this will usually just be a date in * the form of "YYYY-MM-DD", while pre-releases and builds from git may * contain a time component after the date as well, in which case the string - * will be formatted like "YYYY-MM-DDTHH:MMZ" (e.g. "2012-04-30T09:30Z").Test + * will be formatted like "YYYY-MM-DDTHH:MMZ" (e.g. + * "2012-04-30T09:30Z").Test */ public String getReleaseDateString() { return GSTPLUGIN_API.gst_plugin_get_release_date_string(this); } - + /** * Queries if the plugin is loaded into memory. * @@ -168,13 +173,13 @@ public String getReleaseDateString() { public boolean isLoaded() { return GSTPLUGIN_API.gst_plugin_is_loaded(this); } - - /** - * Ensures this plugin is loaded. - * - * @return a potentially new Plugin reference. - */ - public Plugin load() { - return GSTPLUGIN_API.gst_plugin_load(this); - } + +// /** +// * Ensures this plugin is loaded. +// * +// * @return a potentially new Plugin reference. +// */ +// public Plugin load() { +// return GSTPLUGIN_API.gst_plugin_load(this); +// } } diff --git a/src/org/freedesktop/gstreamer/PluginFeature.java b/src/org/freedesktop/gstreamer/PluginFeature.java index 12bb50c9..282e45b7 100644 --- a/src/org/freedesktop/gstreamer/PluginFeature.java +++ b/src/org/freedesktop/gstreamer/PluginFeature.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -19,37 +20,73 @@ */ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.NativeEnum; import static org.freedesktop.gstreamer.lowlevel.GstObjectAPI.GSTOBJECT_API; import static org.freedesktop.gstreamer.lowlevel.GstPluginFeatureAPI.GSTPLUGINFEATURE_API; /** * Base class for contents of a {@link Plugin} - * + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPluginFeature.html + *

* This is a base class for anything that can be added to a Plugin. + * * @see Plugin */ public class PluginFeature extends GstObject { + public static final String GTYPE_NAME = "GstPluginFeature"; - public enum Rank { - GST_RANK_NONE(0), - GST_RANK_MARGINAL(64), - GST_RANK_SECONDARY(128), - GST_RANK_PRIMARY(256); + /** + * Element priority ranks. Defines the order in which the autoplugger (or + * similar rank-picking mechanisms, such as e.g. + * gst_element_make_from_uri()) will choose this element over an alternative + * one with the same function. + * + * These constants serve as a rough guidance for defining the rank of a + * GstPluginFeature. Any value is valid, including values bigger than + * GST_RANK_PRIMARY . + */ + public enum Rank implements NativeEnum { + + /** + * Will be chosen last or not at all. + */ + NONE(0), + + /** + * Unlikely to be chosen. + */ + MARGINAL(64), + + /** + * Likely to be chosen. + */ + SECONDARY(128), + + /** + * Will be chosen first. + */ + PRIMARY(256); - private int value; + private final int value; private Rank(int value) { this.value = value; } - public int getValue() { + @Override + public int intValue() { return value; } } - /** Creates a new instance of PluginFeature */ - public PluginFeature(Initializer init) { + /** + * Creates a new instance of PluginFeature + */ + PluginFeature(Initializer init) { super(init); } @@ -59,7 +96,7 @@ public String toString() { } /** - * Gets the name of a plugin feature. + * Gets the name of a plugin feature. * * @return The name. */ @@ -69,9 +106,9 @@ public String getName() { } /** - * Sets the name of a plugin feature. The name uniquely identifies a feature - * within all features of the same type. Renaming a plugin feature is not - * allowed. + * Sets the name of the plugin feature, getting rid of the old name if there + * was one. + * * @param name The name to set. */ @Override @@ -81,16 +118,23 @@ public boolean setName(String name) { } /** - * Set the rank for the plugin feature. - * Specifies a rank for a plugin feature, so that autoplugging uses - * the most appropriate feature. + * Set the rank for the plugin feature. Specifies a rank for a plugin + * feature, so that autoplugging uses the most appropriate feature. + * * @param rank The rank value - higher number means more priority rank */ public void setRank(int rank) { GSTPLUGINFEATURE_API.gst_plugin_feature_set_rank(this, rank); } + + /** + * Set the rank for the plugin feature. Specifies a rank for a plugin + * feature, so that autoplugging uses the most appropriate feature. + * + * @param rank The rank value + */ public void setRank(Rank rank) { - setRank(rank.getValue()); + setRank(rank.intValue()); } /** @@ -108,25 +152,26 @@ public int getRank() { * @param major Minimum required major version * @param minor Minimum required minor version * @param micro Minimum required micro version - * @return true if the plugin feature has at least the required version, otherwise false. + * @return true if the plugin feature has at least the required version, + * otherwise false. */ public boolean checkVersion(int major, int minor, int micro) { return GSTPLUGINFEATURE_API.gst_plugin_feature_check_version(this, minor, minor, micro); } - + /** * Get the name of the plugin that provides this feature. * * @return the name of the plugin that provides this feature, or NULL if the - * feature is not associated with a plugin. + * feature is not associated with a plugin. */ public String getPluginName() { return GSTPLUGINFEATURE_API.gst_plugin_feature_get_plugin_name(this); } - + /** * Get the plugin that provides this feature. - * + * * @return the plugin that provides this feature, or NULL. */ public Plugin getPlugin() { diff --git a/src/org/freedesktop/gstreamer/Promise.java b/src/org/freedesktop/gstreamer/Promise.java index c386f05a..dd5e798f 100644 --- a/src/org/freedesktop/gstreamer/Promise.java +++ b/src/org/freedesktop/gstreamer/Promise.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2018 Vinicius Tona * Copyright (c) 2018 Antonio Morales * @@ -14,110 +15,138 @@ * You should have received a copy of the GNU Lesser General Public License version 3 along with * this work. If not, see . */ - package org.freedesktop.gstreamer; import static org.freedesktop.gstreamer.lowlevel.GstPromiseAPI.GSTPROMISE_API; +import org.freedesktop.gstreamer.glib.NativeObject; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.Natives; +/** + * A miniobject for future/promise-like functionality + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPromise.html + *

+ */ +@Gst.Since(minor = 14) public class Promise extends MiniObject { - public static final String GTYPE_NAME = "GstPromise"; - public static interface PROMISE_CHANGE { + public static final String GTYPE_NAME = "GstPromise"; + + private GstCallback changeFunction; + /** - * Called whenever the state of the promise is changed from PENDING to any other {@link PromiseResult} - * - * @param promise the original promise that had the callback attached to + * Creates a new instance of Promise. This constructor is used internally. + * + * @param init internal initialization data. */ - public void onChange(Promise promise); - } + Promise(final Initializer init) { + super(init); + } - /** - * Creates a new instance of Promise. This constructor is used internally. - * - * @param init internal initialization data. - */ - public Promise(final Initializer init) { - super(init); - } + /** + * Creates a new instance of promise + */ + public Promise() { + this(Natives.initializer(GSTPROMISE_API.ptr_gst_promise_new())); + Gst.checkVersion(1, 14); // @TODO ideally this check would be before native call! + } - /** - * Creates a new instance of promise - */ - public Promise() { - this(initializer(GSTPROMISE_API.ptr_gst_promise_new())); - } + /** + * Creates a new instance of promise with a callback attached. + * + * @param listener Listener to be called whenever the state of a + * {@link Promise} is changed + */ + public Promise(final PROMISE_CHANGE listener) { + this(new GstCallback() { + public void callback(Promise promise, Pointer userData) { + listener.onChange(promise); + } + }); + } + + private Promise(GstCallback callback) { + this(Natives.initializer(GSTPROMISE_API + .ptr_gst_promise_new_with_change_func(callback, null, null))); + this.changeFunction = callback; + } - /** - * Creates a new instance of promise with a callback attached. - * - * @param listerner Listener to be called whenever the state of a {@link Promise} is changed - */ - public Promise(final PROMISE_CHANGE listener) { - this(new Initializer(GSTPROMISE_API.ptr_gst_promise_new_with_change_func(new GstCallback() { - @SuppressWarnings("unused") - public void callback(Promise promise, Pointer userData) { - listener.onChange(promise); - } - }), false, false)); - } + /** + * Wait for the promise to move out of the PENDING {@link PromiseResult} + * state. If the promise is not in PENDING then it will immediately return. + * + * @return the {@link PromiseResult} of the promise. + */ + public PromiseResult waitResult() { + return GSTPROMISE_API.gst_promise_wait(this); + } - protected static Initializer initializer(final Pointer ptr) { - return new Initializer(ptr, false, true); - } + /** + * Set a reply on the promise. + * + * Will wake up any waiters on the promise with the REPLIED + * {@link PromiseResult} state. If the promise has already been interrupted + * than the replied will not be visible to any waiters + * + * @param structure the {@link Structure} to reply the promise with, caller + * should not use this structure afterward as it is invalidated through + * {@link NativeObject#invalidate()} + */ + public void reply(final Structure structure) { + GSTPROMISE_API.gst_promise_reply(this, structure); + } - /** - * Wait for the promise to move out of the PENDING {@link PromiseResult} state. - * If the promise is not in PENDING then it will immediately return. - * - * @return the {@link PromiseResult} of the promise. - */ - public PromiseResult waitResult() { - return GSTPROMISE_API.gst_promise_wait(this); - } + /** + * Interrupt waiting for the result of the promise. + * + * Any waiters on the promise will receive the INTERRUPTED + * {@link PromiseResult} state. + */ + public void interrupt() { + GSTPROMISE_API.gst_promise_interrupt(this); + } - /** - * Set a reply on the promise. - * - * Will wake up any waiters on the promise with the REPLIED {@link PromiseResult} state. - * If the promise has already been interrupted than the replied will not be visible to any waiters - * - * @param structure the {@link Structure} to reply the promise with - */ - public void reply(final Structure structure) { - GSTPROMISE_API.gst_promise_reply(this, structure); - } + /** + * Expire a promise. + * + * Any waiters on the promise will received the EXPIRED + * {@link PromiseResult} state. + */ + public void expire() { + GSTPROMISE_API.gst_promise_expire(this); + } - /** - * Interrupt waiting for the result of the prommise. - * - * Any waiters on the promise will receive the INTERRUPTED {@link PromiseResult} state. - */ - public void interrupt() { - GSTPROMISE_API.gst_promise_interrupt(this); - } + /** + * Retrieve the reply set on the promise. + * + * The state of the promise must be in the REPLIED {@link PromiseResult} + * state. The return structure is owned by the promise and thus cannot be + * modified. + * + * @return the {@link Structure} set on the promise reply. + */ + public Structure getReply() { + return Structure.objectFor(GSTPROMISE_API.ptr_gst_promise_get_reply(this), false, false); + } - /** - * Expire a promise. - * - * Any waiters on the promise will recieved the EXPIRED {@link PromiseResult} state. - */ - public void expire() { - GSTPROMISE_API.gst_promise_expire(this); - } + /** + * Called whenever the state of the promise is changed from PENDING to any + * other {@link PromiseResult} + */ + public static interface PROMISE_CHANGE { - /** - * Retrieve the reply set on the promise. - * - * The state of the promise must be in the REPLIED {@link PromiseResult} state. - * The return structure is owned by the promise and thus cannot be modified. - * - * @return the {@link Structure} set on the promise reply. - */ - public Structure getReply() { - return Structure.objectFor(GSTPROMISE_API.ptr_gst_promise_get_reply(this), false, false); - } + /** + * Called whenever the state of the promise is changed from PENDING to + * any other {@link PromiseResult} + * + * @param promise the original promise that had the callback attached to + */ + public void onChange(Promise promise); + } } diff --git a/src/org/freedesktop/gstreamer/PromiseResult.java b/src/org/freedesktop/gstreamer/PromiseResult.java index 7b9741ef..abd66117 100644 --- a/src/org/freedesktop/gstreamer/PromiseResult.java +++ b/src/org/freedesktop/gstreamer/PromiseResult.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2018 Antonio Morales * * This file is part of gstreamer-java. @@ -22,15 +23,16 @@ * The result of a {@link Promise} * Available since GStreamer 1.14 */ +@Gst.Since(minor = 14) public enum PromiseResult { - /** The initial state of a promise */ + /** Initial state. Waiting for transition to any other state. */ + @DefaultEnumValue PENDING, - /** The promise was interrupted */ + /** Interrupted by the consumer as it doesn't want the value anymore. */ INTERRUPTED, - /** The promise has been resolved and it has a value */ + /** A producer marked a reply. */ REPLIED, - /** The promise is expired and won't return a result */ - EXPIRED, - /** Unknown result */ - @DefaultEnumValue UNKNOWN; + /** The promise expired (the carrying object lost all refs) and the promise + * will never be fulfilled. */ + EXPIRED } diff --git a/src/org/freedesktop/gstreamer/Query.java b/src/org/freedesktop/gstreamer/Query.java deleted file mode 100644 index d44a3683..00000000 --- a/src/org/freedesktop/gstreamer/Query.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2008 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import org.freedesktop.gstreamer.lowlevel.ReferenceManager; -import org.freedesktop.gstreamer.lowlevel.annotations.HasSubtype; - -import static org.freedesktop.gstreamer.lowlevel.GstQueryAPI.GSTQUERY_API; - -/** - * Base query type - */ -@HasSubtype -public class Query extends MiniObject { - public static final String GTYPE_NAME = "GstQuery"; - - /** - * Internally used constructor. Do not use. - * - * @param init internal initialization data. - */ - public Query(Initializer init) { - super(init); - } - - /** - * Get the structure of this query. - * - * @return The structure of this Query. - */ - public Structure getStructure() { - return ReferenceManager.addKeepAliveReference(GSTQUERY_API.gst_query_get_structure(this), this); - } - -} diff --git a/src/org/freedesktop/gstreamer/QueryType.java b/src/org/freedesktop/gstreamer/QueryType.java deleted file mode 100644 index 31e46492..00000000 --- a/src/org/freedesktop/gstreamer/QueryType.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2008 Wayne Meissner - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * 2005 Wim Taymans - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import java.util.HashMap; -import java.util.Map; - -import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; -import org.freedesktop.gstreamer.lowlevel.GstQueryAPI.GstQueryTypeFlags; - -import static org.freedesktop.gstreamer.lowlevel.GstQueryAPI.GSTQUERY_API; - -/** - * Standard predefined Query types - */ -public final class QueryType implements Comparable { - - private static final Map nameCache = new HashMap(); - - /** invalid query type */ - public static final QueryType UNKNOWN = makeType(0, 0); - /** current position in stream */ - public static final QueryType POSITION = makeType(10, GstQueryTypeFlags.BOTH); - /** total duration of the stream */ - public static final QueryType DURATION = makeType(20, GstQueryTypeFlags.BOTH); - /** latency of stream */ - public static final QueryType LATENCY = makeType(30, GstQueryTypeFlags.BOTH); - /** current jitter of stream */ - public static final QueryType JITTER = makeType(40, GstQueryTypeFlags.BOTH); - /** current rate of the stream */ - public static final QueryType RATE = makeType(50, GstQueryTypeFlags.BOTH); - /** seeking capabilities */ - public static final QueryType SEEKING = makeType(60, GstQueryTypeFlags.BOTH); - /** segment start/stop positions */ - public static final QueryType SEGMENT = makeType(70, GstQueryTypeFlags.BOTH); - /** convert values between formats */ - public static final QueryType CONVERT = makeType(80, GstQueryTypeFlags.BOTH); - /** query supported formats for convert */ - public static final QueryType FORMATS = makeType(90, GstQueryTypeFlags.BOTH); - /** query available media for efficient seeking */ - public static final QueryType BUFFERING = makeType(110, GstQueryTypeFlags.BOTH); - /** a custom application or element defined query */ - public static final QueryType CUSTOM = makeType(120, GstQueryTypeFlags.BOTH); - /** query the URI of the source or sink */ - public static final QueryType URI = makeType(130, GstQueryTypeFlags.BOTH); - /** the buffer allocation properties */ - public static final QueryType ALLOCATION = makeType(140, GstQueryTypeFlags.DOWNSTREAM | GstQueryTypeFlags.SERIALIZED); - /** the scheduling properties */ - public static final QueryType SCHEDULING = makeType(150, GstQueryTypeFlags.UPSTREAM); - /** the accept caps query */ - public static final QueryType ACCEPT_CAPS = makeType(160, GstQueryTypeFlags.BOTH); - /** the caps query */ - public static final QueryType CAPS = makeType(170, GstQueryTypeFlags.BOTH); - /** wait till all serialized data is consumed downstream */ - public static final QueryType DRAIN = makeType(180, GstQueryTypeFlags.DOWNSTREAM | GstQueryTypeFlags.SERIALIZED); - /** query the pipeline-local context from downstream or upstream (since 1.2) */ - public static final QueryType CONTEXT = makeType(190, GstQueryTypeFlags.BOTH); - - private final Integer value; - - private static QueryType makeType(int num, int flags) { - int value = (num << GstQueryAPI.GST_QUERY_NUM_SHIFT) | flags; - QueryType type = new QueryType(value); - return type; - } - - /** - * Returns the QueryType with the specified integer value. - * @param value integer value. - * @return QueryType constant. - * @throws java.lang.IllegalArgumentException if there is no QueryType - * with the specified value. - */ - public static QueryType valueOf(int value) { - return new QueryType(value); - } - - private QueryType(int value) { - this.value = value; - } - - /** - * Gets the integer value of the enum. - * - * @return the integer value for this enum. - */ - public int intValue() { - return value; - } - - /** - * gets the name of this type. - * - * @return the gstreamer name for this type. - */ - public String getName() { - String cachedName = nameCache.get(value); - if(cachedName == null) { - cachedName = GSTQUERY_API.gst_query_type_get_name(this); - nameCache.put(value, cachedName); - } - return cachedName; - } - - /** - * Compares this {@code QueryType} to the specified object. - *

The result is {@code true} if and only if the argument is not - * {@code null} and is a {@code QueryType} object equivalent to this - * {@code QueryType} - * - * @param obj - * @return true if the specified object is equivalent to this - * {@code QueryType} - */ - @Override - public boolean equals(Object obj) { - return obj instanceof QueryType && ((QueryType) obj).value.equals(value); - } - - /** - * Returns a hash code for this {@code QueryType}. - * - * @return a hash code value for this QueryType. - * @see java.lang.Integer#hashCode - */ - @Override - public int hashCode() { - return value.hashCode(); - } - /** - * Compares this QueryType to another. - * - * @param queryType the other QueryType to compare to. - * @return {@code 0} if this {@code QueryType} is equal to queryType. - * A value less than zero if this {@code QueryType} is numerically less than - * queryType. - * A value greater than zero if this {@code QueryType} is numerically - * greater than queryType. - */ - public int compareTo(QueryType queryType) { - return value.compareTo(queryType.value); - } -} diff --git a/src/org/freedesktop/gstreamer/Registry.java b/src/org/freedesktop/gstreamer/Registry.java index 3817271e..634ec21c 100644 --- a/src/org/freedesktop/gstreamer/Registry.java +++ b/src/org/freedesktop/gstreamer/Registry.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -22,10 +23,10 @@ package org.freedesktop.gstreamer; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; +import org.freedesktop.gstreamer.glib.Natives; -import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; import static org.freedesktop.gstreamer.lowlevel.GstPluginAPI.GSTPLUGIN_API; import static org.freedesktop.gstreamer.lowlevel.GstRegistryAPI.GSTREGISTRY_API; @@ -34,6 +35,10 @@ /** * Abstract base class for management of {@link Plugin} objects. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstRegistry.html + *

* One registry holds the metadata of a set of plugins. * All registries build the RegistryPool. *

@@ -97,27 +102,8 @@ public class Registry extends GstObject { public static final String GTYPE_NAME = "GstRegistry"; - public static interface PluginFilter { - public boolean accept(Plugin plugin); - } - - public static interface PluginFeatureFilter { - public boolean accept(PluginFeature feature); - } - - /** - * Retrieves the default registry. - * - * @return The default Registry. - */ - public static Registry get() { - // Need to handle the return value here, as it is a persistent object - // i.e. the java proxy should not dispose of the underlying object when finalized - return GstObject.objectFor(GSTREGISTRY_API.gst_registry_get(), Registry.class, - false, false); - } /** Creates a new instance of Registry */ - public Registry(Initializer init) { + Registry(Initializer init) { super(init); } @@ -149,17 +135,17 @@ public void removePlugin(Plugin plugin) { GSTREGISTRY_API.gst_registry_remove_plugin(this, plugin); } - /** - * Find the {@link PluginFeature} with the given name and type in the registry. - * - * @param name The name of the plugin feature to find. - * @param type The type of the plugin feature to find. - * @return The pluginfeature with the given name and type or null - * if the plugin was not found. - */ - public PluginFeature findPluginFeature(String name, GType type) { - return GSTREGISTRY_API.gst_registry_find_feature(this, name, type); - } +// /** +// * Find the {@link PluginFeature} with the given name and type in the registry. +// * +// * @param name The name of the plugin feature to find. +// * @param type The type of the plugin feature to find. +// * @return The pluginfeature with the given name and type or null +// * if the plugin was not found. +// */ +// public PluginFeature findPluginFeature(String name, GType type) { +// return GSTREGISTRY_API.gst_registry_find_feature(this, name, type); +// } /** * Find a {@link PluginFeature} by name in the registry. @@ -167,7 +153,7 @@ public PluginFeature findPluginFeature(String name, GType type) { * @param name The name of the plugin feature to find. * @return The {@link PluginFeature} or null if not found. */ - public PluginFeature findPluginFeature(String name) { + public PluginFeature lookupFeature(String name) { return GSTREGISTRY_API.gst_registry_lookup_feature(this, name); } @@ -188,31 +174,24 @@ public List getPluginList() { * Get a subset of the Plugins in the registry, filtered by filter. * * @param filter the filter to use - * @param onlyReturnFirstMatch If true, only return the first plugin that matches the filter. * @return A List of {@link Plugin} objects that match the filter. */ - public List getPluginList(final PluginFilter filter, boolean onlyReturnFirstMatch) { - List list = new LinkedList(); - for (Plugin plugin : getPluginList()) { - if (filter.accept(plugin)) { - list.add(plugin); - } - } - return list; + public List getPluginList(final PluginFilter filter) { + return getPluginList().stream().filter(filter::accept).collect(Collectors.toList()); } - /** - * Retrieves a list of {@link PluginFeature} of the {@link Plugin} type. - * - * @param type The plugin type. - * @return a List of {@link PluginFeature} for the plugin type. - */ - public List getPluginFeatureListByType(GType type) { - GList glist = GSTREGISTRY_API.gst_registry_get_feature_list(this, type); - List list = objectList(glist, PluginFeature.class); - GSTPLUGINFEATURE_API.gst_plugin_feature_list_free(glist); - return list; - } +// /** +// * Retrieves a list of {@link PluginFeature} of the {@link Plugin} type. +// * +// * @param type The plugin type. +// * @return a List of {@link PluginFeature} for the plugin type. +// */ +// public List getPluginFeatureListByType(GType type) { +// GList glist = GSTREGISTRY_API.gst_registry_get_feature_list(this, type); +// List list = objectList(glist, PluginFeature.class); +// GSTPLUGINFEATURE_API.gst_plugin_feature_list_free(glist); +// return list; +// } /** * Retrieves a list of {@link PluginFeature} of the named {@link Plugin}. @@ -250,10 +229,31 @@ private List objectList(GList glist, Class objectCla GList next = glist; while (next != null) { if (next.data != null) { - list.add(GstObject.objectFor(next.data, objectClass, true, true)); + list.add(Natives.objectFor(next.data, objectClass, true, true)); } next = next.next(); } return list; } + + public static interface PluginFilter { + public boolean accept(Plugin plugin); + } + +// public static interface PluginFeatureFilter { +// public boolean accept(PluginFeature feature); +// } +// + /** + * Retrieves the default registry. + * + * @return The default Registry. + */ + public static Registry get() { + // Need to handle the return value here, as it is a persistent object + // i.e. the java proxy should not dispose of the underlying object when finalized + return Natives.objectFor(GSTREGISTRY_API.gst_registry_get(), Registry.class, + false, false); + } + } diff --git a/src/org/freedesktop/gstreamer/SDPMessage.java b/src/org/freedesktop/gstreamer/SDPMessage.java index 38906f08..967f640d 100644 --- a/src/org/freedesktop/gstreamer/SDPMessage.java +++ b/src/org/freedesktop/gstreamer/SDPMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2018 Antonio Morales * * This file is part of gstreamer-java. @@ -20,10 +21,18 @@ import static org.freedesktop.gstreamer.lowlevel.GstSDPMessageAPI.GSTSDPMESSAGE_API; -import org.freedesktop.gstreamer.lowlevel.NativeObject; +import org.freedesktop.gstreamer.glib.NativeObject; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.lowlevel.GPointer; +/** + * Wrapping type and helper methods for dealing with SDP messages. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gst-plugins-base-libs/html/gst-plugins-base-libs-GstSDPMessage.html + */ public class SDPMessage extends NativeObject { public static final String GTYPE_NAME = "GstSDPMessage"; @@ -32,17 +41,21 @@ public class SDPMessage extends NativeObject { * * @param init internal initialization data */ - public SDPMessage(Initializer init) { - super(init); + SDPMessage(Initializer init) { + this(new Handle(init.ptr, init.ownsHandle)); } + SDPMessage(Handle handle) { + super(handle); + } + /** * Creates a new instance of SDPMessage */ public SDPMessage() { - this(initializer()); + this(initHandle()); } - + /** * A SDP formatted string representation of SDPMessage. * @@ -67,27 +80,36 @@ public void parseBuffer(String sdpString) { GSTSDPMESSAGE_API.gst_sdp_message_parse_buffer(data, length, this); } - /** - * Creates a copy of this SDPMessage. - * - * @return a copy of SDPMessage. - */ - public SDPMessage copy(boolean shouldInvalidateOriginal) { - Pointer[] ptr = new Pointer[1]; - GSTSDPMESSAGE_API.gst_sdp_message_copy(this, ptr); - if (shouldInvalidateOriginal) { - this.invalidate(); - } - return new SDPMessage(initializer(ptr[0])); - } +// /** +// * Creates a copy of this SDPMessage. +// * +// * @return a copy of SDPMessage. +// */ +// SDPMessage copy(boolean shouldInvalidateOriginal) { +// Pointer[] ptr = new Pointer[1]; +// GSTSDPMESSAGE_API.gst_sdp_message_copy(this, ptr); +// if (shouldInvalidateOriginal) { +// this.invalidate(); +// } +// return new SDPMessage(initializer(ptr[0])); +// } - private static Initializer initializer() { + private static Handle initHandle() { Pointer[] ptr = new Pointer[1]; GSTSDPMESSAGE_API.gst_sdp_message_new(ptr); - return initializer(ptr[0]); + return new Handle(new GPointer(ptr[0]), true); } - protected void disposeNativeHandle(Pointer ptr) { - GSTSDPMESSAGE_API.gst_sdp_message_free(ptr); + private static final class Handle extends NativeObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GSTSDPMESSAGE_API.gst_sdp_message_free(ptr.getPointer()); + } + } } diff --git a/src/org/freedesktop/gstreamer/SDPResult.java b/src/org/freedesktop/gstreamer/SDPResult.java index 8ca26724..e3f52fd9 100644 --- a/src/org/freedesktop/gstreamer/SDPResult.java +++ b/src/org/freedesktop/gstreamer/SDPResult.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2018 Antonio Morales * * This file is part of gstreamer-java. @@ -16,21 +17,27 @@ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.glib.NativeEnum; import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; /** - * The possible results for {@link SDPMessage} functions + * Return values for SDP functions + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gst-plugins-base-libs/html/gst-plugins-base-libs-GstSDPMessage.html#GstSDPResult + * + * @see SDPMessage */ -public enum SDPResult implements IntegerEnum { +public enum SDPResult implements NativeEnum { /** A successful return value*/ OK(0), - /** A function to SDPMessage was given invalid paramters */ - EINVAL(-1), - /** An unknown result */ + /** A function to SDPMessage was given invalid parameters */ @DefaultEnumValue - __UNKNWON_NATIVE_VALUE(~0); + EINVAL(-1); + private final int value; + SDPResult(int value) { this.value = value; } @@ -39,9 +46,9 @@ public enum SDPResult implements IntegerEnum { * Gets the integer value of the enum * @return the integer value for this enum. */ + @Override public int intValue() { return value; } - private int value; } diff --git a/src/org/freedesktop/gstreamer/Sample.java b/src/org/freedesktop/gstreamer/Sample.java index 7c3769b2..4f2f39f3 100644 --- a/src/org/freedesktop/gstreamer/Sample.java +++ b/src/org/freedesktop/gstreamer/Sample.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2015 Neil C Smith + * Copyright (c) 2020 Christophe Lafolet + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2007, 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans @@ -18,62 +19,76 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import static org.freedesktop.gstreamer.lowlevel.GstSampleAPI.GSTMESSAGE_API; +import static org.freedesktop.gstreamer.lowlevel.GstSampleAPI.GSTSAMPLE_API; /** - * Lightweight objects to signal the occurrence of pipeline events. - * - *

Messages are implemented as a subclass of {@link MiniObject} with a generic - * {@link Structure} as the content. This allows for writing custom messages without - * requiring an API change while allowing a wide range of different types - * of messages. - * - *

Messages are posted by objects in the pipeline and are passed to the - * application using the {@link Bus}. - - * The basic use pattern of posting a message on a Bus is as follows: - * - * - * Posting a Message - * - * bus.post(new EOSMessage(source)); - * - * + * A Sample is a small object containing data, a type, timing and extra + * arbitrary information. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstSample.html * - * An {@link Element} usually posts messages on the bus provided by the parent - * container using {@link Element#postMessage postMessage()}. */ public class Sample extends MiniObject { + public static final String GTYPE_NAME = "GstSample"; - /** - * Creates a new instance of Message. - * - * @param init internal initialization data. - */ - public Sample(Initializer init) { + Sample(Initializer init) { super(init); } - + + /** + * Get the {@link Caps} associated with sample, or NULL when there is no caps. + * The caps remains valid as long as sample is valid. + * If you need to hold on to the caps for longer than that, take a ref to the caps + * + * @return caps of sample or NULL when there is no caps. + */ public Caps getCaps() { - return GSTMESSAGE_API.gst_sample_get_caps(this); + return GSTSAMPLE_API.gst_sample_get_caps(this); } /** - * Gets the buffer + * Set the {@link Caps} associated with sample. + * This sample must be writable. + * + * Since GStreamer 1.16 * - * @return the buffer + * @param caps + */ + @Gst.Since(minor = 16) + public void setCaps(Caps caps) { + Gst.checkVersion(1, 16); + GSTSAMPLE_API.gst_sample_set_caps(this, caps); + } + + /** + * Get the {@link Buffer} associated with sample, or NULL when there is no + * buffer. + * The buffer remains valid as long as sample is valid. + * If you need to hold on to it for longer than that, take a ref to the buffer. + * + * @return buffer of sample or NULL when there is no buffer. */ public Buffer getBuffer() { -// Pointer ptr = gst.gst_sample_get_buffer(this); -// -// // Buffer is owned alongside sample - freeing sample -// // will unref the buffer so add another ref -// return new Buffer(new Initializer(ptr, true, true)); - return GSTMESSAGE_API.gst_sample_get_buffer(this); + return GSTSAMPLE_API.gst_sample_get_buffer(this); + } + + /** + * Set the {@link Buffer} associated with sample. + * This sample must be writable. + * + * Since GStreamer 1.16 + * + * @param buffer + */ + @Gst.Since(minor = 16) + public void setBuffer(Buffer buffer) { + Gst.checkVersion(1, 16); + GSTSAMPLE_API.gst_sample_set_buffer(this, buffer); } } diff --git a/src/org/freedesktop/gstreamer/Segment.java b/src/org/freedesktop/gstreamer/Segment.java index e1f81723..5c3b753b 100644 --- a/src/org/freedesktop/gstreamer/Segment.java +++ b/src/org/freedesktop/gstreamer/Segment.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2010 David Hoyt * Copyright (c) 2010 David Hoyt * * This file is part of gstreamer-java. diff --git a/src/org/freedesktop/gstreamer/SegmentFlags.java b/src/org/freedesktop/gstreamer/SegmentFlags.java index 243ecc07..97e31706 100644 --- a/src/org/freedesktop/gstreamer/SegmentFlags.java +++ b/src/org/freedesktop/gstreamer/SegmentFlags.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -17,35 +18,49 @@ */ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.event.SeekFlags; +import org.freedesktop.gstreamer.glib.NativeFlags; + /** * GstSegmentFlags */ -public final class SegmentFlags { +enum SegmentFlags implements NativeFlags { - /** - * no flags - */ - public static final int NONE = SeekFlags.NONE; +// /** +// * no flags +// */ +// public static final int NONE = SeekFlags.NONE; /** * reset the pipeline running_time to the segment running_time */ - public static final int RESET = SeekFlags.FLUSH; + RESET(SeekFlags.FLUSH), + /** - * perform skip playback (Since 1.6) + * perform skip playback */ - public static final int TRICKMODE = SeekFlags.TRICKMODE; - @Deprecated - public static final int SKIP = SeekFlags.TRICKMODE; + TRICKMODE(SeekFlags.TRICKMODE), /** * send SEGMENT_DONE instead of EOS */ - public static final int SEGMENT = SeekFlags.SEGMENT; + SEGMENT(SeekFlags.SEGMENT), /** - * Decode only keyframes, where possible (Since 1.6) + * Decode only keyframes, where possible */ - public static final int TRICKMODE_KEY_UNITS = SeekFlags.TRICKMODE_KEY_UNITS; + TRICKMODE_KEY_UNITS(SeekFlags.TRICKMODE_KEY_UNITS), /** * Do not decode any audio, where possible (Since 1.6) */ - public static final int TRICKMODE_NO_AUDIO = SeekFlags.TRICKMODE_NO_AUDIO; + TRICKMODE_NO_AUDIO(SeekFlags.TRICKMODE_NO_AUDIO); + + private final SeekFlags seekFlags; + + private SegmentFlags(SeekFlags seekFlags) { + this.seekFlags = seekFlags; + } + + @Override + public int intValue() { + return seekFlags.intValue(); + } + } diff --git a/src/org/freedesktop/gstreamer/State.java b/src/org/freedesktop/gstreamer/State.java index a8454c99..ddb07184 100644 --- a/src/org/freedesktop/gstreamer/State.java +++ b/src/org/freedesktop/gstreamer/State.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,47 +16,66 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.lowlevel.EnumMapper; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.glib.NativeEnum; +//import org.freedesktop.gstreamer.lowlevel.EnumMapper; /** - * The posible states an element can be in. + * The possible states an element can be in. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstElement.html#GstState + *

*/ -public enum State implements IntegerEnum { - /** No pending state. */ +public enum State implements NativeEnum { + /** + * No pending state. + */ VOID_PENDING(0), - /** The initial state of an {@link Element}. */ + /** + * The initial state of an {@link Element}. + */ NULL(1), - /** The {@link Element} is ready to go to PAUSED. */ + /** + * The {@link Element} is ready to go to PAUSED. + */ READY(2), - /** The {@link Element} is PAUSED */ + /** + * The {@link Element} is PAUSED + */ PAUSED(3), - /** The {@link Element} is PLAYING */ + /** + * The {@link Element} is PLAYING + */ PLAYING(4); + + private final int value; - State(int value) { + private State(int value) { this.value = value; } - + /** * Gets the integer value of the enum. + * * @return The integer value for this enum. */ + @Override public int intValue() { return value; } - private final int value; - - /** - * Returns the enum constant of this type with the specified integer value. - * @param state integer value. - * @return Enum constant. - * @throws java.lang.IllegalArgumentException if the enum type has no constant with the specified value. - */ - public static final State valueOf(int state) { - return EnumMapper.getInstance().valueOf(state, State.class); - } + +// /** +// * Returns the enum constant of this type with the specified integer value. +// * +// * @param state integer value. +// * @return Enum constant. +// * @throws java.lang.IllegalArgumentException if the enum type has no +// * constant with the specified value. +// */ +// public static final State valueOf(int state) { +// return EnumMapper.getInstance().valueOf(state, State.class); +// } } diff --git a/src/org/freedesktop/gstreamer/StateChangeReturn.java b/src/org/freedesktop/gstreamer/StateChangeReturn.java index 359c56af..405ab879 100644 --- a/src/org/freedesktop/gstreamer/StateChangeReturn.java +++ b/src/org/freedesktop/gstreamer/StateChangeReturn.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -18,21 +19,39 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.NativeEnum; + /** * The possible return values from a state change function. *

* Only {@link StateChangeReturn#FAILURE} is a real failure. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstElement.html#GstStateChangeReturn + *

*/ -public enum StateChangeReturn { +public enum StateChangeReturn implements NativeEnum { /** The state change failed. */ - FAILURE, + FAILURE(0), /** The state change succeeded. */ - SUCCESS, + SUCCESS(1), /** The state change will happen asynchronously. */ - ASYNC, + ASYNC(2), /** * The state change succeeded but the {@link Element} cannot produce data in * {@link State#PAUSED}. This typically happens with live sources. */ - NO_PREROLL; + NO_PREROLL(3); + + private final int value; + + private StateChangeReturn(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } } diff --git a/src/org/freedesktop/gstreamer/StaticPadTemplate.java b/src/org/freedesktop/gstreamer/StaticPadTemplate.java index f75d7e18..57ab89e6 100644 --- a/src/org/freedesktop/gstreamer/StaticPadTemplate.java +++ b/src/org/freedesktop/gstreamer/StaticPadTemplate.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,7 +16,6 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; public class StaticPadTemplate { @@ -35,6 +35,7 @@ public class StaticPadTemplate { /** * Get the name of the template. + * * @return The name of the template. */ public String getName() { @@ -43,6 +44,7 @@ public String getName() { /** * Get the direction (SINK, SRC) of the template. + * * @return The {@link PadDirection} of the template. */ public PadDirection getDirection() { @@ -51,13 +53,16 @@ public PadDirection getDirection() { /** * Get the presence (ALWAYS, SOMETIMES, REQUEST) of the template. + * * @return The {@link PadPresence} of this template. */ public PadPresence getPresence() { return presence; } + /** * Get the {@link Caps} of the template. + * * @return The {@link Caps} for this template. */ public Caps getCaps() { diff --git a/src/org/freedesktop/gstreamer/StreamInfo.java b/src/org/freedesktop/gstreamer/StreamInfo.java deleted file mode 100644 index af0cbd4b..00000000 --- a/src/org/freedesktop/gstreamer/StreamInfo.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2009 Andres Colubri - * Copyright (c) 2008 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import com.sun.jna.Pointer; - -/** - * Object containing specific meta information such as width/height/framerate of - * video streams or samplerate/number of channels of audio. Each stream info object - * has the following properties: - * "object" (GstObject) (the decoder source pad usually) - * "type" (enum) (if this is an audio/video/subtitle stream) - * "decoder" (string) (name of decoder used to decode this stream) - * "mute" (boolean) (to mute or unmute this stream) - * "caps" (GstCaps) (caps of the decoded stream) - * "language-code" (string) (ISO-639 language code for this stream, mostly used for audio/subtitle streams) - * "codec" (string) (format this stream was encoded in) - */ -public class StreamInfo extends GObject { - - public enum StreamType { - UNKNOWN, - AUDIO, /* an audio stream */ - VIDEO, /* a video stream */ - TEXT, /* a subtitle/text stream */ - SUBPICTURE, /* a subtitle in picture-form */ - ELEMENT /* stream handled by an element */ - } - - /** - * For internal gstreamer-java use only - * - * @param init initialization data - */ - public StreamInfo(Initializer init) { - super(init); - } - - public StreamInfo(Pointer ptr, boolean needRef, boolean ownsHandle) { - super(initializer(ptr, needRef, ownsHandle)); - } - - public StreamType getStreamType() { - - int typeVal = (Integer) get("type"); - - assert typeVal < StreamType.values().length: "unknown value for GstStreamType enum - maybe gststreaminfo.h has changed"; - - if (typeVal < StreamType.values().length) { - return StreamType.values()[typeVal]; - } - - return StreamType.UNKNOWN; - } - - /** Source Pad or object of the stream */ - public GstObject getObject() { - Object object = get("object"); - assert object == null || object instanceof GstObject; - return object instanceof GstObject ? (GstObject) object : null; - } - - public String getDecoder() { - return (String) get("decoder"); - } - - public boolean getMute() { - return (Boolean) get("mute"); - } - - public Caps getCaps() { - return (Caps) get("caps"); - } - - public String getLanguageCode() { - return (String) get("language-code"); - } - - public String getCodec() { - return (String) get("codec"); - } -} diff --git a/src/org/freedesktop/gstreamer/Structure.java b/src/org/freedesktop/gstreamer/Structure.java index 988c0a3c..d0c6deca 100644 --- a/src/org/freedesktop/gstreamer/Structure.java +++ b/src/org/freedesktop/gstreamer/Structure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Neil C Smith + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (C) 2009 Tamas Korodi * Copyright (C) 2007 Wayne Meissner @@ -17,36 +17,44 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import com.sun.jna.Pointer; import com.sun.jna.ptr.PointerByReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import org.freedesktop.gstreamer.glib.GObject; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GPointer; import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GValueAPI; -import org.freedesktop.gstreamer.lowlevel.NativeObject; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import static org.freedesktop.gstreamer.lowlevel.GstStructureAPI.GSTSTRUCTURE_API; import static org.freedesktop.gstreamer.lowlevel.GstValueAPI.GSTVALUE_API; /** * Generic structure containing fields of names and values. *

- * A Structure is a collection of key/value pairs. The keys are expressed - * as GQuarks and the values can be of any GType. + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstStructure.html + *

+ * A Structure is a collection of key/value pairs. The keys are expressed as + * GQuarks and the values can be of any GType. *

* In addition to the key/value pairs, a Structure also has a name. The name - * starts with a letter and can be followed by letters, numbers and any of "/-_.:". + * starts with a letter and can be followed by letters, numbers and any of + * "/-_.:". *

- * Structure is used by various GStreamer subsystems to store information - * in a flexible and extensible way. + * Structure is used by various GStreamer subsystems to store information in a + * flexible and extensible way. *

- * A Structure can be created with new {@link #Structure(String)} or + * A Structure can be created with new {@link #Structure(String)} or * {@link #Structure(String, String, Object...)}, which both take a name and an * optional set of key/value pairs along with the types of the values. *

@@ -55,197 +63,265 @@ * Field values can be retrieved with get{Integer,String}() etc functions. *

* Fields can be removed with {@link #removeField} or {@link #removeFields} + * * @see Caps * @see Event */ public class Structure extends NativeObject { - - /** - * Creates a new instance of Structure - */ - public Structure(Initializer init) { - super(init); - } - - private Structure(Pointer ptr) { - this(initializer(ptr)); - } + public static final String GTYPE_NAME = "GstStructure"; + /** * Creates a new, empty #GstStructure with the given name. * * @param name The name of new structure. */ public Structure(String name) { - this(GSTSTRUCTURE_API.ptr_gst_structure_new_empty(name)); + this(new Handle(new GPointer(GSTSTRUCTURE_API.ptr_gst_structure_new_empty(name)), true)); } /** - * Creates a new Structure with the given name. Parses the - * list of variable arguments and sets fields to the values listed. - * Variable arguments should be passed as field name, field type, - * and value. + * Creates a new Structure with the given name. Parses the list of variable + * arguments and sets fields to the values listed. Variable arguments should + * be passed as field name, field type, and value. * * @param name The name of new structure. * @param firstFieldName The name of first field to set * @param data Additional arguments. */ public Structure(String name, String firstFieldName, Object... data) { - this(GSTSTRUCTURE_API.ptr_gst_structure_new(name, firstFieldName, data)); + this(new Handle(new GPointer(GSTSTRUCTURE_API.ptr_gst_structure_new(name, firstFieldName, data)), true)); } - /** - * Creates a Structure from a string representation. - * - * @param data A string representation of a Structure. - * @return A new Structure or null when the string could not be parsed. + * Creates a new instance of Structure */ - public static Structure fromString(String data) { - return new Structure(GSTSTRUCTURE_API.ptr_gst_structure_from_string(data, new PointerByReference())); + Structure(Initializer init) { + this(new Handle(init.ptr, init.ownsHandle)); + } + + private Structure(Handle handle) { + super(handle); } public Structure copy() { return GSTSTRUCTURE_API.gst_structure_copy(this); } - public class InvalidFieldException extends RuntimeException { - - private static final long serialVersionUID = 864118748304334069L; - - public InvalidFieldException(String type, String fieldName) { - super(String.format("Structure does not contain %s field '%s'", type, fieldName)); + public boolean fixateNearestInteger(String field, Integer value) { + return GSTSTRUCTURE_API.gst_structure_fixate_field_nearest_int(this, field, value); + } + /** + * Get the value of the named field as a boolean. Throws + * {@link InvalidFieldException} if the Structure does not contain the named + * field or the named field is not a boolean. + * + * @param fieldName name of field + * @return boolean value of the named field + */ + public boolean getBoolean(String fieldName) { + int[] val = {0}; + if (!GSTSTRUCTURE_API.gst_structure_get_boolean(this, fieldName, val)) { + throw new InvalidFieldException("boolean", fieldName); } + return val[0] != 0; } /** - * Gets ValueList field representation - * @param fieldName The name of the field. - * @return field as ValueList - */ - @Deprecated - public ValueList getValueList(String fieldName) { - GValue val = GSTSTRUCTURE_API.gst_structure_get_value(this, fieldName); - if (val == null) { - throw new InvalidFieldException("ValueList", fieldName); - } - return new ValueList(val); - } - - /** - * Get the value of the named field. Throws {@link InvalidFieldException} if - * the Structure does not contain the named field. - * + * Get the value of the named field as a double. Throws + * {@link InvalidFieldException} if the Structure does not contain the named + * field or the named field is not a double. + * * @param fieldName name of field - * @return Object representation of the named field + * @return double value of the named field */ - public Object getValue(String fieldName) { - GValue val = GSTSTRUCTURE_API.gst_structure_get_value(this, fieldName); - - if (val == null) { - throw new InvalidFieldException("Object", fieldName); - } + public double getDouble(String fieldName) { + double[] val = {0d}; + if (!GSTSTRUCTURE_API.gst_structure_get_double(this, fieldName, val)) { + throw new InvalidFieldException("double", fieldName); + } + return val[0]; + } - return val.getValue(); + /** + * Extract the values of the named field as an array of doubles. If the + * native GType of the field is a GValueArray then this method will return + * an array of the contained values, assuming all contained values are of + * G_TYPE_DOUBLE. Else if the field is of G_TYPE_DOUBLE a single value array + * will be returned. + *

+ * This method will create a new array each time. If you are repeatedly + * calling this method consider using + * {@link #getDoubles(java.lang.String, double[])}. + *

+ * Throws {@link InvalidFieldException} if the field does not exist, or the + * field values contained are not of type G_TYPE_DOUBLE. + *

+ * This method only currently supports lists of values inside a GValueArray + * - other native list types will be supported in future. + * + * @param fieldName name of field + * @return List of values from the named field + */ + public double[] getDoubles(String fieldName) { + return getDoubles(fieldName, null); } - + /** - * Extract the values of the named field as a List of the provided type. - * If the native GType of the field is a GValueArray then this - * method will return a list of the contained values, assuming all contained - * values can be converted to the provided Java type T. Else if the field GType - * can be directly converted to the provided Java type T, a singleton List will - * be returned. + * Extract the values of the named field as an array of doubles. If the + * native GType of the field is a GValueArray then this method will return + * an array of the contained values, assuming all contained values are of + * G_TYPE_DOUBLE. Else if the field is of G_TYPE_DOUBLE a single value array + * will be returned. + *

+ * An array may be passed into this method to contain the result. A new + * array will be created if the array is null or not of the correct length. *

* Throws {@link InvalidFieldException} if the field does not exist, or the - * field values cannot be converted to type T. + * field values contained are not of type G_TYPE_DOUBLE. *

- * This method only currently supports lists of values inside a GValueArray - - * other native list types will be supported in future. - * - * @param - * @param type type of values + * This method only currently supports lists of values inside a GValueArray + * - other native list types will be supported in future. + * * @param fieldName name of field + * @param array an array to hold values, or null * @return List of values from the named field */ - public List getValues(Class type, String fieldName) { + public double[] getDoubles(String fieldName, double[] array) { Object val = getValue(fieldName); if (val instanceof GValueAPI.GValueArray) { GValueAPI.GValueArray arr = (GValueAPI.GValueArray) val; int count = arr.getNValues(); - List values = new ArrayList(count); + double[] values = array == null || array.length != count + ? new double[count] : array; for (int i = 0; i < count; i++) { - Object o = arr.getValue(i); - if (type.isInstance(o)) { - values.add(type.cast(o)); + GValue gval = arr.nth(i); + if (gval.checkHolds(GType.DOUBLE)) { + values[i] = GValueAPI.GVALUE_API.g_value_get_double(gval); } else { - throw new InvalidFieldException(type.getSimpleName(), fieldName); + throw new InvalidFieldException("doubles", fieldName); } } return values; } else { - if (type.isInstance(val)) { - return Collections.singletonList(type.cast(val)); + if (Double.class.isInstance(val)) { + double[] values = array == null || array.length != 1 + ? new double[1] : array; + values[0] = ((Double) val); + return values; } else { - throw new InvalidFieldException(type.getSimpleName(), fieldName); + throw new InvalidFieldException("double", fieldName); } } } + + /** + * Get the number of fields in the {@link Structure}. + * + * @return the structure's filed number. + */ + public int getFields() { + return GSTSTRUCTURE_API.gst_structure_n_fields(this); + } + + + /** + * Gets FOURCC field int representation + * + * @param fieldName The name of the field. + * @return FOURCC field as a 4 byte integer + */ + public int getFourcc(String fieldName) { + int[] val = {0}; + if (!GSTSTRUCTURE_API.gst_structure_get_fourcc(this, fieldName, val)) { + throw new InvalidFieldException("FOURCC", fieldName); + } + return val[0]; + } + + /** + * Gets FOURCC field String representation + * + * @param fieldName The name of the field. + * @return FOURCC field as a String + */ + public String getFourccString(String fieldName) { + int f = getFourcc(fieldName); + byte[] b = {(byte) ((f >> 0) & 0xff), (byte) ((f >> 8) & 0xff), + (byte) ((f >> 16) & 0xff), (byte) ((f >> 24) & 0xff)}; + return new String(b); + } /** - * Get the value of the named field as an int. Throws {@link InvalidFieldException} if - * the Structure does not contain the named field or the named field is not an - * integer. - * + * Get the value of the named field as a Fraction. Throws + * {@link InvalidFieldException} if the Structure does not contain the named + * field or the named field is not a Fraction. + * + * @param fieldName name of field + * @return boolean value of the named field + */ + public Fraction getFraction(String fieldName) { + int[] numerator = {0}; + int[] denominator = {0}; + if (!GSTSTRUCTURE_API.gst_structure_get_fraction(this, fieldName, numerator, denominator)) { + throw new InvalidFieldException("fraction", fieldName); + } + return new Fraction(numerator[0], denominator[0]); + } + /** + * Get the value of the named field as an int. Throws + * {@link InvalidFieldException} if the Structure does not contain the named + * field or the named field is not an integer. + * * @param fieldName name of field * @return int value of the named field */ public int getInteger(String fieldName) { - int[] val = { 0 }; + int[] val = {0}; if (!GSTSTRUCTURE_API.gst_structure_get_int(this, fieldName, val)) { throw new InvalidFieldException("integer", fieldName); } return val[0]; } - + /** - * Extract the values of the named field as an array of integers. - * If the native GType of the field is a GValueArray then this - * method will return an array of the contained values, assuming all contained - * values are of G_TYPE_INT. Else if the field is of G_TYPE_INT a single value - * array will be returned. + * Extract the values of the named field as an array of integers. If the + * native GType of the field is a GValueArray then this method will return + * an array of the contained values, assuming all contained values are of + * G_TYPE_INT. Else if the field is of G_TYPE_INT a single value array will + * be returned. *

- * This method will create a new array each time. If you are repeatedly calling - * this method consider using {@link #getIntegers(java.lang.String, int[])}. + * This method will create a new array each time. If you are repeatedly + * calling this method consider using + * {@link #getIntegers(java.lang.String, int[])}. *

* Throws {@link InvalidFieldException} if the field does not exist, or the * field values contained are not of type G_TYPE_INT. *

- * This method only currently supports lists of values inside a GValueArray - - * other native list types will be supported in future. - * + * This method only currently supports lists of values inside a GValueArray + * - other native list types will be supported in future. + * * @param fieldName name of field * @return List of values from the named field */ public int[] getIntegers(String fieldName) { return getIntegers(fieldName, null); } - /** - * Extract the values of the named field as an array of integers. - * If the native GType of the field is a GValueArray then this - * method will return an array of the contained values, assuming all contained - * values are of G_TYPE_INT. Else if the field is of G_TYPE_INT a single value - * array will be returned. + * Extract the values of the named field as an array of integers. If the + * native GType of the field is a GValueArray then this method will return + * an array of the contained values, assuming all contained values are of + * G_TYPE_INT. Else if the field is of G_TYPE_INT a single value array will + * be returned. *

- * An array may be passed into this method to contain the result. A new array - * will be created if the array is null or not of the correct length. + * An array may be passed into this method to contain the result. A new + * array will be created if the array is null or not of the correct length. *

* Throws {@link InvalidFieldException} if the field does not exist, or the * field values contained are not of type G_TYPE_INT. *

- * This method only currently supports lists of values inside a GValueArray - - * other native list types will be supported in future. - * + * This method only currently supports lists of values inside a GValueArray + * - other native list types will be supported in future. + * * @param fieldName name of field * @param array an array to hold values, or null * @return List of values from the named field @@ -255,8 +331,8 @@ public int[] getIntegers(String fieldName, int[] array) { if (val instanceof GValueAPI.GValueArray) { GValueAPI.GValueArray arr = (GValueAPI.GValueArray) val; int count = arr.getNValues(); - int[] values = array == null || array.length != count ? - new int[count] : array; + int[] values = array == null || array.length != count + ? new int[count] : array; for (int i = 0; i < count; i++) { GValue gval = arr.nth(i); if (gval.checkHolds(GType.INT)) { @@ -268,8 +344,8 @@ public int[] getIntegers(String fieldName, int[] array) { return values; } else { if (Integer.class.isInstance(val)) { - int[] values = array == null || array.length != 1 ? - new int[1] : array; + int[] values = array == null || array.length != 1 + ? new int[1] : array; values[0] = ((Integer) val); return values; } else { @@ -277,107 +353,56 @@ public int[] getIntegers(String fieldName, int[] array) { } } } - - @Deprecated - public int getInteger(String fieldName, int i) { - return getValueList(fieldName).getInteger(i); + + /** + * Sets the name of the structure to the given name. + * + * The name must not be empty, must start with a letter and can be followed + * by letters, numbers and any of "/-_.:". + * + * @param name The new name of the structure. + */ + public void setName(String name) { + GSTSTRUCTURE_API.gst_structure_set_name(this, name); } /** - * Get the value of the named field as a double. Throws {@link InvalidFieldException} if - * the Structure does not contain the named field or the named field is not a - * double. - * - * @param fieldName name of field - * @return double value of the named field + * Get the name of @structure as a string. + * + * @return The name of the structure. */ - public double getDouble(String fieldName) { - double[] val = { 0d }; - if (!GSTSTRUCTURE_API.gst_structure_get_double(this, fieldName, val)) { - throw new InvalidFieldException("double", fieldName); - } - return val[0]; + public String getName() { + return GSTSTRUCTURE_API.gst_structure_get_name(this); } /** - * Extract the values of the named field as an array of doubles. - * If the native GType of the field is a GValueArray then this - * method will return an array of the contained values, assuming all contained - * values are of G_TYPE_DOUBLE. Else if the field is of G_TYPE_DOUBLE a single value - * array will be returned. - *

- * This method will create a new array each time. If you are repeatedly calling - * this method consider using {@link #getDoubles(java.lang.String, double[])}. - *

- * Throws {@link InvalidFieldException} if the field does not exist, or the - * field values contained are not of type G_TYPE_DOUBLE. - *

- * This method only currently supports lists of values inside a GValueArray - - * other native list types will be supported in future. - * - * @param fieldName name of field - * @return List of values from the named field + * Get the @structure's ith field name as a string. + * + * @param i the requested filed number + * @return The name of the structure. */ - public double[] getDoubles(String fieldName) { - return getDoubles(fieldName, null); + public String getName(int i) { + return GSTSTRUCTURE_API.gst_structure_nth_field_name(this, i); } - + /** - * Extract the values of the named field as an array of doubles. - * If the native GType of the field is a GValueArray then this - * method will return an array of the contained values, assuming all contained - * values are of G_TYPE_DOUBLE. Else if the field is of G_TYPE_DOUBLE a single value - * array will be returned. - *

- * An array may be passed into this method to contain the result. A new array - * will be created if the array is null or not of the correct length. - *

- * Throws {@link InvalidFieldException} if the field does not exist, or the - * field values contained are not of type G_TYPE_DOUBLE. - *

- * This method only currently supports lists of values inside a GValueArray - - * other native list types will be supported in future. - * + * Get the value of the named field as a Range. Throws + * {@link InvalidFieldException} if the Structure does not contain the named + * field. + * * @param fieldName name of field - * @param array an array to hold values, or null - * @return List of values from the named field + * @return Range value of the named field */ - public double[] getDoubles(String fieldName, double[] array) { - Object val = getValue(fieldName); - if (val instanceof GValueAPI.GValueArray) { - GValueAPI.GValueArray arr = (GValueAPI.GValueArray) val; - int count = arr.getNValues(); - double[] values = array == null || array.length != count ? - new double[count] : array; - for (int i = 0; i < count; i++) { - GValue gval = arr.nth(i); - if (gval.checkHolds(GType.DOUBLE)) { - values[i] = GValueAPI.GVALUE_API.g_value_get_double(gval); - } else { - throw new InvalidFieldException("doubles", fieldName); - } - } - return values; - } else { - if (Double.class.isInstance(val)) { - double[] values = array == null || array.length != 1 ? - new double[1] : array; - values[0] = ((Double) val); - return values; - } else { - throw new InvalidFieldException("double", fieldName); - } + public Range getRange(String fieldName) { + GValue val = GSTSTRUCTURE_API.gst_structure_get_value(this, fieldName); + if (val == null) { + throw new InvalidFieldException("Range", fieldName); } + return new Range(val); } - - @Deprecated - public double getDouble(String fieldName, int i) { - return getValueList(fieldName).getDouble(i); - } - /** * Get the value of the named field as a String. - * + * * @param fieldName name of field * @return String value of the named field */ @@ -385,155 +410,97 @@ public String getString(String fieldName) { return GSTSTRUCTURE_API.gst_structure_get_string(this, fieldName); } - @Deprecated - public String getString(String fieldName, int i) { - return getValueList(fieldName).getString(i); - } /** - * Get the value of the named field as a boolean. Throws {@link InvalidFieldException} if - * the Structure does not contain the named field or the named field is not a - * boolean. - * - * @param fieldName name of field - * @return boolean value of the named field - */ - public boolean getBoolean(String fieldName) { - int[] val = { 0 }; - if (!GSTSTRUCTURE_API.gst_structure_get_boolean(this, fieldName, val)) { - throw new InvalidFieldException("boolean", fieldName); - } - return val[0] != 0; - } - - @Deprecated - public boolean getBoolean(String fieldName, int i) { - return getValueList(fieldName).getBoolean(i); - } - - /** - * Get the value of the named field as a Fraction. Throws {@link InvalidFieldException} if - * the Structure does not contain the named field or the named field is not a - * Fraction. - * - * @param fieldName name of field - * @return boolean value of the named field - */ - public Fraction getFraction(String fieldName) { - int[] numerator = { 0 }; - int[] denominator = { 0 }; - if (!GSTSTRUCTURE_API.gst_structure_get_fraction(this, fieldName, numerator, denominator)) { - throw new InvalidFieldException("fraction", fieldName); - } - return new Fraction(numerator[0], denominator[0]); - } - /** - * Gets FOURCC field int representation - * @param fieldName The name of the field. - * @return FOURCC field as a 4 byte integer - */ - public int getFourcc(String fieldName) { - int[] val = { 0 }; - if (!GSTSTRUCTURE_API.gst_structure_get_fourcc(this, fieldName, val)) { - throw new InvalidFieldException("FOURCC", fieldName); - } - return val[0]; - } - /** - * Gets FOURCC field String representation - * @param fieldName The name of the field. - * @return FOURCC field as a String - */ - public String getFourccString(String fieldName) { - int f = getFourcc(fieldName); - byte[] b = {(byte)((f>>0)&0xff),(byte)((f>>8)&0xff), - (byte)((f>>16)&0xff),(byte)((f>>24)&0xff)}; - return new String(b); - } - /** - * Get the value of the named field as a Range. Throws {@link InvalidFieldException} if + * Get the value of the named field. Throws {@link InvalidFieldException} if * the Structure does not contain the named field. - * + * * @param fieldName name of field - * @return Range value of the named field + * @return Object representation of the named field */ - public Range getRange(String fieldName) { - GValue val = GSTSTRUCTURE_API.gst_structure_get_value(this, fieldName); + public Object getValue(String fieldName) { + GValue val = GSTSTRUCTURE_API.gst_structure_get_value(this, fieldName); + if (val == null) { - throw new InvalidFieldException("Range", fieldName); + throw new InvalidFieldException("Object", fieldName); } - return new Range(val); - } - public boolean fixateNearestInteger(String field, Integer value) { - return GSTSTRUCTURE_API.gst_structure_fixate_field_nearest_int(this, field, value); + return val.getValue(); } - + /** - * Sets an integer field in the structure. - * - * @param field the name of the field to set. - * @param value the value to set for the field. + * Extract the values of the named field as a List of the provided type. If + * the native GType of the field is a GValueArray then this method will + * return a list of the contained values, assuming all contained values can + * be converted to the provided Java type T. Else if the field GType can be + * directly converted to the provided Java type T, a singleton List will be + * returned. + *

+ * Throws {@link InvalidFieldException} if the field does not exist, or the + * field values cannot be converted to type T. + *

+ * This method currently supports lists of values inside a GValueArray or + * GstValueList. + * + * @param + * @param type type of values + * @param fieldName name of field + * @return List of values from the named field */ - public void setInteger(String field, Integer value) { - GSTSTRUCTURE_API.gst_structure_set(this, field, GType.INT, value); - } - - public void setValue(String field, GType type, Object value) { - GSTSTRUCTURE_API.gst_structure_set(this, field, type, value); - } - - public void setDouble(String field, Double value) { - GSTSTRUCTURE_API.gst_structure_set(this, field, GType.DOUBLE, value); - } + public List getValues(Class type, String fieldName) { + GValue gValue = GSTSTRUCTURE_API.gst_structure_get_value(this, fieldName); + if (gValue == null) { + throw new InvalidFieldException(type.getSimpleName(), fieldName); + } - public void setPointer(String field, Pointer value) { - GSTSTRUCTURE_API.gst_structure_set(this, field, GType.POINTER, value); - } + GType gType = gValue.getType(); + if (gType.equals(GSTVALUE_API.gst_value_list_get_type())) { + int size = GSTVALUE_API.gst_value_list_get_size(gValue); + ArrayList values = new ArrayList<>(size); + for (int i = 0; i values = new ArrayList(count); + for (int i = 0; i < count; i++) { + Object o = arr.getValue(i); + if (type.isInstance(o)) { + values.add(type.cast(o)); + } else { + throw new InvalidFieldException(type.getSimpleName(), fieldName); + } + } + return values; + } else { + if (type.isInstance(val)) { + return Collections.singletonList(type.cast(val)); + } else { + throw new InvalidFieldException(type.getSimpleName(), fieldName); + } + } } /** - * Get the name of @structure as a string. + * Check if the {@link Structure} contains a double field named fieldName. * - * @return The name of the structure. - */ - public String getName() { - return GSTSTRUCTURE_API.gst_structure_get_name(this); - } - - /** - * Sets the name of the structure to the given name. - * - * The name must not be empty, must start with a letter and can be followed - * by letters, numbers and any of "/-_.:". - * - * @param name The new name of the structure. - */ - public void setName(String name) { - GSTSTRUCTURE_API.gst_structure_set_name(this, name); - } - - /** - * Checks if the structure has the given name. - * - * @param name structure name to check for - * @return true if @name matches the name of the structure. + * @param fieldName The name of the field to check. + * @return true if the structure contains a double field named fieldName */ - public boolean hasName(String name) { - return GSTSTRUCTURE_API.gst_structure_has_name(this, name); + public boolean hasDoubleField(String fieldName) { + return hasField(fieldName, GType.DOUBLE); } + /** * Check if the {@link Structure} contains a field named fieldName. * @@ -544,37 +511,31 @@ public boolean hasField(String fieldName) { return GSTSTRUCTURE_API.gst_structure_has_field(this, fieldName); } - /** - * Get the number of fields in the {@link Structure}. - * - * @return the structure's filed number. - */ - public int getFields() { - return GSTSTRUCTURE_API.gst_structure_n_fields(this); - } - + /** * Check if the {@link Structure} contains a field named fieldName. * * @param fieldName The name of the field to check. * @param fieldType The type of the field. - * @return true if the structure contains a field named fieldName and of type fieldType + * @return true if the structure contains a field named fieldName and of + * type fieldType */ - public boolean hasField(String fieldName, GType fieldType) { + boolean hasField(String fieldName, GType fieldType) { return GSTSTRUCTURE_API.gst_structure_has_field_typed(this, fieldName, fieldType); } - + /** * Check if the {@link Structure} contains a field named fieldName. * * @param fieldName The name of the field to check. * @param fieldType The type of the field. - * @return true if the structure contains a field named fieldName and of type fieldType + * @return true if the structure contains a field named fieldName and of + * type fieldType */ public boolean hasField(String fieldName, Class fieldType) { return GSTSTRUCTURE_API.gst_structure_has_field_typed(this, fieldName, GType.valueOf(fieldType)); } - + /** * Check if the {@link Structure} contains an integer field named fieldName. * @@ -584,64 +545,149 @@ public boolean hasField(String fieldName, Class fieldType) { public boolean hasIntField(String fieldName) { return hasField(fieldName, GType.INT); } - /** - * Check if the {@link Structure} contains a double field named fieldName. + * Checks if the structure has the given name. * - * @param fieldName The name of the field to check. - * @return true if the structure contains a double field named fieldName + * @param name structure name to check for + * @return true if @name matches the name of the structure. */ - public boolean hasDoubleField(String fieldName) { - return hasField(fieldName, GType.DOUBLE); + public boolean hasName(String name) { + return GSTSTRUCTURE_API.gst_structure_has_name(this, name); } - /** - * Removes the field with the given name from the structure. - * If the field with the given name does not exist, the structure is unchanged. + * Checks that two structures are equal + * + * @param structure the structure to check if it's equal to this structure + * @return true if both structures are equal + */ + public boolean isEqual(Structure structure) { + return GSTSTRUCTURE_API.gst_structure_is_equal(this, structure); + } + + /** + * Removes the field with the given name from the structure. If the field + * with the given name does not exist, the structure is unchanged. + * * @param fieldName The name of the field to remove. */ public void removeField(String fieldName) { GSTSTRUCTURE_API.gst_structure_remove_field(this, fieldName); } - + /** - * Removes the fields with the given names. - * If a field does not exist, the argument is ignored. - * + * Removes the fields with the given names. If a field does not exist, the + * argument is ignored. + * * @param fieldNames A list of field names to remove. */ public void removeFields(String... fieldNames) { GSTSTRUCTURE_API.gst_structure_remove_fields(this, fieldNames); } - /** - * Get the @structure's ith field name as a string. - * @param i the requested filed number - * @return The name of the structure. - */ - public String getName(int i) { - return GSTSTRUCTURE_API.gst_structure_nth_field_name(this, i); + public void setDouble(String field, Double value) { + GSTSTRUCTURE_API.gst_structure_set(this, field, GType.DOUBLE, value); } + public void setDoubleRange(String field, Double min, Double max) { + GSTSTRUCTURE_API.gst_structure_set(this, field, + GSTVALUE_API.gst_double_range_get_type(), min, max); + } + + public void setFraction(String field, Integer numerator, Integer denominator) { + GSTSTRUCTURE_API.gst_structure_set(this, field, + GSTVALUE_API.gst_fraction_get_type(), numerator, denominator); + } /** - * Checks that two structures are equal - * @param structure the stucture to check if it's equal to this structure - * @return true if both structures are equal + * Sets an integer field in the structure. + * + * @param field the name of the field to set. + * @param value the value to set for the field. */ - public boolean isEqual(Structure structure) { - return GSTSTRUCTURE_API.gst_structure_is_equal(this, structure); + public void setInteger(String field, Integer value) { + GSTSTRUCTURE_API.gst_structure_set(this, field, GType.INT, value); + } + + public void setIntegerRange(String field, Integer min, Integer max) { + GSTSTRUCTURE_API.gst_structure_set(this, field, + GSTVALUE_API.gst_int_range_get_type(), min, max); } + /** + * Set an object field in the structure. + * + * @param field the name of the field to set. + * @param typeName a type into which the provided object could be coerced into. + * @param object the value to set for the field (must not be {@code null}). + */ + public void setObject(String field, String typeName, GObject object) { + GType type = GType.valueOf(typeName); + if (!GType.INVALID.equals(type)) { + if (object != null) { + GType realType = GType.valueOf(object.getTypeName()); + while (!realType.equals(GType.OBJECT) && !realType.equals(type)) { + realType = realType.getParentType(); + } + if (!realType.equals(type)) { + throw new IllegalArgumentException( + "Provided instance of " + object.getTypeName() + " is not a " + typeName); + } + setValue(field, type, object); + } else { + throw new IllegalArgumentException("Null object provided"); + } + } else { + throw new IllegalArgumentException("Unknown GType name: " + typeName); + } + } + + void setPointer(String field, Pointer value) { + GSTSTRUCTURE_API.gst_structure_set(this, field, GType.POINTER, value); + } + void setValue(String field, GType type, Object value) { + GSTSTRUCTURE_API.gst_structure_set(this, field, type, value); + } + @Override public String toString() { return GSTSTRUCTURE_API.gst_structure_to_string(this); } - public static Structure objectFor(Pointer ptr, boolean needRef, boolean ownsHandle) { - return NativeObject.objectFor(ptr, Structure.class, needRef, ownsHandle); + + /** + * Creates a Structure from a string representation. + * + * @param data A string representation of a Structure. + * @return A new Structure or null when the string could not be parsed. + */ + public static Structure fromString(String data) { + return new Structure( + new Handle( + new GPointer(GSTSTRUCTURE_API.ptr_gst_structure_from_string(data, new PointerByReference())) + , true)); } - //-------------------------------------------------------------------------- - protected void disposeNativeHandle(Pointer ptr) { - GSTSTRUCTURE_API.gst_structure_free(ptr); + + static Structure objectFor(Pointer ptr, boolean needRef, boolean ownsHandle) { + return Natives.objectFor(ptr, Structure.class, needRef, ownsHandle); } + public class InvalidFieldException extends RuntimeException { + + private static final long serialVersionUID = 864118748304334069L; + + public InvalidFieldException(String type, String fieldName) { + super(String.format("Structure does not contain %s field '%s'", type, fieldName)); + } + } + + private static final class Handle extends NativeObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GSTSTRUCTURE_API.gst_structure_free(ptr.getPointer()); + } + + } } diff --git a/src/org/freedesktop/gstreamer/Tag.java b/src/org/freedesktop/gstreamer/Tag.java index 871b34b1..0bec63ee 100644 --- a/src/org/freedesktop/gstreamer/Tag.java +++ b/src/org/freedesktop/gstreamer/Tag.java @@ -22,7 +22,7 @@ /** * GStreamer core tags */ -public enum Tag { +enum Tag { /** * The artist name as it should be displayed, e.g. 'Jimi Hendrix' or * 'The Guitar Heroes' diff --git a/src/org/freedesktop/gstreamer/TagFlag.java b/src/org/freedesktop/gstreamer/TagFlag.java index 1bfd1a98..1db1b5fd 100644 --- a/src/org/freedesktop/gstreamer/TagFlag.java +++ b/src/org/freedesktop/gstreamer/TagFlag.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2007 Wayne Meissner * Copyright (C) 2003 Benjamin Otte * @@ -19,18 +20,38 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; + /** * Extra tag flags used when registering tags. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstTagList.html#GstTagFlag + *

*/ -public enum TagFlag { +public enum TagFlag implements NativeEnum { /** Undefined flag. */ - UNDEFINED, + @DefaultEnumValue + UNDEFINED(0), /** Tag is meta data. */ - META, + META(1), /** Tag is encoded. */ - ENCODED, + ENCODED(2), /** Tag is decoded. */ - DECODED, + DECODED(3), /** Number of tag flags. */ - COUNT; + COUNT(4); + + private final int value; + + private TagFlag(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } } diff --git a/src/org/freedesktop/gstreamer/TagList.java b/src/org/freedesktop/gstreamer/TagList.java index c8f9cd9a..0b5013ef 100644 --- a/src/org/freedesktop/gstreamer/TagList.java +++ b/src/org/freedesktop/gstreamer/TagList.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2007 Wayne Meissner @@ -17,7 +18,6 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; @@ -37,23 +37,31 @@ import com.sun.jna.Pointer; import com.sun.jna.ptr.PointerByReference; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.glib.Natives; /** * List of tags and values used to describe media metadata. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstTagList.html + *

*/ -@SuppressWarnings("serial") +//@TODO consider handling Sample fields. public class TagList extends MiniObject { - + public static final String GTYPE_NAME = "GstTagList"; /** * Creates a new instance of TagList + * * @param init internal initialization data. */ - public TagList(Initializer init) { + TagList(Initializer init) { super(init); } - + /** * Constructs a new empty tag list. */ @@ -61,32 +69,32 @@ public TagList() { super(initializer()); } - private static Initializer initializer() { - final Pointer ptr_new_tag_list = GSTTAGLIST_API.ptr_gst_tag_list_new_empty(); - return initializer(ptr_new_tag_list); - } - + private static Initializer initializer() { + final Pointer ptr_new_tag_list = GSTTAGLIST_API.ptr_gst_tag_list_new_empty(); + return Natives.initializer(ptr_new_tag_list); + } + /** * Gets the number of values of type {@code tag} stored in the list. - * + * * @param tag the name of the tag to get the size of. * @return the number of values for {@code tag} in this list. */ public int getValueCount(String tag) { return GSTTAGLIST_API.gst_tag_list_get_tag_size(this, tag); } - + /** * Gets all data values for a tag contained in this list. - * + * * @param tag the name of the tag to retrieve. * @return the data associated with {@code tag}. */ public List getValues(final String tag) { final int size = getValueCount(tag); return new AbstractList() { - public int size() { - return size; + public int size() { + return size; } @Override @@ -95,20 +103,20 @@ public Object get(int index) { } }; } - - /** - * Gets all data values for a tag contained in this list. - * - * @param tag the name of the tag to retrieve. - * @return the data associated with {@code tag}. - */ - public List getValues(Tag tag) { - return getValues(tag.getId()); - } - + +// /** +// * Gets all data values for a tag contained in this list. +// * +// * @param tag the name of the tag to retrieve. +// * @return the data associated with {@code tag}. +// */ +// public List getValues(Tag tag) { +// return getValues(tag.getId()); +// } + /** * Gets data for a tag from this list. - * + * * @param tag the tag to retrieve. * @param index which element of the array of data for this tag to retrieve. * @return the data for the tag. @@ -117,21 +125,21 @@ public Object getValue(String tag, int index) { TagGetter get = MapHolder.getterMap.get(getTagType(tag)); return get != null ? get.get(this, tag, index) : ""; } - - /** - * Gets data for a tag from this list. - * - * @param tag the tag to retrieve. - * @param index which element of the array of data for this tag to retrieve. - * @return the data for the tag. - */ - public Object getValue(Tag tag, int index) { - return getValue(tag.getId(), index); - } - + +// /** +// * Gets data for a tag from this list. +// * +// * @param tag the tag to retrieve. +// * @param index which element of the array of data for this tag to retrieve. +// * @return the data for the tag. +// */ +// public Object getValue(Tag tag, int index) { +// return getValue(tag.getId(), index); +// } + /** * Gets a string tag from this list. - * + * * @param tag the tag to retrieve. * @param index which element of the array of data for this tag to retrieve. * @return the data for the tag. @@ -139,21 +147,21 @@ public Object getValue(Tag tag, int index) { public String getString(String tag, int index) { return getValue(tag, index).toString(); } - - /** - * Gets a string tag from this list. - * - * @param tag the tag to retrieve. - * @param index which element of the array of data for this tag to retrieve. - * @return the data for the tag. - */ - public String getString(Tag tag, int index) { - return getString(tag.getId(), index); - } - + +// /** +// * Gets a string tag from this list. +// * +// * @param tag the tag to retrieve. +// * @param index which element of the array of data for this tag to retrieve. +// * @return the data for the tag. +// */ +// public String getString(Tag tag, int index) { +// return getString(tag.getId(), index); +// } + /** * Gets a numeric tag from this list. - * + * * @param tag the tag to retrieve. * @param index which element of the array of data for this tag to retrieve. * @return the data for the tag. @@ -165,21 +173,21 @@ public Number getNumber(String tag, int index) { } return (Number) data; } - - /** - * Gets a numeric tag from this list. - * - * @param tag the tag to retrieve. - * @param index which element of the array of data for this tag to retrieve. - * @return the data for the tag. - */ - public Number getNumber(Tag tag, int index) { - return getNumber(tag.getId(), index); - } - + +// /** +// * Gets a numeric tag from this list. +// * +// * @param tag the tag to retrieve. +// * @param index which element of the array of data for this tag to retrieve. +// * @return the data for the tag. +// */ +// public Number getNumber(Tag tag, int index) { +// return getNumber(tag.getId(), index); +// } + /** * Gets a list of all the tags contained in this list. - * + * * @return a list of tag names. */ public List getTagNames() { @@ -191,10 +199,10 @@ public void callback(Pointer ptr, String tag, Pointer user_data) { }, null); return list; } - + /** - * Merges this tag list and {@code list2} into a new list. - * If {@code list2} is null, a copy of this list is returned. + * Merges this tag list and {@code list2} into a new list. If {@code list2} + * is null, a copy of this list is returned. * * @param list2 the other tag list to merge with this one. * @param mode the {@link TagMergeMode}. @@ -203,14 +211,14 @@ public void callback(Pointer ptr, String tag, Pointer user_data) { public TagList merge(TagList list2, TagMergeMode mode) { return GSTTAGLIST_API.gst_tag_list_merge(this, list2, mode); } - + /** * Gets the low level type for the tag. - * + * * @param tag the tag * @return the type of {@code tag} */ - private static GType getTagType(String tag) { + private static GType getTagType(String tag) { GType type = MapHolder.tagTypeMap.get(tag); if (type != null) { @@ -219,79 +227,83 @@ private static GType getTagType(String tag) { MapHolder.tagTypeMap.put(tag, type = GSTTAG_API.gst_tag_get_type(tag)); return type; } - + private static interface TagGetter { + Object get(TagList tl, String tag, int index); } - + // // Put the maps in a holder class so they don't get initialized until used. // This helps avoid calling gstreamer methods before Gst.init(). // private static final class MapHolder { - private static final Map getterMap = new HashMap() {{ - put(GType.INT, new TagGetter() { - public Object get(TagList tl, String tag, int index) { - int[] value = { 0 }; - GSTTAGLIST_API.gst_tag_list_get_int_index(tl, tag, index, value); - return value[0]; - } - }); - put(GType.UINT, new TagGetter() { - public Object get(TagList tl, String tag, int index) { - int[] value = { 0 }; - GSTTAGLIST_API.gst_tag_list_get_uint_index(tl, tag, index, value); - return value[0]; - } - }); - put(GType.INT64, new TagGetter() { - public Object get(TagList tl, String tag, int index) { - long[] value = { 0 }; - GSTTAGLIST_API.gst_tag_list_get_int64_index(tl, tag, index, value); - return value[0]; - } - }); - put(GType.DOUBLE, new TagGetter() { - public Object get(TagList tl, String tag, int index) { - double[] value = { 0d }; - GSTTAGLIST_API.gst_tag_list_get_double_index(tl, tag, index, value); - return value[0]; - } - }); - put(GType.STRING, new TagGetter() { - public Object get(TagList tl, String tag, int index) { - Pointer[] value = { null }; - GSTTAGLIST_API.gst_tag_list_get_string_index(tl, tag, index, value); - if (value[0] == null) { - return null; + + private static final Map getterMap = new HashMap() { + { + put(GType.INT, new TagGetter() { + public Object get(TagList tl, String tag, int index) { + int[] value = {0}; + GSTTAGLIST_API.gst_tag_list_get_int_index(tl, tag, index, value); + return value[0]; + } + }); + put(GType.UINT, new TagGetter() { + public Object get(TagList tl, String tag, int index) { + int[] value = {0}; + GSTTAGLIST_API.gst_tag_list_get_uint_index(tl, tag, index, value); + return value[0]; + } + }); + put(GType.INT64, new TagGetter() { + public Object get(TagList tl, String tag, int index) { + long[] value = {0}; + GSTTAGLIST_API.gst_tag_list_get_int64_index(tl, tag, index, value); + return value[0]; } - String ret = value[0].getString(0); - GLIB_API.g_free(value[0]); - return ret; - } - }); - put(GDate.GTYPE, new TagGetter() { - public Object get(TagList tl, String tag, int index) { - PointerByReference value = new PointerByReference(); - GSTTAGLIST_API.gst_tag_list_get_date_index(tl, tag, index, value); - if (value.getValue() == null) { - return null; + }); + put(GType.DOUBLE, new TagGetter() { + public Object get(TagList tl, String tag, int index) { + double[] value = {0d}; + GSTTAGLIST_API.gst_tag_list_get_double_index(tl, tag, index, value); + return value[0]; } - return new GDate(value.getValue(), false, true); - } - }); - put(DateTime.GTYPE, new TagGetter() { - public Object get(TagList tl, String tag, int index) { - PointerByReference value = new PointerByReference(); - GSTTAGLIST_API.gst_tag_list_get_date_time_index(tl, tag, index, value); - if (value.getValue() == null) { - return null; + }); + put(GType.STRING, new TagGetter() { + public Object get(TagList tl, String tag, int index) { + Pointer[] value = {null}; + GSTTAGLIST_API.gst_tag_list_get_string_index(tl, tag, index, value); + if (value[0] == null) { + return null; + } + String ret = value[0].getString(0); + GLIB_API.g_free(value[0]); + return ret; } - return new DateTime(value.getValue(), false, true); - } - }); + }); + put(GType.valueOf(GDate.GTYPE_NAME), new TagGetter() { + public Object get(TagList tl, String tag, int index) { + PointerByReference value = new PointerByReference(); + GSTTAGLIST_API.gst_tag_list_get_date_index(tl, tag, index, value); + if (value.getValue() == null) { + return null; + } + return Natives.objectFor(value.getValue(), GDate.class, false, true); + } + }); + put(GType.valueOf(DateTime.GTYPE_NAME), new TagGetter() { + public Object get(TagList tl, String tag, int index) { + PointerByReference value = new PointerByReference(); + GSTTAGLIST_API.gst_tag_list_get_date_time_index(tl, tag, index, value); + if (value.getValue() == null) { + return null; + } + return new DateTime(value.getValue(), false, true); + } + }); - }}; + } + }; static private final Map tagTypeMap = new ConcurrentHashMap(); } } diff --git a/src/org/freedesktop/gstreamer/TagMergeMode.java b/src/org/freedesktop/gstreamer/TagMergeMode.java index 002651ff..8a71cb02 100644 --- a/src/org/freedesktop/gstreamer/TagMergeMode.java +++ b/src/org/freedesktop/gstreamer/TagMergeMode.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2007 Wayne Meissner * Copyright (C) 2003 Benjamin Otte * @@ -19,29 +20,48 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; + /** * The different tag merging modes are basically replace, overwrite and append, * but they can be seen from two directions. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstTagList.html#GstTagMergeMode + *

* Given two taglists: A - the one that are supplied to * gst_tag_setter_merge_tags() or gst_tag_setter_add_tags() and B - the tags * already in the element, how are the tags merged? In the table below this is * shown for the cases that a tag exists in the list (A) or does not exists (!A) * and combination thereof. */ -public enum TagMergeMode { +public enum TagMergeMode implements NativeEnum { /** Undefined merge mode. */ - UNDEFINED, + @DefaultEnumValue + UNDEFINED(0), /** Replace all tags (clear list and append). */ - REPLACE_ALL, + REPLACE_ALL(1), /** Replace tags */ - REPLACE, + REPLACE(2), /** Append tags */ - APPEND, + APPEND(3), /** Prepend tags */ - PREPEND, + PREPEND(4), /** Keep existing tags */ - KEEP, + KEEP(5), /** Keep all existing tags */ - KEEP_ALL; + KEEP_ALL(6); + + private final int value; + + private TagMergeMode(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } } diff --git a/src/org/freedesktop/gstreamer/URIType.java b/src/org/freedesktop/gstreamer/URIType.java deleted file mode 100644 index c94f3393..00000000 --- a/src/org/freedesktop/gstreamer/URIType.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2010 DHoyt - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import org.freedesktop.gstreamer.lowlevel.EnumMapper; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; - -/** - * The different types of URI direction. - */ -public enum URIType implements IntegerEnum { - /** The URI direction is unknown */ - UNKNOWN(0), - /** The URI is a consumer. */ - SINK(1), - /** The URI is a producer. */ - SRC(2); - - private final int value; - - URIType(int value) { - this.value = value; - } - - /** - * Gets the integer value of the enum. - * @return The integer value for this enum. - */ - public int intValue() { - return value; - } - - /** - * Returns the enum constant of this type with the specified integer value. - * @param uriType integer value. - * @return Enum constant. - * @throws java.lang.IllegalArgumentException if the enum type has no constant with the specified value. - */ - public static final URIType valueOf(int uriType) { - return EnumMapper.getInstance().valueOf(uriType, URIType.class); - } -} diff --git a/src/org/freedesktop/gstreamer/ValueList.java b/src/org/freedesktop/gstreamer/ValueList.java deleted file mode 100644 index 7def3cb1..00000000 --- a/src/org/freedesktop/gstreamer/ValueList.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2010 Raimo Järvi - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import org.freedesktop.gstreamer.lowlevel.GValueAPI; -import org.freedesktop.gstreamer.lowlevel.GstValueAPI; - -@Deprecated -public class ValueList { - public static final String GTYPE_NAME = "GstValueList"; - - private GValueAPI.GValue value; - - ValueList(GValueAPI.GValue value) { - this.value = value; - } - - public int getSize() { - return GstValueAPI.GSTVALUE_API.gst_value_list_get_size(value); - } - - public double getDouble(int index) { - return GValueAPI.GVALUE_API.g_value_get_double(getValue(index)); - } - - public float getFloat(int index) { - return GValueAPI.GVALUE_API.g_value_get_float(getValue(index)); - } - - public int getInteger(int index) { - return GValueAPI.GVALUE_API.g_value_get_int(getValue(index)); - } - - public String getString(int index) { - return GValueAPI.GVALUE_API.g_value_get_string(getValue(index)); - } - - public boolean getBoolean(int index) { - return GValueAPI.GVALUE_API.g_value_get_boolean(getValue(index)); - } - - public Fraction getFraction(int index) { - GValueAPI.GValue value = getValue(index); - return Fraction.objectFor(value); - } - - private GValueAPI.GValue getValue(int index) { - GValueAPI.GValue val = GstValueAPI.GSTVALUE_API.gst_value_list_get_value(value, index); - if (val == null) { - throw new RuntimeException(String.format("List does not contain value %d", index)); - } - return val; - } -} diff --git a/src/org/freedesktop/gstreamer/Version.java b/src/org/freedesktop/gstreamer/Version.java index 493dedf4..32f598da 100644 --- a/src/org/freedesktop/gstreamer/Version.java +++ b/src/org/freedesktop/gstreamer/Version.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2007, 2008 Wayne Meissner + * Copyright (c) 2021 Neil C Smith + * Copyright (c) 2008 Wayne Meissner * * This file is part of gstreamer-java. * @@ -15,62 +16,142 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer; /** - * Describes the version of gstreamer currently in use. + * Describes a GStreamer version. + *

+ * Also upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gst.html#gst_version + *

*/ public class Version { - public Version(long major, long minor, long micro, long nano) { + + /** + * The baseline version of GStreamer supported by these Java bindings. + * Currently GStreamer 1.8. + */ + public static final Version BASELINE = new Version(1, 8); + + private final int major, minor, micro, nano; + + /** + * Constructor for creating a Version with micro and nano set to zero. For + * requesting version in {@link Gst#init(org.freedesktop.gstreamer.Version)} + * prefer using {@link #of(int, int)}. + *

+ * The library only supports major version 1 + * + * @param major Major version of GStreamer + * @param minor Minor version of GStreamer + */ + public Version(int major, int minor) { + this(major, minor, 0, 0); + } + + /** + * Constructor for creating a Version reflecting all aspects of the native + * GStreamer version. + *

+ * The library only supports major version 1 + * + * @param major Major version of GStreamer + * @param minor Minor version of GStreamer + * @param micro Micro (patch) version of GStreamer + * @param nano Nano The nano version of GStreamer : Actual releases have 0, + * GIT versions have 1, prerelease versions have 2 + */ + public Version(int major, int minor, int micro, int nano) { this.major = major; this.minor = minor; this.micro = micro; this.nano = nano; } - + /** * Gets a string representation of the version. - * + * * @return a string representing the version. */ @Override public String toString() { - return String.format("%d.%d.%d%s", + return String.format("%d.%d.%d%s", major, minor, micro, nano == 1 ? " (CVS)" : nano >= 2 ? " (Pre-release)" : ""); } - + /** - * Gets the major version of GStreamer at compile time. + * Gets the major version of GStreamer. + * * @return the major version. */ - public long getMajor() { + public int getMajor() { return major; } - + /** - * Gets the minor version of GStreamer at compile time. + * Gets the minor version of GStreamer. + * * @return the minor version. */ - public long getMinor() { + public int getMinor() { return minor; } - + /** - * Gets the micro version of GStreamer at compile time. + * Gets the micro version of GStreamer. + * * @return the micro version. */ - public long getMicro() { + public int getMicro() { return micro; } - + /** - * Gets the nano version of GStreamer at compile time. + * Gets the nano version of GStreamer. Actual releases have 0, GIT versions + * have 1, prerelease versions have 2 + * * @return the nano version. */ - public long getNano() { + public int getNano() { return nano; } - private final long major, minor, micro, nano; + + /** + * Check whether this version is equal to or greater than the passed in + * version. Roughly comparable to GST_CHECK_VERSION + * + * @param required version to check against + * @return true if this version satisfies the required version + */ + public boolean checkSatisfies(Version required) { + return (major == required.major && minor > required.minor) + || (major == required.major && minor == required.minor && micro >= required.micro); + } + + /** + * Create a Version with specified major and minor version, and micro and + * nano version set to zero. Useful for specifying a required version in + * {@link Gst#init(org.freedesktop.gstreamer.Version)}. + *

+ * The library only supports major version 1 + *

+ * Unlike the constructor this method will throw an exception if the version + * is not greater or equal to {@link #BASELINE}, or the major version isn't + * 1. + * + * @param major major version, currently must be 1 + * @param minor minor version, greater or equal to 8 + * @return requested version + * @throws IllegalArgumentException if the requested version is invalid + */ + public static Version of(int major, int minor) { + if (major == BASELINE.getMajor() + && minor >= BASELINE.getMinor()) { + return new Version(major, minor); + } + throw new IllegalArgumentException("Invalid version"); + } + } diff --git a/src/org/freedesktop/gstreamer/Video.java b/src/org/freedesktop/gstreamer/Video.java deleted file mode 100644 index df82066a..00000000 --- a/src/org/freedesktop/gstreamer/Video.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.freedesktop.gstreamer; - -import static org.freedesktop.gstreamer.lowlevel.GstVideoAPI.GSTVIDEO_API; - -import java.awt.Dimension; - -import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; - -public class Video { - private Video() { - } - - /** - * A convenience function to retrieve a GValue holding the framerate from the caps on a pad. - * - * The pad needs to have negotiated caps containing a framerate property. - * - * @param pad - * @return video framerate, otherwise null if the pad has no configured caps or the configured caps do not contain a framerate. - */ - public static Fraction getVideoFrameRate(Pad pad) { - final GValue gvalue = GSTVIDEO_API.gst_video_frame_rate(pad); - return gvalue != null ? Fraction.objectFor(gvalue) : null; - } - - /** - * Inspect the caps of the provided pad and retrieve the width and height of the video frames it is configured for. - * - * The pad needs to have negotiated caps containing width and height properties. - * @param pad - * @return dimensions of the video, otherwise null if the video size could not be retrieved - */ - public static Dimension getVideoSize(Pad pad) { - int [] width = { 0 }; - int [] height = { 0 }; - return GSTVIDEO_API.gst_video_get_size(pad, width, height) ? new Dimension(width [0], height [0]) : null; - } -} diff --git a/src/org/freedesktop/gstreamer/WebRTCSessionDescription.java b/src/org/freedesktop/gstreamer/WebRTCSessionDescription.java deleted file mode 100644 index 819a5a80..00000000 --- a/src/org/freedesktop/gstreamer/WebRTCSessionDescription.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018 Vinicius Tona - * Copyright (c) 2018 Antonio Morales - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under the terms of the GNU - * Lesser General Public License version 3 only, as published by the Free Software Foundation. - * - * This code 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 version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License version 3 along with - * this work. If not, see . - */ - -package org.freedesktop.gstreamer; - -import static org.freedesktop.gstreamer.lowlevel.GstWebRTCSessionDescriptionAPI.GSTWEBRTCSESSIONDESCRIPTION_API; - -import org.freedesktop.gstreamer.lowlevel.GstWebRTCSessionDescriptionAPI; -import org.freedesktop.gstreamer.lowlevel.NativeObject; - -import com.sun.jna.Pointer; - -public class WebRTCSessionDescription extends NativeObject { - public static final String GTYPE_NAME = "GstWebRTCSessionDescription"; - - protected GstWebRTCSessionDescriptionAPI.WebRTCSessionDescriptionStruct sessionDescriptionStruct; - - /** - * Internally used constructor. Do not use. - * - * @param init internal initialization data. - */ - public WebRTCSessionDescription(Initializer init) { - super(init); - sessionDescriptionStruct = new GstWebRTCSessionDescriptionAPI.WebRTCSessionDescriptionStruct(handle()); - } - - /** - * Creates a new instance of WebRTCSessionDescription. - * - * @param type The {@link WebRTCSDPType} type of the session description - * @param sdpMessage The {@link SDPMessage} of the session description - */ - public WebRTCSessionDescription(WebRTCSDPType type, SDPMessage sdpMessage) { - this(initializer(GSTWEBRTCSESSIONDESCRIPTION_API.ptr_gst_webrtc_session_description_new(type, sdpMessage))); - } - - /** - * Gets the SDPMessage from the WebRTCSessionDescription. - * - * @return the {@link SDPMessage} for the WebRTCSessionDescription - */ - public SDPMessage getSDPMessage() { - SDPMessage originalSDP = (SDPMessage)sessionDescriptionStruct.readField("sdp"); - // making a copy of the SDPMessage since the original SDPMessage in the struct belongs to WebRTCSessionDescription. - // Once WebRTCSessionDescription is disposed it would also dispose of SDPMessage leading to any objects with a reference - // to the original SDPMessage to be invalid and potentially lead to runtime errors. - return originalSDP.copy(true); - } - - protected void disposeNativeHandle(Pointer ptr) { - GSTWEBRTCSESSIONDESCRIPTION_API.gst_webrtc_session_description_free(ptr); - } -} diff --git a/src/org/freedesktop/gstreamer/controller/ARGBControlBinding.java b/src/org/freedesktop/gstreamer/controller/ARGBControlBinding.java new file mode 100644 index 00000000..8c77c67b --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/ARGBControlBinding.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import org.freedesktop.gstreamer.ControlBinding; +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.GstObject; +import org.freedesktop.gstreamer.glib.GObject; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstARGBControlBindingPtr; +import org.freedesktop.gstreamer.lowlevel.GstControlSourcePtr; +import org.freedesktop.gstreamer.lowlevel.GstDirectControlBindingPtr; + +import static org.freedesktop.gstreamer.lowlevel.GstControllerAPI.GSTCONTROLLER_API; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; + +/** + * Attachment for control sources to ARGB properties + *

+ * A value mapping object that attaches multiple control sources to int GObject + * properties representing colors. A control value of 0.0 will turn the color + * component off and a value of 1.0 will be the color level. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstARGBControlBinding.html + *

+ */ +public class ARGBControlBinding extends ControlBinding { + + public static final String GTYPE_NAME = "GstARGBControlBinding"; + + ARGBControlBinding(Initializer init) { + this(new Handle( + init.ptr.as(GstARGBControlBindingPtr.class, GstARGBControlBindingPtr::new), + init.ownsHandle), + init.needRef); + } + + private ARGBControlBinding(Handle handle, boolean needRef) { + super(handle, needRef); + } + + /** + * Create a new control-binding that attaches the given + * {@link ControlSource} to the {@link GObject} property. + * + * @param object the object of the property + * @param propertyName the property-name to attach the control source + * @param controlSourceA the control source for the alpha channel + * @param controlSourceR the control source for the red channel + * @param controlSourceG the control source for the green channel + * @param controlSourceB the control source for the blue channel + * @return new ARGBControlBinding + */ + public static ARGBControlBinding create(GstObject object, + String propertyName, + ControlSource controlSourceA, + ControlSource controlSourceR, + ControlSource controlSourceG, + ControlSource controlSourceB) { + GstARGBControlBindingPtr ptr = GSTCONTROLLER_API.gst_argb_control_binding_new( + Natives.getPointer(object).as(GstObjectPtr.class, GstObjectPtr::new), + propertyName, + controlSourcePtr(controlSourceA), + controlSourcePtr(controlSourceR), + controlSourcePtr(controlSourceG), + controlSourcePtr(controlSourceB)); + return new ARGBControlBinding(new Handle(ptr, true), false); + } + + private static GstControlSourcePtr controlSourcePtr(ControlSource cs) { + return cs == null ? null + : Natives.getPointer(cs).as(GstControlSourcePtr.class, GstControlSourcePtr::new); + } + +} diff --git a/src/org/freedesktop/gstreamer/controller/ControlSource.java b/src/org/freedesktop/gstreamer/controller/ControlSource.java deleted file mode 100644 index 6eab0d2c..00000000 --- a/src/org/freedesktop/gstreamer/controller/ControlSource.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2010 Levente Farkas - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.controller; - -import org.freedesktop.gstreamer.GObject; - -import com.sun.jna.Pointer; - -/** - * The ControlSource is a base class for control value sources that could be used by Controller to get timestamp-value pairs. - */ -public class ControlSource extends GObject { - //private static Logger logger = Logger.getLogger(ControlSource.class.getName()); - - /** - * For internal gstreamer-java use only - * - * @param init initialization data - */ - public ControlSource(Initializer init) { - super(init); - throw new IllegalArgumentException("Cannot instantiate this class"); - } - - public ControlSource(Pointer ptr, boolean needRef, boolean ownsHandle) { - super(initializer(ptr, needRef, ownsHandle)); - } -} diff --git a/src/org/freedesktop/gstreamer/controller/Controllers.java b/src/org/freedesktop/gstreamer/controller/Controllers.java new file mode 100644 index 00000000..5d1c1816 --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/Controllers.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import java.util.stream.Stream; +import org.freedesktop.gstreamer.glib.NativeObject; +import static org.freedesktop.gstreamer.glib.Natives.registration; + +/** + * Controllers registration + */ +public class Controllers implements NativeObject.TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + registration(ARGBControlBinding.class, + ARGBControlBinding.GTYPE_NAME, + ARGBControlBinding::new), + registration(DirectControlBinding.class, + DirectControlBinding.GTYPE_NAME, + DirectControlBinding::new), + registration(ProxyControlBinding.class, + ProxyControlBinding.GTYPE_NAME, + ProxyControlBinding::new), + registration(InterpolationControlSource.class, + InterpolationControlSource.GTYPE_NAME, + InterpolationControlSource::new), + registration(TriggerControlSource.class, + TriggerControlSource.GTYPE_NAME, + TriggerControlSource::new), + registration(LFOControlSource.class, + LFOControlSource.GTYPE_NAME, + LFOControlSource::new), + registration(TimedValueControlSource.class, + TimedValueControlSource.GTYPE_NAME, + TimedValueControlSource::new) + ); + } + +} diff --git a/src/org/freedesktop/gstreamer/controller/DirectControlBinding.java b/src/org/freedesktop/gstreamer/controller/DirectControlBinding.java new file mode 100644 index 00000000..a36cda6a --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/DirectControlBinding.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import org.freedesktop.gstreamer.ControlBinding; +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.GstObject; +import org.freedesktop.gstreamer.glib.GObject; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstControlSourcePtr; +import org.freedesktop.gstreamer.lowlevel.GstDirectControlBindingPtr; + +import static org.freedesktop.gstreamer.lowlevel.GstControllerAPI.GSTCONTROLLER_API; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; + +/** + * Direct attachment for control sources. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstDirectControlBinding.html + *

+ */ +public class DirectControlBinding extends ControlBinding { + + public static final String GTYPE_NAME = "GstDirectControlBinding"; + + DirectControlBinding(Initializer init) { + this(new Handle( + init.ptr.as(GstDirectControlBindingPtr.class, GstDirectControlBindingPtr::new), + init.ownsHandle), + init.needRef); + } + + private DirectControlBinding(Handle handle, boolean needRef) { + super(handle, needRef); + } + + /** + * Create a new control-binding that attaches the {@link ControlSource } to + * the {@link GObject} property. It will map the control source range [0.0 + * ... 1.0] to the full target property range, and clip all values outside + * this range. + * + * @param object the object of the property + * @param propertyName the property-name to attach the control source + * @param controlSource the control source + * @return new DirectControlBinding + */ + public static DirectControlBinding create(GstObject object, String propertyName, ControlSource controlSource) { + GstDirectControlBindingPtr ptr = GSTCONTROLLER_API.gst_direct_control_binding_new( + Natives.getPointer(object).as(GstObjectPtr.class, GstObjectPtr::new), + propertyName, + Natives.getPointer(controlSource).as(GstControlSourcePtr.class, GstControlSourcePtr::new)); + return new DirectControlBinding(new Handle(ptr, true), false); + } + + /** + * Create a new control-binding that attaches the {@link ControlSource } to + * the {@link GObject} property. It will directly map the control source + * values to the target property range without any transformations. + * + * @param object the object of the property + * @param propertyName the property-name to attach the control source + * @param controlSource the control source + * @return new DirectControlBinding + */ + public static DirectControlBinding createAbsolute(GstObject object, + String propertyName, ControlSource controlSource) { + GstDirectControlBindingPtr ptr = GSTCONTROLLER_API.gst_direct_control_binding_new_absolute( + Natives.getPointer(object).as(GstObjectPtr.class, GstObjectPtr::new), + propertyName, + Natives.getPointer(controlSource).as(GstControlSourcePtr.class, GstControlSourcePtr::new)); + return new DirectControlBinding(new Handle(ptr, true), false); + } + +} diff --git a/src/org/freedesktop/gstreamer/controller/InterpolationControlSource.java b/src/org/freedesktop/gstreamer/controller/InterpolationControlSource.java index 9fb8c784..da808df4 100644 --- a/src/org/freedesktop/gstreamer/controller/InterpolationControlSource.java +++ b/src/org/freedesktop/gstreamer/controller/InterpolationControlSource.java @@ -1,44 +1,100 @@ -/* - * Copyright (c) 2010 Levente Farkas - * +/* + * Copyright (c) 2019 Neil C Smith + * * This file is part of gstreamer-java. * - * This code is free software: you can redistribute it and/or modify it under + * This code is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3 only, as * published by the Free Software Foundation. * - * This code 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 + * This code 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 * version 3 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.controller; -import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.lowlevel.GstInterpolationControlSourcePtr; + +import static org.freedesktop.gstreamer.lowlevel.GstControllerAPI.GSTCONTROLLER_API; /** - * The ControlSource is a base class for control value sources that could be used by Controller to get timestamp-value pairs. + * Interpolation control source. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstInterpolationControlSource.html + *

+ * InterpolationControlSource is a {@link ControlSource}, that interpolates + * values between user-given control points. It supports several interpolation + * modes and property types. + *

+ * To use InterpolationControlSource create a new instance, bind it to a + * GParamSpec and set some control points by calling {@link TimedValueControlSource#set(long, double) + * }. + *

+ * All functions are MT-safe. */ -public class InterpolationControlSource extends ControlSource { - //private static Logger logger = Logger.getLogger(InterpolationControlSource.class.getName()); +public class InterpolationControlSource extends TimedValueControlSource { + + public static final String GTYPE_NAME = "GstInterpolationControlSource"; + + /** + * Create a new, unbound InterpolationControlSource. + */ + public InterpolationControlSource() { + this(new Handle(GSTCONTROLLER_API.gst_interpolation_control_source_new(), true), false); + } + + InterpolationControlSource(Initializer init) { + this(new Handle( + init.ptr.as(GstInterpolationControlSourcePtr.class, + GstInterpolationControlSourcePtr::new), + init.ownsHandle), + init.needRef); + } - //private static final GstInterpolationControlSourceAPI gst = GstNative.load(GstInterpolationControlSourceAPI.class); + private InterpolationControlSource(Handle handle, boolean needRef) { + super(handle, needRef); + } /** - * For internal gstreamer-java use only - * - * @param init initialization data + * Interpolation mode to use. + * + * @param mode + * @return this */ - public InterpolationControlSource(Initializer init) { - super(init); - throw new IllegalArgumentException("Cannot instantiate this class"); + public InterpolationControlSource setMode(InterpolationMode mode) { + set("mode", mode.intValue()); + return this; } + + /** + * Current interpolation mode. + * + * @return mode + */ + public InterpolationMode getMode() { + Object val = get("mode"); + if (val instanceof Integer) { + int nativeInt = (Integer) val; + return NativeEnum.fromInt(InterpolationMode.class, nativeInt); + } + return InterpolationMode.NONE; + } + + + private static class Handle extends TimedValueControlSource.Handle { + + public Handle(GstInterpolationControlSourcePtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } - public InterpolationControlSource(Pointer ptr, boolean needRef, boolean ownsHandle) { - super(initializer(ptr, needRef, ownsHandle)); } + } diff --git a/src/org/freedesktop/gstreamer/controller/InterpolationMode.java b/src/org/freedesktop/gstreamer/controller/InterpolationMode.java new file mode 100644 index 00000000..55c40082 --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/InterpolationMode.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import org.freedesktop.gstreamer.glib.NativeEnum; + +/** + * The various interpolation modes available for use with + * {@link InterpolationControlSource}. + */ +// https://gitlab.freedesktop.org/gstreamer/gstreamer/blob/master/libs/gst/controller/gstinterpolationcontrolsource.h#L64 +public enum InterpolationMode implements NativeEnum { + + /** + * Steps-like interpolation, default. + */ + NONE(0), + /** + * Linear interpolation. + */ + LINEAR(1), + /** + * Cubic interpolation (natural), may overshoot the min or max values set by + * the control point, but is more 'curvy'. + */ + CUBIC(2), + /** + * Monotonic cubic interpolation, will not produce any values outside of the + * min-max range set by the control points. + */ + CUBIC_MONOTONIC(3); + + private final int value; + + private InterpolationMode(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + +} diff --git a/src/org/freedesktop/gstreamer/controller/LFOControlSource.java b/src/org/freedesktop/gstreamer/controller/LFOControlSource.java index f7c7a574..1464b83c 100644 --- a/src/org/freedesktop/gstreamer/controller/LFOControlSource.java +++ b/src/org/freedesktop/gstreamer/controller/LFOControlSource.java @@ -1,46 +1,211 @@ -/* - * Copyright (c) 2010 Levente Farkas - * +/* + * Copyright (c) 2019 Neil C Smith + * * This file is part of gstreamer-java. * - * This code is free software: you can redistribute it and/or modify it under + * This code is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3 only, as * published by the Free Software Foundation. * - * This code 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 + * This code 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 * version 3 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.controller; -import org.freedesktop.gstreamer.GObject; - -import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.glib.NativeEnum; +import static org.freedesktop.gstreamer.lowlevel.GstControllerAPI.GSTCONTROLLER_API; +import org.freedesktop.gstreamer.lowlevel.GstLFOControlSourcePtr; /** - * The ControlSource is a base class for control value sources that could be used by Controller to get timestamp-value pairs. + * LFO control source. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstLFOControlSource.html + *

+ * LFOControlSource is a {@link ControlSource}, that provides several periodic + * waveforms as control values. + *

+ * To use InterpolationControlSource create a new instance, bind it to a + * GParamSpec and set the relevant properties. + *

+ * All functions are MT-safe. */ -public class LFOControlSource extends GObject { - //private static Logger logger = Logger.getLogger(LFOControlSource.class.getName()); +public class LFOControlSource extends ControlSource { + + public static final String GTYPE_NAME = "GstLFOControlSource"; + + /** + * Create a new, unbound LFOControlSource. + */ + public LFOControlSource() { + this(new Handle(GSTCONTROLLER_API.gst_lfo_control_source_new(), true), false); + } + + LFOControlSource(Initializer init) { + this(new Handle( + init.ptr.as(GstLFOControlSourcePtr.class, + GstLFOControlSourcePtr::new), + init.ownsHandle), + init.needRef); + } + + private LFOControlSource(Handle handle, boolean needRef) { + super(handle, needRef); + } + + /** + * Specifies the amplitude for the waveform of this LFOControlSource. + *

+ * Allowed values: [0,1] + *

+ * Default value: 1 + * + * @param value amplitude between 0 and 1 + * @return this + */ + public LFOControlSource setAmplitude(double value) { + set("amplitude", value); + return this; + } + + /** + * Get the amplitude of the waveform. + * + * @return amplitude + */ + public double getAmplitude() { + Object val = get("amplitude"); + if (val instanceof Double) { + return (double) val; + } + return 1; + } + + /** + * Specifies the frequency that should be used for the waveform of this + * LFOControlSource. It should be large enough so that the period is longer + * than one nanosecond. + *

+ * Allowed values: >= {@link Double#MIN_VALUE } + *

+ * Default value: 1 + * + * @param value frequency >= {@link Double#MIN_VALUE } + * @return this + */ + public LFOControlSource setFrequency(double value) { + set("frequency", value); + return this; + } + + /** + * Get the frequency of the waveform. + * + * @return amplitude + */ + public double getFrequency() { + Object val = get("frequency"); + if (val instanceof Double) { + return (double) val; + } + return 1; + } - //private static final GstLFOControlSourceAPI gst = GstNative.load(GstLFOControlSourceAPI.class); + /** + * Specifies the value offset for the waveform of this LFOControlSource. + *

+ * Allowed values: [0,1] + *

+ * Default value: 1 + * + * @param value offset between 0 and 1 + * @return this + */ + public LFOControlSource setOffset(double value) { + set("offset", value); + return this; + } /** - * For internal gstreamer-java use only + * Get the value offset of the waveform. * - * @param init initialization data + * @return offset */ - public LFOControlSource(Initializer init) { - super(init); - throw new IllegalArgumentException("Cannot instantiate this class"); + public double getOffset() { + Object val = get("offset"); + if (val instanceof Double) { + return (double) val; + } + return 1; } - public LFOControlSource(Pointer ptr, boolean needRef, boolean ownsHandle) { - super(initializer(ptr, needRef, ownsHandle)); + /** + * Specifies the timeshift to the right that should be used for the waveform + * of this LFOControlSource in nanoseconds. + *

+ * Default value: 0 + * + * @param value timeshift in nanoseconds + * @return this + */ + public LFOControlSource setTimeshift(long value) { + set("timeshift", value); + return this; } + + /** + * Get the timeshift of the waveform. + * + * @return timeshift + */ + public long getTimeshift() { + Object val = get("timeshift"); + if (val instanceof Long) { + return (long) val; + } + return 1; + } + + /** + * Specifies the waveform that should be used for this LFOControlSource. + *

+ * Default value: {@link LFOWaveform#SINE } + * + * @param value waveform + * @return this + */ + public LFOControlSource setWaveform(LFOWaveform value) { + set("waveform", value.intValue()); + return this; + } + + /** + * Get the waveform. + * + * @return waveform + */ + public LFOWaveform getWaveform() { + Object val = get("waveform"); + if (val instanceof Integer) { + int nativeInt = (Integer) val; + return NativeEnum.fromInt(LFOWaveform.class, nativeInt); + } + return LFOWaveform.SINE; + } + + private static class Handle extends ControlSource.Handle { + + public Handle(GstLFOControlSourcePtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + } + } diff --git a/src/org/freedesktop/gstreamer/controller/LFOWaveform.java b/src/org/freedesktop/gstreamer/controller/LFOWaveform.java new file mode 100644 index 00000000..08bad64a --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/LFOWaveform.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import org.freedesktop.gstreamer.glib.NativeEnum; + +/** + * The various waveform modes available for use with {@link LFOControlSource} + */ +// https://gitlab.freedesktop.org/gstreamer/gstreamer/blob/master/libs/gst/controller/gstlfocontrolsource.h#L60 +public enum LFOWaveform implements NativeEnum { + + /** + * Sine waveform. + */ + SINE(0), + + /** + * Square waveform. + */ + SQUARE(1), + + /** + * Saw waveform. + */ + SAW(2), + + /** + * Reverse saw waveform. + */ + REVERSE_SAW(3), + + /** + * Triangle waveform. + */ + TRIANGLE(4) + ; + + + private final int value; + + private LFOWaveform(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + +} diff --git a/src/org/freedesktop/gstreamer/controller/ProxyControlBinding.java b/src/org/freedesktop/gstreamer/controller/ProxyControlBinding.java new file mode 100644 index 00000000..76cc54be --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/ProxyControlBinding.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import org.freedesktop.gstreamer.ControlBinding; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.GstObject; +import org.freedesktop.gstreamer.glib.Natives; + +import static org.freedesktop.gstreamer.lowlevel.GstControllerAPI.GSTCONTROLLER_API; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; +import org.freedesktop.gstreamer.lowlevel.GstProxyControlBindingPtr; + +/** + * Attachment for forwarding control sources + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/gstreamer-libs-GstProxyControlBinding.html + *

+ */ +@Gst.Since(minor = 12) +public class ProxyControlBinding extends ControlBinding { + + public static final String GTYPE_NAME = "GstProxyControlBinding"; + + ProxyControlBinding(Initializer init) { + this(new Handle( + init.ptr.as(GstProxyControlBindingPtr.class, GstProxyControlBindingPtr::new), + init.ownsHandle), + init.needRef); + } + + private ProxyControlBinding(Handle handle, boolean needRef) { + super(handle, needRef); + } + + /** + * Create a ProxyControlBinding that forwards all access to data or + * syncValues() requests from propertyName on object to the control + * binding at refPropertyName on refObject . + * + * @param object the object of the property + * @param propertyName the property-name to attach the control source + * @param refObject a GstObject to forward all ControlBinding requests to + * @param refPropertyName the property name in refObject to control + * @return new ProxyControlBinding + */ + @Gst.Since(minor = 12) + public static ProxyControlBinding create(GstObject object, String propertyName, + GstObject refObject, String refPropertyName) { + GstProxyControlBindingPtr ptr = GSTCONTROLLER_API.gst_proxy_control_binding_new( + Natives.getPointer(object).as(GstObjectPtr.class, GstObjectPtr::new), + propertyName, + Natives.getPointer(refObject).as(GstObjectPtr.class, GstObjectPtr::new), + refPropertyName); + return new ProxyControlBinding(new Handle(ptr, true), false); + } + +} diff --git a/src/org/freedesktop/gstreamer/controller/TimedValueControlSource.java b/src/org/freedesktop/gstreamer/controller/TimedValueControlSource.java new file mode 100644 index 00000000..c4258432 --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/TimedValueControlSource.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import java.util.ArrayList; +import java.util.List; +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; +import static org.freedesktop.gstreamer.lowlevel.GstControllerAPI.GSTCONTROLLER_API; +import org.freedesktop.gstreamer.lowlevel.GstTimedValueControlSourcePtr; + +/** + * Timed value control source base class. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstTimedValueControlSource.html + *

+ * Base class for {@link ControlSource} that use time-stamped values. + *

+ * When overriding bind, chain up first to give this bind implementation a + * chance to setup things. + *

+ * All functions are MT-safe. + */ +public class TimedValueControlSource extends ControlSource { + + public static final String GTYPE_NAME = "GstTimedValueControlSource"; + + private final Handle handle; + + protected TimedValueControlSource(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; + } + + TimedValueControlSource(Initializer init) { + this(new Handle( + init.ptr.as(GstTimedValueControlSourcePtr.class, + GstTimedValueControlSourcePtr::new), + init.ownsHandle), + init.needRef); + } + + /** + * Set the value of given controller-handled property at a certain time. + * + * @param timestamp the time the control-change is scheduled for + * @param value the control value + * @return false if the value could not be set + */ + public boolean set(long timestamp, double value) { + return GSTCONTROLLER_API.gst_timed_value_control_source_set( + handle.getPointer(), timestamp, value); + } + + /** + * Sets multiple timed values at once. + * + * @param timedValues a list of {@link TimedValue} + * @return false if the values could not be set + */ + public boolean setFromList(List timedValues) { + for (TimedValue timedvalue : timedValues) { + boolean ok = set(timedvalue.timestamp, timedvalue.value); + if (!ok) { + return false; + } + } + return true; + } + + /** + * Returns a copy of the list of {@link TimedValue} for the given property. + * + * @return a list of TimedValue + */ + public List getAll() { + GList next = + GSTCONTROLLER_API.gst_timed_value_control_source_get_all(handle.getPointer()); + List list = new ArrayList<>(); + while (next != null) { + if (next.data != null) { + list.add(new TimedValue(next.data.getLong(0), next.data.getDouble(Long.BYTES))); + } + next = next.next(); + } + return list; + } + + /** + * Used to remove the value of given controller-handled property at a + * certain time. + * + * @param timestamp the time the control-change should be removed from + * @return FALSE if the value couldn't be unset (i.e. not found) + */ + public boolean unset(long timestamp) { + return GSTCONTROLLER_API.gst_timed_value_control_source_unset( + handle.getPointer(), timestamp); + } + + /** + * Used to remove all time-stamped values of given controller-handled + * property. + */ + public void unsetAll() { + GSTCONTROLLER_API.gst_timed_value_control_source_unset_all(handle.getPointer()); + } + + /** + * Get the number of control points that are set. + * + * @return the number of control points that are set + */ + public int getCount() { + return GSTCONTROLLER_API.gst_timed_value_control_source_get_count(handle.getPointer()); + } + + /** + * Reset the controlled value cache. + */ + public void invalidateCache() { + GSTCONTROLLER_API.gst_timed_value_control_invalidate_cache(handle.getPointer()); + } + + + protected static class Handle extends ControlSource.Handle { + + public Handle(GstTimedValueControlSourcePtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected GstTimedValueControlSourcePtr getPointer() { + return (GstTimedValueControlSourcePtr) super.getPointer(); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/controller/TriggerControlSource.java b/src/org/freedesktop/gstreamer/controller/TriggerControlSource.java new file mode 100644 index 00000000..e5b8b321 --- /dev/null +++ b/src/org/freedesktop/gstreamer/controller/TriggerControlSource.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.controller; + +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.lowlevel.GstInterpolationControlSourcePtr; + +import static org.freedesktop.gstreamer.lowlevel.GstControllerAPI.GSTCONTROLLER_API; +import org.freedesktop.gstreamer.lowlevel.GstTriggerControlSourcePtr; + +/** + * Trigger control source. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstTriggerControlSource.html + *

+ * TriggerControlSource is a {@link ControlSource}, that returns values from + * user-given control points. It allows for a tolerance on time-stamps. + *

+ * To use TriggerControlSource create a new instance, bind it to a GParamSpec + * and set some control points by calling + * {@link TimedValueControlSource#set(long, double)}. + *

+ * All functions are MT-safe. + */ +public class TriggerControlSource extends TimedValueControlSource { + + public static final String GTYPE_NAME = "GstTriggerControlSource"; + + /** + * Create a new, unbound InterpolationControlSource. + */ + public TriggerControlSource() { + this(new Handle(GSTCONTROLLER_API.gst_trigger_control_source_new(), true), false); + } + + TriggerControlSource(Initializer init) { + this(new Handle( + init.ptr.as(GstTriggerControlSourcePtr.class, + GstTriggerControlSourcePtr::new), + init.ownsHandle), + init.needRef); + } + + private TriggerControlSource(Handle handle, boolean needRef) { + super(handle, needRef); + } + + /** + * Amount of nanoseconds a control time can be off to still trigger. + *

+ * Allowed values: >= 0 + *

+ * Default value: 0 + * + * @param tolerance in nanoseconds + * @return this + */ + public TriggerControlSource setTolerance(long tolerance) { + set("tolerance", tolerance); + return this; + } + + /** + * Current tolerance in nanoseconds. + * + * @return tolerance in nanoseconds + */ + public long getTolerance() { + Object val = get("tolerance"); + if (val instanceof Long) { + return (long) val; + } + return 0L; + } + + private static class Handle extends TimedValueControlSource.Handle { + + public Handle(GstTriggerControlSourcePtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/device/Device.java b/src/org/freedesktop/gstreamer/device/Device.java index 7fc93d97..0e220b95 100644 --- a/src/org/freedesktop/gstreamer/device/Device.java +++ b/src/org/freedesktop/gstreamer/device/Device.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2017 Neil C Smith + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2015 Andres Colubri * Copyright (C) 2013 Olivier Crete * @@ -30,25 +30,53 @@ import static org.freedesktop.gstreamer.lowlevel.GstDeviceAPI.GSTDEVICE_API; /** - * - * @author andres + * Devices are objects representing a device, they contain relevant metadata + * about the device, such as its class and the GstCaps representing the media + * types it can produce or handle. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/gstreamer-GstDevice.html + *

+ * Device objects are created by {@link DeviceProvider} objects, which can be + * aggregated by {@link DeviceMonitor} objects. */ public class Device extends GstObject { public static final String GTYPE_NAME = "GstDevice"; - public Device(Initializer init) { + Device(Initializer init) { super(init); } + /** + * Creates the element with all of the required parameters set to use this + * device. + * + * @param name name of new element, or NULL to automatically create a unique + * name + * @return a new {@link Element} configured to use this device. + */ public Element createElement(String name) { return GSTDEVICE_API.gst_device_create_element(this, name); } + /** + * Get the {@link Caps} that this device supports. + * + * @return The Caps supported by this device. + */ public Caps getCaps() { return GSTDEVICE_API.gst_device_get_caps(this); } + /** + * Gets the "class" of a device. This is a "/" separated list of classes + * that represent this device. They are a subset of the classes of the + * {@link DeviceProvider} that produced this device. + * + * @return class the device class + */ public String getDeviceClass() { Pointer ptr = GSTDEVICE_API.gst_device_get_device_class(this); String ret = ptr.getString(0); @@ -56,6 +84,11 @@ public String getDeviceClass() { return ret; } + /** + * Gets the user-friendly name of the device. + * + * @return name of the device + */ public String getDisplayName() { Pointer ptr = GSTDEVICE_API.gst_device_get_display_name(this); String ret = ptr.getString(0); @@ -63,18 +96,48 @@ public String getDisplayName() { return ret; } + /** + * Check if device matches all of the given classes. + * + * @param classes a "/"-separated list of device classes to match, only + * match if all classes are matched + * @return true if device matches + */ public boolean hasClasses(String classes) { return GSTDEVICE_API.gst_device_has_classes(this, classes); } + /** + * Check if device matches all of the given classes. + * + * @param classes an array of classes to match, only match if all classes + * are matched. + * @return true if device matches + */ public boolean hasClasses(String[] classes) { return GSTDEVICE_API.gst_device_has_classesv(this, classes); } + /** + * Tries to reconfigure an existing element to use the device. If this + * function fails, then one must destroy the element and create a new one + * using {@link #createElement(java.lang.String) } + *

+ * Note: This should only be implemented for elements can change their + * device in the PLAYING state. + * + * @param element the element to be configured + * @return true if the element could be reconfigured to use this device + */ public boolean reconfigureElement(Element element) { return GSTDEVICE_API.gst_device_reconfigure_element(this, element); } + /** + * Gets the extra properties of a device. + * + * @return The extra properties or NULL when there are none. + */ public Structure getProperties() { return GSTDEVICE_API.gst_device_get_properties(this); } diff --git a/src/org/freedesktop/gstreamer/device/DeviceMonitor.java b/src/org/freedesktop/gstreamer/device/DeviceMonitor.java index 2f12ef36..89a782db 100644 --- a/src/org/freedesktop/gstreamer/device/DeviceMonitor.java +++ b/src/org/freedesktop/gstreamer/device/DeviceMonitor.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2017 Neil C Smith + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2015 Andres Colubri * Copyright (C) 2013 Olivier Crete * @@ -25,54 +25,113 @@ import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.Caps; import org.freedesktop.gstreamer.GstObject; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; import static org.freedesktop.gstreamer.lowlevel.GstDeviceMonitorAPI.GSTDEVICEMONITOR_API; /** - * - * @author andres + * A device monitor and prober + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/gstreamer-GstDeviceMonitor.html + *

+ * Applications should create a DeviceMonitor when they want to probe, list and + * monitor devices of a specific type. The DeviceMonitor will create the + * appropriate {@link DeviceProvider} objects and manage them. It will then post + * messages on its {@link Bus} for devices that have been added and removed. + *

+ * The device monitor will monitor all devices matching the filters that the + * application has set. */ public class DeviceMonitor extends GstObject { public static final String GTYPE_NAME = "GstDeviceMonitor"; - public DeviceMonitor(Initializer init) { - super(init); + public DeviceMonitor() { + super(Natives.initializer(GSTDEVICEMONITOR_API.ptr_gst_device_monitor_new(), false, true)); } - static public DeviceMonitor createNew() { - return GSTDEVICEMONITOR_API.gst_device_monitor_new(); + DeviceMonitor(Initializer init) { + super(init); } + /** + * Get the {@link Bus} for this DeviceMonitor + * + * @return bus + */ public Bus getBus() { return GSTDEVICEMONITOR_API.gst_device_monitor_get_bus(this); } + /** + * Adds a filter for which devices will be monitored, any device that + * matches all these classes and the {@link Caps} will be returned. + *

+ * If this function is called multiple times to add more filters, each will + * be matched independently. That is, adding more filters will not further + * restrict what devices are matched. + *

+ * The GstCaps supported by the device as returned by gst_device_get_caps() + * are not intersected with caps filters added using this function. + *

+ * Filters must be added before the GstDeviceMonitor is started. + * + * @param classes device classes to use as filter or NULL for any class. + * @param caps the GstCaps to filter or NULL for ANY. + * @return id the id of the new filter or 0 if no provider matched the + * filter's classes. + */ public int addFilter(String classes, Caps caps) { return GSTDEVICEMONITOR_API.gst_device_monitor_add_filter(this, classes, caps); } + /** + * Remove a filter from the DeviceMonitor using the id that was returned + * from {@link #addFilter(java.lang.String, org.freedesktop.gstreamer.Caps) + * } + * + * @param filterId of filter to remove + * @return true if the filter id was valid and the filter was removed + */ public boolean removeFilter(int filterId) { return GSTDEVICEMONITOR_API.gst_device_monitor_remove_filter(this, filterId); } + /** + * Starts monitoring the devices, once this has succeeded, the + * GST_MESSAGE_DEVICE_ADDED and GST_MESSAGE_DEVICE_REMOVED messages will be + * emitted on the bus when the list of devices changes. + * + * @return true if the device monitoring could be started + */ public boolean start() { return GSTDEVICEMONITOR_API.gst_device_monitor_start(this); } + /** + * Stop monitoring devices. + */ public void stop() { GSTDEVICEMONITOR_API.gst_device_monitor_stop(this); } + /** + * Gets a list of devices from all of the relevant monitors. This may + * actually probe the hardware if the monitor is not currently started. + * + * @return list of {@link Device} + */ public List getDevices() { GList glist = GSTDEVICEMONITOR_API.gst_device_monitor_get_devices(this); - List list = new ArrayList(); + List list = new ArrayList<>(); GList next = glist; while (next != null) { if (next.data != null) { - Device dev = new Device(initializer(next.data, false, true)); + Device dev = new Device(Natives.initializer(next.data, false, true)); list.add(dev); } next = next.next(); diff --git a/src/org/freedesktop/gstreamer/device/DeviceProvider.java b/src/org/freedesktop/gstreamer/device/DeviceProvider.java index fbbe2ffd..557d8f1f 100644 --- a/src/org/freedesktop/gstreamer/device/DeviceProvider.java +++ b/src/org/freedesktop/gstreamer/device/DeviceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Neil C Smith + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2015 Andres Colubri * Copyright (C) 2013 Olivier Crete * @@ -24,6 +24,7 @@ import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.GstObject; import org.freedesktop.gstreamer.Plugin; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GlibAPI; import org.freedesktop.gstreamer.lowlevel.GstDeviceProviderAPI; @@ -31,83 +32,150 @@ import static org.freedesktop.gstreamer.lowlevel.GstDeviceProviderAPI.GSTDEVICEPROVIDER_API; /** - * - * @author andres + * A device provider. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/gstreamer-GstDeviceProvider.html + *

+ * A DeviceProvider subclass is provided by a plugin that handles devices if + * there is a way to programatically list connected devices. It can also + * optionally provide updates to the list of connected devices. + *

+ * Each DeviceProvider subclass is a singleton, a plugin should normally provide + * a single subclass for all devices. + *

+ * Applications would normally use a {@link DeviceMonitor} to monitor devices + * from all relevant providers. */ public class DeviceProvider extends GstObject { + public static final String GTYPE_NAME = "GstDeviceProvider"; - - public DeviceProvider(Initializer init) { + + DeviceProvider(Initializer init) { super(init); } - + + /** + * Whether the provider can monitor. + * + * @return true if the provider can monitor + */ public boolean canMonitor() { return GSTDEVICEPROVIDER_API.gst_device_provider_can_monitor(this); } - public void addMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, String key, String value) { - GSTDEVICEPROVIDER_API.gst_device_provider_class_add_metadata(klass, key, value); - } - - public void addStaticMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, String key, String value) { - GSTDEVICEPROVIDER_API.gst_device_provider_class_add_static_metadata(klass, key, value); - } - - public String getMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, String key) { - return GSTDEVICEPROVIDER_API.gst_device_provider_class_get_metadata(klass, key); - } - - public void setMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, - String longname, String classification, String description, String author) { - GSTDEVICEPROVIDER_API.gst_device_provider_class_set_metadata(klass, longname, classification, description, author); - } - - public void setStaticMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, - String longname, String classification, String description, String author) { - GSTDEVICEPROVIDER_API.gst_device_provider_class_set_static_metadata(klass, longname, classification, description, author); - } - - public void add(Device device) { - GSTDEVICEPROVIDER_API.gst_device_provider_device_add(this, device); - } - - public void remove(Device device) { - GSTDEVICEPROVIDER_API.gst_device_provider_device_remove(this, device); - } - +// /** +// * Set key with value as metadata in klass. +// * +// * @param klass class to set metadata for +// * @param key the key to set +// * @param value the value to set +// */ +// public void addMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, String key, String value) { +// GSTDEVICEPROVIDER_API.gst_device_provider_class_add_metadata(klass, key, value); +// } +// +// /** +// * Get metadata with key in klass. +// * +// * @param klass class to get metadata for +// * @param key the key to get +// * @return the metadata for key +// */ +// public String getMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, String key) { +// return GSTDEVICEPROVIDER_API.gst_device_provider_class_get_metadata(klass, key); +// } + +// public void setMetadata(GstDeviceProviderAPI.GstDeviceProviderClass klass, +// String longname, String classification, String description, String author) { +// GSTDEVICEPROVIDER_API.gst_device_provider_class_set_metadata(klass, longname, classification, description, author); +// } +// public void add(Device device) { +// GSTDEVICEPROVIDER_API.gst_device_provider_device_add(this, device); +// } +// +// public void remove(Device device) { +// GSTDEVICEPROVIDER_API.gst_device_provider_device_remove(this, device); +// } + /** + * Get the {@link Bus} of this DeviceProvider + * + * @return bus + */ public Bus getBus() { return GSTDEVICEPROVIDER_API.gst_device_provider_get_bus(this); } - + + /** + * Gets a list of {@link Device} that this provider understands. This may + * actually probe the hardware if the provider is not currently started. + * + * @return device list + */ public List getDevices() { - GlibAPI.GList glist = GSTDEVICEPROVIDER_API.gst_device_provider_get_devices(this); + GlibAPI.GList glist = GSTDEVICEPROVIDER_API.gst_device_provider_get_devices(this); List list = new ArrayList(); GlibAPI.GList next = glist; while (next != null) { if (next.data != null) { - Device dev = new Device(initializer(next.data, true, true)); + Device dev = new Device(Natives.initializer(next.data, true, true)); list.add(dev); } next = next.next(); } - return list; + + return list; } - + + /** + * Retrieves the factory that was used to create this device provider. + * + * @return the {@link DeviceProviderFactory} used for creating this device + * provider. + */ public DeviceProviderFactory getFactory() { return GSTDEVICEPROVIDER_API.gst_device_provider_get_factory(this); } - public boolean register(Plugin plugin, String name, int rank, GType type) { - return GSTDEVICEPROVIDER_API.gst_device_provider_register(plugin, name, rank, type); - } - +// @TODO rethink this to not require GType +// /** +// * Create a new device providerfactory capable of instantiating objects of +// * the type and add the factory to plugin. +// * +// * @param plugin {@link Plugin} to register the device provider with, or NULL for a static device provider. +// * @param name name of device providers of this type +// * @param rank rank of device provider (higher rank means more importance when autoplugging) +// * @param type +// * @return +// */ +// public boolean register(Plugin plugin, String name, int rank, GType type) { +// return GSTDEVICEPROVIDER_API.gst_device_provider_register(plugin, name, rank, type); +// } + + /** + * Starts providering the devices. This will cause GST_MESSAGE_DEVICE_ADDED + * and GST_MESSAGE_DEVICE_REMOVED messages to be posted on the provider's + * bus when devices are added or removed from the system. + *

+ * Since the DeviceProvider is a singleton, start may already have been + * called by another user of the object, {@link #stop() } needs to be called + * the same number of times. + * + * @return true if the device providering could be started. + */ public boolean start() { return GSTDEVICEPROVIDER_API.gst_device_provider_start(this); } + /** + * Decreases the use-count by one. If the use count reaches zero, this + * DeviceProvider will stop providering the devices. This needs to be called + * the same number of times that {@link #start() } was called. + */ public void stop() { GSTDEVICEPROVIDER_API.gst_device_provider_stop(this); - } + } } diff --git a/src/org/freedesktop/gstreamer/device/DeviceProviderFactory.java b/src/org/freedesktop/gstreamer/device/DeviceProviderFactory.java index 699b7815..a811163d 100644 --- a/src/org/freedesktop/gstreamer/device/DeviceProviderFactory.java +++ b/src/org/freedesktop/gstreamer/device/DeviceProviderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Neil C Smith + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2015 Andres Colubri * Copyright (C) 2013 Olivier Crete * @@ -23,67 +23,124 @@ import java.util.List; import org.freedesktop.gstreamer.GstObject; import org.freedesktop.gstreamer.PluginFeature.Rank; -import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; import static org.freedesktop.gstreamer.lowlevel.GstDeviceProviderFactoryAPI.GSTDEVICEPROVIDERFACTORY_API; +import org.freedesktop.gstreamer.lowlevel.GstPluginAPI; /** + * A factory for {@link DeviceProvider} + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstDeviceProviderFactory.html + *

+ * DeviceProviderFactory is used to create instances of device providers. A + * DeviceProviderfactory can be added to a Plugin as it is also a PluginFeature. * - * @author andres + * Use the {@link #find(java.lang.String)} and + * {@link #get()} functions to create device provider + * instances or use {@link #getByName(java.lang.String)} as a convenient + * shortcut. */ public class DeviceProviderFactory extends GstObject { + public static final String GTYPE_NAME = "GstDeviceProviderFactory"; - - public DeviceProviderFactory(Initializer init) { + + DeviceProviderFactory(Initializer init) { super(init); } - - static public DeviceProviderFactory find(String name) { - return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_find(name); - } - + + /** + * Returns the {@link DeviceProvider} of the type defined by the given + * device provider factory. + * + * @return DeviceProvider or NULL if the device provider couldn't be + * created. + */ public DeviceProvider get() { return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get(this); } + - public DeviceProvider getByName(String factoryName) { - return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get_by_name(factoryName); - } - - public GType getDeviceProviderType() { - return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get_device_provider_type(this); - } - + /** + * Get the metadata on factory with key. + * + * @param key + * @return the metadata with key on factory or null when there was no + * metadata with the given key . + */ public String getMetadata(String key) { return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get_metadata(this, key); } - - public String[] getMetadataKeys() { - return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get_metadata_keys(this); - } - + +// @TODO List ?? +// public String[] getMetadataKeys() { +// return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get_metadata_keys(this); +// } + + /** + * Check if factory matches all of the given classes + * + * @param classes a "/" separate list of classes to match, only match if all + * classes are matched. + * @return true if factory matches + */ public boolean hasClasses(String classes) { return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_has_classes(this, classes); } - - public boolean hasClasses(String[] classes) { - return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_has_classesv(this, classes); + +// public boolean hasClasses(String[] classes) { +// return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_has_classesv(this, classes); +// } + /** + * Search for an device provider factory of the given name. + * + * @param name name of factory to find + * @return DeviceProviderFactory if found, or null + */ + public static DeviceProviderFactory find(String name) { + return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_find(name); } - public List getDeviceProviders(Rank minRank) { + /** + * Returns the device provider of the type defined by the given device + * provider factory. + * + * @param factoryName a named factory to instantiate + * @return DeviceProvider or NULL if the device provider couldn't be + * created. + */ + public static DeviceProvider getByName(String factoryName) { + return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get_by_name(factoryName); + } + + /** + * Get a list of factories with a rank greater or equal to minrank . The + * list of factories is returned by decreasing rank. + * @param minRank minimum rank + * @return a list of + */ + public static List getDeviceProviders(Rank minRank) { GList glist = GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_list_get_device_providers(minRank); - List list = new ArrayList(); - + List list = new ArrayList<>(); + GList next = glist; while (next != null) { if (next.data != null) { - DeviceProvider prov = new DeviceProvider(initializer(next.data, true, true)); - list.add(prov); + DeviceProviderFactory factory = + new DeviceProviderFactory(Natives.initializer(next.data, true, true)); + list.add(factory); } next = next.next(); } - return list; + GstPluginAPI.GSTPLUGIN_API.gst_plugin_list_free(glist); + + return list; } +// public GType getDeviceProviderType() { +// return GSTDEVICEPROVIDERFACTORY_API.gst_device_provider_factory_get_device_provider_type(this); +// } } diff --git a/src/org/freedesktop/gstreamer/elements/AppSink.java b/src/org/freedesktop/gstreamer/elements/AppSink.java index adb69946..36494599 100644 --- a/src/org/freedesktop/gstreamer/elements/AppSink.java +++ b/src/org/freedesktop/gstreamer/elements/AppSink.java @@ -1,6 +1,6 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet - * Copyright (c) 2015 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Wayne Meissner * Copyright (c) 2008 Wayne Meissner @@ -21,127 +21,165 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.elements; -import org.freedesktop.gstreamer.Buffer; import org.freedesktop.gstreamer.Caps; +import org.freedesktop.gstreamer.Element; import org.freedesktop.gstreamer.FlowReturn; import org.freedesktop.gstreamer.Sample; -import org.freedesktop.gstreamer.lowlevel.AppAPI; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; +import static org.freedesktop.gstreamer.lowlevel.AppAPI.APP_API; /** - * A sink {@link org.freedesktop.gstreamer.Element} that enables an application to pull data - * from a pipeline. + * A sink {@link Element} that provides an easy way for applications to extract + * samples from a pipeline. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gst-plugins-base-libs/html/gst-plugins-base-libs-appsink.html + *

+ * Appsink is a sink plugin that supports many different methods for making the + * application get a handle on the GStreamer data in a pipeline. Unlike most + * GStreamer elements, Appsink provides external API functions. + *

+ * The normal way of retrieving samples from appsink is by using the + * {@link #pullSample()} and {@link #pullPreroll()} methods. These methods block + * until a sample becomes available in the sink or when the sink is shut down or + * reaches EOS. (TODO - not currently mapped in the Java bindings. + * There are also timed variants of these methods, + * gst_app_sink_try_pull_sample() and gst_app_sink_try_pull_preroll(), which + * accept a timeout parameter to limit the amount of time to wait). + *

+ * Appsink will internally use a queue to collect buffers from the streaming + * thread. If the application is not pulling samples fast enough, this queue + * will consume a lot of memory over time. The "max-buffers" property can be + * used to limit the queue size. The "drop" property controls whether the + * streaming thread blocks or if older buffers are dropped when the maximum + * queue size is reached. Note that blocking the streaming thread can negatively + * affect real-time performance and should be avoided. + *

+ * If a blocking behaviour is not desirable, setting the "emit-signals" property + * to TRUE will make appsink emit the "new-sample" and "new-preroll" signals + * when a sample can be pulled without blocking. + *

+ * The "caps" property on appsink can be used to control the formats that + * appsink can receive. This property can contain non-fixed caps, the format of + * the pulled samples can be obtained by getting the sample caps. + *

+ * If one of the pull-preroll or pull-sample methods return NULL, the appsink is + * stopped or in the EOS state. You can check for the EOS state with the "eos" + * property or with the {@link #isEOS()} method. + *

+ * The eos signal can also be used to be informed when the EOS state is reached + * to avoid polling. */ public class AppSink extends BaseSink { + public static final String GST_NAME = "appsink"; public static final String GTYPE_NAME = "GstAppSink"; - private static final AppAPI gst() { return AppAPI.APP_API; } - - public AppSink(Initializer init) { + AppSink(Initializer init) { super(init); } - + public AppSink(String name) { - this(makeRawElement(GST_NAME, name)); + this(makeRawElement(GST_NAME, name)); } /** - * Sets the capabilities on the appsink element. + * Sets the {@link Caps} on the AppSink. *

- * After calling this method, the sink will only accept caps that match caps. - * If caps is non-fixed, you must check the caps on the buffers to - * get the actual used caps. - * - * @param caps The Caps to set. + * After calling this method, the sink will only accept Caps that match + * caps. If caps is non-fixed, you must check the caps on + * the buffers to get the actual used caps. + * + * @param caps the Caps to set. */ @Override public void setCaps(Caps caps) { - gst().gst_app_sink_set_caps(this, caps); + APP_API.gst_app_sink_set_caps(this, caps); } /** - * Gets the Caps configured on this AppSink + * Gets the {@link Caps} configured on this AppSink * - * @return The caps configured on this AppSink + * @return the Caps accepted by this sink */ public Caps getCaps() { - return gst().gst_app_sink_get_caps(this); + return APP_API.gst_app_sink_get_caps(this); } /** - * Checks if this AppSink is end-of-stream. + * Check if this AppSink is EOS, which is when no more samples can be pulled + * because an EOS event was received. *

- * If an EOS event has been received, no more buffers can be pulled. - * - * @return true if no more buffers can be pulled and this - * AppSink is EOS. + * This function also returns TRUE when the AppSink is not in the PAUSED or + * PLAYING state. + * + * @return true if no more buffers can be pulled and this AppSink is EOS. */ public boolean isEOS() { - return gst().gst_app_sink_is_eos(this); + return APP_API.gst_app_sink_is_eos(this); } /** - * Get the last preroll buffer in this AppSink. + * Get the last preroll {@link Sample}. This was the Sample that caused the + * AppSink to preroll in the PAUSED state. *

- * This was the buffer that caused the appsink to preroll in the PAUSED state. - * This buffer can be pulled many times and remains available to the application - * even after EOS. + * This function is typically used when dealing with a pipeline in the + * PAUSED state. Calling this function after doing a seek will give the + * sample right after the seek position. *

- * This function is typically used when dealing with a pipeline in the PAUSED - * state. Calling this function after doing a seek will give the buffer right - * after the seek position. + * Calling this function will clear the internal reference to the preroll + * buffer. *

- * Note that the preroll buffer will also be returned as the first buffer - * when calling {@link #pullSample}. + * Note that the preroll sample will also be returned as the first sample + * when calling {@link #pullSample() } *

* If an EOS event was received before any buffers, this function returns - * null. Use {@link #isEOS} to check for the EOS condition. + * NULL. Use {@link #isEOS() } to check for the EOS condition. *

- * This function blocks until a preroll buffer or EOS is received or the appsink - * element is set to the READY/NULL state. + * This function blocks until a preroll sample or EOS is received or the + * AppSink element is set to the READY/NULL state. * - * @return A {@link Buffer} or null when the appsink is stopped or EOS. + * @return a Sample, or null if the AppSink is stopped or EOS */ public Sample pullPreroll() { - return gst().gst_app_sink_pull_preroll(this); + return APP_API.gst_app_sink_pull_preroll(this); } /** - * TODO reword - * Pulls a {@link org.freedesktop.gstreamer.Buffer} from the AppSink. + * This function blocks until a {@link Sample} or EOS becomes available or + * the AppSink element is set to the READY/NULL state. *

- * This function blocks until a buffer or EOS becomes available or the appsink - * element is set to the READY/NULL state. - *

- * This function will only return buffers when the appsink is in the PLAYING - * state. All rendered buffers will be put in a queue so that the application - * can pull buffers at its own rate. Note that when the application does not - * pull buffers fast enough, the queued buffers could consume a lot of memory, - * especially when dealing with raw video frames. + * This function will only return samples when the AppSink is in the PLAYING + * state. All rendered buffers will be put in a queue so that the + * application can pull samples at its own rate. Note that when the + * application does not pull samples fast enough, the queued buffers could + * consume a lot of memory, especially when dealing with raw video frames. *

* If an EOS event was received before any buffers, this function returns - * null. Use {@link #isEOS} to check for the EOS condition. + * NULL. Use {@link #isEOS() } to check for the EOS condition. * - * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS. - * @return A {@link org.freedesktop.gstreamer.Buffer} or NULL when the appsink is stopped or EOS. + * @return a Sample, or null if the AppSink is stopped or EOS */ public Sample pullSample() { - return gst().gst_app_sink_pull_sample(this); + return APP_API.gst_app_sink_pull_sample(this); } /** * Signal emitted when this {@link AppSink} got EOS. */ public static interface EOS { + /** - * @param elem + * EOS signal + * + * @param elem AppSink */ public void eos(AppSink elem); } + /** * Adds a listener for the eos signal. * @@ -155,6 +193,7 @@ public void callback(AppSink elem) { } }); } + /** * Removes a listener for the eos signal * @@ -163,17 +202,21 @@ public void callback(AppSink elem) { public void disconnect(EOS listener) { disconnect(EOS.class, listener); } - + /** - * Signal emitted when this {@link AppSink} when a new buffer is ready. + * Signal emitted when this {@link AppSink} when a new Sample is ready. */ public static interface NEW_SAMPLE { + /** + * New Sample signal * - * @param elem + * @param elem AppSink + * @return FlowReturn */ public FlowReturn newSample(AppSink elem); } + /** * Adds a listener for the new-sample signal. If a blocking * behaviour is not desirable, setting the "emit-signals" property to TRUE @@ -190,25 +233,30 @@ public FlowReturn callback(AppSink elem) { } }); } + /** - * Removes a listener for the new-buffer signal + * Removes a listener for the new-sample signal * - * @param listener The listener that was previously added. + * @param listener a listener that was previously added. */ public void disconnect(NEW_SAMPLE listener) { disconnect(NEW_SAMPLE.class, listener); } - + /** * Signal emitted when this {@link AppSink} when a new buffer is ready. */ public static interface NEW_PREROLL { + /** + * New preroll signal * - * @param elem + * @param elem AppSink + * @return FlowReturn */ public FlowReturn newPreroll(AppSink elem); } + /** * Adds a listener for the new-preroll signal. If a blocking * behaviour is not desirable, setting the "emit-signals" property to TRUE @@ -225,6 +273,7 @@ public FlowReturn callback(AppSink elem) { } }); } + /** * Removes a listener for the new-preroll signal * @@ -234,137 +283,4 @@ public void disconnect(NEW_PREROLL listener) { disconnect(NEW_PREROLL.class, listener); } - /** - * This function blocks until a buffer or EOS becomes available or this - * {@link AppSink} element is set to the READY/NULL state. - */ - public static interface PULL_BUFFER { - /** - * - * @param elem - */ - public Buffer pullBuffer(AppSink elem); - } - /** - * Adds a listener for the pull-buffer signal. - * Note that when the application does not pull buffers fast enough, the - * queued buffers could consume a lot of memory, especially when dealing - * with raw video frames. It's possible to control the behaviour of the - * queue with the "drop" and "max-buffers" properties. - * - * @param listener - */ - public void connect(final PULL_BUFFER listener) { - connect(PULL_BUFFER.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public Buffer callback(AppSink elem) { - return listener.pullBuffer(elem); - } - }); - } - /** - * Removes a listener for the pull-buffer signal - * - * @param listener The listener that was previously added. - */ - public void disconnect(PULL_BUFFER listener) { - disconnect(PULL_BUFFER.class, listener); - } - - /** - * Get the last preroll buffer in this {@link AppSink} element. - */ - public static interface PULL_PREROLL { - /** - * - * @param elem - */ - public Buffer pullPreroll(AppSink elem); - } - /** - * Adds a listener for the pull-preroll signal. - * - * @param listener - */ - public void connect(final PULL_PREROLL listener) { - connect(PULL_PREROLL.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public Buffer callback(AppSink elem) { - return listener.pullPreroll(elem); - } - }); - } - /** - * Removes a listener for the pull-preroll signal - * - * @param listener The listener that was previously added. - */ - public void disconnect(PULL_PREROLL listener) { - disconnect(PULL_PREROLL.class, listener); - } - - /** - * Signal emitted when this {@link AppSink} when a new buffer is ready. - */ - public static interface NEW_BUFFER_LIST { - /** - * - * @param elem - */ - public void newBufferList(AppSink elem); - } - /** - * Adds a listener for the new-buffer-list signal. - * - * @param listener - */ - public void connect(final NEW_BUFFER_LIST listener) { - connect(NEW_BUFFER_LIST.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public void callback(AppSink elem) { - listener.newBufferList(elem); - } - }); - } - /** - * Removes a listener for the new-buffer-list signal - * - * @param listener The listener that was previously added. - */ - public void disconnect(NEW_BUFFER_LIST listener) { - disconnect(NEW_BUFFER_LIST.class, listener); - } - -// /** -// * This function blocks until a buffer list or EOS becomes available or -// * this {@link AppSink} element is set to the READY/NULL state. -// */ -// public static interface PULL_BUFFER_LIST { -// /** -// * -// * @param elem -// */ -// public BufferList pullBufferList(AppSink elem); -// } -// /** -// * Adds a listener for the pull-buffer-list signal. -// * -// * @param listener -// */ -// public void connect(final PULL_BUFFER_LIST listener) { -// connect(PULL_BUFFER_LIST.class, listener, new GstCallback() { -// @SuppressWarnings("unused") -// public BufferList callback(AppSink elem) { -// return listener.pullBufferList(elem); -// } -// }); -// } -// /** -// * Removes a listener for the pull-buffer-list signal -// * -// * @param listener The listener that was previously added. -// */ -// public void disconnect(PULL_BUFFER_LIST listener) { -// disconnect(PULL_BUFFER_LIST.class, listener); -// } } diff --git a/src/org/freedesktop/gstreamer/elements/AppSrc.java b/src/org/freedesktop/gstreamer/elements/AppSrc.java index 1ed49b9e..54f09325 100644 --- a/src/org/freedesktop/gstreamer/elements/AppSrc.java +++ b/src/org/freedesktop/gstreamer/elements/AppSrc.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2009 Andres Colubri * Copyright (c) 2008 Wayne Meissner * Copyright (C) 2007 David Schleef @@ -18,130 +19,244 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.elements; import org.freedesktop.gstreamer.Buffer; import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.FlowReturn; -import org.freedesktop.gstreamer.lowlevel.AppAPI; +import org.freedesktop.gstreamer.Element; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; +import static org.freedesktop.gstreamer.lowlevel.AppAPI.APP_API; import com.sun.jna.ptr.LongByReference; +import org.freedesktop.gstreamer.FlowReturn; +import org.freedesktop.gstreamer.Format; +import org.freedesktop.gstreamer.glib.NativeEnum; /** - * Enables an application to feed buffers into a pipeline. + * A source {@link Element} that provides an easy way for applications to insert + * data into a GStreamer pipeline. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gst-plugins-base-libs/html/gst-plugins-base-libs-appsrc.html + *

+ * Before operating appsrc, the {@link Caps} property must be set to fixed caps + * describing the format of the data that will be pushed with appsrc. An + * exception to this is when pushing buffers with unknown caps, in which case no + * caps should be set. This is typically true of file-like sources that push raw + * byte buffers. If you don't want to explicitly set the caps, you can use + * gst_app_src_push_sample (not yet mapped). This method gets the caps + * associated with the sample and sets them on the appsrc replacing any + * previously set caps (if different from sample's caps). + *

+ * The main way of handing data to the appsrc element is by calling the + * {@link #pushBuffer(Buffer) } method or by emitting the push-buffer action + * signal. This will put the buffer onto a queue from which appsrc will read + * from in its streaming thread. It is important to note that data transport + * will not happen from the thread that performed the push-buffer call. + *

+ * The "max-bytes" property controls how much data can be queued in appsrc + * before appsrc considers the queue full. A filled internal queue will always + * signal the "enough-data" signal, which signals the application that it should + * stop pushing data into appsrc. The "block" property will cause appsrc to + * block the push-buffer method until free data becomes available again. + *

+ * When the internal queue is running out of data, the "need-data" signal is + * emitted, which signals the application that it should start pushing more data + * into appsrc. + *

+ * In addition to the "need-data" and "enough-data" signals, appsrc can emit the + * "seek-data" signal when the "stream-mode" property is set to "seekable" or + * "random-access". The signal argument will contain the new desired position in + * the stream expressed in the unit set with the "format" property. After + * receiving the seek-data signal, the application should push-buffers from the + * new position. + *

+ * These signals allow the application to operate the appsrc in two different + * ways: + *

+ * The push mode, in which the application repeatedly calls the + * push-buffer/push-sample method with a new buffer/sample. Optionally, the + * queue size in the appsrc can be controlled with the enough-data and need-data + * signals by respectively stopping/starting the push-buffer/push-sample calls. + * This is a typical mode of operation for the stream-type "stream" and + * "seekable". Use this mode when implementing various network protocols or + * hardware devices. + *

+ * The pull mode, in which the need-data signal triggers the next push-buffer + * call. This mode is typically used in the "random-access" stream-type. Use + * this mode for file access or other randomly accessable sources. In this mode, + * a buffer of exactly the amount of bytes given by the need-data signal should + * be pushed into appsrc. + *

+ * In all modes, the size property on appsrc should contain the total stream + * size in bytes. Setting this property is mandatory in the random-access mode. + * For the stream and seekable modes, setting this property is optional but + * recommended. + *

+ * When the application has finished pushing data into appsrc, it should call + * {@link #endOfStream() } or emit the end-of-stream action signal. After this + * call, no more buffers can be pushed into appsrc until a flushing seek occurs + * or the state of the appsrc has gone through READY. */ public class AppSrc extends BaseSrc { + public static final String GST_NAME = "appsrc"; public static final String GTYPE_NAME = "GstAppSrc"; - - private static final AppAPI gst() { return AppAPI.APP_API; } - public enum Type { - STREAM, - SEEKABLE, - RANDOM_ACCESS; - } - - public AppSrc(Initializer init) { + AppSrc(Initializer init) { super(init); } - - protected AppSrc(String name) { - this(makeRawElement(GST_NAME, name)); + + public AppSrc(String name) { + this(makeRawElement(GST_NAME, name)); } - + + /** + * Set the capabilities on the appsrc element. This function takes a copy of + * the caps structure. After calling this method, the source will only + * produce caps that match caps . caps must be fixed and the caps on the + * buffers must match the caps or left NULL. + */ @Override public void setCaps(Caps caps) { - gst().gst_app_src_set_caps(this, caps); + APP_API.gst_app_src_set_caps(this, caps); } + + /** + * Get the configured Caps on the AppSrc. + * + * @return the caps + */ public Caps getCaps() { - return gst().gst_app_src_get_caps(this); + return APP_API.gst_app_src_get_caps(this); } + /** + * Set the size of the stream in bytes. A value of -1 means that the size is + * not known. + * + * @param size the size to set, or -1 if not known + */ public void setSize(long size) { - gst().gst_app_src_set_size(this, size); + APP_API.gst_app_src_set_size(this, size); } + + /** + * Get the size of the stream in bytes. A value of -1 means that the size is + * not known. + * + * @return the size of the stream, or -1 if not specified + */ public long getSize() { - return gst().gst_app_src_get_size(this); + return APP_API.gst_app_src_get_size(this); } - public void setStreamType(AppSrc.Type type) { - gst().gst_app_src_set_stream_type(this, type); + /** + * Set the stream type on appsrc . For seekable streams, the "seek" signal + * must be connected to. + * + * @param type stream type + */ + public void setStreamType(AppSrc.StreamType type) { + APP_API.gst_app_src_set_stream_type(this, type); } - AppSrc.Type getStreamType(AppSrc.Type type) { - return gst().gst_app_src_get_stream_type(this); + + /** + * Get the stream type set on the appsrc. + * + * @return stream type + */ + public AppSrc.StreamType getStreamType() { + return APP_API.gst_app_src_get_stream_type(this); } + /** + * Set the maximum amount of bytes that can be queued in appsrc . After the + * maximum amount of bytes are queued, appsrc will emit the "enough-data" + * signal. + * + * @param max number of bytes to queue + */ public void setMaxBytes(long max) { - gst().gst_app_src_set_max_bytes(this, max); + APP_API.gst_app_src_set_max_bytes(this, max); } + + /** + * Get the maximum number of bytes that can be queued. + * + * @return maximum number of bytes + */ public long getMaxBytes() { - return gst().gst_app_src_get_max_bytes(this); + return APP_API.gst_app_src_get_max_bytes(this); } + /** + * Configure the min and max latency in src . If min is set to -1, the + * default latency calculations for pseudo-live sources will be used. + * + * @param min the minimum latency + * @param max the maximum latency + */ public void setLatency(long min, long max) { - gst().gst_app_src_set_latency(this, min, max); + APP_API.gst_app_src_set_latency(this, min, max); } - public void getLatency(long[] minmax) { + + /** + * Get the latency. The returned value is a long array of length two, with + * min latency at index 0 and max latency at index 1. + * + * @return values as {@code long[]{min, max} } + */ + public long[] getLatency() { LongByReference minRef = new LongByReference(); LongByReference maxRef = new LongByReference(); - gst().gst_app_src_get_latency(this, minRef, minRef); - if ((minmax == null) || (minmax.length != 2)) minmax = new long[2]; - minmax[0] = minRef.getValue(); - minmax[1] = maxRef.getValue(); - } - - public void pushBuffer(Buffer buffer) { - gst().gst_app_src_push_buffer(this, buffer); - } - public void endOfStream() { - gst().gst_app_src_end_of_stream(this); + APP_API.gst_app_src_get_latency(this, minRef, minRef); + return new long[]{minRef.getValue(), maxRef.getValue()}; } /** - * Signal emitted when this {@link AppSrc} when no more buffer are available. + * Adds a buffer to the queue of buffers that the appsrc element will push + * to its source pad. This function takes ownership of the buffer. + *

+ * When the block property is TRUE, this function can block until free space + * becomes available in the queue. + * + * @param buffer a {@link Buffer} to push + * @return GST_FLOW_OK when the buffer was successfully queued. + * GST_FLOW_FLUSHING when appsrc is not PAUSED or PLAYING. GST_FLOW_EOS when + * EOS occurred. */ - public static interface END_OF_STREAM { - /** - * - * @param elem - */ - public FlowReturn endOfStream(AppSrc elem); - } - /** - * Adds a listener for the end-of-stream signal - * - * @param listener Listener to be called this when appsrc has no more buffer are available. - */ - public void connect(final END_OF_STREAM listener) { - connect(END_OF_STREAM.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public FlowReturn callback(AppSrc elem) { - return listener.endOfStream(elem); - } - }); + public FlowReturn pushBuffer(Buffer buffer) { + return APP_API.gst_app_src_push_buffer(this, buffer); } + /** - * Removes a listener for the end-of-stream signal - * - * @param listener The listener that was previously added. + * Indicates to the appsrc element that the last buffer queued in the + * element is the last buffer of the stream. + * + * @return GST_FLOW_OK when the EOS was successfuly queued. GST_FLOW_FLUSHING when + * appsrc is not PAUSED or PLAYING. */ - public void disconnect(END_OF_STREAM listener) { - disconnect(END_OF_STREAM.class, listener); + public FlowReturn endOfStream() { + return APP_API.gst_app_src_end_of_stream(this); } /** - * Signal emitted when this {@link AppSrc} has enough data in the queue. + * Signal that the source has enough data. It is recommended that the + * application stops calling push-buffer until the need-data signal is + * emitted again to avoid excessive buffer queueing. */ public static interface ENOUGH_DATA { + /** + * Enough data signal * - * @param elem + * @param elem the appsrc element that emitted the signal */ public void enoughData(AppSrc elem); } + /** * Adds a listener for the enough-data signal * @@ -155,6 +270,7 @@ public void callback(AppSrc elem) { } }); } + /** * Removes a listener for the enough-data signal * @@ -165,16 +281,26 @@ public void disconnect(ENOUGH_DATA listener) { } /** - * Signal emitted when this {@link AppSrc} needs data. + * Signal that the source needs more data. In the callback or from another + * thread you should call push-buffer or end-of-stream. + *

+ * length is just a hint and when it is set to -1, any number of bytes can + * be pushed into appsrc . + *

+ * You can call push-buffer multiple times until the enough-data signal is + * fired. */ public static interface NEED_DATA { + /** + * Need data signal * - * @param elem - * @param size + * @param elem the appsrc element that emitted the signal + * @param size the amount of bytes needed, or -1. This is just a hint. */ public void needData(AppSrc elem, int size); } + /** * Adds a listener for the need-data signal * @@ -188,6 +314,7 @@ public void callback(AppSrc elem, int size) { } }); } + /** * Removes a listener for the need-data signal * @@ -198,59 +325,27 @@ public void disconnect(NEED_DATA listener) { } /** - * Signal emitted when adds a buffer to the queue of buffers that - * this {@link AppSrc} element will push to its source pad. - */ - public static interface PUSH_BUFFER { - /** - * - * @param elem - * @param buffer - */ - public FlowReturn pushBuffer(AppSrc elem, Buffer buffer); - } - /** - * Adds a listener for the push-buffer signal - * - * @param listener Listener to be called when appsrc push buffer. - */ - public void connect(final PUSH_BUFFER listener) { - connect(PUSH_BUFFER.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public FlowReturn callback(AppSrc elem, Buffer buffer) { - return listener.pushBuffer(elem, buffer); - } - }); - } - /** - * Removes a listener for the push-buffer signal - * - * @param listener The listener that was previously added. - */ - public void disconnect(PUSH_BUFFER listener) { - disconnect(PUSH_BUFFER.class, listener); - } - - /** - * Signal emitted when this {@link AppSrc} when it requires the application - * to push buffers from a specific location in the input stream. + * Seek to the given offset, expressed in terms of the {@link Format} set on + * the AppSrc. The next push-buffer should produce buffers from the new + * offset . This callback is only called for seekable stream types. */ public static interface SEEK_DATA { + /** + * Seek data signal. * - * @param elem - * @param position + * @param elem the appsrc element that emitted the signal + * @param position the offset to seek to, expressed in the + * {@link Format} set on the AppSrc + * @return true if the seek succeeded */ public boolean seekData(AppSrc elem, long position); } + /** * Adds a listener for the seek-data signal * - * @param listener Listener to be called when appsrc when its "stream-mode" - * property is set to "seekable" or "random-access". The signal argument - * will contain the new desired position in the stream expressed in the unit - * set with the "format" property. After receiving the seek-data signal, - * the application should push-buffers from the new position. + * @param listener listener to be called when a seek is requested */ public void connect(final SEEK_DATA listener) { connect(SEEK_DATA.class, listener, new GstCallback() { @@ -260,6 +355,7 @@ public boolean callback(AppSrc elem, long position) { } }); } + /** * Removes a listener for the seek-data signal * @@ -268,4 +364,40 @@ public boolean callback(AppSrc elem, long position) { public void disconnect(SEEK_DATA listener) { disconnect(SEEK_DATA.class, listener); } + + /** + * The stream type. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gst-plugins-base-libs/html/gst-plugins-base-libs-appsrc.html#GstAppStreamType + *

+ */ + public enum StreamType implements NativeEnum { + /** + * No seeking is supported in the stream, such as a live stream. + */ + STREAM(0), + /** + * The stream is seekable but seeking might not be very fast, such as + * data from a webserver. + */ + SEEKABLE(1), + /** + * The stream is seekable and seeking is fast, such as in a local file. + */ + RANDOM_ACCESS(2); + + private final int value; + + private StreamType(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + + } } diff --git a/src/org/freedesktop/gstreamer/elements/BaseSink.java b/src/org/freedesktop/gstreamer/elements/BaseSink.java index 75f9dc34..5deecb77 100644 --- a/src/org/freedesktop/gstreamer/elements/BaseSink.java +++ b/src/org/freedesktop/gstreamer/elements/BaseSink.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,224 +16,148 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.elements; -import java.util.concurrent.TimeUnit; - -import org.freedesktop.gstreamer.Buffer; -import org.freedesktop.gstreamer.ClockReturn; -import org.freedesktop.gstreamer.ClockTime; import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.FlowReturn; -import org.freedesktop.gstreamer.MiniObject; -import org.freedesktop.gstreamer.Pad; -import org.freedesktop.gstreamer.lowlevel.BaseSinkAPI; -import org.freedesktop.gstreamer.lowlevel.BaseSinkAPI.GstBaseSinkClass; -import org.freedesktop.gstreamer.lowlevel.BaseSinkAPI.GstBaseSinkStruct; -import org.freedesktop.gstreamer.lowlevel.BaseSinkAPI.ProposeAllocation; -import org.freedesktop.gstreamer.lowlevel.GstAPI; -import org.freedesktop.gstreamer.query.AllocationQuery; - -import com.sun.jna.Pointer; +/** + * A base class for sink elements. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstBaseSink.html + *

+ */ public class BaseSink extends Element { + public static final String GTYPE_NAME = "GstBaseSink"; - private static final BaseSinkAPI gst() { return BaseSinkAPI.BASESINK_API; } - - public BaseSink(Initializer init) { + protected BaseSink(Initializer init) { super(init); } - - public FlowReturn preroll(MiniObject obj) { - return gst().gst_base_sink_do_preroll(this, obj); - } - public FlowReturn waitPreroll() { - return gst().gst_base_sink_wait_preroll(this); - } - public void setSync(boolean sync) { - gst().gst_base_sink_set_sync(this, sync); - } - public boolean isSync() { - return gst().gst_base_sink_get_sync(this); - } - public void setMaximumLateness(long lateness, TimeUnit units) { - gst().gst_base_sink_set_max_lateness(this, units.toNanos(lateness)); - } - public long getMaximumLateness(TimeUnit units) { - return units.convert(gst().gst_base_sink_get_max_lateness(this),TimeUnit.NANOSECONDS); - } - public void setQOSEnabled(boolean qos) { - gst().gst_base_sink_set_qos_enabled(this, qos); - } - public boolean isQOSEnabled() { - return gst().gst_base_sink_is_qos_enabled(this); - } - public void enableAsync(boolean enabled) { - gst().gst_base_sink_set_async_enabled(this, enabled); - } - public boolean isAsync() { - return gst().gst_base_sink_is_async_enabled(this); - } - public void setTsOffset(long offset) { - gst().gst_base_sink_set_ts_offset(this, offset); - } - public long getTsOffset() { - return gst().gst_base_sink_get_ts_offset(this); - } - public Buffer getLastBuffer() { - return gst().gst_base_sink_get_last_buffer(this); - } - public void enableLastBuffer(boolean enable) { - gst().gst_base_sink_set_last_buffer_enabled(this, enable); - } - public boolean isLastBufferEnabled() { - return gst().gst_base_sink_is_last_buffer_enabled(this); - } - public boolean queryLatency(boolean live, boolean upstream_live, ClockTime min_latency, ClockTime max_latency) { - return gst().gst_base_sink_query_latency(this, live, upstream_live, min_latency, max_latency); - } - public ClockTime getLatency() { - return gst().gst_base_sink_get_latency(this); - } - public void setRenderDelay(ClockTime delay) { - gst().gst_base_sink_set_render_delay(this, delay); - } - public ClockTime getRenderDelay() { - return gst().gst_base_sink_get_render_delay(this); - } - public void setBlocksize(int blocksize) { - gst().gst_base_sink_set_blocksize(this, blocksize); - } - public int getBlocksize() { - return gst().gst_base_sink_get_blocksize(this); - } - public ClockReturn waitClock(ClockTime time, /* GstClockTimeDiff */ Pointer jitter) { - return gst().gst_base_sink_wait_clock(this, time, jitter); - } - public FlowReturn waitEOS(ClockTime time, /* GstClockTimeDiff */ Pointer jitter) { - return gst().gst_base_sink_wait_eos(this, time, jitter); - } - - /** - * Signal emitted when this {@link BaseSink} has a {@link Buffer} ready. - * - * @see #connect(HANDOFF) - * @see #disconnect(HANDOFF) - */ - public static interface HANDOFF { - /** - * Called when an {@link BaseSink} has a {@link Buffer} ready. - * - * @param sink the sink which has a buffer ready. - * @param buffer the buffer for the data. - * @param pad the pad on the element. - */ - public void handoff(BaseSink sink, Buffer buffer, Pad pad); - } - private static class HANDOFFCallback implements GstAPI.GstCallback { - final HANDOFF listener; - public HANDOFFCallback(final HANDOFF listener) { - this.listener = listener; - //Native.setCallbackThreadInitializer(this, new CallbackThreadInitializer(true, false, "BaseSink Handoff")); - } - @SuppressWarnings("unused") - public void callback(BaseSink sink, Buffer buffer, Pad pad) { - listener.handoff(sink, buffer, pad); -// if (last) -// Native.detach(true); - } - } - /** - * Add a listener for the handoff signal on this sink - * - * @param listener The listener to be called when a {@link Buffer} is ready. - */ - public void connect(final HANDOFF listener) { - connect(HANDOFF.class, listener, new HANDOFFCallback(listener)); - } - /** - * Remove a listener for the handoff signal - * - * @param listener The listener that was previously added. - */ - public void disconnect(HANDOFF listener) { - disconnect(HANDOFF.class, listener); - } - - private static class PrerollHandoff implements GstAPI.GstCallback { - final PREROLL_HANDOFF listener; - public PrerollHandoff(final PREROLL_HANDOFF listener) { - this.listener = listener; - //Native.setCallbackThreadInitializer(this, new CallbackThreadInitializer(true, false, "BaseSink Preroll Handoff")); - } - @SuppressWarnings("unused") - public void callback(BaseSink sink, Buffer buffer, Pad pad) { - listener.prerollHandoff(sink, buffer, pad); -// if (last) -// Native.detach(true); - } - } - /** - * Signal emitted when this {@link BaseSink} has a {@link Buffer} ready. - * - * @see #connect(PREROLL_HANDOFF) - * @see #disconnect(PREROLL_HANDOFF) - */ - public static interface PREROLL_HANDOFF { - /** - * Called when a {@link BaseSink} has a {@link Buffer} ready. - * - * @param sink the sink instance. - * @param buffer the buffer that just has been received. - * @param pad the pad that received it. - */ - public void prerollHandoff(BaseSink sink, Buffer buffer, Pad pad); - } - /** - * Add a listener for the preroll-handoff signal. - * - * @param listener The listener to be called when a {@link Buffer} is ready. - */ - public void connect(final PREROLL_HANDOFF listener) { - connect(PREROLL_HANDOFF.class, listener, new PrerollHandoff(listener)); - } - /** - * Remove a listener for the preroll-handoff signal. - * - * @param listener The listener that was previously added. - */ - public void disconnect(PREROLL_HANDOFF listener) { - disconnect(PREROLL_HANDOFF.class, listener); - } + // @TODO review - all properties are available by set / get mechanism + // some other methods require the preroll lock, but we don't have a way to + // access. Methods for subclasses should probably be protected. - /** - * Signal emitted when this {@link BaseSink} received a {@link ProposeAllocation} query. - * - * @see #setProposeAllocationHandler(ProposeAllocationHandler) - */ - public static interface ProposeAllocationHandler { - public boolean proposeAllocation(BaseSink sink, AllocationQuery query); - } - - /** - * Set a handler for the {@link ProposeAllocation} query on this sink - * - * @param handler The handler to be called when a {@link ProposeAllocation} is received. - */ - public void setProposeAllocationHandler(final ProposeAllocationHandler handler) { - GstBaseSinkStruct struct = new GstBaseSinkStruct(handle()); - struct.readField("element"); - GstBaseSinkClass basesinkClass = new GstBaseSinkClass(struct.element.object.object.g_type_instance.g_class.getPointer()); - basesinkClass.propose_allocation = new ProposeAllocation() { - @Override - public boolean callback(BaseSink sink, AllocationQuery query) { - return handler.proposeAllocation(sink, query); - } - }; - basesinkClass.writeField("propose_allocation"); - } +// public FlowReturn doPreroll(MiniObject obj) { +// return BASESINK_API.gst_base_sink_do_preroll(this, obj); +// } +// +// public FlowReturn waitPreroll() { +// return BASESINK_API.gst_base_sink_wait_preroll(this); +// } +// +// public void setSync(boolean sync) { +// BASESINK_API.gst_base_sink_set_sync(this, sync); +// } +// +// public boolean isSync() { +// return BASESINK_API.gst_base_sink_get_sync(this); +// } +// +// public void setMaximumLateness(long lateness, TimeUnit units) { +// BASESINK_API.gst_base_sink_set_max_lateness(this, units.toNanos(lateness)); +// } +// +// public long getMaximumLateness(TimeUnit units) { +// return units.convert(BASESINK_API.gst_base_sink_get_max_lateness(this), TimeUnit.NANOSECONDS); +// } +// +// public void setQOSEnabled(boolean qos) { +// BASESINK_API.gst_base_sink_set_qos_enabled(this, qos); +// } +// +// public boolean isQOSEnabled() { +// return BASESINK_API.gst_base_sink_is_qos_enabled(this); +// } +// +// public void enableAsync(boolean enabled) { +// BASESINK_API.gst_base_sink_set_async_enabled(this, enabled); +// } +// +// public boolean isAsync() { +// return BASESINK_API.gst_base_sink_is_async_enabled(this); +// } +// +// public void setTsOffset(long offset) { +// BASESINK_API.gst_base_sink_set_ts_offset(this, offset); +// } +// +// public long getTsOffset() { +// return BASESINK_API.gst_base_sink_get_ts_offset(this); +// } +// +// public Buffer getLastBuffer() { +// return BASESINK_API.gst_base_sink_get_last_buffer(this); +// } +// +// public void enableLastBuffer(boolean enable) { +// BASESINK_API.gst_base_sink_set_last_buffer_enabled(this, enable); +// } +// +// public boolean isLastBufferEnabled() { +// return BASESINK_API.gst_base_sink_is_last_buffer_enabled(this); +// } +// +// public boolean queryLatency(boolean live, boolean upstream_live, ClockTime min_latency, ClockTime max_latency) { +// return BASESINK_API.gst_base_sink_query_latency(this, live, upstream_live, min_latency, max_latency); +// } +// +// public ClockTime getLatency() { +// return BASESINK_API.gst_base_sink_get_latency(this); +// } +// +// public void setRenderDelay(ClockTime delay) { +// BASESINK_API.gst_base_sink_set_render_delay(this, delay); +// } +// +// public ClockTime getRenderDelay() { +// return BASESINK_API.gst_base_sink_get_render_delay(this); +// } +// +// public void setBlocksize(int blocksize) { +// BASESINK_API.gst_base_sink_set_blocksize(this, blocksize); +// } +// +// public int getBlocksize() { +// return BASESINK_API.gst_base_sink_get_blocksize(this); +// } +// +// public ClockReturn waitClock(ClockTime time, /* GstClockTimeDiff */ Pointer jitter) { +// return BASESINK_API.gst_base_sink_wait_clock(this, time, jitter); +// } +// +// public FlowReturn waitEOS(ClockTime time, /* GstClockTimeDiff */ Pointer jitter) { +// return BASESINK_API.gst_base_sink_wait_eos(this, time, jitter); +// } +// @TODO work out strategy for overriding methods +// +// /** +// * Signal emitted when this {@link BaseSink} received a {@link ProposeAllocation} query. +// * +// * @see #setProposeAllocationHandler(ProposeAllocationHandler) +// */ +// public static interface ProposeAllocationHandler { +// public boolean proposeAllocation(BaseSink sink, AllocationQuery query); +// } +// +// /** +// * Set a handler for the {@link ProposeAllocation} query on this sink +// * +// * @param handler The handler to be called when a {@link ProposeAllocation} is received. +// */ +// public void setProposeAllocationHandler(final ProposeAllocationHandler handler) { +// GstBaseSinkStruct struct = new GstBaseSinkStruct(handle()); +// struct.readField("element"); +// GstBaseSinkClass basesinkClass = new GstBaseSinkClass(struct.element.object.object.g_type_instance.g_class.getPointer()); +// basesinkClass.propose_allocation = new ProposeAllocation() { +// @Override +// public boolean callback(BaseSink sink, AllocationQuery query) { +// return handler.proposeAllocation(sink, query); +// } +// }; +// basesinkClass.writeField("propose_allocation"); +// } } diff --git a/src/org/freedesktop/gstreamer/elements/BaseSrc.java b/src/org/freedesktop/gstreamer/elements/BaseSrc.java index 4a94b66b..4f210b12 100644 --- a/src/org/freedesktop/gstreamer/elements/BaseSrc.java +++ b/src/org/freedesktop/gstreamer/elements/BaseSrc.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007 Wayne Meissner * @@ -18,92 +19,57 @@ */ package org.freedesktop.gstreamer.elements; -import org.freedesktop.gstreamer.Buffer; -import org.freedesktop.gstreamer.ClockTime; import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.FlowReturn; -import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Pad; -import org.freedesktop.gstreamer.lowlevel.BaseSrcAPI; -import org.freedesktop.gstreamer.lowlevel.GstAPI; +/** + * A base class for source elements. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstBaseSrc.html + *

+ */ public class BaseSrc extends Element { + public static final String GTYPE_NAME = "GstBaseSrc"; - private static final BaseSrcAPI gst() { return BaseSrcAPI.BASESRC_API; } - - public BaseSrc(Initializer init) { + protected BaseSrc(Initializer init) { super(init); } - public FlowReturn waitPlaying() { - return gst().gst_base_src_wait_playing(this); - } - public void setLive(boolean live) { - gst().gst_base_src_set_live(this, live); - } - public boolean isLive() { - return gst().gst_base_src_is_live(this); - } - public void setFormat(Format format) { - gst().gst_base_src_set_format(this, format); - } - public boolean queryLatency(boolean[] live, ClockTime[] min_latency, ClockTime[] max_latency) { - return gst().gst_base_src_query_latency(this, live, min_latency, max_latency); - } - public void setBlocksize(long blocksize) { - gst().gst_base_src_set_blocksize(this, blocksize); - } - public long getBlocksize() { - return gst().gst_base_src_get_blocksize(this); - } - public void setTimestamp(boolean timestamp) { - gst().gst_base_src_set_do_timestamp(this, timestamp); - } - public boolean getTimestamp() { - return gst().gst_base_src_get_do_timestamp(this); - } - public boolean newSeamlessSegment(long start, long stop, long position) { - return gst().gst_base_src_new_seamless_segment(this, start, stop, position); - } - - /** - * Signal emitted when this {@link BaseSrc} has a {@link Buffer} ready. - * - * @see #connect(HANDOFF) - * @see #disconnect(HANDOFF) - */ - public static interface HANDOFF { - /** - * Called when an {@link BaseSrc} has a {@link Buffer} ready. - * - * @param src the source which has a buffer ready. - * @param buffer the buffer for the data. - * @param pad the pad on the element. - */ - public void handoff(BaseSrc src, Buffer buffer, Pad pad); - } - - /** - * Add a listener for the handoff signal on this source - * - * @param listener The listener to be called when a {@link Buffer} is ready. - */ - public void connect(final HANDOFF listener) { - connect(HANDOFF.class, listener, new GstAPI.GstCallback() { - @SuppressWarnings("unused") - public void callback(BaseSrc src, Buffer buffer, Pad pad) { - listener.handoff(src, buffer, pad); - } - }); - } + // @TODO review - all properties are available by set / get mechanism + // some other methods require the stream lock, but we don't have a way to + // access. Methods for subclasses should probably be protected. - /** - * Remove a listener for the handoff signal - * - * @param listener The listener that was previously added. - */ - public void disconnect(HANDOFF listener) { - disconnect(HANDOFF.class, listener); - } +// public FlowReturn waitPlaying() { +// return gst().gst_base_src_wait_playing(this); +// } +// public void setLive(boolean live) { +// gst().gst_base_src_set_live(this, live); +// } +// public boolean isLive() { +// return gst().gst_base_src_is_live(this); +// } +// public void setFormat(Format format) { +// gst().gst_base_src_set_format(this, format); +// } +// public boolean queryLatency(boolean[] live, ClockTime[] min_latency, ClockTime[] max_latency) { +// return gst().gst_base_src_query_latency(this, live, min_latency, max_latency); +// } +// public void setBlocksize(long blocksize) { +// gst().gst_base_src_set_blocksize(this, blocksize); +// } +// public long getBlocksize() { +// return gst().gst_base_src_get_blocksize(this); +// } +// public void setTimestamp(boolean timestamp) { +// gst().gst_base_src_set_do_timestamp(this, timestamp); +// } +// public boolean getTimestamp() { +// return gst().gst_base_src_get_do_timestamp(this); +// } +// public boolean newSeamlessSegment(long start, long stop, long position) { +// return gst().gst_base_src_new_seamless_segment(this, start, stop, position); +// } +// } diff --git a/src/org/freedesktop/gstreamer/elements/BaseTransform.java b/src/org/freedesktop/gstreamer/elements/BaseTransform.java index 5dbe8c17..4f929924 100644 --- a/src/org/freedesktop/gstreamer/elements/BaseTransform.java +++ b/src/org/freedesktop/gstreamer/elements/BaseTransform.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * * This file is part of gstreamer-java. @@ -19,12 +20,19 @@ import org.freedesktop.gstreamer.Element; +/** + * A base class for simple transform filters. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer-libs/html/GstBaseTransform.html + *

+ */ public class BaseTransform extends Element { + public static final String GTYPE_NAME = "GstBaseTransform"; - //private static final BaseTransformAPI gst() { return BaseTransformAPI.BASETRANSFORM_API; } - - public BaseTransform(Initializer init) { + protected BaseTransform(Initializer init) { super(init); } } diff --git a/src/org/freedesktop/gstreamer/elements/DecodeBin.java b/src/org/freedesktop/gstreamer/elements/DecodeBin.java index be4ad0da..d9b0e42e 100644 --- a/src/org/freedesktop/gstreamer/elements/DecodeBin.java +++ b/src/org/freedesktop/gstreamer/elements/DecodeBin.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2010 DHoyt * @@ -16,25 +17,38 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.elements; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import org.freedesktop.gstreamer.Bin; import org.freedesktop.gstreamer.Caps; import org.freedesktop.gstreamer.Element; import org.freedesktop.gstreamer.ElementFactory; import org.freedesktop.gstreamer.Pad; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.query.Query; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValueArray; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GValueAPI; /** - * Utility {@link org.gstreamer.Element} to automatically identify media stream types and hook - * up elements. + * A {@link Bin} that auto-magically constructs a decoding pipeline using + * available decoders and demuxers via auto-plugging. + *

+ * {@link URIDecodeBin} uses DecodeBin internally and is often more convenient + * to use, as it creates a suitable source element as well. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gst-plugins-base-plugins/html/gst-plugins-base-plugins-decodebin.html + *

*/ public class DecodeBin extends Bin { - public static final String GST_NAME = "decodebin"; + + public static final String GST_NAME = "decodebin"; public static final String GTYPE_NAME = "GstDecodeBin"; /** @@ -46,14 +60,16 @@ public DecodeBin(String name) { super(makeRawElement(GST_NAME, name)); } - public DecodeBin(Initializer init) { + DecodeBin(Initializer init) { super(init); } /** - * Signal is emitted when a pad for which there is no further possible decoding is added to the {@link DecodeBin}. + * Signal is emitted when a pad for which there is no further possible + * decoding is added to the {@link DecodeBin}. */ public static interface UNKNOWN_TYPE { + /** * @param element The element which has the new Pad. * @param pad the new Pad. @@ -61,6 +77,7 @@ public static interface UNKNOWN_TYPE { */ public void unknownType(DecodeBin element, Pad pad, Caps caps); } + /** * Adds a listener for the unknown-type signal * @@ -74,6 +91,7 @@ public void callback(DecodeBin elem, Pad pad, Caps caps) { } }); } + /** * Removes a listener for the unknown-type signal * @@ -84,16 +102,25 @@ public void disconnect(UNKNOWN_TYPE listener) { } /** - * Signal is emitted when a pad for which there is no further possible decoding is added to the {@link DecodeBin}. + * Signal is emitted when a pad for which there is no further possible + * decoding is added to the {@link DecodeBin}. */ public static interface AUTOPLUG_CONTINUE { + /** + * Autoplug continue signal. + * * @param element The element which has the new Pad. * @param pad the new Pad. * @param caps the caps of the pad that cannot be resolved. + * @return TRUE if you wish decodebin to look for elements that can + * handle the given caps . If FALSE, those caps will be considered as + * final and the pad will be exposed as such (see 'pad-added' signal of + * {@link Element}). */ public boolean autoplugContinue(DecodeBin element, Pad pad, Caps caps); } + /** * Adds a listener for the autoplug-continue signal * @@ -107,6 +134,7 @@ public boolean callback(DecodeBin elem, Pad pad, Caps caps) { } }); } + /** * Removes a listener for the autoplug-continue signal * @@ -117,21 +145,27 @@ public void disconnect(AUTOPLUG_CONTINUE listener) { } /** - * This function is emitted when an array of possible factories for caps on pad is needed. - * {@link DecodeBin} will by default return an array with all compatible factories, sorted by rank. - * - * If this function returns NULL, pad will be exposed as a final caps. - * - * If this function returns an empty array, the pad will be considered as having an unhandled type media type. + * This function is emitted when an array of possible factories for caps on + * pad is needed. {@link DecodeBin} will by default return an array with all + * compatible factories, sorted by rank. + *

+ * If this function returns {@link Optional#empty()}, pad will be exposed as + * a final caps. + *

+ * If this function returns an empty list, the pad will be considered as + * having an unhandled type media type. */ public static interface AUTOPLUG_FACTORIES { + /** * @param element The element which has the new Pad. * @param pad the new Pad. * @param caps the caps of the pad that cannot be resolved. + * @return */ - public GValueArray autoplugFactories(DecodeBin element, Pad pad, Caps caps); + public Optional> autoplugFactories(DecodeBin element, Pad pad, Caps caps); } + /** * Adds a listener for the autoplug-factories signal * @@ -141,10 +175,17 @@ public void connect(final AUTOPLUG_FACTORIES listener) { connect(AUTOPLUG_FACTORIES.class, listener, new GstCallback() { @SuppressWarnings("unused") public GValueArray callback(DecodeBin elem, Pad pad, Caps caps) { - return listener.autoplugFactories(elem, pad, caps); + Optional> factories + = listener.autoplugFactories(elem, pad, caps); + return factories.map(list -> { + GValueArray array = new GValueArray(list.size(), false); + list.forEach(factory -> array.append(new GValueAPI.GValue(GType.OBJECT, factory))); + return array; + }).orElse(null); } }); } + /** * Removes a listener for the autoplug-factories signal * @@ -163,14 +204,20 @@ public void disconnect(AUTOPLUG_FACTORIES listener) { * The callee should copy and modify factories. */ public static interface AUTOPLUG_SORT { + /** - * @param element The element which has the new Pad. - * @param pad the new Pad. - * @param caps the caps of the pad that cannot be resolved. - * @param factories A GValueArray of possible GstElementFactory to use. + * Autoplug sort + * + * @param element The element which has the new Pad + * @param pad the new Pad + * @param caps the caps of the pad that cannot be resolved + * @param factories A List of possible {@link ElementFactory} to use + * @return resorted list or {@link Optional#empty()} */ - public GValueArray autoplugSort(DecodeBin element, Pad pad, Caps caps, GValueArray factories); + public Optional> autoplugSort( + DecodeBin element, Pad pad, Caps caps, List factories); } + /** * Adds a listener for the autoplug-sort signal * @@ -180,10 +227,25 @@ public void connect(final AUTOPLUG_SORT listener) { connect(AUTOPLUG_SORT.class, listener, new GstCallback() { @SuppressWarnings("unused") public GValueArray callback(DecodeBin elem, Pad pad, Caps caps, GValueArray factories) { - return listener.autoplugSort(elem, pad, caps, factories); + int size = factories.getNValues(); + List list = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + Object factory = factories.getValue(i); + if (factory instanceof ElementFactory) { + list.add((ElementFactory) factory); + } + } + Optional> response + = listener.autoplugSort(elem, pad, caps, list); + return response.map(l -> { + GValueArray array = new GValueArray(l.size(), false); + list.forEach(factory -> array.append(new GValueAPI.GValue(GType.OBJECT, factory))); + return array; + }).orElse(null); } }); } + /** * Removes a listener for the autoplug-sort signal * @@ -193,20 +255,22 @@ public void disconnect(AUTOPLUG_SORT listener) { disconnect(AUTOPLUG_SORT.class, listener); } - public enum GstAutoplugSelectResult implements IntegerEnum { - GST_AUTOPLUG_SELECT_TRY(0), - GST_AUTOPLUG_SELECT_EXPOSE(1), - GST_AUTOPLUG_SELECT_SKIP(2); + public enum AutoplugSelectResult implements NativeEnum { + TRY(0), + EXPOSE(1), + SKIP(2); - GstAutoplugSelectResult (final int value) { + AutoplugSelectResult(final int value) { this.value = value; } + /** * Gets the integer value of the enum. + * * @return The integer value for this enum. */ @Override - public int intValue() { + public int intValue() { return value; } private final int value; @@ -215,20 +279,35 @@ public int intValue() { /** * Once {@link DecodeBin} has found the possible ElementFactory objects to * try for caps on pad, this signal is emitted. The purpose of the signal is - * for the application to perform additional filtering on the - * element factory array. - * - * The callee should copy and modify factories. + * for the application to perform additional filtering on the element + * factory array. + *

+ * The signal handler should return a {@link AutoplugSelectResult} value + * indicating what decodebin should do next. + *

+ * A value of {@link AutoplugSelectResult#TRY} will try to autoplug an + * element from factory. + *

+ * A value of {@link AutoplugSelectResult#EXPOSE} will expose pad without + * plugging any element to it. + *

+ * A value of {@link AutoplugSelectResult#SKIP} will skip factory and move + * to the next factory. */ public static interface AUTOPLUG_SELECT { + /** - * @param element The element which has the new Pad. - * @param pad the new Pad. - * @param caps the caps of the pad that cannot be resolved. - * @param factories A GValueArray of possible GstElementFactory to use. + * @param element The decodebin + * @param pad the new Pad + * @param caps the caps of the pad + * @param factory the {@link ElementFactory} to use + * @return an {@link AutoplugSelectResult} that indicates the required + * operation. the default handler will always return + * {@link AutoplugSelectResult#TRY} */ - public GstAutoplugSelectResult autoplugSelect(final DecodeBin element, final Pad pad, final Caps caps, final ElementFactory factory); + public AutoplugSelectResult autoplugSelect(final DecodeBin element, final Pad pad, final Caps caps, final ElementFactory factory); } + /** * Adds a listener for the autoplug-select signal * @@ -237,11 +316,12 @@ public static interface AUTOPLUG_SELECT { public void connect(final AUTOPLUG_SELECT listener) { connect(AUTOPLUG_SELECT.class, listener, new GstCallback() { @SuppressWarnings("unused") - public GstAutoplugSelectResult callback(final DecodeBin elem, final Pad pad, final Caps caps, final ElementFactory factory) { + public AutoplugSelectResult callback(final DecodeBin elem, final Pad pad, final Caps caps, final ElementFactory factory) { return listener.autoplugSelect(elem, pad, caps, factory); } }); } + /** * Removes a listener for the autoplug-select signal * @@ -252,10 +332,12 @@ public void disconnect(final AUTOPLUG_SELECT listener) { } /** - * This signal is emitted whenever an autoplugged element that is not linked downstream yet and not exposed does a query. - * It can be used to tell the element about the downstream supported caps for example.. + * This signal is emitted whenever an autoplugged element that is not linked + * downstream yet and not exposed does a query. It can be used to tell the + * element about the downstream supported caps for example.. */ public static interface AUTOPLUG_QUERY { + /** * @param element the decodebin. * @param pad the pad. @@ -264,7 +346,7 @@ public static interface AUTOPLUG_QUERY { */ public boolean autoplugQuery(DecodeBin element, Pad pad, Element child, Query query); } - + /** * Adds a listener for the autoplug-query signal * @@ -278,6 +360,7 @@ public boolean callback(DecodeBin elem, Pad pad, Element child, Query query) { } }); } + /** * Removes a listener for the autoplug-query signal * @@ -286,16 +369,19 @@ public boolean callback(DecodeBin elem, Pad pad, Element child, Query query) { public void disconnect(AUTOPLUG_QUERY listener) { disconnect(AUTOPLUG_QUERY.class, listener); } - + /** - * This signal is emitted once {@link DecodeBin} has finished decoding all the data. + * This signal is emitted once {@link DecodeBin} has finished decoding all + * the data. */ public static interface DRAINED { + /** * @param element The element */ - public GValueArray drained(DecodeBin element); + public void drained(DecodeBin element); } + /** * Adds a listener for the drained signal * @@ -304,11 +390,12 @@ public static interface DRAINED { public void connect(final DRAINED listener) { connect(DRAINED.class, listener, new GstCallback() { @SuppressWarnings("unused") - public GValueArray callback(DecodeBin elem) { - return listener.drained(elem); + public void callback(DecodeBin elem) { + listener.drained(elem); } }); } + /** * Removes a listener for the drained signal * diff --git a/src/org/freedesktop/gstreamer/elements/Elements.java b/src/org/freedesktop/gstreamer/elements/Elements.java new file mode 100644 index 00000000..97a887b0 --- /dev/null +++ b/src/org/freedesktop/gstreamer/elements/Elements.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.elements; + +import java.util.stream.Stream; +import org.freedesktop.gstreamer.glib.NativeObject; +import static org.freedesktop.gstreamer.glib.Natives.registration; + +/** + * + * @author Neil C Smith - https://www.neilcsmith.net + */ +public class Elements implements NativeObject.TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + registration(AppSink.class, AppSink.GTYPE_NAME, AppSink::new), + registration(AppSrc.class, AppSrc.GTYPE_NAME, AppSrc::new), + registration(BaseSrc.class, BaseSrc.GTYPE_NAME, BaseSrc::new), + registration(BaseSink.class, BaseSink.GTYPE_NAME, BaseSink::new), + registration(BaseTransform.class, BaseTransform.GTYPE_NAME, BaseTransform::new), + registration(DecodeBin.class, DecodeBin.GTYPE_NAME, DecodeBin::new), + registration(PlayBin.class, PlayBin.GTYPE_NAME, PlayBin::new), + registration(URIDecodeBin.class, URIDecodeBin.GTYPE_NAME, URIDecodeBin::new)); + + } + +} diff --git a/src/org/freedesktop/gstreamer/elements/PlayBin.java b/src/org/freedesktop/gstreamer/elements/PlayBin.java index 41c7b51c..fdb838fe 100644 --- a/src/org/freedesktop/gstreamer/elements/PlayBin.java +++ b/src/org/freedesktop/gstreamer/elements/PlayBin.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2009 Andres Colubri * Copyright (c) 2007 Wayne Meissner @@ -17,52 +18,45 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.elements; -import com.sun.jna.Pointer; -import com.sun.jna.ptr.ByReference; -import com.sun.jna.ptr.PointerByReference; -import java.awt.Dimension; import java.io.File; import java.net.URI; +import java.util.EnumSet; +import java.util.Set; +import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.ElementFactory; -import org.freedesktop.gstreamer.Fraction; import org.freedesktop.gstreamer.Format; +import org.freedesktop.gstreamer.Gst; import org.freedesktop.gstreamer.Pad; import org.freedesktop.gstreamer.Pipeline; import org.freedesktop.gstreamer.TagList; -import org.freedesktop.gstreamer.Video; +import org.freedesktop.gstreamer.glib.NativeFlags; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; -import org.freedesktop.gstreamer.lowlevel.NativeObject; - /** *

- * Playbin provides a stand-alone everything-in-one abstraction for an - * audio and/or video player. - *

+ * Playbin provides a stand-alone everything-in-one abstraction for an audio + * and/or video player. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/playback/playbin.html *

- * At this stage, playbin is considered UNSTABLE. The API provided in the - * signals and properties may yet change in the near future. When playbin - * is stable, it will probably replace playbin - *

*

* It can handle both audio and video files and features *

*
    *
  • - * automatic file type recognition and based on that automatic - * selection and usage of the right audio/video/subtitle demuxers/decoders + * automatic file type recognition and based on that automatic selection and + * usage of the right audio/video/subtitle demuxers/decoders *
  • *
  • * visualisations for audio files *
  • *
  • - * subtitle support for video files. Subtitles can be store in external - * files. + * subtitle support for video files. Subtitles can be store in external files. *
  • *
  • * stream selection between different video/audio/subtitles streams @@ -81,46 +75,37 @@ *
  • *
*

- * playbin is a {@link org.freedesktop.gstreamer.Pipeline}. It will notify the application of everything - * that's happening (errors, end of stream, tags found, state changes, etc.) - * by posting messages on its {@link org.freedesktop.gstreamer.Bus}. The application needs to watch the - * bus. - * + * playbin is a {@link Pipeline}. It will notify the application of everything + * that's happening (errors, end of stream, tags found, state changes, etc.) by + * posting messages on its {@link Bus}. The application needs to watch the bus. *

* Playback can be initiated by setting the playbin to PLAYING state using - * {@link #setState setState} or {@link #play play}. Note that the state change will take place in - * the background in a separate thread, when the function returns playback - * is probably not happening yet and any errors might not have occured yet. - * Applications using playbin should ideally be written to deal with things - * completely asynchroneous. - *

- * + * {@link #setState setState} or {@link #play play}. Note that the state change + * will take place in the background in a separate thread, when the function + * returns playback is probably not happening yet and any errors might not have + * occured yet. Applications using playbin should ideally be written to deal + * with things completely asynchroneous. *

- * When playback has finished (an EOS message has been received on the bus) - * or an error has occured (an ERROR message has been received on the bus) or - * the user wants to play a different track, playbin should be set back to - * READY or NULL state, then the input file/URI should be set to the new - * location and then playbin be set to PLAYING state again. - *

- * + * When playback has finished (an EOS message has been received on the bus) or + * an error has occured (an ERROR message has been received on the bus) or the + * user wants to play a different track, playbin should be set back to READY or + * NULL state, then the input file/URI should be set to the new location and + * then playbin be set to PLAYING state again. *

- * Seeking can be done using {@link #seek seek} on the playbin element. - * Again, the seek will not be executed instantaneously, but will be done in a + * Seeking can be done using {@link #seek seek} on the playbin element. Again, + * the seek will not be executed instantaneously, but will be done in a * background thread. When the seek call returns the seek will most likely still * be in process. An application may wait for the seek to finish (or fail) using * {@link #getState(long)} with -1 as the timeout, but this will block the user * interface and is not recommended at all. - * *

- * Applications may query the current position and duration of the stream - * via {@link #queryPosition} and {@link #queryDuration} and - * setting the format passed to {@link Format#TIME}. If the query was successful, - * the duration or position will have been returned in units of nanoseconds. - *

- * - * Was playbin2 + * Applications may query the current position and duration of the stream via + * {@link #queryPosition} and {@link #queryDuration} and setting the format + * passed to {@link Format#TIME}. If the query was successful, the duration or + * position will have been returned in units of nanoseconds. */ public class PlayBin extends Pipeline { + public static final String GST_NAME = "playbin"; public static final String GTYPE_NAME = "GstPlayBin"; @@ -150,22 +135,10 @@ public PlayBin(String name, URI uri) { * @param init proxy initialization args * */ - public PlayBin(Initializer init) { + PlayBin(Initializer init) { super(init); } - - /*private static String slashify(String path, boolean isDirectory) { - String p = path; - if (File.separatorChar != '/') - p = p.replace(File.separatorChar, '/'); - if (!p.startsWith("/")) - p = "/" + p; - if (!p.endsWith("/") && isDirectory) - p = p + "/"; - return p; - }*/ - /** * Sets the media file to play. * @@ -186,7 +159,8 @@ public void setURI(URI uri) { /** * Sets the audio output Element. - *

To disable audio output, call this method with a null argument. + *

+ * To disable audio output, call this method with a null argument. * * @param element The element to use for audio output. */ @@ -196,7 +170,8 @@ public void setAudioSink(Element element) { /** * Sets the video output Element. - *

To disable video output, call this method with a null argument. + *

+ * To disable video output, call this method with a null argument. * * @param element The element to use for video output. */ @@ -206,7 +181,8 @@ public void setVideoSink(Element element) { /** * Sets the text output Element. - *

To disable text output, call this method with a null argument. + *

+ * To disable text output, call this method with a null argument. * * @param element The element to use for text output. */ @@ -230,28 +206,40 @@ public void setVisualization(Element element) { * @param element The Element to set as the output. */ private void setElement(String key, Element element) { - if (element == null) { - element = ElementFactory.make("fakesink", "fake-" + key); - } set(key, element); } /** - * Set the volume for the PlayBin. + * Set the flags to control the behaviour of this PlayBin. + *

+ * The default value is + * soft-colorbalance+deinterlace+soft-volume+text+audio+video + *

+ * See individual {@link PlayFlags} value descriptions for more information. * - * @param percent Percentage (between 0 and 100) to set the volume to. + * @param flags set of {@link PlayFlags} */ - public void setVolumePercent(int percent) { - setVolume(Math.max(Math.min((double) percent, 100d), 0d) / 100d); + public void setFlags(Set flags) { + set("flags", NativeFlags.toInt(flags)); } /** - * Get the current volume. - * @return The current volume as a percentage between 0 and 100 of the max volume. - */ - public int getVolumePercent() { - return (int) ((getVolume() * 100d) + 0.5); + * Get the current flags set on this PlayBin. The returned set will be + * writable so that it can be altered and passed back to + * {@link #setFlags(java.util.Set)}. + * + * @return set of current {@link PlayFlags} + */ + public Set getFlags() { + Object flags = get("flags"); + if (flags instanceof Number) { + return NativeFlags.fromInt(PlayFlags.class, + ((Number) flags).intValue()); + } else { + return EnumSet.noneOf(PlayFlags.class); + } } + /** * Sets the audio playback volume. * @@ -263,60 +251,28 @@ public void setVolume(double volume) { /** * Gets the current volume. - * @return The current volume as a percentage between 0 and 100 of the max volume. + * + * @return The current volume as a percentage between 0 and 100 of the max + * volume. */ public double getVolume() { return ((Number) get("volume")).doubleValue(); } - - /** - * Retrieves the framerate from the caps of the video sink's pad. - * - * @return frame rate (frames per second), or 0 if the framerate is not - * available - */ - public double getVideoSinkFrameRate() { - for (Element sink : getSinks()) { - for (Pad pad : sink.getPads()) { - Fraction frameRate = Video.getVideoFrameRate(pad); - if (frameRate != null) { - return frameRate.toDouble(); - } - } - } - return 0; - } /** - * Retrieves the width and height of the video frames configured in the caps - * of the video sink's pad. - * - * @return dimensions of the video frames, or null if the video frame size is - * not available - */ - public Dimension getVideoSize() { - for (Element sink : getSinks()) { - for (Pad pad : sink.getPads()) { - Dimension size = Video.getVideoSize(pad); - if (size != null) { - return size; - } - } - } - return null; - } - - /** - * Get the currently playing audio stream. By default the first audio stream with data is played. - * Default value: -1 + * Get the currently playing audio stream. By default the first audio stream + * with data is played. Default value: -1 + * * @return the currently playing audio stream index (index is zero based). */ public int getCurrentAudio() { - return (Integer)get("current-audio"); + return (Integer) get("current-audio"); } /** - * Set the currently playing audio stream. By default the first audio stream with data is played. + * Set the currently playing audio stream. By default the first audio stream + * with data is played. + * * @param n audio stream index to switch to (index is zero based). */ public void setCurrentAudio(int n) { @@ -325,16 +281,17 @@ public void setCurrentAudio(int n) { /** * Get the total number of available audio streams. + * * @return total number of available audio streams */ public int getNAudio() { - return (Integer)get("n-audio"); + return (Integer) get("n-audio"); } /** - * Retrieve the stream-combiner sinkpad for a specific text stream. - * This pad can be used for notifications of caps changes, - * stream-specific queries, etc. + * Retrieve the stream-combiner sinkpad for a specific text stream. This pad + * can be used for notifications of caps changes, stream-specific queries, + * etc. * * @param audioStreamIndex a text stream number * @return a GstPad, or NULL when the stream number does not exist. @@ -344,27 +301,31 @@ public Pad getAudioPad(int audioStreamIndex) { } /** - * Retrieve the tags of a specific audio stream number. - * This information can be used to select a stream. + * Retrieve the tags of a specific audio stream number. This information can + * be used to select a stream. * * @param audioStreamIndex an audio stream number - * @return a GstTagList with tags or {@code null} when the stream number does not exist. + * @return a GstTagList with tags or {@code null} when the stream number + * does not exist. */ public TagList getAudioTags(int audioStreamIndex) { return emit(TagList.class, "get-audio-tags", audioStreamIndex); } /** - * Get the currently playing text stream. By default the first text stream with data is played. - * Default value: -1 + * Get the currently playing text stream. By default the first text stream + * with data is played. Default value: -1 + * * @return the currently playing text stream index (index is zero based). */ public int getCurrentText() { - return (Integer)get("current-text"); + return (Integer) get("current-text"); } /** - * Set the currently playing text stream. By default the first text stream with data is played. + * Set the currently playing text stream. By default the first text stream + * with data is played. + * * @param n text stream index to switch to (index is zero based). */ public void setCurrentText(int n) { @@ -373,16 +334,17 @@ public void setCurrentText(int n) { /** * Get the total number of available text streams. + * * @return total number of available text streams */ public int getNText() { - return (Integer)get("n-text"); + return (Integer) get("n-text"); } /** - * Retrieve the stream-combiner sinkpad for a specific text stream. - * This pad can be used for notifications of caps changes, - * stream-specific queries, etc. + * Retrieve the stream-combiner sinkpad for a specific text stream. This pad + * can be used for notifications of caps changes, stream-specific queries, + * etc. * * @param textStreamIndex a text stream number * @return a GstPad, or {@code null} when the stream number does not exist. @@ -392,25 +354,17 @@ public Pad getTextPad(int textStreamIndex) { } /** - * Retrieve the tags of a specific text stream number. - * This information can be used to select a stream. + * Retrieve the tags of a specific text stream number. This information can + * be used to select a stream. * * @param textStreamIndex a text stream number - * @return a GstTagList with tags or {@code null} when the stream number does not exist. + * @return a GstTagList with tags or {@code null} when the stream number + * does not exist. */ public TagList getTextTags(int textStreamIndex) { return emit(TagList.class, "get-text-tags", textStreamIndex); } - /** - * Signal emitted when the current uri is about to finish. You can set - * the uri and suburi to make sure that playback continues. - */ - public static interface ABOUT_TO_FINISH { - /** - */ - public void aboutToFinish(PlayBin element); - } /** * Adds a listener for the about-to-finish signal */ @@ -422,24 +376,16 @@ public void callback(PlayBin elem) { } }); } + /** * Removes a listener for the about-to-finish signal - * + * * @param listener The listener that was previously added. */ public void disconnect(ABOUT_TO_FINISH listener) { disconnect(ABOUT_TO_FINISH.class, listener); } - /** - * Signal is emitted whenever the number or order of the video streams has changed. - * The application will most likely want to select a new video stream. - */ - public static interface VIDEO_CHANGED { - /** - */ - public void videoChanged(PlayBin element); - } /** * Adds a listener for the video-changed signal */ @@ -451,24 +397,16 @@ public void callback(PlayBin elem) { } }); } + /** * Removes a listener for the video-changed signal - * + * * @param listener The listener that was previously added. */ public void disconnect(VIDEO_CHANGED listener) { disconnect(VIDEO_CHANGED.class, listener); } - /** - * Signal is emitted whenever the number or order of the audio streams has changed. - * The application will most likely want to select a new audio stream. - */ - public static interface AUDIO_CHANGED { - /** - */ - public void audioChanged(PlayBin element); - } /** * Adds a listener for the audio-changed signal */ @@ -480,24 +418,16 @@ public void callback(PlayBin elem) { } }); } + /** * Removes a listener for the audio-changed signal - * + * * @param listener The listener that was previously added. */ public void disconnect(AUDIO_CHANGED listener) { disconnect(AUDIO_CHANGED.class, listener); } - /** - * Signal is emitted whenever the number or order of the audio streams has changed. - * The application will most likely want to select a new audio stream. - */ - public static interface TEXT_CHANGED { - /** - */ - public void textChanged(PlayBin element); - } /** * Adds a listener for the audio-changed signal */ @@ -509,25 +439,16 @@ public void callback(PlayBin elem) { } }); } + /** * Removes a listener for the text-changed signal - * + * * @param listener The listener that was previously added. */ public void disconnect(TEXT_CHANGED listener) { disconnect(TEXT_CHANGED.class, listener); } - /** - * Signal is emitted whenever the tags of a video stream have changed. - * The application will most likely want to get the new tags. - */ - public static interface VIDEO_TAGS_CHANGED { - /** - * @param stream stream index with changed tags - */ - public void videoTagsChanged(PlayBin element, int stream); - } /** * Adds a listener for the video-tags-changed signal */ @@ -539,25 +460,16 @@ public void callback(PlayBin elem, int stream) { } }); } + /** * Removes a listener for the video-tags-changed signal - * + * * @param listener The listener that was previously added. */ public void disconnect(VIDEO_TAGS_CHANGED listener) { disconnect(VIDEO_TAGS_CHANGED.class, listener); } - /** - * Signal is emitted whenever the tags of an audio stream have changed. - * The application will most likely want to get the new tags. - */ - public static interface AUDIO_TAGS_CHANGED { - /** - * @param stream stream index with changed tags - */ - public void audioTagsChanged(PlayBin element, int stream); - } /** * Adds a listener for the audio-tags-changed signal */ @@ -569,25 +481,16 @@ public void callback(PlayBin elem, int stream) { } }); } + /** * Removes a listener for the audio-tags-changed signal - * + * * @param listener The listener that was previously added. */ public void disconnect(AUDIO_TAGS_CHANGED listener) { disconnect(AUDIO_TAGS_CHANGED.class, listener); } - /** - * Signal is emitted whenever the tags of a text stream have changed. - * The application will most likely want to get the new tags. - */ - public static interface TEXT_TAGS_CHANGED { - /** - * @param stream stream index with changed tags - */ - public void textTagsChanged(PlayBin element, int stream); - } /** * Adds a listener for the audio-tags-changed signal */ @@ -599,12 +502,189 @@ public void callback(PlayBin elem, int stream) { } }); } + /** * Removes a listener for the text-tags-changed signal - * + * * @param listener The listener that was previously added. */ public void disconnect(TEXT_TAGS_CHANGED listener) { disconnect(TEXT_TAGS_CHANGED.class, listener); } + + /** + * Add a listener for the element-setup signal. + * + * @param listener listener to add + */ + @Gst.Since(minor = 10) + public void connect(final ELEMENT_SETUP listener) { + Gst.checkVersion(1, 10); + connect(ELEMENT_SETUP.class, listener, new GstCallback() { + @SuppressWarnings("unused") + public void callback(PlayBin playbin, Element element) { + listener.elementSetup(playbin, element); + } + }); + } + + /** + * Remove listener for the element-setup signal. + * + * @param listener listener to remove + */ + @Gst.Since(minor = 10) + public void disconnect(ELEMENT_SETUP listener) { + disconnect(ELEMENT_SETUP.class, listener); + } + + /** + * Add a listener for the source-setup signal. + * + * @param listener listener to add + */ + public void connect(final SOURCE_SETUP listener) { + connect(SOURCE_SETUP.class, listener, new GstCallback() { + @SuppressWarnings("unused") + public void callback(PlayBin playbin, Element element) { + listener.sourceSetup(playbin, element); + } + }); + } + + /** + * Remove listener for the source-setup signal. + * + * @param listener listener to remove + */ + public void disconnect(SOURCE_SETUP listener) { + disconnect(SOURCE_SETUP.class, listener); + } + + /** + * Signal emitted when the current uri is about to finish. You can set the + * uri and suburi to make sure that playback continues. + */ + public static interface ABOUT_TO_FINISH { + + /** + */ + public void aboutToFinish(PlayBin element); + } + + /** + * Signal is emitted whenever the number or order of the video streams has + * changed. The application will most likely want to select a new video + * stream. + */ + public static interface VIDEO_CHANGED { + + /** + */ + public void videoChanged(PlayBin element); + } + + /** + * Signal is emitted whenever the number or order of the audio streams has + * changed. The application will most likely want to select a new audio + * stream. + */ + public static interface AUDIO_CHANGED { + + /** + */ + public void audioChanged(PlayBin element); + } + + /** + * Signal is emitted whenever the number or order of the audio streams has + * changed. The application will most likely want to select a new audio + * stream. + */ + public static interface TEXT_CHANGED { + + /** + */ + public void textChanged(PlayBin element); + } + + /** + * Signal is emitted whenever the tags of a video stream have changed. The + * application will most likely want to get the new tags. + */ + public static interface VIDEO_TAGS_CHANGED { + + /** + * @param stream stream index with changed tags + */ + public void videoTagsChanged(PlayBin element, int stream); + } + + /** + * Signal is emitted whenever the tags of an audio stream have changed. The + * application will most likely want to get the new tags. + */ + public static interface AUDIO_TAGS_CHANGED { + + /** + * @param stream stream index with changed tags + */ + public void audioTagsChanged(PlayBin element, int stream); + } + + /** + * Signal is emitted whenever the tags of a text stream have changed. The + * application will most likely want to get the new tags. + */ + public static interface TEXT_TAGS_CHANGED { + + /** + * @param stream stream index with changed tags + */ + public void textTagsChanged(PlayBin element, int stream); + } + + /** + * This signal is emitted when a new element is added to playbin or any of + * its sub-bins. This signal can be used to configure elements, e.g. to set + * properties on decoders. This is functionally equivalent to connecting to + * the deep-element-added signal, but more convenient. + *

+ * This signal is usually emitted from the context of a GStreamer streaming + * thread, so might be called at the same time as code running in the main + * application thread. + */ + @Gst.Since(minor = 10) + public static interface ELEMENT_SETUP { + + /** + * Element setup signal callback. + * + * @param playbin signal source + * @param element added element + */ + public void elementSetup(PlayBin playbin, Element element); + } + + /** + * This signal is emitted after the source element has been created, so it + * can be configured by setting additional properties (e.g. set a proxy + * server for an http source, or set the device and read speed for an audio + * cd source). This is functionally equivalent to connecting to the + * notify::source signal, but more convenient. + *

+ * This signal is usually emitted from the context of a GStreamer streaming + * thread. + */ + public static interface SOURCE_SETUP { + + /** + * Source setup signal callback. + * + * @param playbin signal source + * @param element source element added + */ + public void sourceSetup(PlayBin playbin, Element element); + } + } diff --git a/src/org/freedesktop/gstreamer/elements/PlayFlags.java b/src/org/freedesktop/gstreamer/elements/PlayFlags.java new file mode 100644 index 00000000..ab601264 --- /dev/null +++ b/src/org/freedesktop/gstreamer/elements/PlayFlags.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.elements; + +import org.freedesktop.gstreamer.glib.NativeFlags; + +/** + * Extra flags to configure the behaviour of the {@link PlayBin} sinks. + */ +public enum PlayFlags implements NativeFlags { + + /** + * Enable rendering of the video stream. + */ + VIDEO(1 << 0), + /** + * Enable rendering of the audio stream. + */ + AUDIO(1 << 1), + /** + * Enable rendering of subtitles. + */ + TEXT(1 << 2), + /** + * Enable rendering of visualisations when there is no video stream. + */ + VIS(1 << 3), + /** + * Use software volume. + */ + SOFT_VOLUME(1 << 4), + /** + * Only allow native audio formats, this omits configuration of audioconvert + * and audioresample. + */ + NATIVE_AUDIO(1 << 5), + /** + * Only allow native video formats, this omits configuration of videoconvert + * and videoscale. + */ + NATIVE_VIDEO(1 << 6), + /** + * Enable progressive download buffering for selected formats. + */ + DOWNLOAD(1 << 7), + /** + * Enable buffering of the demuxed or parsed data. + */ + BUFFERING(1 << 8), + /** + * Deinterlace raw video (if native not forced). + */ + DEINTERLACE(1 << 9), + /** + * Use a software filter for colour balance. + */ + SOFT_COLORBALANCE(1 << 10), + /** + * Force audio/video filters to be applied if set. + */ + FORCE_FILTERS(1 << 11), + /** + * Force to use only software-based decoders ignoring those with hardware + * class. + */ + FORCE_SW_DECODERS(1 << 12); + + private final int value; + + private PlayFlags(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + +} diff --git a/src/org/freedesktop/gstreamer/elements/URIDecodeBin.java b/src/org/freedesktop/gstreamer/elements/URIDecodeBin.java index 12fec85a..84904a2c 100644 --- a/src/org/freedesktop/gstreamer/elements/URIDecodeBin.java +++ b/src/org/freedesktop/gstreamer/elements/URIDecodeBin.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * * This file is part of gstreamer-java. @@ -17,37 +18,50 @@ */ package org.freedesktop.gstreamer.elements; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import org.freedesktop.gstreamer.Bin; import org.freedesktop.gstreamer.Caps; import org.freedesktop.gstreamer.Element; import org.freedesktop.gstreamer.ElementFactory; import org.freedesktop.gstreamer.Pad; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.query.Query; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValueArray; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GValueAPI; /** - * Java representation of gstreamer uridecodebin + * A {@link Bin} that decodes data from a URI into raw media. It selects a + * source element that can handle the given “uri” scheme and connects it to a + * decodebin. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gst-plugins-base-plugins/html/gst-plugins-base-plugins-uridecodebin.html + *

*/ - public class URIDecodeBin extends Bin { - public static final String GST_NAME = "uridecodebin"; + public static final String GST_NAME = "uridecodebin"; public static final String GTYPE_NAME = "GstURIDecodeBin"; public URIDecodeBin(final String name) { this(makeRawElement(GST_NAME, name)); } - public URIDecodeBin(final Initializer init) { + URIDecodeBin(final Initializer init) { super(init); } /** - * Signal is emitted when a pad for which there is no further possible decoding is added to the {@link URIDecodeBin}. + * Signal is emitted when a pad for which there is no further possible + * decoding is added to the {@link URIDecodeBin}. */ public static interface UNKNOWN_TYPE { + /** * @param element The element which has the new Pad. * @param pad the new Pad. @@ -55,6 +69,7 @@ public static interface UNKNOWN_TYPE { */ public void unknownType(URIDecodeBin element, Pad pad, Caps caps); } + /** * Adds a listener for the unknown-type signal * @@ -68,7 +83,7 @@ public void callback(URIDecodeBin elem, Pad pad, Caps caps) { } }); } - + /** * Removes a listener for the unknown-type signal * @@ -79,73 +94,96 @@ public void disconnect(UNKNOWN_TYPE listener) { } /** - * Signal is emitted when a pad for which there is no further possible decoding is added to the {@link URIDecodeBin}. + * Signal is emitted when a pad for which there is no further possible + * decoding is added to the {@link URIDecodeBin}. */ public static interface AUTOPLUG_CONTINUE { + /** + * Autoplug continue signal. + * * @param element The element which has the new Pad. * @param pad the new Pad. * @param caps the caps of the pad that cannot be resolved. + * @return TRUE if you wish decodebin to look for elements that can + * handle the given caps . If FALSE, those caps will be considered as + * final and the pad will be exposed as such (see 'pad-added' signal of + * {@link Element}). */ - public boolean autoplugContinue(final URIDecodeBin element, final Pad pad, final Caps caps); + public boolean autoplugContinue(URIDecodeBin element, Pad pad, Caps caps); } + /** * Adds a listener for the autoplug-continue signal * - * @param autoplug_CONTINUE Listener to be called + * @param listener Listener to be called */ - public void connect(final AUTOPLUG_CONTINUE autoplug_CONTINUE) { - connect(AUTOPLUG_CONTINUE.class, autoplug_CONTINUE, new GstCallback() { + public void connect(final AUTOPLUG_CONTINUE listener) { + connect(AUTOPLUG_CONTINUE.class, listener, new GstCallback() { @SuppressWarnings("unused") - public boolean callback(final URIDecodeBin elem, final Pad pad, final Caps caps) { - return autoplug_CONTINUE.autoplugContinue(elem, pad, caps); + public boolean callback(URIDecodeBin elem, Pad pad, Caps caps) { + return listener.autoplugContinue(elem, pad, caps); } }); } + /** * Removes a listener for the autoplug-continue signal * * @param listener The listener that was previously added. */ - public void disconnect(final AUTOPLUG_CONTINUE listener) { + public void disconnect(AUTOPLUG_CONTINUE listener) { disconnect(AUTOPLUG_CONTINUE.class, listener); } /** - * This function is emitted when an array of possible factories for caps on pad is needed. - * {@link URIDecodeBin} will by default return an array with all compatible factories, sorted by rank. - * - * If this function returns NULL, pad will be exposed as a final caps. - * - * If this function returns an empty array, the pad will be considered as having an unhandled type media type. + * This function is emitted when an array of possible factories for caps on + * pad is needed. {@link URIDecodeBin} will by default return an array with all + * compatible factories, sorted by rank. + *

+ * If this function returns {@link Optional#EMPTY}, pad will be exposed as a + * final caps. + *

+ * If this function returns an empty list, the pad will be considered as + * having an unhandled type media type. */ public static interface AUTOPLUG_FACTORIES { + /** * @param element The element which has the new Pad. * @param pad the new Pad. * @param caps the caps of the pad that cannot be resolved. + * @return */ - public GValueArray autoplugFactories(final URIDecodeBin element, final Pad pad, final Caps caps); + public Optional> autoplugFactories(URIDecodeBin element, Pad pad, Caps caps); } + /** * Adds a listener for the autoplug-factories signal * - * @param listener Listener to be called + * @param listener Listener to be called */ public void connect(final AUTOPLUG_FACTORIES listener) { connect(AUTOPLUG_FACTORIES.class, listener, new GstCallback() { @SuppressWarnings("unused") - public GValueArray callback(final URIDecodeBin elem, final Pad pad, final Caps caps) { - return listener.autoplugFactories(elem, pad, caps); + public GValueArray callback(URIDecodeBin elem, Pad pad, Caps caps) { + Optional> factories + = listener.autoplugFactories(elem, pad, caps); + return factories.map(list -> { + GValueArray array = new GValueArray(list.size(), false); + list.forEach(factory -> array.append(new GValueAPI.GValue(GType.OBJECT, factory))); + return array; + }).orElse(null); } }); } + /** * Removes a listener for the autoplug-factories signal * * @param listener The listener that was previously added. */ - public void disconnect(final AUTOPLUG_FACTORIES listener) { + public void disconnect(AUTOPLUG_FACTORIES listener) { disconnect(AUTOPLUG_FACTORIES.class, listener); } @@ -158,27 +196,48 @@ public void disconnect(final AUTOPLUG_FACTORIES listener) { * The callee should copy and modify factories. */ public static interface AUTOPLUG_SORT { + /** - * @param element The element which has the new Pad. - * @param pad the new Pad. - * @param caps the caps of the pad that cannot be resolved. - * @param factories A GValueArray of possible GstElementFactory to use. + * Autoplug sort + * + * @param element The element which has the new Pad + * @param pad the new Pad + * @param caps the caps of the pad that cannot be resolved + * @param factories A List of possible {@link ElementFactory} to use + * @return resorted list or {@link Optional#EMPTY} */ - public GValueArray autoplugSort(URIDecodeBin element, Pad pad, Caps caps, GValueArray factories); + public Optional> autoplugSort( + URIDecodeBin element, Pad pad, Caps caps, List factories); } + /** * Adds a listener for the autoplug-sort signal * - * @param listener Listener to be called + * @param listener Listener to be called */ public void connect(final AUTOPLUG_SORT listener) { connect(AUTOPLUG_SORT.class, listener, new GstCallback() { @SuppressWarnings("unused") public GValueArray callback(URIDecodeBin elem, Pad pad, Caps caps, GValueArray factories) { - return listener.autoplugSort(elem, pad, caps, factories); + int size = factories.getNValues(); + List list = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + Object factory = factories.getValue(i); + if (factory instanceof ElementFactory) { + list.add((ElementFactory) factory); + } + } + Optional> response + = listener.autoplugSort(elem, pad, caps, list); + return response.map(l -> { + GValueArray array = new GValueArray(l.size(), false); + list.forEach(factory -> array.append(new GValueAPI.GValue(GType.OBJECT, factory))); + return array; + }).orElse(null); } }); } + /** * Removes a listener for the autoplug-sort signal * @@ -188,20 +247,22 @@ public void disconnect(AUTOPLUG_SORT listener) { disconnect(AUTOPLUG_SORT.class, listener); } - public enum GstAutoplugSelectResult implements IntegerEnum { - GST_AUTOPLUG_SELECT_TRY(0), - GST_AUTOPLUG_SELECT_EXPOSE(1), - GST_AUTOPLUG_SELECT_SKIP(2); + public enum AutoplugSelectResult implements NativeEnum { + TRY(0), + EXPOSE(1), + SKIP(2); - GstAutoplugSelectResult (final int value) { + AutoplugSelectResult(final int value) { this.value = value; } + /** * Gets the integer value of the enum. + * * @return The integer value for this enum. */ @Override - public int intValue() { + public int intValue() { return value; } private final int value; @@ -210,20 +271,35 @@ public int intValue() { /** * Once {@link URIDecodeBin} has found the possible ElementFactory objects to * try for caps on pad, this signal is emitted. The purpose of the signal is - * for the application to perform additional filtering on the - * element factory array. - * - * The callee should copy and modify factories. + * for the application to perform additional filtering on the element + * factory array. + *

+ * The signal handler should return a {@link AutoplugSelectResult} value + * indicating what decodebin should do next. + *

+ * A value of {@link AutoplugSelectResult#TRY} will try to autoplug an + * element from factory. + *

+ * A value of {@link AutoplugSelectResult#EXPOSE} will expose pad without + * plugging any element to it. + *

+ * A value of {@link AutoplugSelectResult#SKIP} will skip factory and move + * to the next factory. */ public static interface AUTOPLUG_SELECT { + /** - * @param element The element which has the new Pad. - * @param pad the new Pad. - * @param caps the caps of the pad that cannot be resolved. - * @param factories A GValueArray of possible GstElementFactory to use. + * @param element The decodebin + * @param pad the new Pad + * @param caps the caps of the pad + * @param factory the {@link ElementFactory} to use + * @return an {@link AutoplugSelectResult} that indicates the required + * operation. the default handler will always return + * {@link AutoplugSelectResult#TRY} */ - public GstAutoplugSelectResult autoplugSelect(final URIDecodeBin element, final Pad pad, final Caps caps, final ElementFactory factory); + public AutoplugSelectResult autoplugSelect(final URIDecodeBin element, final Pad pad, final Caps caps, final ElementFactory factory); } + /** * Adds a listener for the autoplug-select signal * @@ -232,11 +308,12 @@ public static interface AUTOPLUG_SELECT { public void connect(final AUTOPLUG_SELECT listener) { connect(AUTOPLUG_SELECT.class, listener, new GstCallback() { @SuppressWarnings("unused") - public GstAutoplugSelectResult callback(final URIDecodeBin elem, final Pad pad, final Caps caps, final ElementFactory factory) { + public AutoplugSelectResult callback(final URIDecodeBin elem, final Pad pad, final Caps caps, final ElementFactory factory) { return listener.autoplugSelect(elem, pad, caps, factory); } }); } + /** * Removes a listener for the autoplug-select signal * @@ -247,19 +324,21 @@ public void disconnect(final AUTOPLUG_SELECT listener) { } /** - * This signal is emitted whenever an autoplugged element that is not linked downstream yet and not exposed does a query. - * It can be used to tell the element about the downstream supported caps for example.. + * This signal is emitted whenever an autoplugged element that is not linked + * downstream yet and not exposed does a query. It can be used to tell the + * element about the downstream supported caps for example.. */ public static interface AUTOPLUG_QUERY { + /** - * @param element the uridecodebin. + * @param element the decodebin. * @param pad the pad. * @param child the child element doing the query * @param query the query. */ public boolean autoplugQuery(URIDecodeBin element, Pad pad, Element child, Query query); } - + /** * Adds a listener for the autoplug-query signal * @@ -273,6 +352,7 @@ public boolean callback(URIDecodeBin elem, Pad pad, Element child, Query query) } }); } + /** * Removes a listener for the autoplug-query signal * @@ -283,14 +363,17 @@ public void disconnect(AUTOPLUG_QUERY listener) { } /** - * This signal is emitted once {@link URIDecodeBin} has finished decoding all the data. + * This signal is emitted once {@link URIDecodeBin} has finished decoding all + * the data. */ public static interface DRAINED { + /** * @param element The element */ - public GValueArray drained(URIDecodeBin element); + public void drained(URIDecodeBin element); } + /** * Adds a listener for the drained signal * @@ -299,11 +382,12 @@ public static interface DRAINED { public void connect(final DRAINED listener) { connect(DRAINED.class, listener, new GstCallback() { @SuppressWarnings("unused") - public GValueArray callback(URIDecodeBin elem) { - return listener.drained(elem); + public void callback(URIDecodeBin elem) { + listener.drained(elem); } }); } + /** * Removes a listener for the drained signal * @@ -314,15 +398,17 @@ public void disconnect(DRAINED listener) { } /** - * Signal is emitted after the source has been created, so it can be configured by setting additionnal properties. + * Signal is emitted after the source has been created, so it can be + * configured by setting additionnal properties. */ public static interface SOURCE_SETUP { + /** * * @param bin the container. * @param elem the created source */ - public void sourceSetup(Element bin, Element elem); + public void sourceSetup(URIDecodeBin bin, Element elem); } /** @@ -333,11 +419,12 @@ public static interface SOURCE_SETUP { public void connect(final SOURCE_SETUP listener) { this.connect(SOURCE_SETUP.class, listener, new GstCallback() { @SuppressWarnings("unused") - public void callback(final Element bin, final Element elem) { + public void callback(final URIDecodeBin bin, final Element elem) { listener.sourceSetup(bin, elem); } }); } + /** * Removes a listener for the source-setup signal * @@ -347,5 +434,4 @@ public void disconnect(final SOURCE_SETUP listener) { this.disconnect(SOURCE_SETUP.class, listener); } - } diff --git a/src/org/freedesktop/gstreamer/event/BufferSizeEvent.java b/src/org/freedesktop/gstreamer/event/BufferSizeEvent.java index 4bd50c7f..b31d51ee 100644 --- a/src/org/freedesktop/gstreamer/event/BufferSizeEvent.java +++ b/src/org/freedesktop/gstreamer/event/BufferSizeEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,93 +19,92 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; import org.freedesktop.gstreamer.Format; +import org.freedesktop.gstreamer.glib.Natives; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** - * Notification of new latency adjustment. + * A buffersize event. The event is sent downstream and notifies elements that + * they should provide a buffer of the specified dimensions. *

- * The event is sent upstream from the sinks and - * notifies elements that they should add an additional latency to the - * timestamps before synchronising against the clock. + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-buffer-size *

- * The latency is mostly used in live sinks and is always expressed in - * the time format. + * When the async flag is set, a thread boundary is preferred. */ public class BufferSizeEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. + * * @param init initialization data. */ - public BufferSizeEvent(Initializer init) { + BufferSizeEvent(Initializer init) { super(init); } - + /** - * Creates a new buffersize event. - *

The event is sent downstream and notifies elements that they should + * Creates a new buffersize event. + *

+ * The event is sent downstream and notifies elements that they should * provide a buffer of the specified dimensions. + *

+ * When the async flag is set, a thread boundary is preferred. * - *

When the async flag is set, a thread boundary is preferred. - * * @param format buffer format * @param minsize minimum buffer size * @param maxsize maximum buffer size - * @param async thread behavior + * @param async thread behaviour */ public BufferSizeEvent(Format format, long minsize, long maxsize, boolean async) { - super(initializer(gst.ptr_gst_event_new_buffer_size(format, minsize, maxsize, async))); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_buffer_size(format, minsize, maxsize, async))); } - + /** * Gets the format of the buffersize event. - * + * * @return the format. */ public Format getFormat() { Format[] format = new Format[1]; - gst.gst_event_parse_buffer_size(this, format, null, null, null); + GSTEVENT_API.gst_event_parse_buffer_size(this, format, null, null, null); return format[0]; } - + /** * Gets the minimum buffer size. - * + * * @return the minimum buffer size. */ public long getMinimumSize() { - long[] size = { 0 }; - gst.gst_event_parse_buffer_size(this, null, size, null, null); + long[] size = {0}; + GSTEVENT_API.gst_event_parse_buffer_size(this, null, size, null, null); return size[0]; } - + /** * Gets the maximum buffer size. - * + * * @return the maximum buffer size. */ public long getMaximumSize() { - long[] size = { 0 }; - gst.gst_event_parse_buffer_size(this, null, null, size, null); + long[] size = {0}; + GSTEVENT_API.gst_event_parse_buffer_size(this, null, null, size, null); return size[0]; } - + /** * Gets the preferred thread behaviour. - * + * * @return true if a thread boundary is preferred. */ public boolean isAsync() { - boolean[] async = { false }; - gst.gst_event_parse_buffer_size(this, null, null, null, async); + boolean[] async = {false}; + GSTEVENT_API.gst_event_parse_buffer_size(this, null, null, null, async); return async[0]; } } diff --git a/src/org/freedesktop/gstreamer/event/CapsEvent.java b/src/org/freedesktop/gstreamer/event/CapsEvent.java index 532147af..04b18406 100644 --- a/src/org/freedesktop/gstreamer/event/CapsEvent.java +++ b/src/org/freedesktop/gstreamer/event/CapsEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * * This file is part of gstreamer-java. @@ -15,24 +16,30 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; - import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; - +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; +/** + * A CAPS event for {@link Caps}. The caps event can only travel downstream + * synchronized with the buffer flow and contains the format of the buffers that + * will follow after the event. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-caps + *

+ */ public class CapsEvent extends Event { - - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; /** * This constructor is for internal use only. + * * @param init initialization data. */ - public CapsEvent(Initializer init) { + CapsEvent(Initializer init) { super(init); } @@ -40,6 +47,6 @@ public CapsEvent(Initializer init) { * Creates a new caps event. */ public CapsEvent(final Caps caps) { - super(initializer(gst.ptr_gst_event_new_caps(caps))); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_caps(caps))); } -} \ No newline at end of file +} diff --git a/src/org/freedesktop/gstreamer/event/EOSEvent.java b/src/org/freedesktop/gstreamer/event/EOSEvent.java index 8b49ac88..cfc02c3b 100644 --- a/src/org/freedesktop/gstreamer/event/EOSEvent.java +++ b/src/org/freedesktop/gstreamer/event/EOSEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,43 +19,48 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.Bus; +import org.freedesktop.gstreamer.FlowReturn; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** - * End-Of-Stream. No more data is to be expected to follow without a {@link NewSegmentEvent}. + * End-Of-Stream event. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-eos *

- * The eos event can only travel downstream - * synchronized with the buffer flow. Elements that receive the EOS - * event on a pad can return {@link org.gstreamer.FlowReturn#UNEXPECTED} when data after - * the EOS event arrives. + * The eos event can only travel downstream synchronized with the buffer flow. + * Elements that receive the EOS event on a pad can return + * {@link FlowReturn#EOS} when data after the EOS event arrives. *

- * The EOS event will travel down to the sink elements in the pipeline - * which will then post the {@link org.gstreamer.Bus.EOS} message on the bus after they have + * The EOS event will travel down to the sink elements in the pipeline which + * will then post the {@link Bus.EOS } message on the bus after they have * finished playing any buffered data. *

- * When all sinks have posted an EOS message, an EOS message is - * forwarded to the application. + * When all sinks have posted an EOS message, an EOS message is forwarded to the + * application. + *

+ * The EOS event itself will not cause any state transitions of the pipeline. */ public class EOSEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. + * * @param init initialization data. */ - public EOSEvent(Initializer init) { + EOSEvent(Initializer init) { super(init); } - + /** - * Creates a new EOS event. + * Creates a new EOS event. */ public EOSEvent() { - super(initializer(gst.ptr_gst_event_new_eos())); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_eos())); } -} \ No newline at end of file +} diff --git a/src/org/freedesktop/gstreamer/event/Event.java b/src/org/freedesktop/gstreamer/event/Event.java new file mode 100644 index 00000000..9bfbaf2c --- /dev/null +++ b/src/org/freedesktop/gstreamer/event/Event.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2007, 2008 Wayne Meissner + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.event; + +import java.util.EnumMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; +import org.freedesktop.gstreamer.MiniObject; +import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.lowlevel.ReferenceManager; +import org.freedesktop.gstreamer.lowlevel.annotations.HasSubtype; + +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; + +/** + * Base type of all events. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html + *

+ * Events are passed between elements in parallel to the data stream. Some + * events are serialized with buffers, others are not. Some events only travel + * downstream, others only upstream. Some events can travel both upstream and + * downstream. + *

+ * The events are used to signal special conditions in the datastream such as + * EOS (end of stream) or the start of a new stream-segment. + *

+ * Events are also used to flush the pipeline of any pending data. + * + * @see Pad#pushEvent + * @see Pad#sendEvent + * @see Element#sendEvent + */ +@HasSubtype +public class Event extends MiniObject { + + public static final String GTYPE_NAME = "GstEvent"; + + private static final Map> TYPE_MAP + = new EnumMap<>(EventType.class); + + static { + TYPE_MAP.put(EventType.BUFFERSIZE, BufferSizeEvent::new); + TYPE_MAP.put(EventType.EOS, EOSEvent::new); + TYPE_MAP.put(EventType.CAPS, CapsEvent::new); + TYPE_MAP.put(EventType.RECONFIGURE, ReconfigureEvent::new); + TYPE_MAP.put(EventType.STREAM_START, StreamStartEvent::new); + TYPE_MAP.put(EventType.LATENCY, LatencyEvent::new); + TYPE_MAP.put(EventType.FLUSH_START, FlushStartEvent::new); + TYPE_MAP.put(EventType.FLUSH_STOP, FlushStopEvent::new); + TYPE_MAP.put(EventType.NAVIGATION, NavigationEvent::new); + TYPE_MAP.put(EventType.SEGMENT, SegmentEvent::new); + TYPE_MAP.put(EventType.SEEK, SeekEvent::new); + TYPE_MAP.put(EventType.TAG, TagEvent::new); + TYPE_MAP.put(EventType.QOS, QOSEvent::new); + TYPE_MAP.put(EventType.STEP, StepEvent::new); + } + + /** + * This constructor is for internal use only. + * + * @param init initialization data. + */ + Event(Initializer init) { + super(init); + } + + /** + * Gets the structure containing the data in this event. + * + * @return a structure. + */ + public Structure getStructure() { + return ReferenceManager.addKeepAliveReference(GSTEVENT_API.gst_event_get_structure(this), this); + } + + private static Event create(Initializer init) { + GstEventAPI.EventStruct struct = new GstEventAPI.EventStruct(init.ptr.getPointer()); + EventType type = (EventType) struct.readField("type"); + return TYPE_MAP.getOrDefault(type, Event::new).apply(init); + } + + public static class Types implements TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + Natives.registration(Event.class, GTYPE_NAME, Event::create) + ); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/EventType.java b/src/org/freedesktop/gstreamer/event/EventType.java similarity index 84% rename from src/org/freedesktop/gstreamer/EventType.java rename to src/org/freedesktop/gstreamer/event/EventType.java index 57ef1ade..98d4eb1f 100644 --- a/src/org/freedesktop/gstreamer/EventType.java +++ b/src/org/freedesktop/gstreamer/event/EventType.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -17,24 +18,26 @@ * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.lowlevel.EnumMapper; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeEnum; import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; -import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; - /** * The standard event types that can be sent in a pipeline. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#GstEventType + *

* The custom event types can be used for private messages between elements * that can't be expressed using normal GStreamer buffer passing semantics. *

* Custom events carry an arbitrary {@link Structure}. Specific custom events * are distinguished by the name of the structure. */ -public enum EventType implements IntegerEnum { +public enum EventType implements NativeEnum { /** Unknown event */ @DefaultEnumValue UNKNOWN(0, 0), @@ -67,6 +70,11 @@ public enum EventType implements IntegerEnum { * running-time and stream-time. */ SEGMENT(70, Flags.DOWNSTREAM | Flags.SERIALIZED | Flags.STICKY), + /** + * A new #GstStreamCollection is available (Since 1.10) + */ + @Gst.Since(minor = 10) + STREAM_COLLECTION(75, Flags.DOWNSTREAM | Flags.SERIALIZED | Flags.STICKY | Flags.STICKY_MULTI), /** * A new set of metadata tags has been found in the stream. */ @@ -80,6 +88,13 @@ public enum EventType implements IntegerEnum { * should be emitted in sync with rendering. */ SINK_MESSAGE(100, Flags.DOWNSTREAM | Flags.SERIALIZED | Flags.STICKY | Flags.STICKY_MULTI), + /** + * Indicates that there is no more data for the stream group ID in the + * message. Sent before EOS in some instances and should be handled mostly + * the same. (Since 1.10) + */ + @Gst.Since(minor = 10) + STREAM_GROUP_DONE(105, Flags.DOWNSTREAM | Flags.SERIALIZED | Flags.STICKY), /** * End-Of-Stream. No more data is to be expected to follow without a SEGMENT * event. @@ -139,7 +154,11 @@ public enum EventType implements IntegerEnum { * A request for a new playback position based on TOC entry's UID. */ TOC_SELECT(250, Flags.UPSTREAM), - + /** + * A request to select one or more streams (Since 1.10) + */ + @Gst.Since(minor = 10) + SELECT_STREAMS(260, Flags.UPSTREAM), /* custom events start here */ /** * Upstream custom event @@ -167,25 +186,19 @@ public enum EventType implements IntegerEnum { CUSTOM_BOTH_OOB(320, Flags.BOTH) ; - EventType(int num, int flags) { + private static final int SHIFT = 8; + + private final int value; + + private EventType(int num, int flags) { this.value = (num << SHIFT) | flags; } - /** Gets the native value of this enum */ + @Override public int intValue() { return value; } - /** Gets a human-readable name for this value */ - public String getName() { - return GSTEVENT_API.gst_event_type_get_name(this); - } - /** Gets the Enum for a native value */ - public static final EventType valueOf(int type) { - return EnumMapper.getInstance().valueOf(type, EventType.class); - } - - private static final int SHIFT = 8; private static final class Flags { public static final int UPSTREAM = 1 << 0; public static final int DOWNSTREAM = 1 << 1; @@ -194,5 +207,4 @@ private static final class Flags { public static final int STICKY_MULTI = 1 << 4; public static final int BOTH = UPSTREAM | DOWNSTREAM; } - private final int value; } diff --git a/src/org/freedesktop/gstreamer/event/FlushStartEvent.java b/src/org/freedesktop/gstreamer/event/FlushStartEvent.java index 4f1e203c..af2424e4 100644 --- a/src/org/freedesktop/gstreamer/event/FlushStartEvent.java +++ b/src/org/freedesktop/gstreamer/event/FlushStartEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,25 +19,28 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; +import org.freedesktop.gstreamer.FlowReturn; import org.freedesktop.gstreamer.Pad; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** * Start a flush operation. *

- * The flush start event can be sent - * upstream and downstream and travels out-of-bounds with the dataflow. + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-flush-start + *

+ * The flush start event can be sent upstream and downstream and travels + * out-of-bounds with the dataflow. *

* It marks pads as being flushing and will make them return - * {@link org.gstreamer.FlowReturn#WRONG_STATE} when used for data flow with {@link Pad#pushEvent}, - * {@link Pad#chain}, Pad#allocBuffer, {@link Pad#getRange} and - * {@link Pad#pullRange}. Any event (except a {@link FlushStopEvent}) received - * on a flushing pad will return {@code false} immediately. + * {@link FlowReturn#FLUSHING} when used for data flow with {@link Pad#pushEvent}, + * {@link Pad#chain}, {@link Pad#getRange} and {@link Pad#pullRange}. Any event + * (except a {@link FlushStopEvent}) received on a flushing pad will return + * {@code false} immediately. *

* Elements should unlock any blocking functions and exit their streaming * functions as fast as possible when this event is received. @@ -47,20 +51,20 @@ */ public class FlushStartEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - + /** * This constructor is for internal use only. + * * @param init initialization data. */ - public FlushStartEvent(Initializer init) { + FlushStartEvent(Initializer init) { super(init); } - + /** * Creates a new flush start event. */ public FlushStartEvent() { - super(initializer(gst.ptr_gst_event_new_flush_start())); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_flush_start())); } } diff --git a/src/org/freedesktop/gstreamer/event/FlushStopEvent.java b/src/org/freedesktop/gstreamer/event/FlushStopEvent.java index 9fef6fff..9edfc852 100644 --- a/src/org/freedesktop/gstreamer/event/FlushStopEvent.java +++ b/src/org/freedesktop/gstreamer/event/FlushStopEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,43 +19,42 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** * Stop a flush operation. *

- * The flush stop event can be sent upstream and downstream and travels - * out-of-bounds with the dataflow. + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-flush-stop *

- * It is typically sent after sending a {@link FlushStartEvent} event to make the - * pads accept data again. + * The flush stop event can be sent upstream and downstream and travels + * out-of-bounds with the dataflow. It is typically sent after sending a + * {@link FlushStartEvent} event to make the pads accept data again. *

- * Elements can process this event synchronized with the dataflow since - * the preceeding FLUSH_START event stopped the dataflow. + * Elements can process this event synchronized with the dataflow since the + * preceeding FLUSH_START event stopped the dataflow. *

- * This event is typically generated to complete a seek and to resume - * dataflow. + * This event is typically generated to complete a seek and to resume dataflow. */ public class FlushStopEvent extends Event { - - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - + /** * This constructor is for internal use only. + * * @param init initialization data. */ - public FlushStopEvent(Initializer init) { + FlushStopEvent(Initializer init) { super(init); } - + /** - * Creates a new flush stop event. + * Creates a new flush stop event. */ public FlushStopEvent() { - super(initializer(gst.ptr_gst_event_new_flush_stop())); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_flush_stop())); } } diff --git a/src/org/freedesktop/gstreamer/event/ForceKeyUnit.java b/src/org/freedesktop/gstreamer/event/ForceKeyUnit.java deleted file mode 100644 index 54f46ba9..00000000 --- a/src/org/freedesktop/gstreamer/event/ForceKeyUnit.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2014 Tom Greenwood - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ -package org.freedesktop.gstreamer.event; - -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.lowlevel.GstVideoAPI; - -/** - * Force key unit event that forces out key frames - */ -public class ForceKeyUnit extends Event { - private static final GstVideoAPI gst = GstVideoAPI.GSTVIDEO_API; - - /** - * Constructor - used internally! - * - * @param init - */ - public ForceKeyUnit(Initializer init) - { - super(init); - } - - /** - * Constructor for a downstream force key unit. - * See http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gst-plugins-base-libs-gstvideo.html#gst-video-event-new-downstream-force-key-unit - * - * @param timestamp - * @param stream_time - * @param running_time - * @param all_headers - * @param count - */ - public ForceKeyUnit(ClockTime timestamp, ClockTime stream_time, ClockTime running_time, - boolean all_headers, int count) - { - super(initializer( - gst.ptr_gst_video_event_new_downstream_force_key_unit( - timestamp, stream_time, running_time, - all_headers, count))); - } - /** - * Constructor for upstream force key unit - * See http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gst-plugins-base-libs-gstvideo.html#gst-video-event-new-upstream-force-key-unit - * - * @param running_time Clock time to produce unit - use ClockTime.NONE for immediate - * @param all_headers - * @param count - */ - public ForceKeyUnit(ClockTime running_time, boolean all_headers, int count) - { - super(initializer( - gst.ptr_gst_video_event_new_upstream_force_key_unit(running_time, all_headers, count))); - } -} diff --git a/src/org/freedesktop/gstreamer/event/LatencyEvent.java b/src/org/freedesktop/gstreamer/event/LatencyEvent.java index c7e55e56..8d7f1045 100644 --- a/src/org/freedesktop/gstreamer/event/LatencyEvent.java +++ b/src/org/freedesktop/gstreamer/event/LatencyEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,52 +19,53 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** * Notification of new latency adjustment. *

- * The event is sent upstream from the sinks and - * notifies elements that they should add an additional latency to the - * timestamps before synchronising against the clock. + * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-latency + *

+ * The event is sent upstream from the sinks and notifies elements that they + * should add an additional latency to the timestamps before synchronising + * against the clock. *

- * The latency is mostly used in live sinks and is always expressed in - * the time format. + * The latency is mostly used in live sinks and is always expressed in the time + * format. */ public class LatencyEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. + * * @param init initialization data. */ - public LatencyEvent(Initializer init) { + LatencyEvent(Initializer init) { super(init); } - + /** - * Create a new latency event. - * + * Create a new latency event. + * * @param latency the new latency value to add to timestamps. */ - public LatencyEvent(ClockTime latency) { - super(initializer(gst.ptr_gst_event_new_latency(latency))); + public LatencyEvent(long latency) { + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_latency(latency))); } - + /** * Gets the latency in the latency event. - * + * * @return the latency. */ - public ClockTime getLatency() { - ClockTime[] latency = new ClockTime[1]; - gst.gst_event_parse_latency(this, latency); + public long getLatency() { + long[] latency = new long[1]; + GSTEVENT_API.gst_event_parse_latency(this, latency); return latency[0]; } } diff --git a/src/org/freedesktop/gstreamer/event/NavigationEvent.java b/src/org/freedesktop/gstreamer/event/NavigationEvent.java index 1229461c..fe71b903 100644 --- a/src/org/freedesktop/gstreamer/event/NavigationEvent.java +++ b/src/org/freedesktop/gstreamer/event/NavigationEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,44 +19,44 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GType; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** - * Navigation events are usually used for communicating user requests, such as + * Navigation events are usually used for communicating user requests, such as * mouse or keyboard movements, to upstream elements. */ +// @TODO API seems OK but need to check and update against GstNavigation public class NavigationEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. + * * @param init initialization data. */ - public NavigationEvent(Initializer init) { + NavigationEvent(Initializer init) { super(init); } - + /** * Creates a new navigation event from the given description. - *

Unless you really need a custom navigation event, use one of the - * static convenience methods for creating navigation events. - * + *

+ * Unless you really need a custom navigation event, use one of the static + * convenience methods for creating navigation events. + * * @param structure the description of the navigation event. */ public NavigationEvent(Structure structure) { - this(initializer(gst.ptr_gst_event_new_navigation(structure))); + this(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_navigation(structure))); } - + /** * Gets a human-readable string representation of this navigation event. - * + * * @return a string */ @Override @@ -67,18 +68,18 @@ public String toString() { event, s.getString("key")); } else if (event.startsWith("mouse-")) { return String.format("%s: [x=%f, y=%f button=%x]", - event, + event, s.getDouble("pointer_x"), s.getDouble("pointer_y"), s.getInteger("button")); } else { return String.format("%s", - s.getString("event")); + s.getString("event")); } } - + /** * Creates a mouse navigation event. - * + * * @param event the type of mouse event. * @param x the X location of the mouse cursor * @param y the Y location of the mouse cursor @@ -88,10 +89,10 @@ public String toString() { public static NavigationEvent createMouseEvent(String event, double x, double y, int button) { return new MouseEvent(event, x, y, button); } - + /** * Creates a mouse move navigation event. - * + * * @param x the X location of the mouse cursor * @param y the Y location of the mouse cursor * @param button the button(s) currently pressed @@ -100,10 +101,10 @@ public static NavigationEvent createMouseEvent(String event, double x, double y, public static NavigationEvent createMouseMoveEvent(double x, double y, int button) { return createMouseEvent("mouse-move", x, y, button); } - + /** * Creates a mouse button press navigation event. - * + * * @param x the X location of the mouse cursor * @param y the Y location of the mouse cursor * @param button the button(s) currently pressed @@ -112,10 +113,10 @@ public static NavigationEvent createMouseMoveEvent(double x, double y, int butto public static NavigationEvent createMouseButtonPressEvent(double x, double y, int button) { return createMouseEvent("mouse-button-press", x, y, button); } - + /** * Creates a mouse button release navigation event. - * + * * @param x the X location of the mouse cursor * @param y the Y location of the mouse cursor * @param button the button(s) currently pressed @@ -124,10 +125,10 @@ public static NavigationEvent createMouseButtonPressEvent(double x, double y, in public static NavigationEvent createMouseButtonReleaseEvent(double x, double y, int button) { return createMouseEvent("mouse-button-release", x, y, button); } - + /** * Creates a new key navigation event. - * + * * @param event the type of key event. * @param key the ascii key code for the key. * @return a new navigation event @@ -135,59 +136,63 @@ public static NavigationEvent createMouseButtonReleaseEvent(double x, double y, public static NavigationEvent createKeyEvent(String event, String key) { return new KeyEvent(event, key); } - + /** * Creates a new key press navigation event. - * + * * @param key the ascii key code for the key. * @return a new navigation event */ public static NavigationEvent createKeyPressEvent(String key) { return createKeyEvent("key-press", key); } - + /** * Creates a new key release navigation event. - * + * * @param key the ascii key code for the key. * @return a new navigation event */ public static NavigationEvent createKeyReleaseEvent(String key) { return createKeyEvent("key-release", key); } + private static final class MouseEvent extends NavigationEvent { + public MouseEvent(String event, double x, double y, int button) { - super(new Structure("application/x-gst-navigation", - "event", GType.STRING, event, - "button", GType.INT, button, - "pointer_x", GType.DOUBLE, x, - "pointer_y", GType.DOUBLE, y)); + super(new Structure("application/x-gst-navigation", + "event", GType.STRING, event, + "button", GType.INT, button, + "pointer_x", GType.DOUBLE, x, + "pointer_y", GType.DOUBLE, y)); } - + /** * Gets a human-readable string representation of this navigation event. - * + * * @return a string */ @Override public String toString() { Structure s = getStructure(); return String.format("%s: [x=%f, y=%f button=%x]", - s.getString("event"), + s.getString("event"), s.getDouble("pointer_x"), s.getDouble("pointer_y"), s.getInteger("button")); } } + private static final class KeyEvent extends NavigationEvent { + public KeyEvent(String event, String key) { - super(new Structure("application/x-gst-navigation", - "event", GType.STRING, event, - "key", GType.STRING, key)); + super(new Structure("application/x-gst-navigation", + "event", GType.STRING, event, + "key", GType.STRING, key)); } - + /** * Gets a human-readable string representation of this navigation event. - * + * * @return a string */ @Override diff --git a/src/org/freedesktop/gstreamer/event/QOSEvent.java b/src/org/freedesktop/gstreamer/event/QOSEvent.java index a5660f12..1894ba41 100644 --- a/src/org/freedesktop/gstreamer/event/QOSEvent.java +++ b/src/org/freedesktop/gstreamer/event/QOSEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,126 +19,124 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.QOSType; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** - * A quality message. Used to indicate to upstream elements that the downstream + * A quality message. Used to indicate to upstream elements that the downstream * elements are being starved of or flooded with data. - * - *

The QOS event is generated in an element that wants an upstream - * element to either reduce or increase its rate because of - * high/low CPU load or other resource usage such as network performance. - * Typically sinks generate these events for each buffer they receive. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-qos + *

+ * proportion indicates the real-time performance of the streaming in + * the element that generated the QoS event (usually the sink). The value is + * generally computed based on more long term statistics about the streams + * timestamps compared to the clock. + *

+ * A value < 1.0 indicates that the upstream element is producing data faster + * than real-time. A value > 1.0 indicates that the upstream element is not + * producing data fast enough. 1.0 is the ideal proportion value. The + * proportion value can safely be used to lower or increase the quality of the + * element. + *

+ * difference is the difference against the clock in running time of + * the last buffer that caused the element to generate the QOS event. A negative + * value means that the buffer with timestamp arrived in time. A + * positive value indicates how late the buffer with timestamp was. + *

+ * timestamp is the timestamp of the last buffer that cause the element + * to generate the QOS event. It is expressed in running time and thus an ever + * increasing value. + *

+ * The upstream element can use the diff and timestamp values + * to decide whether to process more buffers. For positive difference, + * all buffers with timestamp <= timestamp + difference will + * certainly arrive late in the sink as well. + *

+ * The application can use general event probes to intercept the QoS event and + * implement custom application specific QoS handling. * */ public class QOSEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - + /** * This constructor is for internal use only. + * * @param init initialization data. */ - public QOSEvent(Initializer init) { + QOSEvent(Initializer init) { super(init); } - + /** * Creates a new quality-of-service event. - *

- * proportion indicates the real-time performance of the streaming in the - * element that generated the QoS event (usually the sink). The value is - * generally computed based on more long term statistics about the streams - * timestamps compared to the clock. - *

- * A value < 1.0 indicates that the upstream element is producing data faster - * than real-time. A value > 1.0 indicates that the upstream element is not - * producing data fast enough. 1.0 is the ideal proportion value. The - * proportion value can safely be used to lower or increase the quality of - * the element. - *

- * difference is the difference against the clock in running time of the last - * buffer that caused the element to generate the QOS event. A negative value - * means that the buffer with timestamp arrived in time. A positive value - * indicates how late the buffer with timestamp was. - *

- * timestamp is the timestamp of the last buffer that cause the element - * to generate the QOS event. It is expressed in running time and thus an ever - * increasing value. - *

- * The upstream element can use the diff and timestamp values to decide - * whether to process more buffers. For positive difference, all buffers with - * timestamp <= timestamp + difference will certainly arrive late in the sink - * as well. - *

- * The application can use general event probes to intercept the QoS - * event and implement custom application specific QoS handling. - * + * + * * @param proportion the proportion of the qos message * @param difference the time difference of the last Clock sync * @param timestamp the timestamp of the buffer */ - public QOSEvent(QOSType type, double proportion, long difference, ClockTime timestamp) { - super(initializer(gst.ptr_gst_event_new_qos(type, proportion, difference, timestamp))); + public QOSEvent(QOSType type, double proportion, long difference, long timestamp) { + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_qos(type, proportion, difference, timestamp))); } - + /** * @return the proportion. */ public QOSType getType() { - QOSType[] type = { null }; - gst.gst_event_parse_qos(this, type, null, null, (ClockTime[]) null); + QOSType[] type = {null}; + GSTEVENT_API.gst_event_parse_qos(this, type, null, null, (long[]) null); return type[0]; } - + /** * Gets the proportion value of this event. *

- * The proportion indicates the real-time performance of the streaming in the - * element that generated the QoS event (usually the sink). The value is + * The proportion indicates the real-time performance of the streaming in + * the element that generated the QoS event (usually the sink). The value is * generally computed based on more long term statistics about the streams * timestamps compared to the clock. - * + * * @return the proportion. */ public double getProportion() { - double[] p = { 0d }; - gst.gst_event_parse_qos(this, null, p, null, (ClockTime[]) null); + double[] p = {0d}; + GSTEVENT_API.gst_event_parse_qos(this, null, p, null, (long[]) null); return p[0]; } - + /** * Gets the difference value of this event. *

* This is the difference against the clock in running time of the last - * buffer that caused the element to generate the QOS event. A negative value - * means that the buffer with timestamp arrived in time. A positive value - * indicates how late the buffer with timestamp was. - * + * buffer that caused the element to generate the QOS event. A negative + * value means that the buffer with timestamp arrived in time. A + * positive value indicates how late the buffer with timestamp was. + * * @return the difference. */ public long getDifference() { - long[] diff = { 0 }; - gst.gst_event_parse_qos(this, null, null, diff, (ClockTime[]) null); + long[] diff = {0}; + GSTEVENT_API.gst_event_parse_qos(this, null, null, diff, (long[]) null); return diff[0]; } - + /** * Gets the timestamp from this event. *

- * This is the timestamp of the last buffer that caused the element to generate the - * QOS event. It is expressed in running time and thus an ever increasing value. - * + * This is the timestamp of the last buffer that caused the element to + * generate the QOS event. It is expressed in running time and thus an ever + * increasing value. + * * @return the timestamp */ - public ClockTime getTimestamp() { - ClockTime[] timestamp = new ClockTime[1]; - gst.gst_event_parse_qos(this, null, null, null, timestamp); + public long getTimestamp() { + long[] timestamp = new long[1]; + GSTEVENT_API.gst_event_parse_qos(this, null, null, null, timestamp); return timestamp[0]; } -} +} diff --git a/src/org/freedesktop/gstreamer/QOSType.java b/src/org/freedesktop/gstreamer/event/QOSType.java similarity index 77% rename from src/org/freedesktop/gstreamer/QOSType.java rename to src/org/freedesktop/gstreamer/event/QOSType.java index 636b14d8..ea747694 100644 --- a/src/org/freedesktop/gstreamer/QOSType.java +++ b/src/org/freedesktop/gstreamer/event/QOSType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Neil C Smith + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -16,28 +16,43 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.event; + +import org.freedesktop.gstreamer.glib.NativeEnum; /** * The different types of QoS events that can be given to the * gst_event_new_qos() method. */ -public enum QOSType { +public enum QOSType implements NativeEnum { /** * The QoS event type that is produced when downstream elements are * producing data too quickly and the element can't keep up processing the * data. Upstream should reduce their processing rate. This type is also * used when buffers arrive early or in time. */ - OVERFLOW, + OVERFLOW(0), /** * The QoS event type that is produced when downstream elements are * producing data too slowly and need to speed up their processing rate. */ - UNDERFLOW, + UNDERFLOW(1), /** * The QoS event type that is produced when the application enabled * throttling to limit the datarate. */ - THROTTLE; + THROTTLE(2); + + private final int value; + + private QOSType(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + + } diff --git a/src/org/freedesktop/gstreamer/event/ReconfigureEvent.java b/src/org/freedesktop/gstreamer/event/ReconfigureEvent.java index e50de57a..1693345c 100644 --- a/src/org/freedesktop/gstreamer/event/ReconfigureEvent.java +++ b/src/org/freedesktop/gstreamer/event/ReconfigureEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * * This file is part of gstreamer-java. @@ -15,23 +16,30 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; - -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; - - +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; + +/** + * A reconfigure event. The purpose of the reconfigure event is to travel + * upstream and make elements renegotiate their caps or reconfigure their buffer + * pools. This is useful when changing properties on elements or changing the + * topology of the pipeline. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-reconfigure + *

+ */ public class ReconfigureEvent extends Event { - - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; /** * This constructor is for internal use only. + * * @param init initialization data. */ - public ReconfigureEvent(Initializer init) { + ReconfigureEvent(Initializer init) { super(init); } @@ -39,7 +47,7 @@ public ReconfigureEvent(Initializer init) { * Creates a new reconfigure event. */ public ReconfigureEvent() { - super(initializer(gst.ptr_gst_event_new_reconfigure())); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_reconfigure())); } -} \ No newline at end of file +} diff --git a/src/org/freedesktop/gstreamer/event/SeekEvent.java b/src/org/freedesktop/gstreamer/event/SeekEvent.java index fbe5c8bd..21da6fec 100644 --- a/src/org/freedesktop/gstreamer/event/SeekEvent.java +++ b/src/org/freedesktop/gstreamer/event/SeekEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -21,14 +22,19 @@ package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; +import java.util.EnumSet; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.SeekType; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** * A request for a new playback position and rate. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-seek + *

* The seek event configures playback of the pipeline between start to stop * at the speed given in rate, also called a playback segment. *

@@ -43,7 +49,7 @@ * position of 0, a stop position of -1 and a rate of 1.0. The currently * configured playback segment can be queried with #GST_QUERY_SEGMENT. *

- * and stopType specify how to adjust the currently configured + * startType and stopType specify how to adjust the currently configured * start and stop fields in segment. Adjustments can be made relative or * absolute to the last configured values. A type of {@link SeekType#NONE} means * that the position should not be updated. @@ -61,13 +67,12 @@ * current position with a {@link SeekType#SET} to the desired position. */ public class SeekEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; /** * This constructor is for internal use only. * @param init initialization data. */ - public SeekEvent(Initializer init) { + SeekEvent(Initializer init) { super(init); } @@ -82,11 +87,12 @@ public SeekEvent(Initializer init) { * @param stopType the type and flags for the new stop position * @param stop the value of the new stop position */ - public SeekEvent(double rate, Format format, int flags, + public SeekEvent(double rate, Format format, EnumSet flags, SeekType startType, long start, SeekType stopType, long stop) { - super(initializer(gst.ptr_gst_event_new_seek(sanitizeRate(rate), format, - flags, startType, start, stopType, stop))); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_seek(sanitizeRate(rate), format, + NativeFlags.toInt(flags), startType, start, stopType, stop))); } + private static double sanitizeRate(double rate) { if (rate == 0d) { throw new IllegalArgumentException("Cannot have rate == 0.0"); @@ -96,7 +102,7 @@ private static double sanitizeRate(double rate) { /** * Gets the playback rate. * - * A rate of 1.0 means normal playback rate, 2.0 means double speed. + * A rate of 1.0 means normal playback rate, 2.0 means double speed. * Negative values means backwards playback. A value of 0.0 for the * rate is not allowed and should be accomplished instead by PAUSING the * pipeline. @@ -105,7 +111,7 @@ private static double sanitizeRate(double rate) { */ public double getRate() { double[] rate = { 0d }; - gst.gst_event_parse_seek(this, rate, null, null, null, null, null, null); + GSTEVENT_API.gst_event_parse_seek(this, rate, null, null, null, null, null, null); return rate[0]; } @@ -116,19 +122,19 @@ public double getRate() { */ public Format getFormat() { Format[] format = new Format[1]; - gst.gst_event_parse_seek(this, null, format, null, null, null, null, null); + GSTEVENT_API.gst_event_parse_seek(this, null, format, null, null, null, null, null); return format[0]; } /** - * Gets the {@link org.freedesktop.gstreamer.SeekFlags} of this seek event. + * Gets the {@link SeekFlags} of this seek event. * * @return the seek flags. */ - public int getFlags() { + public EnumSet getFlags() { int[] flags = { 0 }; - gst.gst_event_parse_seek(this, null, null, flags, null, null, null, null); - return flags[0]; + GSTEVENT_API.gst_event_parse_seek(this, null, null, flags, null, null, null, null); + return NativeFlags.fromInt(SeekFlags.class, flags[0]); } /** @@ -138,7 +144,7 @@ public int getFlags() { */ public SeekType getStartType() { SeekType[] type = new SeekType[1]; - gst.gst_event_parse_seek(this, null, null, null, type, null, null, null); + GSTEVENT_API.gst_event_parse_seek(this, null, null, null, type, null, null, null); return type[0]; } @@ -149,7 +155,7 @@ public SeekType getStartType() { */ public long getStart() { long[] value = { 0 }; - gst.gst_event_parse_seek(this, null, null, null, null, value, null, null); + GSTEVENT_API.gst_event_parse_seek(this, null, null, null, null, value, null, null); return value[0]; } @@ -160,7 +166,7 @@ public long getStart() { */ public SeekType getStopType() { SeekType[] type = new SeekType[1]; - gst.gst_event_parse_seek(this, null, null, null, null, null, type, null); + GSTEVENT_API.gst_event_parse_seek(this, null, null, null, null, null, type, null); return type[0]; } @@ -171,7 +177,7 @@ public SeekType getStopType() { */ public long getStop() { long[] value = { 0 }; - gst.gst_event_parse_seek(this, null, null, null, null, null, null, value); + GSTEVENT_API.gst_event_parse_seek(this, null, null, null, null, null, null, value); return value[0]; } } diff --git a/src/org/freedesktop/gstreamer/SeekFlags.java b/src/org/freedesktop/gstreamer/event/SeekFlags.java similarity index 67% rename from src/org/freedesktop/gstreamer/SeekFlags.java rename to src/org/freedesktop/gstreamer/event/SeekFlags.java index b055d809..fec54832 100644 --- a/src/org/freedesktop/gstreamer/SeekFlags.java +++ b/src/org/freedesktop/gstreamer/event/SeekFlags.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -15,11 +16,17 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.event; + +import org.freedesktop.gstreamer.glib.NativeFlags; /** * Flags to be used with {@link Pipeline#seek seek} or - * {@link org.gstreamer.event.SeekEvent#SeekEvent SeekEvent} + * {@link org.freedesktop.gstreamer.event.SeekEvent#SeekEvent SeekEvent} + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#GstSeekFlags *

* All flags can be used together. *

@@ -37,72 +44,72 @@ * continue playback. With this seek method it is possible to perform seamless * looping or simple linear editing. */ -public final class SeekFlags { +public enum SeekFlags implements NativeFlags { - /** - * No flag. - */ - public final static int NONE = 0; +// /** +// * No flag. +// */ +// NONE(0), /** * Flush pipeline. */ - public final static int FLUSH = 1 << 0; + FLUSH(1 << 0), /** * Accurate position is requested, this might be considerably slower for * some formats. */ - public final static int ACCURATE = 1 << 1; - + ACCURATE(1 << 1), /** * Seek to the nearest keyframe. This might be faster but less accurate. */ - public final static int KEY_UNIT = 1 << 2; - + KEY_UNIT(1 << 2), /** * Perform a segment seek. */ - public final static int SEGMENT = 1 << 3; - - /** - * when doing fast forward or fast reverse playback, allow elements to skip - * frames instead of generating all frames. Since 0.10.22. - */ - @Deprecated - public final static int SKIP = 1 << 4; - + SEGMENT(1 << 3), /** * when doing fast forward or fast reverse playback, allow elements to skip * frames instead of generating all frames. (Since 1.6) */ - public final static int TRICKMODE = (1 << 4); + TRICKMODE(1 << 4), /** * go to a location before the requested position, if * %GST_SEEK_FLAG_KEY_UNIT this means the keyframe at or before the * requested position the one at or before the seek target. */ - public final static int SNAP_BEFORE = (1 << 5); + SNAP_BEFORE(1 << 5), /** * go to a location after the requested position, if %GST_SEEK_FLAG_KEY_UNIT * this means the keyframe at of after the requested position. */ - public final static int SNAP_AFTER = (1 << 6); - /** - * go to a position near the requested position, if %GST_SEEK_FLAG_KEY_UNIT - * this means the keyframe closest to the requested position, if both - * keyframes are at an equal distance, behaves like - * %GST_SEEK_FLAG_SNAP_BEFORE. - */ - public final static int SNAP_NEAREST = SNAP_BEFORE | SNAP_AFTER; + SNAP_AFTER(1 << 6), + // /** + // * go to a position near the requested position, if %GST_SEEK_FLAG_KEY_UNIT + // * this means the keyframe closest to the requested position, if both + // * keyframes are at an equal distance, behaves like + // * %GST_SEEK_FLAG_SNAP_BEFORE. + // */ + // public final static int SNAP_NEAREST = SNAP_BEFORE | SNAP_AFTER; /** * when doing fast forward or fast reverse playback, request that elements * only decode keyframes and skip all other content, for formats that have - * keyframes. (Since 1.6) + * keyframes. */ - public final static int TRICKMODE_KEY_UNITS = (1 << 7); + TRICKMODE_KEY_UNITS(1 << 7), /** * when doing fast forward or fast reverse playback, request that audio * decoder elements skip decoding and output only gap events or silence. - * (Since 1.6) */ - public final static int TRICKMODE_NO_AUDIO = (1 << 8); + TRICKMODE_NO_AUDIO(1 << 8); + + private final int value; + + private SeekFlags(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } } diff --git a/src/org/freedesktop/gstreamer/SeekType.java b/src/org/freedesktop/gstreamer/event/SeekType.java similarity index 74% rename from src/org/freedesktop/gstreamer/SeekType.java rename to src/org/freedesktop/gstreamer/event/SeekType.java index 82f363f0..b0912e27 100644 --- a/src/org/freedesktop/gstreamer/SeekType.java +++ b/src/org/freedesktop/gstreamer/event/SeekType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Neil C Smith + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -17,7 +17,9 @@ * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.event; + +import org.freedesktop.gstreamer.glib.NativeEnum; /** * The different types of seek events. @@ -27,11 +29,22 @@ * be provided. The seek event is then inserted into the graph with * gst_pad_send_event() or gst_element_send_event(). */ -public enum SeekType { +public enum SeekType implements NativeEnum{ /** No change in position is required. */ - NONE, + NONE(0), /** Absolute position is requested. */ - SET, + SET(1), /** Relative position to duration is requested. */ - END; + END(2); + + private final int value; + + private SeekType(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } } diff --git a/src/org/freedesktop/gstreamer/event/SegmentEvent.java b/src/org/freedesktop/gstreamer/event/SegmentEvent.java index 29b514a0..260f0341 100644 --- a/src/org/freedesktop/gstreamer/event/SegmentEvent.java +++ b/src/org/freedesktop/gstreamer/event/SegmentEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,48 +19,66 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; - import com.sun.jna.Pointer; import org.freedesktop.gstreamer.Segment; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstAPI; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstSegmentStruct; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** - * - * The newsegment event marks the range of buffers to be processed. All - * data not within the segment range is not to be processed. This can be - * used intelligently by plugins to apply more efficient methods of skipping - * unneeded data. - *

- * The position value of the segment is used in conjunction with the start - * value to convertTo the buffer timestamps into the stream time. This is - * usually done in sinks to report the current stream_time. * - * - * @author wayne + * An event for {@link Segment} + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-segment + *

+ * The segment event marks the range of buffers to be processed. All data not + * within the segment range is not to be processed. This can be used + * intelligently by plugins to apply more efficient methods of skipping unneeded + * data. The valid range is expressed with the start and stop values. + *

+ * The time value of the segment is used in conjunction with the start value to + * convert the buffer timestamps into the stream time. This is usually done in + * sinks to report the current stream_time. time represents the stream_time of a + * buffer carrying a timestamp of start . time cannot be -1. + *

+ * start cannot be -1, stop can be -1. If there is a valid stop given, it must + * be greater or equal the start , including when the indicated playback rate is + * {@literal < 0} + *

+ * The applied_rate value provides information about any rate adjustment that + * has already been made to the timestamps and content on the buffers of the + * stream. (rate * applied_rate ) should always equal the rate that has been + * requested for playback. For example, if an element has an input segment with + * intended playback rate of 2.0 and applied_rate of 1.0, it can adjust incoming + * timestamps and buffer content by half and output a segment event with rate of + * 1.0 and applied_rate of 2.0 + *

+ * After a segment event, the buffer stream time is calculated with: + *

+ * time + (TIMESTAMP(buf) - start) * ABS (rate * applied_rate) */ +//@TODO needs work to parse segments public class SegmentEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. + * * @param init initialization data. */ - public SegmentEvent(Initializer init) { + SegmentEvent(Initializer init) { super(init); } - + /** * Allocates a new segment event with the given segment. */ - public SegmentEvent(GstSegmentStruct segment) { - this(initializer(gst.ptr_gst_event_new_segment(segment))); + SegmentEvent(GstSegmentStruct segment) { + this(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_segment(segment))); } /** @@ -70,9 +89,9 @@ public SegmentEvent(GstSegmentStruct segment) { * * @return the Segment stored in this event. */ - public GstAPI.GstSegmentStruct getSegment() { + GstAPI.GstSegmentStruct getSegment() { Pointer[] segmentPointer = new Pointer[1]; - gst.gst_event_parse_segment(this, segmentPointer); + GSTEVENT_API.gst_event_parse_segment(this, segmentPointer); GstSegmentStruct result = new GstAPI.GstSegmentStruct(segmentPointer[0]); result.read(); return result; diff --git a/src/org/freedesktop/gstreamer/event/StepEvent.java b/src/org/freedesktop/gstreamer/event/StepEvent.java index 5ed56e0b..223bc995 100644 --- a/src/org/freedesktop/gstreamer/event/StepEvent.java +++ b/src/org/freedesktop/gstreamer/event/StepEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2010 DHoyt * Copyright (c) 2010 Levente Farkas * @@ -16,42 +17,52 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** - * StepEvent. The purpose of the step event is to instruct a sink to skip amount (expressed in format) of media. - * It can be used to implement stepping through the video frame by frame or for doing fast trick modes. - * A rate of <= 0.0 is not allowed, pause the pipeline or reverse the playback direction of the pipeline - * to get the same effect. - * The flush flag will clear any pending data in the pipeline before starting the step operation. - * The intermediate flag instructs the pipeline that this step operation is part of a larger step operation. + * StepEvent. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-step + *

+ * The purpose of the step event is to instruct a sink to skip amount (expressed + * in format) of media. It can be used to implement stepping through the video + * frame by frame or for doing fast trick modes. A rate of {@literal <= 0.0} is + * not allowed, pause the pipeline or reverse the playback direction of the + * pipeline to get the same effect. + *

+ * The flush flag will clear any pending data in the pipeline before starting + * the step operation. + *

+ * The intermediate flag instructs the pipeline that this step operation is part + * of a larger step operation. */ public class StepEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. + * * @param init initialization data. */ - public StepEvent(Initializer init) { + StepEvent(Initializer init) { super(init); } - + /** - * Creates a new StepEvent event. - * @param format the format of amount - * @param amount the amount of data to step - * @param rate the step rate - * @param flush flushing steps - * @param intermediate intermediate steps + * Creates a new StepEvent event. + * + * @param format the format of amount + * @param amount the amount of data to step + * @param rate the step rate + * @param flush flushing steps + * @param intermediate intermediate steps */ public StepEvent(Format format, long amount, double rate, boolean flush, boolean intermediate) { - super(initializer(gst.ptr_gst_event_new_step(format, amount, rate, flush, intermediate))); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_step(format, amount, rate, flush, intermediate))); } -} \ No newline at end of file +} diff --git a/src/org/freedesktop/gstreamer/event/StreamStartEvent.java b/src/org/freedesktop/gstreamer/event/StreamStartEvent.java index bf955b6d..cc5e6f4b 100644 --- a/src/org/freedesktop/gstreamer/event/StreamStartEvent.java +++ b/src/org/freedesktop/gstreamer/event/StreamStartEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * * This file is part of gstreamer-java. @@ -19,26 +20,32 @@ package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; - +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; +/** + * Stream Start event. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-stream-start + *

+ */ public class StreamStartEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. * @param init initialization data. */ - public StreamStartEvent(Initializer init) { + StreamStartEvent(Initializer init) { super(init); } /** * Creates a new EOS event. + * @param stream_id identifier for this stream */ public StreamStartEvent(final String stream_id) { - super(initializer(gst.ptr_gst_event_new_stream_start(stream_id))); + super(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_stream_start(stream_id))); } } \ No newline at end of file diff --git a/src/org/freedesktop/gstreamer/event/TagEvent.java b/src/org/freedesktop/gstreamer/event/TagEvent.java index 8c53d709..90cfad47 100644 --- a/src/org/freedesktop/gstreamer/event/TagEvent.java +++ b/src/org/freedesktop/gstreamer/event/TagEvent.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,59 +19,72 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.event; -import org.freedesktop.gstreamer.Event; import org.freedesktop.gstreamer.TagList; import org.freedesktop.gstreamer.lowlevel.ReferenceManager; import com.sun.jna.Pointer; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.glib.Natives; +import static org.freedesktop.gstreamer.lowlevel.GstEventAPI.GSTEVENT_API; /** - * A new set of metadata tags has been found in the stream. + * A metadata tag event. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstEvent.html#gst-event-new-tag + *

+ * The scope of the taglist specifies if the taglist applies to the complete + * medium or only to this specific stream. As the tag event is a sticky event, + * elements should merge tags received from upstream with a given scope with + * their own tags with the same scope and create a new tag event from it. */ public class TagEvent extends Event { - private static final GstEventAPI gst = GstEventAPI.GSTEVENT_API; - /** * This constructor is for internal use only. + * * @param init initialization data. */ - public TagEvent(Initializer init) { + TagEvent(Initializer init) { super(init); } + /** * Creates a new TagEvent. - *

Note: This constructor takes ownership of the TagList. Attempts to - * access the TagList after passing it to this constructor will throw an exception. - * + *

+ * Note: This constructor takes ownership of the TagList. Attempts to + * access the TagList after passing it to this constructor will throw an + * exception. + * * @param taglist the taglist to transmit with the event. */ public TagEvent(TagList taglist) { - this(initializer(gst.ptr_gst_event_new_tag(taglist))); + this(Natives.initializer(GSTEVENT_API.ptr_gst_event_new_tag(taglist))); } - + /** * Gets the {@link TagList} stored in this event. - *

Note: The TagList is owned by the event, so it should only be + *

+ * Note: The TagList is owned by the event, so it should only be * accessed whilst holding a reference to this TagEvent. - * + * * @return the TagList stored in this event. */ public TagList getTagList() { Pointer[] taglist = new Pointer[1]; - gst.gst_event_parse_tag(this, taglist); - TagList tl = new TagList(taglistInitializer(taglist[0], false, false)); + GSTEVENT_API.gst_event_parse_tag(this, taglist); +// TagList tl = new TagList(taglistInitializer(taglist[0], false, false)); + TagList tl = Natives.objectFor(taglist[0], TagList.class, false, false); ReferenceManager.addKeepAliveReference(tl, this); return tl; } - private static Initializer taglistInitializer(Pointer ptr, boolean needRef, boolean ownsHandle) { - if (ptr == null) { - throw new IllegalArgumentException("Invalid native pointer"); - } - return new Initializer(ptr, needRef, ownsHandle); - } -} \ No newline at end of file +// private static Initializer taglistInitializer(Pointer ptr, boolean needRef, boolean ownsHandle) { +// if (ptr == null) { +// throw new IllegalArgumentException("Invalid native pointer"); +// } +// return new Initializer(ptr, needRef, ownsHandle); +// } +} diff --git a/src/org/freedesktop/gstreamer/glib/GCancellable.java b/src/org/freedesktop/gstreamer/glib/GCancellable.java index a7eb701f..b966fca8 100644 --- a/src/org/freedesktop/gstreamer/glib/GCancellable.java +++ b/src/org/freedesktop/gstreamer/glib/GCancellable.java @@ -1,17 +1,35 @@ +/* + * Copyright (c) 2021 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + package org.freedesktop.gstreamer.glib; -import org.freedesktop.gstreamer.GObject; -import org.freedesktop.gstreamer.lowlevel.GioAPI; +import static org.freedesktop.gstreamer.lowlevel.GioAPI.GIO_API; public class GCancellable extends GObject{ public static final String GTYPE_NAME = "GCancellable"; public GCancellable() { - this(initializer(GioAPI.g_cancellable_new())); + this(Natives.initializer(GIO_API.g_cancellable_new())); } - public GCancellable(Initializer init) { + private GCancellable(Initializer init) { super(init); } diff --git a/src/org/freedesktop/gstreamer/glib/GDate.java b/src/org/freedesktop/gstreamer/glib/GDate.java index 159498ee..7c62e34b 100644 --- a/src/org/freedesktop/gstreamer/glib/GDate.java +++ b/src/org/freedesktop/gstreamer/glib/GDate.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2007 Wayne Meissner * @@ -19,49 +20,66 @@ package org.freedesktop.gstreamer.glib; -import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GlibAPI; -import org.freedesktop.gstreamer.lowlevel.NativeObject; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.lowlevel.GPointer; +/** + * Wrapper to the GDate data structure. + * + * See upstream documentation at https://developer.gnome.org/glib/stable/glib-Date-and-Time-Functions.html + */ public class GDate extends NativeObject { + public static final String GTYPE_NAME = "GDate"; - public static final GType GTYPE = GType.valueOf(GTYPE_NAME); - public static GDate createInstance(int day, int month, int year) { - return new GDate(GlibAPI.GLIB_API.g_date_new_dmy(day, month , year), false, true); - } - public static GDate createInstance(int julian_day) { - return new GDate(GlibAPI.GLIB_API.g_date_new_julian(julian_day), false, true); + GDate(Initializer init) { + this(new Handle(init.ptr, init.ownsHandle)); } - public GDate(Initializer init) { - super(init); - } - public GDate(Pointer ptr, boolean needRef, boolean ownsHandle) { - this(initializer(ptr, needRef, ownsHandle)); - } - - @Override - protected void disposeNativeHandle(Pointer ptr) { - GlibAPI.GLIB_API.g_date_free(ptr); + GDate(Handle handle) { + super(handle); } - public int getYear() { - return GlibAPI.GLIB_API.g_date_get_year(handle()); + public int getDay() { + return GlibAPI.GLIB_API.g_date_get_day(getRawPointer()); } public int getMonth() { - return GlibAPI.GLIB_API.g_date_get_month(handle()); + return GlibAPI.GLIB_API.g_date_get_month(getRawPointer()); } - - public int getDay() { - return GlibAPI.GLIB_API.g_date_get_day(handle()); + public int getYear() { + return GlibAPI.GLIB_API.g_date_get_year(getRawPointer()); } @Override public String toString() { return "" + getYear() + "-" + getMonth() + "-" + getDay(); } + + public static GDate createInstance(int day, int month, int year) { + Pointer ptr = GlibAPI.GLIB_API.g_date_new_dmy(day, month, year); + return new GDate(new Handle(new GPointer(ptr), true)); + } + + public static GDate createInstance(int julian_day) { + Pointer ptr = GlibAPI.GLIB_API.g_date_new_julian(julian_day); + return new GDate(new Handle(new GPointer(ptr), true)); + } + + private static final class Handle extends NativeObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GlibAPI.GLIB_API.g_date_free(ptr.getPointer()); + } + + } + } diff --git a/src/org/freedesktop/gstreamer/GError.java b/src/org/freedesktop/gstreamer/glib/GError.java similarity index 73% rename from src/org/freedesktop/gstreamer/GError.java rename to src/org/freedesktop/gstreamer/glib/GError.java index be00c95e..f03466ec 100644 --- a/src/org/freedesktop/gstreamer/GError.java +++ b/src/org/freedesktop/gstreamer/glib/GError.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -16,36 +17,29 @@ * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.glib; -import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; - -import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; +import java.util.Objects; /** - * Base gstreamer error type. + * Base GLib error type. */ public class GError { - /** - * Creates a new instance of GError - *

- * Note: This takes ownership of the passed in GErrorStruct. - * @param error - */ - GError(GErrorStruct error) { - code = error.getCode(); - message = error.getMessage(); - GLIB_API.g_error_free(error); - } + + private final int code; + private final String message; /** - * Gets a string representation of this error. + * Creates a new instance of GError * - * @return a string representing the error. + * @param code native int error code + * @param message error message text */ - public String getMessage() { - return message; + public GError(int code, String message) { + this.code = code; + this.message = Objects.requireNonNull(message); } + /** * Gets a numeric code representing this error. * @@ -54,7 +48,12 @@ public String getMessage() { public final int getCode() { return code; } - - private final int code; - private final String message; + /** + * Gets a string representation of this error. + * + * @return a string representing the error. + */ + public String getMessage() { + return message; + } } diff --git a/src/org/freedesktop/gstreamer/glib/GInetAddress.java b/src/org/freedesktop/gstreamer/glib/GInetAddress.java index 86b904d1..2f44270f 100644 --- a/src/org/freedesktop/gstreamer/glib/GInetAddress.java +++ b/src/org/freedesktop/gstreamer/glib/GInetAddress.java @@ -1,18 +1,36 @@ +/* + * Copyright (c) 2021 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + package org.freedesktop.gstreamer.glib; -import org.freedesktop.gstreamer.GObject; -import org.freedesktop.gstreamer.lowlevel.GioAPI; +import static org.freedesktop.gstreamer.lowlevel.GioAPI.GIO_API; public class GInetAddress extends GObject{ public static final String GTYPE_NAME = "GInetAddress"; - public GInetAddress(Initializer init) { + GInetAddress(Initializer init) { super(init); } public String getAddress() { - return GioAPI.g_inet_address_to_string(getNativeAddress()); + return GIO_API.g_inet_address_to_string(getRawPointer()); } } diff --git a/src/org/freedesktop/gstreamer/glib/GInetSocketAddress.java b/src/org/freedesktop/gstreamer/glib/GInetSocketAddress.java new file mode 100644 index 00000000..1a39ef8a --- /dev/null +++ b/src/org/freedesktop/gstreamer/glib/GInetSocketAddress.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + +package org.freedesktop.gstreamer.glib; + +import static org.freedesktop.gstreamer.lowlevel.GioAPI.GIO_API; + +import com.sun.jna.Pointer; + +public class GInetSocketAddress extends GSocketAddress { + + public static final String GTYPE_NAME = "GInetSocketAddress"; + + public GInetSocketAddress(String address, int port) { + this(createRawAddress(address, port)); + } + + GInetSocketAddress(Initializer init) { + super(init); + } + + public GInetAddress getAddress() { + return (GInetAddress) get("address"); + } + + public int getPort() { + return (Integer) get("port"); + } + + private static Initializer createRawAddress(String address, int port) { + Pointer nativePointer = GIO_API.g_inet_socket_address_new_from_string(address, port); + if (nativePointer == null) { + throw new GLibException("Can not create "+GInetSocketAddress.class.getSimpleName()+" for "+address+":"+port+", please check that the IP address is valid, with format x.x.x.x"); + } + return Natives.initializer(nativePointer); + } +} diff --git a/src/org/freedesktop/gstreamer/glib/GLib.java b/src/org/freedesktop/gstreamer/glib/GLib.java index e0dd3971..a123ceac 100644 --- a/src/org/freedesktop/gstreamer/glib/GLib.java +++ b/src/org/freedesktop/gstreamer/glib/GLib.java @@ -1,4 +1,8 @@ /* + * + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2018 Ingo Randalf + * * This file is part of gstreamer-java. * * gstreamer-java is free software: you can redistribute it and/or modify @@ -16,19 +20,91 @@ */ package org.freedesktop.gstreamer.glib; +import java.util.stream.Stream; import org.freedesktop.gstreamer.lowlevel.GlibAPI; +import static org.freedesktop.gstreamer.glib.Natives.registration; +/** + * Miscellaneous Utility Functions — a selection of portable utility functions from GLib + * + * Documentation derived from https://developer.gnome.org/glib/stable/glib-Miscellaneous-Utility-Functions.html#g-setenv + */ public class GLib { - - public static boolean setEnv(String variable, final String value, boolean overwrite) { - return GlibAPI.GLIB_API.g_setenv(variable, value, overwrite); + + /** + * Sets an environment variable. On UNIX, both the variable's name and value + * can be arbitrary byte strings, except that the variable's name cannot + * contain '='. On Windows, they should be in UTF-8. + * + * Note that on some systems, when variables are overwritten, the memory + * used for the previous variables and its value isn't reclaimed. + * + * You should be mindful of the fact that environment variable handling in + * UNIX is not thread-safe, and your program may crash if one thread calls + * g_setenv() while another thread is calling getenv(). (And note that many + * functions, such as gettext(), call getenv() internally.) This function is + * only safe to use at the very start of your program, before creating any + * other threads (or creating objects that create worker threads of their + * own). + * + * @param variable the environment variable to set, must not contain '='. + * @param value the value to set the variable to. + * @param overwrite whether to change the variable if it already exists. + * @return FALSE if the environment variable couldn't be set. + */ + public static boolean setEnv(String variable, final String value, boolean overwrite) { + return GlibAPI.GLIB_API.g_setenv(variable, value, overwrite); } - - public static String getEnv(String variable) { - return GlibAPI.GLIB_API.g_getenv(variable); - } - + + /** + * Returns the value of an environment variable. + * + * On UNIX, the name and value are byte strings which might or might not be + * in some consistent character set and encoding. On Windows, they are in + * UTF-8. On Windows, in case the environment variable's value contains + * references to other environment variables, they are expanded. + * + * @param variable the environment variable to get. + * @return the value of the environment variable, or NULL if the environment + * variable is not found. + */ + public static String getEnv(String variable) { + return GlibAPI.GLIB_API.g_getenv(variable); + } + + /** + * Removes an environment variable from the environment. + * + * Note that on some systems, when variables are overwritten, the memory + * used for the previous variables and its value isn't reclaimed. + * + * You should be mindful of the fact that environment variable handling in + * UNIX is not thread-safe, and your program may crash if one thread calls + * g_unsetenv() while another thread is calling getenv(). (And note that + * many functions, such as gettext(), call getenv() internally.) This + * function is only safe to use at the very start of your program, before + * creating any other threads (or creating objects that create worker + * threads of their own). + * + * @param variable the environment variable to remove, must not contain '='. + */ public static void unsetEnv(String variable) { - GlibAPI.GLIB_API.g_unsetenv(variable); + GlibAPI.GLIB_API.g_unsetenv(variable); + } + + public static class Types implements NativeObject.TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + registration(GDate.class, GDate.GTYPE_NAME, GDate::new), + registration(GInetAddress.class, GInetAddress.GTYPE_NAME, GInetAddress::new), + registration(GSocket.class, GSocket.GTYPE_NAME, GSocket::new), + registration(GSocketAddress.class, GSocketAddress.GTYPE_NAME, GSocketAddress::new), + registration(GInetSocketAddress.class, GInetSocketAddress.GTYPE_NAME, GInetSocketAddress::new) + + ); + } + } } diff --git a/src/org/freedesktop/gstreamer/Closure.java b/src/org/freedesktop/gstreamer/glib/GLibException.java similarity index 54% rename from src/org/freedesktop/gstreamer/Closure.java rename to src/org/freedesktop/gstreamer/glib/GLibException.java index f0c6cf49..cdce9737 100644 --- a/src/org/freedesktop/gstreamer/Closure.java +++ b/src/org/freedesktop/gstreamer/glib/GLibException.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2008 Wayne Meissner - * + * Copyright (c) 2019 Neil C Smith + * * This file is part of gstreamer-java. * * This code is free software: you can redistribute it and/or modify it under @@ -16,19 +16,28 @@ * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.glib; /** - * A signal callback. - *

- * Each closure should have exactly one method named 'invoke' which will be invoked - * when the signal is triggered from gstreamer - *

+ * Thrown when a gstreamer error occurs. */ -public interface Closure { +public class GLibException extends RuntimeException { + + /** + * Creates a new instance of GLibException without detail message. + */ + public GLibException() { + } + /** - * The name of the method to invoke. + * Constructs an instance of GLibException with the specified detail message. + * + * @param msg the detail message. */ - public static final String METHOD_NAME = "invoke"; - + public GLibException(String msg) { + super(msg); + } + public GLibException(GError error) { + super(error.getMessage()); + } } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GMainContext.java b/src/org/freedesktop/gstreamer/glib/GMainContext.java similarity index 56% rename from src/org/freedesktop/gstreamer/lowlevel/GMainContext.java rename to src/org/freedesktop/gstreamer/glib/GMainContext.java index b036f498..6fcdc3af 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GMainContext.java +++ b/src/org/freedesktop/gstreamer/glib/GMainContext.java @@ -17,8 +17,9 @@ * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer.lowlevel; +package org.freedesktop.gstreamer.glib; +import org.freedesktop.gstreamer.lowlevel.*; import com.sun.jna.Pointer; import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; @@ -26,28 +27,41 @@ * */ public class GMainContext extends RefCountedObject { + public GMainContext() { - this(initializer(GLIB_API.g_main_context_new())); + this(Natives.initializer(GLIB_API.g_main_context_new())); } private GMainContext(Initializer init) { - super(init); + super(new Handle(init.ptr, init.ownsHandle), init.needRef); } + public int attach(GSource source) { return GLIB_API.g_source_attach(source, this); } public static GMainContext getDefaultContext() { - return new GMainContext(initializer(GLIB_API.g_main_context_default(), false, false)); + return new GMainContext(Natives.initializer(GLIB_API.g_main_context_default(), false, false)); } - protected void ref() { - GLIB_API.g_main_context_ref(handle()); - } - protected void unref() { - GLIB_API.g_main_context_unref(handle()); - } + private static final class Handle extends RefCountedObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GLIB_API.g_main_context_unref(ptr); + } + + @Override + protected void ref() { + GLIB_API.g_main_context_ref(getPointer()); + } - @Override - protected void disposeNativeHandle(Pointer ptr) { - GLIB_API.g_main_context_unref(ptr); + @Override + protected void unref() { + GLIB_API.g_main_context_unref(getPointer()); + } + } } diff --git a/src/org/freedesktop/gstreamer/GObject.java b/src/org/freedesktop/gstreamer/glib/GObject.java similarity index 54% rename from src/org/freedesktop/gstreamer/GObject.java rename to src/org/freedesktop/gstreamer/glib/GObject.java index 34886c6a..0a191c5f 100644 --- a/src/org/freedesktop/gstreamer/GObject.java +++ b/src/org/freedesktop/gstreamer/glib/GObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Neil C Smith + * Copyright (c) 2020 Neil C Smith * Copyright (C) 2014 Tom Greenwood * Copyright (C) 2009 Levente Farkas * Copyright (C) 2009 Tamas Korodi @@ -20,36 +20,27 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.glib; import static org.freedesktop.gstreamer.lowlevel.GObjectAPI.GOBJECT_API; import static org.freedesktop.gstreamer.lowlevel.GSignalAPI.GSIGNAL_API; import static org.freedesktop.gstreamer.lowlevel.GValueAPI.GVALUE_API; -import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import org.freedesktop.gstreamer.glib.GQuark; -import org.freedesktop.gstreamer.lowlevel.EnumMapper; import org.freedesktop.gstreamer.lowlevel.GObjectAPI; -import org.freedesktop.gstreamer.lowlevel.GObjectAPI.GObjectStruct; import org.freedesktop.gstreamer.lowlevel.GObjectAPI.GParamSpec; -import org.freedesktop.gstreamer.lowlevel.GSignalAPI; import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; import org.freedesktop.gstreamer.lowlevel.GstTypes; import org.freedesktop.gstreamer.lowlevel.IntPtr; -import org.freedesktop.gstreamer.lowlevel.NativeObject; -import org.freedesktop.gstreamer.lowlevel.RefCountedObject; import com.sun.jna.Callback; import com.sun.jna.CallbackThreadInitializer; @@ -59,122 +50,270 @@ import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.freedesktop.gstreamer.MiniObject; +import org.freedesktop.gstreamer.lowlevel.GObjectPtr; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GstMiniObjectPtr; /** - * This is an abstract class providing some GObject-like facilities in a common - * base class. Not intended for direct use. + * GObject is the fundamental type providing the common attributes and methods + * for all object types in libraries based on the GObject system. + *

+ * See upstream documentation at https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html + * */ public abstract class GObject extends RefCountedObject { - private static final Logger logger = Logger.getLogger(GObject.class.getName()); + private static final Level LIFECYCLE = Level.FINE; - - private static final Map strongReferences = new ConcurrentHashMap(); - + private static final Logger LOG = Logger.getLogger(GObject.class.getName()); + private static final CallbackThreadInitializer GCALLBACK_THREAD_INIT + = new CallbackThreadInitializer(true, + Boolean.getBoolean("glib.detachCallbackThreads"), + "GCallback"); + private static final Map STRONG_REFS + = new ConcurrentHashMap(); + private static final GObjectAPI.GToggleNotify TOGGLE_NOTIFY = new ToggleNotify(); + + private final Handle handle; private Map, Map> callbackListeners; - private Map> signalClosures; - - private final IntPtr objectID = new IntPtr(System.identityHashCode(this)); - - public GObject(Initializer init) { - super(init.needRef ? initializer(init.ptr, false, init.ownsHandle) : init); - logger.entering("GObject", "", new Object[] { init }); - if (init.ownsHandle) { - final boolean is_floating = GOBJECT_API.g_object_is_floating(init.ptr); - logger.fine("Initialising owned handle for " + this.getClass().getName() - + " FLOATING = " + is_floating - + " refs = " + getRefCount() - + " need ref = " + init.needRef); - - if (!init.needRef) { - // Toggle ref has created extra reference - if (is_floating) - { + + protected GObject(Initializer init) { + this(new Handle(init.ptr.as(GObjectPtr.class, GObjectPtr::new), init.ownsHandle), init.needRef); + } + + protected GObject(Handle handle, boolean needRef) { + super(handle); + this.handle = handle; + if (handle.ownsReference()) { + final boolean is_floating = GOBJECT_API.g_object_is_floating(handle.getPointer()); + LOG.log(LIFECYCLE, () -> String.format( + "Initialising owned handle for %s floating = %b refs = %d need ref = %b", + this.getClass().getName(), + is_floating, + getRefCount(), + needRef)); + + if (!needRef) { + if (is_floating) { // Sink floating ref - sink(); + handle.sink(); } } - + // If only one ref then this is weak! This would only be the case where we didn't own if (getRefCount() >= 1) { - strongReferences.put(this, Boolean.TRUE); + STRONG_REFS.put(this, Boolean.TRUE); } - - GOBJECT_API.g_object_add_toggle_ref(init.ptr, toggle, objectID); - if (!init.needRef) { + + GOBJECT_API.g_object_add_toggle_ref(handle.getPointer(), TOGGLE_NOTIFY, handle.objectID); + if (!needRef) { // Toggle ref has created extra reference - unref(); + handle.unref(); + } + } + } + + public void connect(Class listenerClass, T listener, Callback cb) { + String signal = listenerClass.getSimpleName().toLowerCase().replaceAll("_", "-"); + connect(signal, listenerClass, listener, cb); + } + + public synchronized void connect(String signal, Class listenerClass, T listener, Callback cb) { + Native.setCallbackThreadInitializer(cb, GCALLBACK_THREAD_INIT); + addCallback(listenerClass, listener, new SignalCallback(signal, cb)); + } + + public synchronized void disconnect(Class listenerClass, T listener) { + removeCallback(listenerClass, listener); + } + + public synchronized void emit(String signal, Object... arguments) { + GSIGNAL_API.g_signal_emit_by_name(this, signal, arguments); + } + + public synchronized T emit(Class resultType, String signal, Object... arguments) { + PointerByReference pointerToResult = new PointerByReference(null); + Object[] fullArguments = Arrays.copyOf(arguments, arguments.length + 1); + fullArguments[arguments.length] = pointerToResult; + emit(signal, fullArguments); + Pointer result = pointerToResult.getValue(); + if (result == null) { + return null; + } else { + return Natives.objectFor(result, resultType, false, true); + } + } + + /** + * Gets the current value of a GObject property. + * + * @param property The name of the property to get. + * + * @return A java value representing the GObject property value. + */ + public Object get(String property) { + LOG.entering("GObject", "get", new Object[]{property}); + GObjectAPI.GParamSpec propertySpec = findProperty(property); + if (propertySpec == null) { + throw new IllegalArgumentException("Unknown property: " + property); + } + final GType propType = propertySpec.value_type; + GValue propValue = new GValue(); + GVALUE_API.g_value_init(propValue, propType); + GOBJECT_API.g_object_get_property(this, property, propValue); + if (propType.equals(GType.INT)) { + return GVALUE_API.g_value_get_int(propValue); + } else if (propType.equals(GType.UINT)) { + return GVALUE_API.g_value_get_uint(propValue); + } else if (propType.equals(GType.CHAR)) { + return Integer.valueOf(GVALUE_API.g_value_get_char(propValue)); + } else if (propType.equals(GType.UCHAR)) { + return Integer.valueOf(GVALUE_API.g_value_get_uchar(propValue)); + } else if (propType.equals(GType.LONG)) { + return GVALUE_API.g_value_get_long(propValue).longValue(); + } else if (propType.equals(GType.ULONG)) { + return GVALUE_API.g_value_get_ulong(propValue).longValue(); + } else if (propType.equals(GType.INT64)) { + return GVALUE_API.g_value_get_int64(propValue); + } else if (propType.equals(GType.UINT64)) { + return GVALUE_API.g_value_get_uint64(propValue); + } else if (propType.equals(GType.BOOLEAN)) { + return GVALUE_API.g_value_get_boolean(propValue); + } else if (propType.equals(GType.FLOAT)) { + return GVALUE_API.g_value_get_float(propValue); + } else if (propType.equals(GType.DOUBLE)) { + return GVALUE_API.g_value_get_double(propValue); + } else if (propType.equals(GType.STRING)) { + return GVALUE_API.g_value_get_string(propValue); + } else if (propType.equals(GType.OBJECT)) { + return GVALUE_API.g_value_dup_object(propValue); + } else if (GVALUE_API.g_value_type_transformable(propType, GType.OBJECT)) { + return GVALUE_API.g_value_dup_object(transform(propValue, GType.OBJECT)); + } else if (GVALUE_API.g_value_type_transformable(propType, GType.INT)) { + return GVALUE_API.g_value_get_int(transform(propValue, GType.INT)); + } else if (GVALUE_API.g_value_type_transformable(propType, GType.INT64)) { + return GVALUE_API.g_value_get_int64(transform(propValue, GType.INT64)); + } else if (propValue.checkHolds(GType.BOXED)) { + // @TODO we already know the GType here - optimise this! + Class cls = GstTypes.classFor(propType); + if (cls != null) { + Pointer ptr = GVALUE_API.g_value_get_boxed(propValue); + return Natives.objectFor(ptr, cls, true, true); } } + throw new IllegalArgumentException("Unknown conversion from GType=" + propType); + } + + /** + * Gets the default value set to GObject property. + * + * @param property The name of the property. + * @return A java value representing the GObject property's default + * value. + */ + public Object getPropertyDefaultValue(String property) { + GObjectAPI.GParamSpec propertySpec = findProperty(property); + if (propertySpec == null) { + throw new IllegalArgumentException("Unknown property: " + property); + } + final GType propType = propertySpec.value_type; + return findProperty(property, propType).getDefault(); + } + + /** + * Gets the maximum value should be set to GObject property. + * + * @param property The name of the property. + * @return A java value representing the GObject property's maximum + * value. + */ + public Object getPropertyMaximumValue(String property) { + GObjectAPI.GParamSpec propertySpec = findProperty(property); + if (propertySpec == null) { + throw new IllegalArgumentException("Unknown property: " + property); + } + final GType propType = propertySpec.value_type; + return findProperty(property, propType).getMaximum(); } /** - * Sink floating reference. This will turn a floating - * reference into a real one. + * Gets the minimum value should be set to GObject property. + * + * @param property The name of the property. + * @return A java value representing the GObject property's minimum + * value. */ - protected void sink() - { - GOBJECT_API.g_object_ref_sink(this.handle()); + public Object getPropertyMinimumValue(String property) { + GObjectAPI.GParamSpec propertySpec = findProperty(property); + if (propertySpec == null) { + throw new IllegalArgumentException("Unknown property: " + property); + } + final GType propType = propertySpec.value_type; + return findProperty(property, propType).getMinimum(); } - + /** - * Get the reference count for an object. Should only really be used for debugging + * Get the reference count for an object. Should only really be used for + * debugging + * * @return The reference count for the object */ - public int getRefCount() - { + public int getRefCount() { // Check if disposed as a disposed object may // have been free'd and we mustn't access it's // memory or we face possible SEGFAULT - if (!isDisposed()) - { - final GObjectStruct struct = new GObjectStruct(this); - return struct.ref_count; + GPointer ptr = handle.getPointer(); + + if (ptr != null) { +// final GObjectStruct struct = new GObjectStruct(this); +// return struct.ref_count; + +// ref count is after pointer to GTypeInstance + int count = ptr.getPointer().getInt(Native.POINTER_SIZE); + return count; } return 0; } - - /** - * Gives the type value. - */ - public GType getType() { - return GObject.getType(this.handle()); - } - - public static GType getType(Pointer ptr) { - // Quick getter for GType without allocation - // same as : new GObjectStruct(ptr).g_type_instance.g_class.g_type - Pointer g_class = ptr.getPointer(0); - if(Native.SIZE_T_SIZE == 8) { - return GType.valueOf(g_class.getLong(0)); - } else if (Native.SIZE_T_SIZE == 4) { - return GType.valueOf( ((long) g_class.getInt(0)) & 0xffffffffL ); - } else { - throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); - } - } /** - * Gives the type name. + * Get the native GType type name. + * @return GType type name */ public String getTypeName() { - return this.getType().getTypeName(); + return handle.getPointer().getGType().getTypeName(); } - + + public List listPropertyNames() { + GObjectAPI.GParamSpec[] lst = listProperties(); + List result = new ArrayList(lst.length); + for (int i = 0; i < lst.length; i++) { + result.add(lst[i].g_name); + } + return result; + } + /** * Sets the value of a GObject property. * * @param property The property to set. - * @param data The value for the property. This must be of the type expected - * by gstreamer. + * @param data The value for the property. This must be of the type expected + * by gstreamer. As a convenience, NativeEnums will be converted to their + * int value. */ public void set(String property, Object data) { - logger.entering("GObject", "set", new Object[] { property, data }); + LOG.entering("GObject", "set", new Object[]{property, data}); GObjectAPI.GParamSpec propertySpec = findProperty(property); - if (propertySpec == null || data == null) { + if (propertySpec == null /*|| data == null*/) { throw new IllegalArgumentException("Unknown property: " + property); } + if (data instanceof NativeEnum) { + data = ((NativeEnum) data).intValue(); + } final GType propType = propertySpec.value_type; - + GValue propValue = new GValue(); GVALUE_API.g_value_init(propValue, propType); if (propType.equals(GType.INT)) { @@ -212,6 +351,8 @@ public void set(String property, Object data) { uriString = "file://" + path; } GVALUE_API.g_value_set_string(propValue, uriString); + } else if (data == null) { + GVALUE_API.g_value_set_string(propValue, null); } else { GVALUE_API.g_value_set_string(propValue, data.toString()); } @@ -232,205 +373,125 @@ public void set(String property, Object data) { GOBJECT_API.g_object_set(this, property, data); return; } - GOBJECT_API.g_param_value_validate(propertySpec, propValue); + GOBJECT_API.g_param_value_validate(propertySpec, propValue); GOBJECT_API.g_object_set_property(this, property, propValue); GVALUE_API.g_value_unset(propValue); // Release any memory } - /** - * Gets the default value set to GObject property. - * @param property The name of the property. - * @return A java value representing the GObject property's default value. - */ - public Object getPropertyDefaultValue(String property) { - GObjectAPI.GParamSpec propertySpec = findProperty(property); - if (propertySpec == null) { - throw new IllegalArgumentException("Unknown property: " + property); + protected synchronized void addCallback(Class listenerClass, T listener, GCallback cb) { + final Map, Map> signals = getCallbackMap(); + Map map = signals.get(listenerClass); + if (map == null) { + map = new HashMap(); + signals.put(listenerClass, map); } - final GType propType = propertySpec.value_type; - return findProperty(property, propType).getDefault(); + map.put(listener, cb); } - - /** - * Gets the minimum value should be set to GObject property. - * @param property The name of the property. - * @return A java value representing the GObject property's minimum value. - */ - public Object getPropertyMinimumValue(String property) { - GObjectAPI.GParamSpec propertySpec = findProperty(property); - if (propertySpec == null) { - throw new IllegalArgumentException("Unknown property: " + property); + + @Override + public void dispose() { + super.dispose(); + STRONG_REFS.remove(this); + } + + @Override + public void invalidate() { + try { + // Need to increase the ref count before removing the toggle ref, so + // ensure the native object is not destroyed. + if (handle.ownsReference()) { + handle.ref(); + + // Disconnect the callback. + GOBJECT_API.g_object_remove_toggle_ref(handle.getPointer(), TOGGLE_NOTIFY, handle.objectID); + } + STRONG_REFS.remove(this); + } finally { + super.invalidate(); } - final GType propType = propertySpec.value_type; - return findProperty(property, propType).getMinimum(); } - - /** - * Gets the maximum value should be set to GObject property. - * @param property The name of the property. - * @return A java value representing the GObject property's maximum value. - */ - public Object getPropertyMaximumValue(String property) { - GObjectAPI.GParamSpec propertySpec = findProperty(property); - if (propertySpec == null) { - throw new IllegalArgumentException("Unknown property: " + property); + + protected synchronized void removeCallback(Class listenerClass, T listener) { + final Map, Map> signals = getCallbackMap(); + Map map = signals.get(listenerClass); + if (map != null) { + GCallback cb = map.remove(listener); + if (cb != null) { + cb.remove(); + } + if (map.isEmpty()) { + signals.remove(listenerClass); + if (callbackListeners.isEmpty()) { + callbackListeners = null; + } + } } - final GType propType = propertySpec.value_type; - return findProperty(property, propType).getMaximum(); } - - /** - * Gets the current value of a GObject property. - * - * @param property The name of the property to get. - * - * @return A java value representing the GObject property value. - */ - public Object get(String property) { - logger.entering("GObject", "get", new Object[] { property }); - GObjectAPI.GParamSpec propertySpec = findProperty(property); - if (propertySpec == null) { - throw new IllegalArgumentException("Unknown property: " + property); + + // public static T objectFor(Pointer ptr, Class defaultClass) { +// return objectFor(ptr, defaultClass, true); +// } + private GObjectAPI.GParamSpec findProperty(String propertyName) { + Pointer ptr = GOBJECT_API.g_object_class_find_property(getRawPointer().getPointer(0), propertyName); + if (ptr == null) { + return null; } - final GType propType = propertySpec.value_type; - GValue propValue = new GValue(); - GVALUE_API.g_value_init(propValue, propType); - GOBJECT_API.g_object_get_property(this, property, propValue); - if (propType.equals(GType.INT)) { - return GVALUE_API.g_value_get_int(propValue); - } else if (propType.equals(GType.UINT)) { - return GVALUE_API.g_value_get_uint(propValue); - } else if (propType.equals(GType.CHAR)) { - return Integer.valueOf(GVALUE_API.g_value_get_char(propValue)); - } else if (propType.equals(GType.UCHAR)) { - return Integer.valueOf(GVALUE_API.g_value_get_uchar(propValue)); - } else if (propType.equals(GType.LONG)) { - return GVALUE_API.g_value_get_long(propValue).longValue(); - } else if (propType.equals(GType.ULONG)) { - return GVALUE_API.g_value_get_ulong(propValue).longValue(); - } else if (propType.equals(GType.INT64)) { - return GVALUE_API.g_value_get_int64(propValue); - } else if (propType.equals(GType.UINT64)) { - return GVALUE_API.g_value_get_uint64(propValue); - } else if (propType.equals(GType.BOOLEAN)) { - return GVALUE_API.g_value_get_boolean(propValue); - } else if (propType.equals(GType.FLOAT)) { - return GVALUE_API.g_value_get_float(propValue); - } else if (propType.equals(GType.DOUBLE)) { - return GVALUE_API.g_value_get_double(propValue); - } else if (propType.equals(GType.STRING)) { - return GVALUE_API.g_value_get_string(propValue); - } else if (propType.equals(GType.OBJECT)) { - return GVALUE_API.g_value_dup_object(propValue); - } else if (GVALUE_API.g_value_type_transformable(propType, GType.OBJECT)) { - return GVALUE_API.g_value_dup_object(transform(propValue, GType.OBJECT)); - } else if (GVALUE_API.g_value_type_transformable(propType, GType.INT)) { - return GVALUE_API.g_value_get_int(transform(propValue, GType.INT)); - } else if (GVALUE_API.g_value_type_transformable(propType, GType.INT64)) { - return GVALUE_API.g_value_get_int64(transform(propValue, GType.INT64)); - } else if (propValue.checkHolds(GType.BOXED)) { - Class cls = GstTypes.classFor(propType); - if (cls != null) { - Pointer ptr = GVALUE_API.g_value_get_boxed(propValue); - return objectFor(ptr, cls, -1, true); - } - } - throw new IllegalArgumentException("Unknown conversion from GType=" + propType); - } - - public List listPropertyNames() { - GObjectAPI.GParamSpec[] lst = listProperties(); - List result = new ArrayList(lst.length); - for (int i = 0; i < lst.length; i++) - result.add(lst[i].g_name); - return result; - } - - private GObjectAPI.GParamSpec[] listProperties() { - IntByReference len = new IntByReference(); - Pointer ptrs = GOBJECT_API.g_object_class_list_properties(handle().getPointer(0), len); - if (ptrs == null) - return null; - - GParamSpec[] props = new GParamSpec[len.getValue()]; - int offset = 0; - for (int i = 0; i < len.getValue(); i++) { - props[i] = new GObjectAPI.GParamSpec(ptrs.getPointer(offset)); - offset += Native.POINTER_SIZE; - } - return props; - } - - public GType getType(String property) { - logger.entering("GObject", "getType", new Object[] { property }); - GObjectAPI.GParamSpec propertySpec = findProperty(property); - if (propertySpec == null) { - throw new IllegalArgumentException("Unknown property: " + property); - } - final GType propType = propertySpec.value_type; - return propType; + return new GObjectAPI.GParamSpec(ptr); } - - /** - * Gets the pointer to the the value of the specified property. - * - * @param property The name of the property to get. - * - * @return A java pointer. - */ - public Pointer getPointer(String property) { - logger.entering("GObject", "getPointer", new Object[] { property }); - GObjectAPI.GParamSpec propertySpec = findProperty(property); - if (propertySpec == null) { - throw new IllegalArgumentException("Unknown property: " + property); - } - PointerByReference refPtr = new PointerByReference(); - GOBJECT_API.g_object_get(this, property, refPtr, null); - Pointer ptr = refPtr.getValue(); - return ptr; - } - - private static GValue transform(GValue src, GType dstType) { - GValue dst = new GValue(); - GVALUE_API.g_value_init(dst, dstType); - GVALUE_API.g_value_transform(src, dst); - return dst; - } - private static void transform(Object data, GType type, GValue dst) { - GValue src = new GValue(); - GVALUE_API.g_value_init(src, type); - setGValue(src, type, data); - GVALUE_API.g_value_transform(src, dst); - } - private static boolean setGValue(GValue value, GType type, Object data) { + private GObjectAPI.GParamSpecTypeSpecific findProperty(String propertyName, GType type) { + Pointer ptr = GOBJECT_API.g_object_class_find_property(getRawPointer().getPointer(0), propertyName); if (type.equals(GType.INT)) { - GVALUE_API.g_value_set_int(value, intValue(data)); + return new GObjectAPI.GParamSpecInt(ptr); } else if (type.equals(GType.UINT)) { - GVALUE_API.g_value_set_uint(value, intValue(data)); + return new GObjectAPI.GParamSpecUInt(ptr); } else if (type.equals(GType.CHAR)) { - GVALUE_API.g_value_set_char(value, (byte) intValue(data)); + return new GObjectAPI.GParamSpecChar(ptr); } else if (type.equals(GType.UCHAR)) { - GVALUE_API.g_value_set_uchar(value, (byte) intValue(data)); + return new GObjectAPI.GParamSpecUChar(ptr); + } else if (type.equals(GType.BOOLEAN)) { + return new GObjectAPI.GParamSpecBoolean(ptr); } else if (type.equals(GType.LONG)) { - GVALUE_API.g_value_set_long(value, new NativeLong(longValue(data))); + return new GObjectAPI.GParamSpecLong(ptr); } else if (type.equals(GType.ULONG)) { - GVALUE_API.g_value_set_ulong(value, new NativeLong(longValue(data))); + return new GObjectAPI.GParamSpecLong(ptr); } else if (type.equals(GType.INT64)) { - GVALUE_API.g_value_set_int64(value, longValue(data)); + return new GObjectAPI.GParamSpecInt64(ptr); } else if (type.equals(GType.UINT64)) { - GVALUE_API.g_value_set_uint64(value, longValue(data)); - } else if (type.equals(GType.BOOLEAN)) { - GVALUE_API.g_value_set_boolean(value, booleanValue(data)); + return new GObjectAPI.GParamSpecInt64(ptr); } else if (type.equals(GType.FLOAT)) { - GVALUE_API.g_value_set_float(value, floatValue(data)); + return new GObjectAPI.GParamSpecFloat(ptr); } else if (type.equals(GType.DOUBLE)) { - GVALUE_API.g_value_set_double(value, doubleValue(data)); - } else { - return false; + return new GObjectAPI.GParamSpecDouble(ptr); + } else if (type.equals(GType.STRING)) { + return new GObjectAPI.GParamSpecString(ptr); } - return true; + throw new IllegalArgumentException("Unknown conversion from GType=" + type); + } + + private synchronized final Map, Map> getCallbackMap() { + if (callbackListeners == null) { + callbackListeners = new ConcurrentHashMap, Map>(); + } + return callbackListeners; } + + private GObjectAPI.GParamSpec[] listProperties() { + IntByReference len = new IntByReference(); + Pointer ptrs = GOBJECT_API.g_object_class_list_properties(getRawPointer().getPointer(0), len); + if (ptrs == null) { + return null; + } + + GParamSpec[] props = new GParamSpec[len.getValue()]; + int offset = 0; + for (int i = 0; i < len.getValue(); i++) { + props[i] = new GObjectAPI.GParamSpec(ptrs.getPointer(offset)); + offset += Native.POINTER_SIZE; + } + return props; + } + private static boolean booleanValue(Object value) { if (value instanceof Boolean) { return ((Boolean) value).booleanValue(); @@ -441,22 +502,16 @@ private static boolean booleanValue(Object value) { } throw new IllegalArgumentException("Expected boolean value, not " + value.getClass()); } - private static int intValue(Object value) { - if (value instanceof Number) { - return ((Number) value).intValue(); - } else if (value instanceof String) { - return Integer.parseInt((String) value); - } - throw new IllegalArgumentException("Expected integer value, not " + value.getClass()); - } - private static long longValue(Object value) { + + private static double doubleValue(Object value) { if (value instanceof Number) { - return ((Number) value).longValue(); + return ((Number) value).doubleValue(); } else if (value instanceof String) { - return Long.parseLong((String) value); + return Double.parseDouble((String) value); } - throw new IllegalArgumentException("Expected long value, not " + value.getClass()); + throw new IllegalArgumentException("Expected double value, not " + value.getClass()); } + private static float floatValue(Object value) { if (value instanceof Number) { return ((Number) value).floatValue(); @@ -465,55 +520,70 @@ private static float floatValue(Object value) { } throw new IllegalArgumentException("Expected float value, not " + value.getClass()); } - private static double doubleValue(Object value) { + + private static int intValue(Object value) { if (value instanceof Number) { - return ((Number) value).doubleValue(); + return ((Number) value).intValue(); } else if (value instanceof String) { - return Double.parseDouble((String) value); + return Integer.parseInt((String) value); } - throw new IllegalArgumentException("Expected double value, not " + value.getClass()); + throw new IllegalArgumentException("Expected integer value, not " + value.getClass()); } - @Override - protected void disposeNativeHandle(Pointer ptr) { - logger.log(LIFECYCLE, "Removing toggle ref " + getClass().getSimpleName() + " (" + ptr + ")"); - GOBJECT_API.g_object_remove_toggle_ref(ptr, toggle, objectID); - strongReferences.remove(this); - } - @Override - protected void ref() { - GOBJECT_API.g_object_ref(this); + private static long longValue(Object value) { + if (value instanceof Number) { + return ((Number) value).longValue(); + } else if (value instanceof String) { + return Long.parseLong((String) value); + } + throw new IllegalArgumentException("Expected long value, not " + value.getClass()); } - @Override - protected void unref() { - GOBJECT_API.g_object_unref(this); + private static boolean setGValue(GValue value, GType type, Object data) { + if (type.equals(GType.INT)) { + GVALUE_API.g_value_set_int(value, intValue(data)); + } else if (type.equals(GType.UINT)) { + GVALUE_API.g_value_set_uint(value, intValue(data)); + } else if (type.equals(GType.CHAR)) { + GVALUE_API.g_value_set_char(value, (byte) intValue(data)); + } else if (type.equals(GType.UCHAR)) { + GVALUE_API.g_value_set_uchar(value, (byte) intValue(data)); + } else if (type.equals(GType.LONG)) { + GVALUE_API.g_value_set_long(value, new NativeLong(longValue(data))); + } else if (type.equals(GType.ULONG)) { + GVALUE_API.g_value_set_ulong(value, new NativeLong(longValue(data))); + } else if (type.equals(GType.INT64)) { + GVALUE_API.g_value_set_int64(value, longValue(data)); + } else if (type.equals(GType.UINT64)) { + GVALUE_API.g_value_set_uint64(value, longValue(data)); + } else if (type.equals(GType.BOOLEAN)) { + GVALUE_API.g_value_set_boolean(value, booleanValue(data)); + } else if (type.equals(GType.FLOAT)) { + GVALUE_API.g_value_set_float(value, floatValue(data)); + } else if (type.equals(GType.DOUBLE)) { + GVALUE_API.g_value_set_double(value, doubleValue(data)); + } else { + return false; + } + return true; } - @Override - protected void invalidate() { - try { - // Need to increase the ref count before removing the toggle ref, so - // ensure the native object is not destroyed. - if (ownsHandle.get()) { - ref(); - // Disconnect the callback. - GOBJECT_API.g_object_remove_toggle_ref(handle(), toggle, objectID); - } - strongReferences.remove(this); - } finally { - super.invalidate(); - } + private static GValue transform(GValue src, GType dstType) { + GValue dst = new GValue(); + GVALUE_API.g_value_init(dst, dstType); + GVALUE_API.g_value_transform(src, dst); + return dst; } - - protected NativeLong g_signal_connect(String signal, Callback callback) { - logger.entering("GObject", "g_signal_connect", new Object[] { signal, callback }); - return GOBJECT_API.g_signal_connect_data(this, signal, callback, null, null, 0); + + private static void transform(Object data, GType type, GValue dst) { + GValue src = new GValue(); + GVALUE_API.g_value_init(src, type); + setGValue(src, type, data); + GVALUE_API.g_value_transform(src, dst); } - private final static CallbackThreadInitializer cbi = new CallbackThreadInitializer(true, false, "GCallback"); - - abstract protected class GCallback { + protected abstract class GCallback { + protected final Callback cb; protected final NativeLong id; volatile boolean connected = false; @@ -521,307 +591,155 @@ abstract protected class GCallback { protected GCallback(NativeLong id, Callback cb) { this.id = id != null ? id : new NativeLong(0); this.cb = cb; - this.connected = this.id.intValue() != 0; + this.connected = this.id.longValue() != 0; } + void remove() { if (connected) { disconnect(); connected = false; } } + abstract protected void disconnect(); - @Override - protected final void finalize() { - // Ensure the native callback is removed - remove(); - } + } + + /** + * Base interface for classes that implement a GInterface + */ + public static interface GInterface { + + /** + * Get the GObject implementing this interface. + * + * @return implementing GObject + */ + public GObject getGObject(); + + } + private final class SignalCallback extends GCallback { + protected SignalCallback(String signal, Callback cb) { - super(g_signal_connect(signal, cb), cb); + super(handle.connectSignal(signal, cb), cb); if (!connected) { throw new IllegalArgumentException(String.format("Failed to connect signal '%s'", signal)); } } + @Override synchronized protected void disconnect() { - GOBJECT_API.g_signal_handler_disconnect(GObject.this, id); - } - } - private synchronized final Map, Map> getCallbackMap() { - if (callbackListeners == null) { - callbackListeners = new ConcurrentHashMap, Map>(); + handle.disconnectSignal(id); } - return callbackListeners; } - private synchronized final Map> getClosureMap() { - if (signalClosures == null) { - signalClosures = new ConcurrentHashMap>(); - } - return signalClosures; - } - - protected synchronized void addCallback(Class listenerClass, T listener, GCallback cb) { - final Map, Map> signals = getCallbackMap(); - Map map = signals.get(listenerClass); - if (map == null) { - map = new HashMap(); - signals.put(listenerClass, map); + + protected static class Handle extends RefCountedObject.Handle { + + private final IntPtr objectID; + private final Set signals; + + public Handle(GObjectPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + this.objectID = new IntPtr(System.identityHashCode(this)); + signals = new HashSet<>(); } - map.put(listener, cb); - } - - public synchronized void removeCallback(Class listenerClass, T listener) { - final Map, Map> signals = getCallbackMap(); - Map map = signals.get(listenerClass); - if (map != null) { - GCallback cb = map.remove(listener); - if (cb != null) { - cb.remove(); - } - if (map.isEmpty()) { - signals.remove(listenerClass); - if (callbackListeners.isEmpty()) { - callbackListeners = null; - } + + private synchronized NativeLong connectSignal(String signal, Callback cb) { + NativeLong id = GOBJECT_API.g_signal_connect_data(getPointer(), signal, cb, null, null, 0); + if (id.longValue() != 0) { + signals.add(id); } + return id; } - } - public void connect(Class listenerClass, T listener, Callback cb) { - String signal = listenerClass.getSimpleName().toLowerCase().replaceAll("_", "-"); - connect(signal, listenerClass, listener, cb); - } - - public synchronized void connect(String signal, Class listenerClass, T listener, Callback cb) { - Native.setCallbackThreadInitializer(cb, cbi); - addCallback(listenerClass, listener, new SignalCallback(signal, cb)); - } - - public synchronized void disconnect(Class listenerClass, T listener) { - removeCallback(listenerClass, listener); - } - private final class ClosureProxy implements GSignalAPI.GSignalCallbackProxy { - private final Closure closure; - private final Method method; - private final Class[] parameterTypes; - NativeLong id; - - protected ClosureProxy(String signal, Closure closure) { - this.closure = closure; - - Method invoke = null; - for (Method m : closure.getClass().getDeclaredMethods()) { - if (m.getName().equals(Closure.METHOD_NAME)) { - invoke = m; - break; - } - } - if (invoke == null) { - throw new IllegalArgumentException(closure.getClass() - + " does not have an invoke method"); - } - invoke.setAccessible(true); - this.method = invoke; - // - // The closure does not have a 'user_data' pointer, so push it in as the - // last arg. The last arg will be dropped later in callback() - // - parameterTypes = new Class[method.getParameterTypes().length + 1]; - parameterTypes[parameterTypes.length - 1] = Pointer.class; - for (int i = 0; i < method.getParameterTypes().length; ++i) { - Class paramType = method.getParameterTypes()[i]; - Class nativeType = paramType; - if (ClockTime.class.isAssignableFrom(paramType)) { - nativeType = long.class; - } else if (NativeObject.class.isAssignableFrom(paramType)) { - nativeType = Pointer.class; - } else if (Enum.class.isAssignableFrom(paramType)) { - nativeType = int.class; - } else if (String.class.isAssignableFrom(paramType)) { - nativeType = Pointer.class; - } else if (Boolean.class.isAssignableFrom(paramType)) { - nativeType = int.class; - } - parameterTypes[i] = nativeType; - } - NativeLong connectID = GSIGNAL_API.g_signal_connect_data(GObject.this, - signal, this, null, null, 0); - if (connectID.intValue() == 0) { - throw new IllegalArgumentException(String.format("Failed to connect signal '%s'", signal)); + + private synchronized void disconnectSignal(NativeLong id) { + if (signals.remove(id)) { + GOBJECT_API.g_signal_handler_disconnect(getPointer(), id); } - this.id = connectID; } - synchronized protected void disconnect() { - if (id != null && id.intValue() != 0) { - GOBJECT_API.g_signal_handler_disconnect(GObject.this, id); - id = null; - } + + private synchronized void clearSignals() { + signals.forEach(id -> GOBJECT_API.g_signal_handler_disconnect(getPointer(), id)); + signals.clear(); } + @Override - protected void finalize() { - // Ensure the native callback is removed - disconnect(); - } - @SuppressWarnings({"unchecked","rawtypes"}) - public Object callback(Object[] parameters) { - - try { - // Drop the last arg - it is the 'user_data' pointer - Object[] methodParameters = new Object[parameters.length - 1]; - - for (int i = 0; i < methodParameters.length; ++i) { - Class paramType = method.getParameterTypes()[i]; - Object nativeParam = parameters[i]; - Object javaParam = nativeParam; - if (nativeParam == null) { - continue; - } - if (ClockTime.class.isAssignableFrom(paramType)) { - javaParam = ClockTime.valueOf((Long) nativeParam, - TimeUnit.NANOSECONDS); - } else if (NativeObject.class.isAssignableFrom(paramType)) { - javaParam = objectFor((Pointer) nativeParam, - paramType, 1, true); - } else if (Enum.class.isAssignableFrom(paramType)) { - javaParam = EnumMapper.getInstance().valueOf((Integer) nativeParam, - paramType); - } else if (String.class.isAssignableFrom(paramType)) { - javaParam = ((Pointer) nativeParam).getString(0); - } else if (Boolean.class.isAssignableFrom(paramType)) { - javaParam = Boolean.valueOf(((Integer) nativeParam).intValue() != 0); - } else { - javaParam = nativeParam; - } - methodParameters[i] = javaParam; - } - - return method.invoke(closure, methodParameters); - } catch (Throwable t) { - return Integer.valueOf(0); - } + public void invalidate() { + clearSignals(); + super.dispose(); } - public Class[] getParameterTypes() { - return parameterTypes; + @Override + public void dispose() { + clearSignals(); + super.dispose(); } - public Class getReturnType() { - return method.getReturnType(); + @Override + protected void disposeNativeHandle(GPointer ptr) { + GOBJECT_API.g_object_remove_toggle_ref((GObjectPtr) ptr, TOGGLE_NOTIFY, objectID); } - } - public synchronized void connect(String signal, Closure closure) { - final Map> signals = getClosureMap(); - Map m = signals.get(signal); - if (m == null) { - m = new HashMap(); - signals.put(signal, m); + + @Override + protected void ref() { + GOBJECT_API.g_object_ref(getPointer()); } - m.put(closure, new ClosureProxy(signal, closure)); - } - public synchronized void disconnect(String signal, Closure closure) { - final Map> signals = signalClosures; - if (signals == null) { - return; + + /** + * Sink floating reference. This will turn a floating reference into a + * real one. + */ + protected void sink() { + GOBJECT_API.g_object_ref_sink(getPointer()); } - Map map = signals.get(signal); - if (map != null) { - ClosureProxy cb = map.remove(closure); - if (cb != null) { - cb.disconnect(); - } - if (map.isEmpty()) { - signals.remove(signal); - if (signalClosures.isEmpty()) { - signalClosures = null; - } - } + + @Override + protected void unref() { + GOBJECT_API.g_object_unref(getPointer()); } - } - - public synchronized void emit(int signal_id, GQuark detail, Object... arguments) { - GSIGNAL_API.g_signal_emit(this, signal_id, detail, arguments); - } - - public synchronized void emit(String signal, Object... arguments) { - GSIGNAL_API.g_signal_emit_by_name(this, signal, arguments); - } - public synchronized T emit(Class resultType, String signal, Object... arguments) { - PointerByReference pointerToResult = new PointerByReference(null); - Object[] fullArguments = Arrays.copyOf(arguments, arguments.length + 1); - fullArguments[arguments.length] = pointerToResult; - emit(signal, fullArguments); - Pointer result = pointerToResult.getValue(); - if (result == null) { - return null; - } else { - return NativeObject.objectFor(result, resultType, false, true); + @Override + protected GObjectPtr getPointer() { + return (GObjectPtr) super.getPointer(); + } + + @Override + public String toString() { + GObjectPtr ptr = getPointer(); + if (ptr != null) { + return ptr.getGType().getTypeName() + " : " + objectID; + } else { + return "Disposed handle"; + } } - } -// public static T objectFor(Pointer ptr, Class defaultClass) { -// return objectFor(ptr, defaultClass, true); -// } - - private GObjectAPI.GParamSpec findProperty(String propertyName) { - Pointer ptr = GOBJECT_API.g_object_class_find_property(handle().getPointer(0), propertyName); - if (ptr == null) - return null; - return new GObjectAPI.GParamSpec(ptr); } - - private GObjectAPI.GParamSpecTypeSpecific findProperty(String propertyName, GType type) { - Pointer ptr = GOBJECT_API.g_object_class_find_property(handle().getPointer(0), propertyName); - if (type.equals(GType.INT)) - return new GObjectAPI.GParamSpecInt(ptr); - else if(type.equals(GType.UINT)) - return new GObjectAPI.GParamSpecUInt(ptr); - else if(type.equals(GType.CHAR)) - return new GObjectAPI.GParamSpecChar(ptr); - else if(type.equals(GType.UCHAR)) - return new GObjectAPI.GParamSpecUChar(ptr); - else if(type.equals(GType.BOOLEAN)) - return new GObjectAPI.GParamSpecBoolean(ptr); - else if(type.equals(GType.LONG)) - return new GObjectAPI.GParamSpecLong(ptr); - else if(type.equals(GType.ULONG)) - return new GObjectAPI.GParamSpecLong(ptr); - else if(type.equals(GType.INT64)) - return new GObjectAPI.GParamSpecInt64(ptr); - else if(type.equals(GType.UINT64)) - return new GObjectAPI.GParamSpecInt64(ptr); - else if(type.equals(GType.FLOAT)) - return new GObjectAPI.GParamSpecFloat(ptr); - else if(type.equals(GType.DOUBLE)) - return new GObjectAPI.GParamSpecDouble(ptr); - else if(type.equals(GType.STRING)) - return new GObjectAPI.GParamSpecString(ptr); - throw new IllegalArgumentException("Unknown conversion from GType=" + type); - } - /* - * Hooks to/from native disposal - */ - private static final GObjectAPI.GToggleNotify toggle = new GObjectAPI.GToggleNotify() { + + private static final class ToggleNotify implements GObjectAPI.GToggleNotify { + + @Override public void callback(Pointer data, Pointer ptr, boolean is_last_ref) { - + /* - * Manage the strong reference to this instance. When this is the last - * reference to the underlying object, remove the strong reference so - * it can be garbage collected. If it is owned by someone else, then make - * it a strong ref, so the java GObject for the underlying C object can - * be retained for later retrieval + * Manage the strong reference to this instance. When this is the last + * reference to the underlying object, remove the strong reference so + * it can be garbage collected. If it is owned by someone else, then make + * it a strong ref, so the java GObject for the underlying C object can + * be retained for later retrieval */ GObject o = (GObject) NativeObject.instanceFor(ptr); if (o == null) { return; } - logger.log(LIFECYCLE, "toggle_ref " + o.getClass().getSimpleName() + - " (" + ptr + ")" + " last_ref=" + is_last_ref); + LOG.log(LIFECYCLE, "toggle_ref " + o.getClass().getSimpleName() + + " (" + ptr + ")" + " last_ref=" + is_last_ref); if (is_last_ref) { - strongReferences.remove(o); + STRONG_REFS.remove(o); } else { - strongReferences.put(o, Boolean.TRUE); + STRONG_REFS.put(o, Boolean.TRUE); } } - }; + } + } diff --git a/src/org/freedesktop/gstreamer/glib/GQuark.java b/src/org/freedesktop/gstreamer/glib/GQuark.java index 2efb28e4..5d9a043a 100644 --- a/src/org/freedesktop/gstreamer/glib/GQuark.java +++ b/src/org/freedesktop/gstreamer/glib/GQuark.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2007 Wayne Meissner * @@ -21,21 +22,31 @@ import org.freedesktop.gstreamer.lowlevel.GObjectAPI; +/** + * Quarks — a 2-way association between a string and a unique integer identifier. + * + * See upstream documentation at https://developer.gnome.org/glib/stable/glib-Quarks.html + * + */ public class GQuark { + private final int value; + public GQuark(int value) { this.value = value; } + public int intValue() { return value; } - public static GQuark valueOf(String quark) { - return GObjectAPI.GOBJECT_API.g_quark_from_string(quark); - } - @Override public String toString() { return GObjectAPI.GOBJECT_API.g_quark_to_string(this); } + + public static GQuark valueOf(String quark) { + return GObjectAPI.GOBJECT_API.g_quark_from_string(quark); + } } diff --git a/src/org/freedesktop/gstreamer/glib/GSocket.java b/src/org/freedesktop/gstreamer/glib/GSocket.java new file mode 100644 index 00000000..27aab519 --- /dev/null +++ b/src/org/freedesktop/gstreamer/glib/GSocket.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.glib; + +import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; + +import static org.freedesktop.gstreamer.lowlevel.GioAPI.GIO_API; +import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; + +public class GSocket extends GObject { + + public static final String GTYPE_NAME = "GSocket"; + + public GSocket(GSocketFamily family, GSocketType type, GSocketProtocol protocol) throws GLibException { + this(makeRawSocket(family, type, protocol)); + } + + GSocket(Initializer init) { + super(init); + } + + public GSocket bind(String address, int port) { + GInetSocketAddress boundAddress = new GInetSocketAddress(address, port); + GErrorStruct reference = new GErrorStruct(); + GErrorStruct[] errorArray = (GErrorStruct[]) reference.toArray(1); + if (!GIO_API.g_socket_bind(getRawPointer(), + Natives.getRawPointer(boundAddress), + true, + reference.getPointer())) { + throw new GLibException(extractAndClearError(errorArray[0])); + } + return this; + } + + public void connect(String address, int port) { + GInetSocketAddress connectedAddress = new GInetSocketAddress(address, port); + GErrorStruct reference = new GErrorStruct(); + GErrorStruct[] errorArray = (GErrorStruct[]) reference.toArray(1); + if (!GIO_API.g_socket_connect(getRawPointer(), + Natives.getRawPointer(connectedAddress), + Natives.getRawPointer(new GCancellable()), + reference.getPointer())) { + throw new GLibException(extractAndClearError(errorArray[0])); + } + } + + public int getFD() { + return (Integer) get("fd"); + } + + public GInetSocketAddress getLocalAddress() { + return (GInetSocketAddress) get("local-address"); + } + + public GInetSocketAddress getRemoteAddress() { + return (GInetSocketAddress) get("remote-address"); + } + + public GSocketFamily getSocketFamily() { + return GSocketFamily.fromGioValue((Integer) get("family")); + } + + public GSocketProtocol getSocketProtocol() { + return GSocketProtocol.fromGioValue((Integer) get("protocol")); + } + + public GSocketType getSocketType() { + return GSocketType.fromGioValue((Integer) get("type")); + } + + public boolean isBlocking() { + return (Boolean) get("blocking"); + } + + private static String extractAndClearError(GErrorStruct struct) { + struct.read(); + String err = struct.getMessage(); + GLIB_API.g_error_free(struct.getPointer()); + return err; + } + + private static Initializer makeRawSocket(GSocketFamily family, GSocketType type, GSocketProtocol protocol) throws GLibException { + GErrorStruct reference = new GErrorStruct(); + GErrorStruct[] errorArray = (GErrorStruct[]) reference.toArray(1); + Pointer socketPointer = GIO_API.g_socket_new(family.toGioValue(), type.toGioValue(), protocol.toGioValue(), reference.getPointer()); + if (socketPointer == null) { + throw new GLibException(extractAndClearError(errorArray[0])); + } + return Natives.initializer(socketPointer); + } + +} diff --git a/src/org/freedesktop/gstreamer/glib/GSocketAddress.java b/src/org/freedesktop/gstreamer/glib/GSocketAddress.java index 40d24aee..39537556 100644 --- a/src/org/freedesktop/gstreamer/glib/GSocketAddress.java +++ b/src/org/freedesktop/gstreamer/glib/GSocketAddress.java @@ -1,6 +1,23 @@ -package org.freedesktop.gstreamer.glib; +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ -import org.freedesktop.gstreamer.GObject; +package org.freedesktop.gstreamer.glib; public class GSocketAddress extends GObject{ diff --git a/src/org/freedesktop/gstreamer/glib/GSocketFamily.java b/src/org/freedesktop/gstreamer/glib/GSocketFamily.java index d4dd62d2..1cfacb80 100644 --- a/src/org/freedesktop/gstreamer/glib/GSocketFamily.java +++ b/src/org/freedesktop/gstreamer/glib/GSocketFamily.java @@ -1,3 +1,23 @@ +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + + package org.freedesktop.gstreamer.glib; import java.util.HashMap; @@ -18,10 +38,6 @@ public enum GSocketFamily { fastResolveMap.put(dataUnitType.toGioValue(), dataUnitType); } } - - public static GSocketFamily fromGioValue(int gioValue) { - return fastResolveMap.get(gioValue); - } private int gioValue; @@ -32,5 +48,9 @@ private GSocketFamily(int gioValue) { public int toGioValue() { return gioValue; } + + public static GSocketFamily fromGioValue(int gioValue) { + return fastResolveMap.get(gioValue); + } } diff --git a/src/org/freedesktop/gstreamer/glib/GSocketProtocol.java b/src/org/freedesktop/gstreamer/glib/GSocketProtocol.java index 24235560..e2473b82 100644 --- a/src/org/freedesktop/gstreamer/glib/GSocketProtocol.java +++ b/src/org/freedesktop/gstreamer/glib/GSocketProtocol.java @@ -1,3 +1,23 @@ +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + + package org.freedesktop.gstreamer.glib; import java.util.HashMap; @@ -17,10 +37,6 @@ public enum GSocketProtocol { } } - public static GSocketProtocol fromGioValue(int gioValue) { - return fastResolveMap.get(gioValue); - } - private int gioValue; private GSocketProtocol(int gioValue) { @@ -30,5 +46,9 @@ private GSocketProtocol(int gioValue) { public int toGioValue() { return gioValue; } + + public static GSocketProtocol fromGioValue(int gioValue) { + return fastResolveMap.get(gioValue); + } } diff --git a/src/org/freedesktop/gstreamer/glib/GSocketType.java b/src/org/freedesktop/gstreamer/glib/GSocketType.java index fcc7bc33..ed79c34e 100644 --- a/src/org/freedesktop/gstreamer/glib/GSocketType.java +++ b/src/org/freedesktop/gstreamer/glib/GSocketType.java @@ -1,3 +1,23 @@ +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + + package org.freedesktop.gstreamer.glib; import java.util.HashMap; @@ -15,10 +35,6 @@ public enum GSocketType { fastResolveMap.put(dataUnitType.toGioValue(), dataUnitType); } } - - public static GSocketType fromGioValue(int gioValue) { - return fastResolveMap.get(gioValue); - } private int gioValue; @@ -29,5 +45,9 @@ private GSocketType(int gioValue) { public int toGioValue() { return gioValue; } + + public static GSocketType fromGioValue(int gioValue) { + return fastResolveMap.get(gioValue); + } } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GSource.java b/src/org/freedesktop/gstreamer/glib/GSource.java similarity index 62% rename from src/org/freedesktop/gstreamer/lowlevel/GSource.java rename to src/org/freedesktop/gstreamer/glib/GSource.java index 731ac2ca..e1def235 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GSource.java +++ b/src/org/freedesktop/gstreamer/glib/GSource.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -16,8 +17,9 @@ * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer.lowlevel; +package org.freedesktop.gstreamer.glib; +import org.freedesktop.gstreamer.lowlevel.*; import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; import java.util.concurrent.Callable; @@ -28,16 +30,18 @@ */ public class GSource extends RefCountedObject { - public GSource(Initializer init) { - super(init); + GSource(Initializer init) { + super(new Handle(init.ptr, init.ownsHandle), init.needRef); } + public int attach(GMainContext context) { return GLIB_API.g_source_attach(this, context); } + public void setCallback(final Callable call) { this.callback = new GlibAPI.GSourceFunc() { public boolean callback(Pointer data) { - if (GLIB_API.g_source_is_destroyed(handle())) { + if (GLIB_API.g_source_is_destroyed(getRawPointer())) { return false; } try { @@ -49,18 +53,30 @@ public boolean callback(Pointer data) { }; GLIB_API.g_source_set_callback(this, callback, null, null); } + private GlibAPI.GSourceFunc callback; - protected void ref() { - GLIB_API.g_source_ref(handle()); - } - protected void unref() { - GLIB_API.g_source_unref(handle()); - } + private static final class Handle extends RefCountedObject.Handle { + + Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GLIB_API.g_source_destroy(ptr.getPointer()); + GLIB_API.g_source_unref(ptr.getPointer()); + } + + @Override + protected void ref() { + GLIB_API.g_source_ref(getPointer().getPointer()); + } - @Override - protected void disposeNativeHandle(Pointer ptr) { - GLIB_API.g_source_destroy(ptr); - GLIB_API.g_source_unref(ptr); + @Override + protected void unref() { + GLIB_API.g_source_unref(getPointer().getPointer()); + } + } } diff --git a/src/org/freedesktop/gstreamer/glib/MainContextExecutorService.java b/src/org/freedesktop/gstreamer/glib/MainContextExecutorService.java index ee0831cb..50d1ace5 100644 --- a/src/org/freedesktop/gstreamer/glib/MainContextExecutorService.java +++ b/src/org/freedesktop/gstreamer/glib/MainContextExecutorService.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007, 2008 Wayne Meissner * * This file is part of gstreamer-java. @@ -32,21 +33,12 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.freedesktop.gstreamer.lowlevel.GMainContext; -import org.freedesktop.gstreamer.lowlevel.GSource; - /** * Wraps the glib main loop/main context in a ScheduledExecutor interface. */ public class MainContextExecutorService extends AbstractExecutorService implements ScheduledExecutorService { - private final GMainContext context; - private GSource idleSource = null; private final List bgTasks = new LinkedList(); - private volatile boolean running = true; - - public MainContextExecutorService(GMainContext context) { - this.context = context; - } + private final GMainContext context; private final Callable idleCallback = new Callable() { public Boolean call() throws Exception { @@ -62,33 +54,20 @@ public Boolean call() throws Exception { return false; } }; - private void invokeLater(final Runnable r) { - // System.out.println("Scheduling idle callbacks"); - synchronized (bgTasks) { - boolean empty = bgTasks.isEmpty(); - bgTasks.add(r); - // Only trigger the callback if there were no existing elements in the list - // otherwise it is already triggered - if (empty) { - idleSource = GLIB_API.g_idle_source_new(); - idleSource.setCallback(idleCallback); - idleSource.attach(context); - } - } + private GSource idleSource = null; + private volatile boolean running = true; + + public MainContextExecutorService(GMainContext context) { + this.context = context; } - public void shutdown() { - shutdownNow(); + public boolean awaitTermination(long timeout, TimeUnit units) throws InterruptedException { + throw new UnsupportedOperationException("Not supported yet."); } - - public List shutdownNow() { - List tasks = new ArrayList(); - synchronized (bgTasks) { - tasks.addAll(bgTasks); - bgTasks.clear(); - } - return tasks; + public void execute(Runnable runnable) { + invokeLater(runnable); } + public boolean isShutdown() { return !running; } @@ -99,12 +78,45 @@ public boolean isTerminated() { } } - public boolean awaitTermination(long timeout, TimeUnit units) throws InterruptedException { - throw new UnsupportedOperationException("Not supported yet."); + public ScheduledFuture schedule(Runnable runnable, long delay, TimeUnit units) { + return new ScheduledTimeout(Executors.callable(runnable), delay, units); } - public void execute(Runnable runnable) { - invokeLater(runnable); + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit units) { + return new ScheduledTimeout(callable, delay, units); + } + + public ScheduledFuture scheduleAtFixedRate(Runnable runnable, long initialiDelay, long period, TimeUnit units) { + return new ScheduledTimeout(Executors.callable(runnable), initialiDelay, period, units); + } + + public ScheduledFuture scheduleWithFixedDelay(Runnable runnable, long initialiDelay, long delay, TimeUnit units) { + return new ScheduledTimeout(Executors.callable(runnable), initialiDelay, delay, units); + } + public void shutdown() { + shutdownNow(); + } + public List shutdownNow() { + List tasks = new ArrayList(); + synchronized (bgTasks) { + tasks.addAll(bgTasks); + bgTasks.clear(); + } + return tasks; + } + private void invokeLater(final Runnable r) { + // System.out.println("Scheduling idle callbacks"); + synchronized (bgTasks) { + boolean empty = bgTasks.isEmpty(); + bgTasks.add(r); + // Only trigger the callback if there were no existing elements in the list + // otherwise it is already triggered + if (empty) { + idleSource = GLIB_API.g_idle_source_new(); + idleSource.setCallback(idleCallback); + idleSource.attach(context); + } + } } private class ScheduledTimeout extends FutureTask implements ScheduledFuture { private volatile GSource source; @@ -150,9 +162,9 @@ private final int getMilliseconds(long time) { private void start(long timeout, Callable callback) { int milliseconds = getMilliseconds(timeout); /* - * If the timeout is a multiple of seconds, use the more efficient - * g_timeout_add_seconds, if it is available. - */ + * If the timeout is a multiple of seconds, use the more efficient + * g_timeout_add_seconds, if it is available. + */ if ((milliseconds % 1000) == 0) { try { source = GLIB_API.g_timeout_source_new_seconds(milliseconds / 1000); @@ -174,19 +186,4 @@ public int compareTo(Delayed delayed) { throw new UnsupportedOperationException("Not supported yet."); } } - public ScheduledFuture schedule(Runnable runnable, long delay, TimeUnit units) { - return new ScheduledTimeout(Executors.callable(runnable), delay, units); - } - - public ScheduledFuture schedule(Callable callable, long delay, TimeUnit units) { - return new ScheduledTimeout(callable, delay, units); - } - - public ScheduledFuture scheduleAtFixedRate(Runnable runnable, long initialiDelay, long period, TimeUnit units) { - return new ScheduledTimeout(Executors.callable(runnable), initialiDelay, period, units); - } - - public ScheduledFuture scheduleWithFixedDelay(Runnable runnable, long initialiDelay, long delay, TimeUnit units) { - return new ScheduledTimeout(Executors.callable(runnable), initialiDelay, delay, units); - } } diff --git a/src/org/freedesktop/gstreamer/glib/NativeEnum.java b/src/org/freedesktop/gstreamer/glib/NativeEnum.java new file mode 100644 index 00000000..5c76227e --- /dev/null +++ b/src/org/freedesktop/gstreamer/glib/NativeEnum.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.glib; + +/** + * Interface for enums that wrap a native int-based enum. + * + * @param Java enum type + */ +public interface NativeEnum> { + + public int intValue(); + + /** + * Convert a native int value to the specified NativeEnum type. + * + * @param enum type + * @param type enum class + * @param intValue native int value + * @return enum value + * @throws IllegalArgumentException if no enum value matches the specified + * native int value + */ + public static & NativeEnum> T fromInt(Class type, int intValue) { + for (T value : type.getEnumConstants()) { + if (value.intValue() == intValue) { + return value; + } + } + + throw new IllegalArgumentException("Value " + intValue + " is unacceptable for " + + type.getSimpleName() + " enum"); + } + + /** + * Convert a native int value to the specified NativeEnum type, allowing for + * a default value (or null) to be returned instead of throwing an exception + * on invalid values. + * + * @param enum type + * @param type enum class + * @param defValue default value to return if no match (may be null) + * @param intValue native int value + * @return enum value + */ + public static & NativeEnum> T fromInt(Class type, T defValue, int intValue) { + for (T value : type.getEnumConstants()) { + if (value.intValue() == intValue) { + return value; + } + } + + return defValue; + } + +} diff --git a/src/org/freedesktop/gstreamer/glib/NativeFlags.java b/src/org/freedesktop/gstreamer/glib/NativeFlags.java new file mode 100644 index 00000000..704c3188 --- /dev/null +++ b/src/org/freedesktop/gstreamer/glib/NativeFlags.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.glib; + +import java.util.EnumSet; +import java.util.Set; + +/** + * Interface for enums that represent native bit flags. + * @param type of flag enum + */ +public interface NativeFlags> extends NativeEnum { + + public static & NativeFlags> int toInt(Set flags) { + int ret = 0; + for (FLAG flag : flags) { + ret |= flag.intValue(); + } + return ret; + } + + public static & NativeFlags> int toInt(EnumSet flags) { + int ret = 0; + for (FLAG flag : flags) { + ret |= flag.intValue(); + } + return ret; + } + + public static & NativeFlags> EnumSet + fromInt(Class type, int val) { + EnumSet set = EnumSet.allOf(type); + set.removeIf(f -> ((val & f.intValue()) == 0)); + return set; + } + +} diff --git a/src/org/freedesktop/gstreamer/glib/NativeObject.java b/src/org/freedesktop/gstreamer/glib/NativeObject.java new file mode 100644 index 00000000..4b2a873d --- /dev/null +++ b/src/org/freedesktop/gstreamer/glib/NativeObject.java @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2007 Wayne Meissner + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.glib; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.sun.jna.Pointer; +import java.lang.ref.ReferenceQueue; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Stream; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GTypedPtr; +import org.freedesktop.gstreamer.lowlevel.GstTypes; + +/** + * + */ +public abstract class NativeObject implements AutoCloseable { + + private static final Level LIFECYCLE = Level.FINE; + private static final Logger LOG = Logger.getLogger(NativeObject.class.getName()); + private static final ConcurrentMap INSTANCES = new ConcurrentHashMap<>(); + + final Handle handle; + private final Pointer ptr; + +// /** +// * Creates a new instance of NativeObject +// */ + protected NativeObject(Handle handle) { + this.handle = Objects.requireNonNull(handle); + this.ptr = handle.ptrRef.get().getPointer(); + if (handle.isCacheable()) { + // need to put all nativeRef in map now so WeakReference doesn't go out of scope + INSTANCES.put(this.ptr, new NativeRef(this, handle)); + } + } + + /** + * Disown this object. The underlying native object will no longer be + * disposed of when this Java object is explicitly or implicitly disposed. + *

+ * The underlying reference will remain valid. + */ + public void disown() { + LOG.log(LIFECYCLE, "Disowning " + getRawPointer()); + handle.ownsReference.set(false); + } + + /** + * Implements {@link AutoCloseable#close()} by calling {@link #dispose() }. + *

+ * If writing a NativeObject subclass you almost certainly want to override + * dispose() to customize behaviour unless you have a very specific reason + * that try-with-resources should work differently. + */ + @Override + public void close() { + dispose(); + } + + /** + * Dispose this object, and potentially clear (free, unref, etc.) the + * underlying native object if this object owns the reference. + *

+ * After calling this method this object should not be used. + */ + public void dispose() { + LOG.log(LIFECYCLE, "Disposing object " + getClass().getName() + " = " + handle); + handle.dispose(); + } + + @Override + public boolean equals(Object o) { + return o instanceof NativeObject && ((NativeObject) o).ptr.equals(ptr); + } + + protected GPointer getPointer() { + GPointer ptr = handle.ptrRef.get(); + if (ptr == null) { + throw new IllegalStateException("Native object has been disposed"); + } + return ptr; + } + + protected Pointer getRawPointer() { + GPointer ptr = handle.ptrRef.get(); + if (ptr == null) { + throw new IllegalStateException("Native object has been disposed"); + } + return ptr.getPointer(); + } + + @Override + public int hashCode() { + return ptr.hashCode(); + } + + /** + * Invalidate this object without clearing (free, unref, etc.) the + * underlying native object. + *

+ * After calling this method this object should not be used. + */ + public void invalidate() { + LOG.log(LIFECYCLE, () -> "Invalidating object " + this + " = " + getRawPointer()); + handle.invalidate(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + getRawPointer() + ")"; + } + + static T objectFor(GPointer gptr, Class cls, int refAdjust, boolean ownsHandle) { + + // Ignore null pointers + if (gptr == null) { + return null; + } + + NativeObject obj = NativeObject.instanceFor(gptr.getPointer()); + + if (obj != null && cls.isInstance(obj)) { + if (ownsHandle && !obj.handle.ownsReference()) { + obj.handle.ownsReference.set(true); + } else if (refAdjust < 0) { + try { + // Lose the extra ref added by gstreamer + ((RefCountedObject.Handle) obj.handle).unref(); + } catch (ClassCastException ex) { + // A none ref-counted object should not get here! + LOG.log(LIFECYCLE, "None ref-counted object returned again from caller owns return.", ex); + } + } + return cls.cast(obj); + } + + final GType gtype = gptr instanceof GTypedPtr ? ((GTypedPtr) gptr).getGType() : null; + // + // For a GObject, MiniObject, ..., use the GType field to find the most + // exact class match + // + if (gtype != null) { + TypeRegistration reg = GstTypes.registrationFor(gtype); + if (reg != null) { + return cls.cast(reg.factory.apply( + new Initializer(gptr, refAdjust > 0, ownsHandle))); + } + } + + LOG.log(Level.FINE, () -> String.format("Unregistered type requested : %s", cls.getSimpleName())); + + try { + Constructor constructor = cls.getDeclaredConstructor(Initializer.class); + constructor.setAccessible(true); + T retVal = constructor.newInstance(new Initializer(gptr, refAdjust > 0, ownsHandle)); + //retVal.initNativeHandle(ptr, refAdjust > 0, ownsHandle); + return retVal; + } catch (SecurityException ex) { + throw new RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } catch (InstantiationException ex) { + throw new RuntimeException(ex); + } catch (NoSuchMethodException ex) { + throw new RuntimeException(ex); + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + } + + static NativeObject instanceFor(Pointer ptr) { + WeakReference ref = INSTANCES.get(ptr); + + // + // If the reference was there, but the object it pointed to had been collected, remove it from the map + // + if (ref != null && ref.get() == null) { + INSTANCES.remove(ptr); + } + return ref != null ? ref.get() : null; + } + + /** + * A class for propagating low level pointer arguments up the constructor + * chain. + * + * @see Natives#initializer(com.sun.jna.Pointer, boolean, boolean) + */ + public static final class Initializer { + + public final GPointer ptr; + public final boolean needRef, ownsHandle; + + Initializer(GPointer ptr, boolean needRef, boolean ownsHandle) { + this.ptr = ptr; + this.needRef = needRef; + this.ownsHandle = ownsHandle; + } + + } + + private static final class NativeRef extends WeakReference { + + private static final boolean REAP_ON_EDT = Boolean.getBoolean("glib.reapOnEDT"); + private static final ReferenceQueue QUEUE = new ReferenceQueue<>(); + private static final ExecutorService REAPER + = Executors.newSingleThreadExecutor((r) -> { + Thread t = new Thread(r, "NativeObject Reaper"); + t.setDaemon(true); + return t; + }); + + static { + REAPER.submit(() -> { + while (true) { + try { + NativeRef ref = (NativeRef) QUEUE.remove(); + LOG.log(LIFECYCLE, () -> "Disposing of " + ref.type + " : " + ref.handle.ptrRef.get()); + if (REAP_ON_EDT) { + Gst.invokeLater(ref.handle::dispose); + } else { + ref.handle.dispose(); + } + } catch (Throwable t) { + LOG.log(Level.WARNING, "Reaper thread exception", t); + } + } + }); + } + + private final Handle handle; + private final String type; + + private NativeRef(NativeObject obj, Handle handle) { + super(obj, QUEUE); + this.type = obj.getClass().getSimpleName(); + this.handle = handle; + } + + } + + /** + * A class for managing the underlying native pointer. + */ + protected static abstract class Handle { + + private final AtomicReference ptrRef; + private final AtomicBoolean ownsReference; + + /** + * Construct a Handle for the supplied native reference. + * + * @param ptr native reference + * @param ownsReference whether the Handle owns the native reference and + * should dispose it when itself disposed. + */ + public Handle(GPointer ptr, boolean ownsReference) { + this.ptrRef = new AtomicReference<>(ptr); + this.ownsReference = new AtomicBoolean(ownsReference); + } + + /** + * Disown the native reference. After calling this method, + * {@link #ownsReference()} will return {@code false}. + */ + public void disown() { + ownsReference.set(false); + } + + /** + * Invalidate the handle. After calling this method, {@link #getPointer() + * } will return {@code null}, {@link #ownsReference() } will return + * {@code false}, and any NativeObject weak reference cached for this + * pointer will be removed. Unlike calling {@link #dispose() } the + * native handle will not be disposed - {@link #disposeNativeHandle(org.freedesktop.gstreamer.lowlevel.GPointer) + * } will not be called. + */ + public void invalidate() { + GPointer ptr = ptrRef.getAndSet(null); + ownsReference.set(false); + if (ptr != null) { + INSTANCES.remove(ptr.getPointer()); + } + } + + /** + * Dispose the handle, and dispose the native reference if owned by this + * handle. After calling this method, {@link #getPointer() + * } will return {@code null}, {@link #ownsReference() } will return + * {@code false}, and any NativeObject weak reference cached for this + * pointer will be removed. + */ + public void dispose() { + GPointer ptr = ptrRef.getAndSet(null); + if (ptr != null) { + INSTANCES.remove(ptr.getPointer()); + if (ownsReference.compareAndSet(true, false)) { + disposeNativeHandle(ptr); + } + } + } + + /** + * Control whether a WeakReference to the NativeObject wrapping this + * Handle should be created and cached. This means that the same + * NativeObject instance will be returned for identical native pointers, + * and that the Handle will be disposed automatically when the + * NativeObject is garbage collected. + *

+ * The default implementation always returns {@code true}. Subclasses + * may override this behaviour if required. + * + * @return true if the NativeObject should be cached and automatically + * disposed + */ + public boolean isCacheable() { + return true; + } + + /** + * Subclasses should override this method to dispose of the native + * reference (free, unref, etc.). The pointer supplied should be used - + * {@link #getPointer()} will return {@code null} by the time this + * method is called. + * + * @param ptr native reference + */ + protected abstract void disposeNativeHandle(GPointer ptr); + + /** + * Get the native pointer, or null. Subclasses may override to return a + * GPointer subclass. + * + * @return native pointer or null + */ + protected GPointer getPointer() { + return ptrRef.get(); + } + + /** + * Test whether this Handle owns the underlying native reference - + * should dispose the native reference on {@link #dispose() }. + * + * @return true if this Handle owns the reference. + */ + protected boolean ownsReference() { + return ownsReference.get(); + } + } + + /** + * Registration for creating native object subclasses for specific GTypes. + * + * @see Natives#registration(java.lang.Class, java.lang.String, + * java.util.function.Function) + * @param type + */ + public static class TypeRegistration { + + private final Class javaType; + private final String gTypeName; + private final Function factory; + + TypeRegistration(Class javaType, String gTypeName, Function factory) { + this.javaType = javaType; + this.gTypeName = gTypeName; + this.factory = factory; + } + + public Class getJavaType() { + return javaType; + } + + public String getGTypeName() { + return gTypeName; + } + + public Function getFactory() { + return factory; + } + + } + + /** + * Register implementations of this interface via the {@link ServiceLoader} + * mechanism to provide new native object registrations externally. + */ + public static interface TypeProvider { + + /** + * A {@link Stream} of {@link TypeRegistration} to register. + * + * @return stream of type registrations + */ + public Stream> types(); + + } +} diff --git a/src/org/freedesktop/gstreamer/glib/Natives.java b/src/org/freedesktop/gstreamer/glib/Natives.java new file mode 100644 index 00000000..9d5133cb --- /dev/null +++ b/src/org/freedesktop/gstreamer/glib/Natives.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.glib; + +import com.sun.jna.Pointer; +import java.util.ServiceLoader; +import java.util.function.Function; +import org.freedesktop.gstreamer.MiniObject; +import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.lowlevel.GObjectPtr; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GstMiniObjectPtr; +import org.freedesktop.gstreamer.lowlevel.GstStructurePtr; + +/** + * Here be Dragons! + *

+ * This class provides utility functions for working with the underlying native + * bindings, creating {@link NativeObject} from pointers and extracting native + * pointers from NativeObjects. It should normally only be necessary to make use + * of these methods if extending the bindings externally or interacting with + * other native code. + */ +public final class Natives { + + private Natives() { + } + + /** + * Create a {@link NativeObject.Initializer} for the provided Pointer. + *

+ * This initializer will own the handle. + *

+ * This initializer will not request a ref increase (only relevant if used + * with instance of {@link RefCountedObject}) + * + * @param ptr native pointer + * @return initializer + */ + public static final NativeObject.Initializer initializer(Pointer ptr) { + NativeObject.Initializer initializer = initializer(ptr, false, true); + return initializer; + } + + /** + * Create a {@link NativeObject.Initializer} for the provided Pointer. + *

+ * This initializer will own the handle. + * + * @param ptr native pointer + * @param needRef whether to request a ref increase (only relevant if used + * with instance of {@link RefCountedObject}) + * @return initializer + */ + public static final NativeObject.Initializer initializer(Pointer ptr, boolean needRef) { + NativeObject.Initializer initializer = initializer(ptr, needRef, true); + return initializer; + } + + /** + * Create a {@link NativeObject.Initializer} for the provided Pointer. + * + * @param ptr native pointer + * @param needRef whether to request a ref increase (only relevant if used + * with instance of {@link RefCountedObject}) + * @param ownsHandle whether the NativeObject will own the handle, and + * should dispose of the native resource when GC'd or explicitly disposed. + * @return initializer + */ + public static final NativeObject.Initializer initializer(Pointer ptr, boolean needRef, boolean ownsHandle) { + if (ptr == null) { + throw new IllegalArgumentException("Invalid native pointer"); + } + return new NativeObject.Initializer(new GPointer(ptr), needRef, ownsHandle); + } + + + /** + * Get a {@link NativeObject} instance of the requested type for the + * provided Pointer. Will return a cached instance if one already exists. + * + * @param NativeObject type to return + * @param ptr native Pointer + * @param cls Class of type T + * @param needRef whether to request a ref increase (only relevant if T is + * subclass of {@link RefCountedObject}) + * @param ownsHandle whether the NativeObject will own the handle, and + * should dispose of the native resource when GC'd or explicitly disposed. + * @return native object of type T + */ + public static T objectFor(Pointer ptr, Class cls, boolean needRef, boolean ownsHandle) { + return objectFor(ptr, cls, needRef ? 1 : 0, ownsHandle); + } + + /** + * Get a {@link NativeObject} instance of the requested type for the + * provided Pointer. Will return a cached instance if one already exists. + * + * @param NativeObject type to return + * @param ptr native Pointer + * @param cls Class of type T + * @param needRef whether to request a ref increase (only relevant if T is + * subclass of {@link RefCountedObject}) + * @param ownsHandle whether the NativeObject will own the handle, and + * should dispose of the native resource when GC'd or explicitly disposed. + * @return native object of type T + */ + public static T objectFor(GPointer ptr, Class cls, boolean needRef, boolean ownsHandle) { + return NativeObject.objectFor(ptr, cls, needRef ? 1 : 0, ownsHandle); + } + + /** + * Get a {@link NativeObject} instance of the requested type for the + * provided Pointer, for use with native functions returning + * {@code Transfer Full} or {@code Transfer Floating} results. + *

+ * This method will return a cached instance if one already exists. If the + * cached instance is a {@link RefCountedObject} this method will release a + * reference. + * + * @param NativeObject type to return + * @param ptr native Pointer + * @param cls Class of type T + * @return native object of type T + */ + public static T callerOwnsReturn(Pointer ptr, Class cls) { + return objectFor(ptr, cls, -1, true); + } + + /** + * Get a {@link NativeObject} instance of the requested type for the + * provided Pointer, for use with native functions returning + * {@code Transfer Full} or {@code Transfer Floating} results. + *

+ * This method will return a cached instance if one already exists. If the + * cached instance is a {@link RefCountedObject} this method will release a + * reference. + * + * @param NativeObject type to return + * @param ptr native Pointer + * @param cls Class of type T + * @return native object of type T + */ + public static T callerOwnsReturn(GPointer ptr, Class cls) { + return NativeObject.objectFor(ptr, cls, -1, true); + } + + private static T objectFor(Pointer ptr, Class cls, int refAdjust, boolean ownsHandle) { + final GPointer gptr = GObject.class.isAssignableFrom(cls) ? new GObjectPtr(ptr) + : MiniObject.class.isAssignableFrom(cls) ? new GstMiniObjectPtr(ptr) + : Structure.class.isAssignableFrom(cls) ? new GstStructurePtr(ptr) + : new GPointer(ptr); + return NativeObject.objectFor(gptr, cls, refAdjust, ownsHandle); + } + + /** + * Get the underlying raw native Pointer for a {@link NativeObject}. + * + * @param obj native object + * @return native pointer + * @throws IllegalStateException if the native reference has been + * invalidated or disposed + */ + public static Pointer getRawPointer(NativeObject obj) { + return obj.getRawPointer(); + } + + /** + * Get the underlying native typed GPointer for a {@link NativeObject}. + * + * @param obj native object + * @return native typed pointer + * @throws IllegalStateException if the native reference has been + * invalidated or disposed + */ + public static GPointer getPointer(NativeObject obj) { + return obj.getPointer(); + } + + /** + * Return whether underlying native pointer is owned by this object. + * + * @param obj native object which may hold a reference to native pointer + * @return whether underlying native pointer is owned by this object + */ + public static boolean ownsReference(NativeObject obj) { + return obj.handle.ownsReference(); + } + + /** + * Returns whether this object is valid or not. + * + * @param obj native object + * @return whether this object is valid or not + */ + public static boolean validReference(NativeObject obj) { + return obj.handle.getPointer() != null; + } + + /** + * Increase the reference count of a {@link RefCountedObject} + * + * @param type of object + * @param obj object to increase reference count on + * @return object + */ + public static T ref(T obj) { + ((RefCountedObject.Handle) obj.handle).ref(); + return obj; + } + + /** + * Decrease the reference count of a {@link RefCountedObject} + * + * @param type of object + * @param obj object to decrease reference count on + * @return object + */ + public static T unref(T obj) { + ((RefCountedObject.Handle) obj.handle).unref(); + return obj; + } + + /** + * Create a {@link NativeObject.TypeRegistration} for linking + * {@link NativeObject} subclasses to GTypes. + *

+ * Be careful to respect the link between Java type hierarchy of registered + * classes and the underlying GType hierarchy. eg. if the GType is a + * subclass of GObject then the Java type must extend from {@link GObject} + *

+ * The factory function should normally be a constructor reference. + *

+ * Registrations can be provided externally using + * {@link NativeObject.TypeProvider} instances registered for use with + * {@link ServiceLoader} + * + * @param Java type + * @param javaType Java type class + * @param gTypeName name of the GType + * @param factory a factory function to return an instance of T given a + * {@link NativeObject.Initializer}. Normally a constructor reference - + * {@code T::new} + * @return registration + */ + public static NativeObject.TypeRegistration + registration(Class javaType, String gTypeName, Function factory) { + return new NativeObject.TypeRegistration<>(javaType, gTypeName, factory); + } + +} diff --git a/src/org/freedesktop/gstreamer/glib/RefCountedObject.java b/src/org/freedesktop/gstreamer/glib/RefCountedObject.java new file mode 100644 index 00000000..021ff738 --- /dev/null +++ b/src/org/freedesktop/gstreamer/glib/RefCountedObject.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2007, 2008 Wayne Meissner + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.glib; + +import org.freedesktop.gstreamer.lowlevel.GPointer; + +/** + * A {@link NativeObject} that has an associated reference count + */ +public abstract class RefCountedObject extends NativeObject { + +// /** +// * Creates a new instance of RefCountedObject +// */ +// protected RefCountedObject(Initializer init) { +// this(new Handle()) +// if (init.ownsHandle && init.needRef) { +// ref(); +// } +// } + + protected RefCountedObject(Handle handle) { + super(handle); + + } + + protected RefCountedObject(Handle handle, boolean needRef) { + super(handle); + if (needRef) { + handle.ref(); + } + } + + + + // overridden in subclasses +// protected abstract void ref(); +// +// protected abstract void unref(); + + protected static abstract class Handle extends NativeObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + protected abstract void ref(); + + protected abstract void unref(); + + } +} diff --git a/src/org/freedesktop/gstreamer/interfaces/ColorBalance.java b/src/org/freedesktop/gstreamer/interfaces/ColorBalance.java index 9743a038..0c3d7bd1 100644 --- a/src/org/freedesktop/gstreamer/interfaces/ColorBalance.java +++ b/src/org/freedesktop/gstreamer/interfaces/ColorBalance.java @@ -16,7 +16,6 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.interfaces; import java.util.List; @@ -25,95 +24,100 @@ import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; import com.sun.jna.Pointer; +import java.util.ArrayList; +import org.freedesktop.gstreamer.lowlevel.GlibAPI; import static org.freedesktop.gstreamer.lowlevel.GstColorBalanceAPI.GSTCOLORBALANCE_API; public class ColorBalance extends GstInterface { - /** - * Wraps the {@link Element} in a ColorBalance interface - * - * @param element - * the element to use as a ColorBalance - * @return a ColorBalance for the element - */ - public static final ColorBalance wrap(Element element) { - return new ColorBalance(element); - } - /** - * Creates a new ColorBalance instance - * - * @param element - * the element that implements the ColorBalance interface - */ - private ColorBalance(Element element) { - super(element, GSTCOLORBALANCE_API.gst_color_balance_get_type()); - } + /** + * Wraps the {@link Element} in a ColorBalance interface + * + * @param element the element to use as a ColorBalance + * @return a ColorBalance for the element + */ + public static final ColorBalance wrap(Element element) { + return new ColorBalance(element); + } + + /** + * Creates a new ColorBalance instance + * + * @param element the element that implements the ColorBalance interface + */ + private ColorBalance(Element element) { + super(element, GSTCOLORBALANCE_API.gst_color_balance_get_type()); + } - /** - * Retrieves a list of ColorBalanceChannels from the ColorBalance - * - * @return a list of color balance channels available on this device - */ - public List getChannelList() { - return objectList(GSTCOLORBALANCE_API.gst_color_balance_list_channels(this), - new ListElementCreator() { - public ColorBalanceChannel create(Pointer pointer) { - return channelFor(pointer, true); - } - }); - } + /** + * Retrieves a list of ColorBalanceChannels from the ColorBalance + * + * @return a list of color balance channels available on this device + */ + public List getChannelList() { + + GlibAPI.GList glist = GSTCOLORBALANCE_API.gst_color_balance_list_channels(this); + List list = new ArrayList<>(); + GlibAPI.GList next = glist; + while (next != null) { + if (next.data != null) { + list.add(channelFor(next.data, true)); + } + next = next.next(); + } + return list; + } + + /** + * Retrieves a ColorBalanceChannel for the given Pointer + * + * @param pointer + * @param needRef + * @return a ColorBalanceChannel instance + */ + private final ColorBalanceChannel channelFor(Pointer pointer, + boolean needRef) { + return new ColorBalanceChannel(this, pointer, needRef, true); + } - /** - * Retrieves a ColorBalanceChannel for the given Pointer - * - * @param pointer - * @param needRef - * @return a ColorBalanceChannel instance - */ - private final ColorBalanceChannel channelFor(Pointer pointer, - boolean needRef) { - return new ColorBalanceChannel(this, pointer, needRef, true); - } + /** + * Signal emitted when color balance value changed + * + * @see #connect(VALUE_CHANGED) + * @see #disconnect(VALUE_CHANGED) + */ + public static interface VALUE_CHANGED { - /** - * Signal emitted when color balance value changed - * - * @see #connect(VALUE_CHANGED) - * @see #disconnect(VALUE_CHANGED) - */ - public static interface VALUE_CHANGED { - /** - * Called when the color balance channel value changes - */ - public void colorBalanceValueChanged(ColorBalance colorBalance, - ColorBalanceChannel channel, int value); - } + /** + * Called when the color balance channel value changes + */ + public void colorBalanceValueChanged(ColorBalance colorBalance, + ColorBalanceChannel channel, int value); + } - /** - * Add a listener for norm-changed messages. - * - * @param listener - * the listener to be called when the norm changes - */ - public void connect(final VALUE_CHANGED listener) { - element.connect(VALUE_CHANGED.class, listener, new GstCallback() { - @SuppressWarnings("unused") - public boolean callback(Pointer colorBalance, ColorBalanceChannel channel, int value) { - listener.colorBalanceValueChanged(ColorBalance.this, channel, value); - return true; - } - }); - } + /** + * Add a listener for norm-changed messages. + * + * @param listener the listener to be called when the norm changes + */ + public void connect(final VALUE_CHANGED listener) { + element.connect(VALUE_CHANGED.class, listener, new GstCallback() { + @SuppressWarnings("unused") + public boolean callback(Pointer colorBalance, ColorBalanceChannel channel, int value) { + listener.colorBalanceValueChanged(ColorBalance.this, channel, value); + return true; + } + }); + } - /** - * Disconnect the listener for norm-changed messages. - * - * @param listener - * the listener that was registered to receive the message. - */ - public void disconnect(VALUE_CHANGED listener) { - element.disconnect(VALUE_CHANGED.class, listener); - } + /** + * Disconnect the listener for norm-changed messages. + * + * @param listener the listener that was registered to receive the message. + */ + public void disconnect(VALUE_CHANGED listener) { + element.disconnect(VALUE_CHANGED.class, listener); + } } diff --git a/src/org/freedesktop/gstreamer/interfaces/ColorBalanceChannel.java b/src/org/freedesktop/gstreamer/interfaces/ColorBalanceChannel.java index bf8065a5..1bd7c3f4 100644 --- a/src/org/freedesktop/gstreamer/interfaces/ColorBalanceChannel.java +++ b/src/org/freedesktop/gstreamer/interfaces/ColorBalanceChannel.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Tamas Korodi * @@ -21,10 +22,11 @@ import static org.freedesktop.gstreamer.lowlevel.GstColorBalanceAPI.GSTCOLORBALANCE_API; -import org.freedesktop.gstreamer.GObject; +import org.freedesktop.gstreamer.glib.GObject; import org.freedesktop.gstreamer.lowlevel.GstColorBalanceAPI; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.Natives; public class ColorBalanceChannel extends GObject { public static final String GTYPE_NAME = "GstColorBalanceChannel"; @@ -37,14 +39,14 @@ public class ColorBalanceChannel extends GObject { * * @param init */ - public ColorBalanceChannel(Initializer init) { + ColorBalanceChannel(Initializer init) { super(init); throw new IllegalArgumentException("Cannot instantiate"); } ColorBalanceChannel(ColorBalance colorBalance, Pointer ptr, boolean needRef, boolean ownsHandle) { - super(initializer(ptr, needRef, ownsHandle)); + super(Natives.initializer(ptr, needRef, ownsHandle)); struct = new GstColorBalanceAPI.ColorBalanceChannelStruct(ptr); this.colorBalance = colorBalance; } diff --git a/src/org/freedesktop/gstreamer/interfaces/GstInterface.java b/src/org/freedesktop/gstreamer/interfaces/GstInterface.java index d0299ea3..5a0c6ad5 100644 --- a/src/org/freedesktop/gstreamer/interfaces/GstInterface.java +++ b/src/org/freedesktop/gstreamer/interfaces/GstInterface.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2008 Wayne Meissner * @@ -20,52 +21,23 @@ package org.freedesktop.gstreamer.interfaces; import org.freedesktop.gstreamer.lowlevel.GType; -import org.freedesktop.gstreamer.lowlevel.NativeValue; -import java.util.ArrayList; -import java.util.List; import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.GObject; -import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; -import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.GObject; /** * Base type for all gstreamer interface proxies */ -public class GstInterface extends NativeValue { - protected final Pointer handle; +class GstInterface implements GObject.GInterface { protected final Element element; protected GstInterface(Element element, GType type) { this.element = element; - handle = element.getNativeAddress(); } - protected Object nativeValue() { - return handle; - } - public Element getElement() { - return element; - } - - protected interface ListElementCreator { - E create(Pointer pointer); + + @Override + public Element getGObject() { + return element; } - /** - * Build a {@link java.util.List} of {@link Object} from the native GList. - * @param glist The native list to get the objects from. - * @param creator The proxy class to wrap the list elements in. - * @return The converted list. - */ - protected List objectList(GList glist, ListElementCreator creator) { - List list = new ArrayList(); - GList next = glist; - while (next != null) { - if (next.data != null) { - list.add(creator.create(next.data)); - } - next = next.next(); - } - return list; - } } diff --git a/src/org/freedesktop/gstreamer/interfaces/Navigation.java b/src/org/freedesktop/gstreamer/interfaces/Navigation.java index 1dfcef20..9d7e98c1 100644 --- a/src/org/freedesktop/gstreamer/interfaces/Navigation.java +++ b/src/org/freedesktop/gstreamer/interfaces/Navigation.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Tamas Korodi * diff --git a/src/org/freedesktop/gstreamer/interfaces/Property.java b/src/org/freedesktop/gstreamer/interfaces/Property.java deleted file mode 100644 index 7cd2ce8e..00000000 --- a/src/org/freedesktop/gstreamer/interfaces/Property.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2008 Andres Colubri - * Copyright (c) 2008 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.interfaces; - -import org.freedesktop.gstreamer.GObject; -import org.freedesktop.gstreamer.lowlevel.GObjectAPI; - -import com.sun.jna.Pointer; - -/** - * - */ -public class Property extends GObject { - private final GObjectAPI.GParamSpec spec; - - /** - * For internal gstreamer-java use only - * - * @param init initialization data - */ - public Property(Initializer init) { - super(init); - throw new IllegalArgumentException("Cannot instantiate this class"); - } - - Property(Pointer ptr, boolean needRef, boolean ownsHandle) { - super(initializer(ptr, needRef, ownsHandle)); - spec = new GObjectAPI.GParamSpec(ptr); - } - - public String getName() { - return spec.g_name; - } - - GObjectAPI.GParamSpec getSpec() { - return spec; - } -} diff --git a/src/org/freedesktop/gstreamer/interfaces/PropertyProbe.java b/src/org/freedesktop/gstreamer/interfaces/PropertyProbe.java deleted file mode 100644 index 71a72d5e..00000000 --- a/src/org/freedesktop/gstreamer/interfaces/PropertyProbe.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2009 Levente Farkas - * Copyright (c) 2008 Andres Colubri - * Copyright (c) 2008 Wayne Meissner - * Copyright (C) 2003 Ronald Bultje - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.interfaces; - -import java.util.ArrayList; -import java.util.List; - -import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; -import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValueArray; - -import com.sun.jna.Pointer; - -import static org.freedesktop.gstreamer.lowlevel.GstPropertyProbeAPI.GSTPROPERTYPROBE_API; - -/** - * Interface for elements that provide mixer operations - */ -public class PropertyProbe extends GstInterface { - /** - * Wraps the {@link Element} in a PropertyProbe interface - * - * @param element the element to use as a PropertyProbe - * @return a PropertyProbe for the element - */ - public static final PropertyProbe wrap(Element element) { - return new PropertyProbe(element); - } - - /** - * Creates a new PropertyProbe instance - * - * @param element the element that implements the PropertyProbe interface - */ - private PropertyProbe(Element element) { - super(element, GSTPROPERTYPROBE_API.gst_property_probe_get_type()); - } - - /** - * Gets a list of available properties for this property probe/element. - * - * @return a list of Property instances - */ - public List getProperties() { - return propertiesList(GSTPROPERTYPROBE_API.gst_property_probe_get_properties(this), true, true); - } - - public Property getProperty(String name) { - Pointer ptr = GSTPROPERTYPROBE_API.gst_property_probe_get_property(this, name); - if (ptr == null) { - return null; - } - Property p = new Property(ptr, false, false); - return p; - } - - public void probeProperty(Property property) { - if (property != null) - { - GSTPROPERTYPROBE_API.gst_property_probe_probe_property(this, property.getSpec()); - } - } - - public void probeProperty(String name) { - GSTPROPERTYPROBE_API.gst_property_probe_probe_property_name(this, name); - } - - public boolean needsProbe(Property property) { - if (property != null) - { - return GSTPROPERTYPROBE_API.gst_property_probe_needs_probe(this, property.getSpec()); - } - else return false; - } - - public boolean needsProbe(String name) { - return GSTPROPERTYPROBE_API.gst_property_probe_needs_probe_name(this, name); - } - - public Object[] getValues(Property property) { - if (property != null) - { - return valuesArray(GSTPROPERTYPROBE_API.gst_property_probe_get_values(this, property.getSpec())); - } - else return null; - } - - public Object[] getValues(String name) { - return valuesArray(GSTPROPERTYPROBE_API.gst_property_probe_get_values_name(this, name)); - } - - public Object[] probeAndGetValues(Property property) { - if (property != null) - { - return valuesArray(GSTPROPERTYPROBE_API.gst_property_probe_probe_and_get_values(this, property.getSpec())); - } - else return null; - } - - public Object[] probeAndGetValues(String name) { - return valuesArray(GSTPROPERTYPROBE_API.gst_property_probe_probe_and_get_values_name(this, name)); - } - - /** - * Build a {@link java.util.List} of {@link Object} from the native GList. - * @param glist The native list to get the objects from. - * @param objectClass The proxy class to wrap the list elements in. - * @return The converted list. - */ - private List propertiesList(GList glist, boolean needRef, boolean ownsHandle) { - List list = new ArrayList(); - GList next = glist; - while (next != null) { - if (next.data != null) { - list.add(new Property(next.data, needRef, ownsHandle)); - } - next = next.next(); - } - return list; - } - - private Object[] valuesArray(Pointer ptr) { - if (ptr == null) return null; - GValueArray valueArray = null; - try - { - valueArray = new GValueArray(ptr); - } - catch (NullPointerException ex) - { - // This probably means that there are not values available for the - // specified property. - return null; - } - Object[] objectArray = new Object[valueArray.n_values]; - for (int i = 0; i < valueArray.n_values; i++) - { - objectArray[i] = valueArray.getValue(i); - } - return objectArray; - } -} diff --git a/src/org/freedesktop/gstreamer/interfaces/VideoOrientation.java b/src/org/freedesktop/gstreamer/interfaces/VideoOrientation.java index 78b602f9..ddfe6032 100644 --- a/src/org/freedesktop/gstreamer/interfaces/VideoOrientation.java +++ b/src/org/freedesktop/gstreamer/interfaces/VideoOrientation.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil c Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Tamas Korodi * @@ -45,21 +46,21 @@ private VideoOrientation(Element element) { super(element, GSTVIDEOORIENTATION_API.gst_video_orientation_get_type()); } - public boolean getHflip(boolean flip) { - return GSTVIDEOORIENTATION_API.gst_video_orientation_get_hflip(this, flip); - } - - public boolean getVflip(boolean flip) { - return GSTVIDEOORIENTATION_API.gst_video_orientation_get_vflip(this, flip); - } - - public boolean getHcenter(int center) { - return GSTVIDEOORIENTATION_API.gst_video_orientation_get_hcenter(this, center); - } - - public boolean getVcenter(int center) { - return GSTVIDEOORIENTATION_API.gst_video_orientation_get_vcenter(this, center); - } +// public boolean getHflip(boolean flip) { +// return GSTVIDEOORIENTATION_API.gst_video_orientation_get_hflip(this, flip); +// } +// +// public boolean getVflip(boolean flip) { +// return GSTVIDEOORIENTATION_API.gst_video_orientation_get_vflip(this, flip); +// } +// +// public boolean getHcenter(int center) { +// return GSTVIDEOORIENTATION_API.gst_video_orientation_get_hcenter(this, center); +// } +// +// public boolean getVcenter(int center) { +// return GSTVIDEOORIENTATION_API.gst_video_orientation_get_vcenter(this, center); +// } public boolean setHflip(boolean flip) { return GSTVIDEOORIENTATION_API.gst_video_orientation_set_hflip(this, flip); diff --git a/src/org/freedesktop/gstreamer/interfaces/VideoOverlay.java b/src/org/freedesktop/gstreamer/interfaces/VideoOverlay.java index 9d44a810..1731b497 100644 --- a/src/org/freedesktop/gstreamer/interfaces/VideoOverlay.java +++ b/src/org/freedesktop/gstreamer/interfaces/VideoOverlay.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2009 Levente Farkas * Copyright (C) 2009 Tamas Korodi * Copyright (C) 2008 Wayne Meissner @@ -24,16 +25,15 @@ import static org.freedesktop.gstreamer.lowlevel.GstVideoOverlayAPI.GSTVIDEOOVERLAY_API; +import com.sun.jna.Pointer; import org.freedesktop.gstreamer.BusSyncReply; import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.message.Message; -import com.sun.jna.Native; -import com.sun.jna.NativeLong; -import com.sun.jna.Platform; /** - * Interface for elements providing tuner operations + * Interface for setting/getting a window system resource on elements + * supporting it to configure a window into which to render a video. */ public class VideoOverlay extends GstInterface { /** @@ -48,21 +48,21 @@ public static VideoOverlay wrap(Element element) { /** * Convenience function to check if the given message is a "prepare-window-handle". - * This usefull for setup native window handles with {@link BusSyncReply}. + * This useful for setup native window handles with {@link BusSyncReply}. * * @param message * @return */ - public static boolean isVideoOverlayPrepareWindowHandleMessage(Message message) { + public static boolean isPrepareWindowHandleMessage(Message message) { return GSTVIDEOOVERLAY_API.gst_is_video_overlay_prepare_window_handle_message(message); } /** - * Creates a new XOverlay instance + * Creates a new VideoOverlay instance * - * @param element the element that implements the tuner interface + * @param element the element that implements the overlay interface */ - protected VideoOverlay(Element element) { + private VideoOverlay(Element element) { super(element, GSTVIDEOOVERLAY_API.gst_video_overlay_get_type()); } @@ -72,25 +72,7 @@ protected VideoOverlay(Element element) { * @param handle A native handle to use to display video. */ public void setWindowHandle(long handle) { - GSTVIDEOOVERLAY_API.gst_video_overlay_set_window_handle(this, new NativeLong(handle)); - } - /** - * Sets the native window for the {@link Element} to use to display video. - * - * @param window A native window to use to display video, or null to - * stop using the previously set window. - */ - public void setWindowHandle(java.awt.Component window) { - if (window == null) { - setWindowHandle(0); - return; - } - if (window.isLightweight()) - throw new IllegalArgumentException("Component must be a native window"); - if (Platform.isWindows()) - GSTVIDEOOVERLAY_API.gst_video_overlay_set_window_handle(this, Native.getComponentPointer(window)); - else - setWindowHandle(Native.getComponentID(window)); + GSTVIDEOOVERLAY_API.gst_video_overlay_set_window_handle(this, new Pointer(handle)); } /** @@ -116,7 +98,7 @@ public void handleEvent(boolean handle_events) { * Configure a subregion as a video target within the window set by * {@link #setWindowHandle(long)}. If this is not used or not supported * the video will fill the area of the window set as the overlay to 100%. - * By specifying the rectangle, the video can be overlayed to a specific + * By specifying the rectangle, the video can be overlaid to a specific * region of that window only. After setting the new rectangle one should * call {@link #expose()} to force a redraw. To unset the region pass -1 * for the width and height parameters. @@ -132,28 +114,5 @@ public void handleEvent(boolean handle_events) { public boolean setRenderRectangle(int x, int y, int width, int height) { return GSTVIDEOOVERLAY_API.gst_video_overlay_set_render_rectangle(this, x, y, width, height); } - - /** - * Configure a subregion as a video target within the window set by - * {@link #setWindowHandle(long)}. If this is not used or not supported - * the video will fill the area of the window set as the overlay to 100%. - * By specifying the rectangle, the video can be overlayed to a specific - * region of that window only. After setting the new rectangle one should - * call {@link #expose()} to force a redraw. To unset the region pass -1 - * for the width and height parameters. - * - * This method is needed for non fullscreen video overlay in UI toolkits - * that do not support subwindows. - * - * @param overlay - * @param x - * @param y - * @param width - * @param height - */ - @Deprecated - public boolean setRenderRectangle(VideoOverlay overlay, int x, int y, int width, int height) { - return GSTVIDEOOVERLAY_API.gst_video_overlay_set_render_rectangle(this, x, y, width, height); - } } diff --git a/src/org/freedesktop/gstreamer/interfaces/XOverlay.java b/src/org/freedesktop/gstreamer/interfaces/XOverlay.java deleted file mode 100644 index da45b944..00000000 --- a/src/org/freedesktop/gstreamer/interfaces/XOverlay.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2009 Levente Farkas - * Copyright (C) 2009 Tamas Korodi - * Copyright (C) 2008 Wayne Meissner - * Copyright (C) 2003 Ronald Bultje - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.interfaces; - -import org.freedesktop.gstreamer.Element; - -import com.sun.jna.Native; -import com.sun.jna.NativeLong; -import com.sun.jna.Platform; - -import static org.freedesktop.gstreamer.lowlevel.GstXOverlayAPI.GSTXOVERLAY_API; - -/** - * Interface for elements providing tuner operations - */ -public class XOverlay extends GstInterface { - /** - * Wraps the {@link Element} in a XOverlay interface - * - * @param element the element to use as a XOverlay - * @return a XOverlay for the element - */ - public static XOverlay wrap(Element element) { - return new XOverlay(element); - } - - /** - * Creates a new XOverlay instance - * - * @param element the element that implements the tuner interface - */ - protected XOverlay(Element element) { - super(element, GSTXOVERLAY_API.gst_x_overlay_get_type()); - } - - /** - * Sets the native window for the {@link Element} to use to display video. - * - * @param handle A native handle to use to display video. - */ - public void setWindowHandle(long handle) { - GSTXOVERLAY_API.gst_x_overlay_set_window_handle(this, new NativeLong(handle)); - } - /** - * Sets the native window for the {@link Element} to use to display video. - * - * @param window A native window to use to display video, or null to - * stop using the previously set window. - */ - public void setWindowHandle(java.awt.Component window) { - if (window == null) { - setWindowHandle(0); - return; - } - if (window.isLightweight()) - throw new IllegalArgumentException("Component must be a native window"); - if (Platform.isWindows()) - GSTXOVERLAY_API.gst_x_overlay_set_window_handle(this, Native.getComponentPointer(window)); - else - setWindowHandle(Native.getComponentID(window)); - } - - /** - * Tell an overlay that it has been exposed. This will redraw the current frame - * in the drawable even if the pipeline is PAUSED. - */ - public void expose() { - GSTXOVERLAY_API.gst_x_overlay_expose(this); - } - - /** - * Tell an overlay that it should handle events from the window system. - * These events are forwared upstream as navigation events. In some window - * system, events are not propagated in the window hierarchy if a client is - * listening for them. This method allows you to disable events handling - * completely from the XOverlay. - */ - public void handleEvent(boolean handle_events) { - GSTXOVERLAY_API.gst_x_overlay_handle_events(this, handle_events); - } - - /** - * Configure a subregion as a video target within the window set by - * {@link #setWindowHandle(long)}. If this is not used or not supported - * the video will fill the area of the window set as the overlay to 100%. - * By specifying the rectangle, the video can be overlayed to a specific - * region of that window only. After setting the new rectangle one should - * call {@link #expose()} to force a redraw. To unset the region pass -1 - * for the width and height parameters. - * - * This method is needed for non fullscreen video overlay in UI toolkits - * that do not support subwindows. - * - * @param overlay - * @param x - * @param y - * @param width - * @param height - */ - public boolean setRenderRectangle(XOverlay overlay, int x, int y, int width, int height) { - return GSTXOVERLAY_API.gst_x_overlay_set_render_rectangle(this, x, y, width, height); - } -} diff --git a/src/org/freedesktop/gstreamer/io/StreamLock.java b/src/org/freedesktop/gstreamer/io/StreamLock.java deleted file mode 100644 index f3f38560..00000000 --- a/src/org/freedesktop/gstreamer/io/StreamLock.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2012 chris@crazyfool.org - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License version 3 only, as published by the Free Software Foundation. - * - * This code 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 version 3 - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License version 3 along with this work. If not, see - * . - */ - -package org.freedesktop.gstreamer.io; - -public class StreamLock { - protected boolean done; - - public StreamLock() { - done = false; - } - - public boolean isDone() { - return done; - } - - public void setDone() { - synchronized (this) { - done = true; - this.notifyAll(); - } - } -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/AppAPI.java b/src/org/freedesktop/gstreamer/lowlevel/AppAPI.java index ad2052da..f3e7f10b 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/AppAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/AppAPI.java @@ -48,8 +48,8 @@ public interface AppAPI extends com.sun.jna.Library { void gst_app_src_set_size(AppSrc appsrc, long size); long gst_app_src_get_size(AppSrc appsrc); - void gst_app_src_set_stream_type(AppSrc appsrc, AppSrc.Type type); - AppSrc.Type gst_app_src_get_stream_type(AppSrc appsrc); + void gst_app_src_set_stream_type(AppSrc appsrc, AppSrc.StreamType type); + AppSrc.StreamType gst_app_src_get_stream_type(AppSrc appsrc); void gst_app_src_set_max_bytes(AppSrc appsrc, long max); long gst_app_src_get_max_bytes(AppSrc appsrc); diff --git a/src/org/freedesktop/gstreamer/lowlevel/BaseSinkAPI.java b/src/org/freedesktop/gstreamer/lowlevel/BaseSinkAPI.java index c24429cb..edf31569 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/BaseSinkAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/BaseSinkAPI.java @@ -22,16 +22,15 @@ import java.util.Arrays; import java.util.List; -import org.freedesktop.gstreamer.ActivateMode; +import org.freedesktop.gstreamer.PadMode; import org.freedesktop.gstreamer.Buffer; import org.freedesktop.gstreamer.Caps; import org.freedesktop.gstreamer.ClockReturn; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Event; +import org.freedesktop.gstreamer.event.Event; import org.freedesktop.gstreamer.FlowReturn; import org.freedesktop.gstreamer.MiniObject; import org.freedesktop.gstreamer.Pad; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.query.Query; import org.freedesktop.gstreamer.elements.BaseSink; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstSegmentStruct; @@ -60,7 +59,7 @@ public static final class GstBaseSinkStruct extends com.sun.jna.Structure { /*< protected >*/ public volatile Pad sinkpad; - public volatile ActivateMode pad_mode; + public volatile PadMode pad_mode; /*< protected >*/ /* with LOCK */ public volatile long offset; @@ -263,17 +262,17 @@ protected List getFieldOrder() { boolean gst_base_sink_is_last_buffer_enabled(BaseSink sink); /* latency */ - boolean gst_base_sink_query_latency(BaseSink sink, boolean live, boolean upstream_live, ClockTime min_latency, ClockTime max_latency); - ClockTime gst_base_sink_get_latency(BaseSink sink); + boolean gst_base_sink_query_latency(BaseSink sink, boolean live, boolean upstream_live, long min_latency, long max_latency); + long gst_base_sink_get_latency(BaseSink sink); /* render delay */ - void gst_base_sink_set_render_delay(BaseSink sink, ClockTime delay); - ClockTime gst_base_sink_get_render_delay(BaseSink sink); + void gst_base_sink_set_render_delay(BaseSink sink, long delay); + long gst_base_sink_get_render_delay(BaseSink sink); /* blocksize */ void gst_base_sink_set_blocksize(BaseSink sink, int blocksize); int gst_base_sink_get_blocksize(BaseSink sink); - ClockReturn gst_base_sink_wait_clock(BaseSink sink, ClockTime time, /* GstClockTimeDiff */ Pointer jitter); - FlowReturn gst_base_sink_wait_eos(BaseSink sink, ClockTime time, /* GstClockTimeDiff */ Pointer jitter); + ClockReturn gst_base_sink_wait_clock(BaseSink sink, long time, /* GstlongDiff */ Pointer jitter); + FlowReturn gst_base_sink_wait_eos(BaseSink sink, long time, /* GstlongDiff */ Pointer jitter); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/BaseSrcAPI.java b/src/org/freedesktop/gstreamer/lowlevel/BaseSrcAPI.java index 8cff21ea..14a3a2af 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/BaseSrcAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/BaseSrcAPI.java @@ -21,8 +21,7 @@ import org.freedesktop.gstreamer.Buffer; import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Event; +import org.freedesktop.gstreamer.event.Event; import org.freedesktop.gstreamer.FlowReturn; import org.freedesktop.gstreamer.Format; import org.freedesktop.gstreamer.Pad; @@ -38,7 +37,7 @@ import com.sun.jna.ptr.LongByReference; import java.util.Arrays; import java.util.List; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.query.Query; /** * GstBaseSrc methods and structures @@ -268,7 +267,7 @@ protected List getFieldOrder() { void gst_base_src_set_format(BaseSrc src, Format format); - boolean gst_base_src_query_latency(BaseSrc src, boolean[] live, ClockTime[] min_latency, ClockTime[] max_latency); + boolean gst_base_src_query_latency(BaseSrc src, boolean[] live, long[] min_latency, long[] max_latency); void gst_base_src_set_blocksize(BaseSrc src, long blocksize); long gst_base_src_get_blocksize(BaseSrc src); diff --git a/src/org/freedesktop/gstreamer/lowlevel/BaseTransformAPI.java b/src/org/freedesktop/gstreamer/lowlevel/BaseTransformAPI.java index d9011ba8..29d1268d 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/BaseTransformAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/BaseTransformAPI.java @@ -21,8 +21,7 @@ import org.freedesktop.gstreamer.Buffer; import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Event; +import org.freedesktop.gstreamer.event.Event; import org.freedesktop.gstreamer.FlowReturn; import org.freedesktop.gstreamer.Pad; import org.freedesktop.gstreamer.PadDirection; @@ -37,7 +36,7 @@ import com.sun.jna.ptr.IntByReference; import java.util.Arrays; import java.util.List; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.query.Query; /** * GstBaseTransform methods and structures @@ -232,7 +231,7 @@ protected List getFieldOrder() { void gst_base_transform_set_in_place(BaseTransform trans, boolean in_place); boolean gst_base_transform_is_in_place(BaseTransform trans); - void gst_base_transform_update_qos(BaseTransform trans, double proportion, long diff, ClockTime timestamp); + void gst_base_transform_update_qos(BaseTransform trans, double proportion, long diff, long timestamp); void gst_base_transform_set_qos_enabled(BaseTransform trans, boolean enabled); boolean gst_base_transform_is_qos_enabled(BaseTransform trans); diff --git a/src/org/freedesktop/gstreamer/lowlevel/EnumMapper.java b/src/org/freedesktop/gstreamer/lowlevel/EnumMapper.java index 7738d984..0cd53666 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/EnumMapper.java +++ b/src/org/freedesktop/gstreamer/lowlevel/EnumMapper.java @@ -22,6 +22,7 @@ import java.util.EnumSet; import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; +import org.freedesktop.gstreamer.glib.NativeEnum; /** * Maps to and from native int and an Enum value. @@ -34,7 +35,7 @@ public static EnumMapper getInstance() { } public int intValue(Enum value) { - return value instanceof IntegerEnum ? ((IntegerEnum) value).intValue() : value.ordinal(); + return value instanceof NativeEnum ? ((NativeEnum) value).intValue() : value.ordinal(); } public > E valueOf(int value, Class enumClass) { // @@ -42,9 +43,9 @@ public > E valueOf(int value, Class enumClass) { // Storing the values in a Map might be faster, but by the time you deal // with locking overhead, its hardly worth it for small enums. // - if (IntegerEnum.class.isAssignableFrom(enumClass)) { + if (NativeEnum.class.isAssignableFrom(enumClass)) { for (E e : EnumSet.allOf(enumClass)) { - if (((IntegerEnum) e).intValue() == value) { + if (((NativeEnum) e).intValue() == value) { return e; } } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GNative.java b/src/org/freedesktop/gstreamer/lowlevel/GNative.java index a92af3c4..ccbb75c8 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GNative.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GNative.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -22,9 +23,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.freedesktop.gstreamer.ClockTime; import com.sun.jna.Library; import com.sun.jna.Native; @@ -35,15 +33,13 @@ * */ public final class GNative { - // gstreamer on win32 names the dll files one of foo.dll, libfoo.dll and libfoo-0.dll - // private static String[] windowsNameFormats = { "%s", "lib%s", "lib%s-0" }; private final static String[] nameFormats; static { String defFormats = "%s"; if (Platform.isWindows()) { - defFormats = "%s|lib%s|lib%s-0"; + defFormats = "%s-0|%s|lib%s-0|lib%s"; } else if (Platform.isMac()) { defFormats = "%s.0|%s"; } @@ -127,25 +123,10 @@ public Object fromNative(Object value, Class javaType) { } }; - private static final Converter clocktimeConverter = new Converter() { - public Class nativeType() { - return long.class; - } - - public Object toNative(Object value) { - return value != null ? ((ClockTime) value).toNanos() : 0L; - } - - public Object fromNative(Object value, Class javaType) { - return ClockTime.valueOf(value != null ? (Long) value : 0L, TimeUnit.NANOSECONDS); - } - }; private static Converter getConverter(Class javaType) { if (Enum.class.isAssignableFrom(javaType)) return enumConverter; - else if (ClockTime.class == javaType) - return clocktimeConverter; else if (boolean.class == javaType || Boolean.class == javaType) return booleanConverter; return null; diff --git a/src/org/freedesktop/gstreamer/lowlevel/GObjectAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GObjectAPI.java index d0c8299e..8c469cdb 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GObjectAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GObjectAPI.java @@ -25,7 +25,7 @@ import java.util.HashMap; import java.util.List; -import org.freedesktop.gstreamer.GObject; +import org.freedesktop.gstreamer.glib.GObject; import org.freedesktop.gstreamer.glib.GQuark; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; @@ -60,28 +60,32 @@ public interface GObjectAPI extends Library { interface GClosureNotify extends Callback { void callback(Pointer data, Pointer closure); } - NativeLong g_signal_connect_data(GObject obj, String signal, Callback callback, Pointer data, +// NativeLong g_signal_connect_data(GObject obj, String signal, Callback callback, Pointer data, +// GClosureNotify destroy_data, int connect_flags); +// void g_signal_handler_disconnect(GObject obj, NativeLong id); + NativeLong g_signal_connect_data(GObjectPtr obj, String signal, Callback callback, Pointer data, GClosureNotify destroy_data, int connect_flags); - void g_signal_handler_disconnect(GObject obj, NativeLong id); - boolean g_object_is_floating(Pointer obj); + void g_signal_handler_disconnect(GObjectPtr obj, NativeLong id); + boolean g_object_is_floating(GObjectPtr obj); /** Sink floating ref * https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#g-object-ref-sink */ Pointer g_object_ref_sink(Pointer ptr); + GObjectPtr g_object_ref_sink(GObjectPtr ptr); interface GToggleNotify extends Callback { void callback(Pointer data, Pointer obj, boolean is_last_ref); } void g_object_add_toggle_ref(Pointer object, GToggleNotify notify, Pointer data); void g_object_remove_toggle_ref(Pointer object, GToggleNotify notify, Pointer data); - void g_object_add_toggle_ref(Pointer object, GToggleNotify notify, IntPtr data); - void g_object_remove_toggle_ref(Pointer object, GToggleNotify notify, IntPtr data); + void g_object_add_toggle_ref(GObjectPtr object, GToggleNotify notify, IntPtr data); + void g_object_remove_toggle_ref(GObjectPtr object, GToggleNotify notify, IntPtr data); interface GWeakNotify extends Callback { void callback(IntPtr data, Pointer obj); } void g_object_weak_ref(GObject object, GWeakNotify notify, IntPtr data); void g_object_weak_unref(GObject object, GWeakNotify notify, IntPtr data); - Pointer g_object_ref(GObject object); - void g_object_unref(GObject object); + Pointer g_object_ref(GObjectPtr object); + void g_object_unref(GObjectPtr object); GParamSpec g_object_class_find_property(GObjectClass oclass, String property_name); Pointer g_object_class_find_property(Pointer oclass, String property_name); @@ -102,6 +106,7 @@ interface GWeakNotify extends Callback { int g_type_depth(GType type); Pointer g_type_create_instance(GType type); void g_type_free_instance(Pointer instance); + boolean g_type_is_a(GType type, GType is_a_type); GType g_type_register_static(GType parent_type, String type_name, GTypeInfo info, /* GTypeFlags */ int flags); @@ -148,11 +153,8 @@ public static final class GObjectStruct extends com.sun.jna.Structure { public volatile int ref_count; public volatile Pointer qdata; public GObjectStruct() {} - public GObjectStruct(GObject obj) { - this(obj.handle()); - } - public GObjectStruct(Pointer ptr) { - super(ptr); + public GObjectStruct(GObjectPtr ptr) { + super(ptr.getPointer()); read(); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GObjectPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GObjectPtr.java new file mode 100644 index 00000000..dfed07d5 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GObjectPtr.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +/** + * Base GObject pointer + */ +public class GObjectPtr extends GTypedPtr { + + public GObjectPtr() { + } + + public GObjectPtr(Pointer ptr) { + super(ptr); + } + + @Override + public GType getGType() { + // Quick getter for GType without allocation + // same as : new GObjectStruct(ptr).g_type_instance.g_class.g_type + Pointer g_class = getPointer().getPointer(0); + if (Native.SIZE_T_SIZE == 8) { + return GType.valueOf(g_class.getLong(0)); + } else if (Native.SIZE_T_SIZE == 4) { + return GType.valueOf(((long) g_class.getInt(0)) & 0xffffffffL); + } else { + throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); + } + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GPointer.java b/src/org/freedesktop/gstreamer/lowlevel/GPointer.java new file mode 100644 index 00000000..6ab4d9b0 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GPointer.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; +import java.util.function.Function; + +/** + * Base GLib pointer + */ +public class GPointer extends PointerType { + + public GPointer() { + } + + public GPointer(Pointer ptr) { + super(ptr); + } + + public T as(Class cls, Function converter) { + if (cls.isInstance(this)) { + return cls.cast(this); + } else { + return converter.apply(this.getPointer()); + } + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GSignalAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GSignalAPI.java index 657a21fe..d7d71af3 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GSignalAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GSignalAPI.java @@ -23,7 +23,7 @@ import java.util.HashMap; import java.util.List; -import org.freedesktop.gstreamer.GObject; +import org.freedesktop.gstreamer.glib.GObject; import org.freedesktop.gstreamer.glib.GQuark; import org.freedesktop.gstreamer.lowlevel.GObjectAPI.GClosureNotify; diff --git a/src/org/freedesktop/gstreamer/lowlevel/GType.java b/src/org/freedesktop/gstreamer/lowlevel/GType.java index 1ae54eae..80a935f4 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GType.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Christophe Lafolet + * Copyright (c) 2020 Christophe Lafolet * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -87,11 +87,7 @@ public GType() { } public static GType valueOf(long value) { - GType result = gTypeByValues.get(value); - if (result == null) { - gTypeByValues.put(value, result = new GType(value)); - } - return result; + return gTypeByValues.computeIfAbsent(value, GType::new); } public static GType valueOf(String typeName) { @@ -149,7 +145,8 @@ public String getTypeName() { } @Override - public String toString() { - return "[" + this.getTypeName() + ":" + super.longValue() + "]"; + public String toString() { + String gtypeName = this.equals(INVALID) ? "invalid" : this.getTypeName(); + return "[" + gtypeName + ":" + super.longValue() + "]"; } -} \ No newline at end of file +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GTypeMapper.java b/src/org/freedesktop/gstreamer/lowlevel/GTypeMapper.java index 4cdb1b96..d0b9b2aa 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GTypeMapper.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GTypeMapper.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -21,10 +22,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.net.URI; -import java.util.concurrent.TimeUnit; - -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.QueryType; import org.freedesktop.gstreamer.glib.GQuark; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; import org.freedesktop.gstreamer.lowlevel.annotations.ConstField; @@ -43,6 +40,10 @@ import com.sun.jna.ToNativeContext; import com.sun.jna.ToNativeConverter; import com.sun.jna.TypeConverter; +import org.freedesktop.gstreamer.glib.GObject; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.glib.RefCountedObject; /** * @@ -51,14 +52,12 @@ public class GTypeMapper extends com.sun.jna.DefaultTypeMapper { public GTypeMapper() { - addTypeConverter(QueryType.class, querytypeConverter); addToNativeConverter(URI.class, uriConverter); - addTypeConverter(ClockTime.class, clocktimeConverter); } - private static ToNativeConverter nativeValueArgumentConverter = new ToNativeConverter() { + private static ToNativeConverter interfaceConverter = new ToNativeConverter() { public Object toNative(Object arg, ToNativeContext context) { - return arg != null ? ((NativeValue) arg).nativeValue() : null; + return arg != null ? Natives.getRawPointer(((GObject.GInterface) arg).getGObject()) : null; } public Class nativeType() { @@ -66,26 +65,12 @@ public Class nativeType() { } }; - private static TypeConverter clocktimeConverter = new TypeConverter() { - - public Object fromNative(Object arg, FromNativeContext arg1) { - return ClockTime.valueOf((Long) arg, TimeUnit.NANOSECONDS); - } - - public Class nativeType() { - return long.class; - } - - public Object toNative(Object arg, ToNativeContext arg1) { - return arg != null ? ((ClockTime) arg).convertTo(TimeUnit.NANOSECONDS) : 0L; - } - }; private static TypeConverter nativeObjectConverter = new TypeConverter() { public Object toNative(Object arg, ToNativeContext context) { if (arg == null) { return null; } - Pointer ptr = ((NativeObject) arg).handle(); + Pointer ptr = Natives.getRawPointer((NativeObject) arg); // // Deal with any adjustments to the proxy neccessitated by gstreamer @@ -100,11 +85,10 @@ public Object toNative(Object arg, ToNativeContext context) { Annotation[] annotations = parameterAnnotations[index]; for (int i = 0; i < annotations.length; ++i) { if (annotations[i] instanceof Invalidate) { - ((Handle) arg).invalidate(); + ((NativeObject) arg).invalidate(); break; } else if (annotations[i] instanceof IncRef) { - ((RefCountedObject) arg).ref(); - break; + Natives.ref((RefCountedObject) arg); } } } @@ -123,16 +107,23 @@ public Object fromNative(Object result, FromNativeContext context) { // returned from functions, so drop a ref here // boolean ownsHandle = ((MethodResultContext) context).getMethod().isAnnotationPresent(CallerOwnsReturn.class); - int refadj = ownsHandle ? -1 : 0; - return NativeObject.objectFor((Pointer) result, (Class) context.getTargetType(), refadj, ownsHandle); +// int refadj = ownsHandle ? -1 : 0; +// return NativeObject.objectFor((Pointer) result, (Class) context.getTargetType(), refadj, ownsHandle); + if (ownsHandle) { + return Natives.callerOwnsReturn((Pointer) result, (Class) context.getTargetType()); + } else { + return Natives.objectFor((Pointer) result, (Class) context.getTargetType(), false, false); + } } if (context instanceof CallbackParameterContext) { - return NativeObject.objectFor((Pointer) result, (Class) context.getTargetType(), 1, true); +// return NativeObject.objectFor((Pointer) result, (Class) context.getTargetType(), 1, true); + return Natives.objectFor((Pointer) result, (Class) context.getTargetType(), true, true); } if (context instanceof StructureReadContext) { StructureReadContext sctx = (StructureReadContext) context; boolean ownsHandle = sctx.getField().getAnnotation(ConstField.class) == null; - return NativeObject.objectFor((Pointer) result, (Class) context.getTargetType(), 1, ownsHandle); +// return NativeObject.objectFor((Pointer) result, (Class) context.getTargetType(), 1, ownsHandle); + return Natives.objectFor((Pointer) result, (Class) context.getTargetType(), true, true); } throw new IllegalStateException("Cannot convert to NativeObject from " + context); } @@ -234,20 +225,20 @@ public Class nativeType() { return Native.POINTER_SIZE == 8 ? Long.class : Integer.class; } }; - private TypeConverter querytypeConverter = new TypeConverter() { - - public Object toNative(Object arg, ToNativeContext context) { - return ((QueryType)arg).intValue(); - } - - public Object fromNative(Object arg0, FromNativeContext arg1) { - return QueryType.valueOf(((Number) arg0).intValue()); - } - - public Class nativeType() { - return Integer.class; - } - }; +// private TypeConverter querytypeConverter = new TypeConverter() { +// +// public Object toNative(Object arg, ToNativeContext context) { +// return ((QueryType)arg).intValue(); +// } +// +// public Object fromNative(Object arg0, FromNativeContext arg1) { +// return QueryType.valueOf(((Number) arg0).intValue()); +// } +// +// public Class nativeType() { +// return Integer.class; +// } +// }; private static ToNativeConverter uriConverter = new ToNativeConverter() { public Object toNative(Object arg0, ToNativeContext arg1) { @@ -291,8 +282,8 @@ public FromNativeConverter getFromNativeConverter(Class type) { public ToNativeConverter getToNativeConverter(Class type) { if (NativeObject.class.isAssignableFrom(type)) { return nativeObjectConverter; - } else if (NativeValue.class.isAssignableFrom(type)) { - return nativeValueArgumentConverter; + } else if (GObject.GInterface.class.isAssignableFrom(type)) { + return interfaceConverter; } else if (Enum.class.isAssignableFrom(type)) { return enumConverter; } else if (Boolean.class == type || boolean.class == type) { diff --git a/src/org/freedesktop/gstreamer/lowlevel/GTypedPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GTypedPtr.java new file mode 100644 index 00000000..ad84312c --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GTypedPtr.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * Base GLib pointer with GType + */ +public abstract class GTypedPtr extends GPointer { + + public GTypedPtr() { + } + + public GTypedPtr(Pointer ptr) { + super(ptr); + } + + public abstract GType getGType(); + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GValueAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GValueAPI.java index 5bfa2dfa..ccf9a7e4 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GValueAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GValueAPI.java @@ -25,13 +25,15 @@ import java.util.HashMap; import java.util.List; -import org.freedesktop.gstreamer.GObject; +import org.freedesktop.gstreamer.glib.GObject; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; import org.freedesktop.gstreamer.lowlevel.annotations.Invalidate; import com.sun.jna.Library; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.glib.Natives; /** * @@ -168,10 +170,12 @@ public Object getValue() { Class cls = GstTypes.classFor(g_type); if (cls != null) { Pointer ptr = GVALUE_API.g_value_get_boxed(this); - return NativeObject.objectFor(ptr, cls, 1, true); +// return NativeObject.objectFor(ptr, cls, 1, true); + return Natives.objectFor(ptr, cls, true, true); } } - return GVALUE_API.g_value_get_object(this); +// return GVALUE_API.g_value_get_object(this); + return GVALUE_API.g_value_dup_object(this); } public Integer toInt() { @@ -252,8 +256,12 @@ public GValueArray() { } public GValueArray(int n_prealloced) { + this(n_prealloced, true); + } + + public GValueArray(int n_prealloced, boolean ownsMemory) { this(GVALUE_API.g_value_array_new(n_prealloced)); - ownsMemory = true; + this.ownsMemory = ownsMemory; } public GValueArray(Pointer pointer) { @@ -300,11 +308,6 @@ public Object getValue(int i) { return v == null ? null : v.getValue(); } - @Override - protected void finalize() throws Throwable { - free(); - } - public void free() { if (ownsMemory) { GVALUE_API.g_value_array_free(this); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GioAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GioAPI.java index abba3681..607cbd8e 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GioAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GioAPI.java @@ -1,26 +1,45 @@ +/* + * Copyright (c) 2021 Neil C Smith + * Copyright (c) 2016 Isaac Raño Jares + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + package org.freedesktop.gstreamer.lowlevel; -import com.sun.jna.Native; +import com.sun.jna.Library; import com.sun.jna.Pointer; +import java.util.Collections; -public class GioAPI { +public interface GioAPI extends Library { - static { - Native.register("gio-2.0"); - } + public static final GioAPI GIO_API = + GNative.loadLibrary("gio-2.0", GioAPI.class, Collections.emptyMap()); // GInetAddress - public static native String g_inet_address_to_string(Pointer gInetAddress); + public String g_inet_address_to_string(Pointer gInetAddress); // GstSocketAddress - public static native Pointer g_inet_socket_address_new_from_string(String address, int port); + public Pointer g_inet_socket_address_new_from_string(String address, int port); // GstSocket - public static native Pointer g_socket_new(int gSocketFamilyEnumValue, int gSocketTypeEnumValue, int gSocketProtcolEnumValue, Pointer gErrorStructArrayPointer); - public static native boolean g_socket_bind(Pointer gSocketPointer, Pointer gSocketAddressPointer, boolean allowReuse, Pointer gErrorStructArrayPointer); - public static native boolean g_socket_connect(Pointer gSocketPointer, Pointer gSocketAddressPointer, Pointer gCancellablePointer, Pointer gErrorStructArrayPointer); + public Pointer g_socket_new(int gSocketFamilyEnumValue, int gSocketTypeEnumValue, int gSocketProtcolEnumValue, Pointer gErrorStructArrayPointer); + public boolean g_socket_bind(Pointer gSocketPointer, Pointer gSocketAddressPointer, boolean allowReuse, Pointer gErrorStructArrayPointer); + public boolean g_socket_connect(Pointer gSocketPointer, Pointer gSocketAddressPointer, Pointer gCancellablePointer, Pointer gErrorStructArrayPointer); // GCancellable - public static native Pointer g_cancellable_new(); + public Pointer g_cancellable_new(); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GlibAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GlibAPI.java index 8a23c353..7fb51bcb 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GlibAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GlibAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007 Wayne Meissner * @@ -29,6 +30,8 @@ import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.ptr.PointerByReference; +import org.freedesktop.gstreamer.glib.GMainContext; +import org.freedesktop.gstreamer.glib.GSource; /** * @@ -49,17 +52,16 @@ public interface GlibAPI extends Library { boolean g_main_loop_is_running(MainLoop loop); @CallerOwnsReturn GMainContext g_main_loop_get_context(MainLoop loop); void g_main_loop_quit(MainLoop loop); - void g_main_loop_ref(MainLoop ptr); - void g_main_loop_unref(MainLoop ptr); - void g_main_loop_unref(Pointer ptr); + void g_main_loop_ref(GPointer ptr); + void g_main_loop_unref(GPointer ptr); /* * GMainContext functions */ Pointer g_main_context_new(); - void g_main_context_ref(Pointer context); - void g_main_context_unref(Pointer context); + void g_main_context_ref(GPointer context); + void g_main_context_unref(GPointer context); Pointer g_main_context_default(); boolean g_main_context_pending(GMainContext ctx); boolean g_main_context_acquire(GMainContext ctx); @@ -105,8 +107,8 @@ interface GDestroyNotify extends Callback { int g_timeout_add_full(int priority, int interval, GSourceFunc function, Pointer data, GDestroyNotify notify); int g_timeout_add_seconds(int interval, GSourceFunc function, Pointer data); + GstAPI.GErrorStruct g_error_new(int quark, int code, String message); void g_error_free(Pointer error); - void g_error_free(GstAPI.GErrorStruct error); void g_source_remove(int id); void g_free(Pointer ptr); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstARGBControlBindingPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstARGBControlBindingPtr.java new file mode 100644 index 00000000..364588fa --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstARGBControlBindingPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstARGBControlBindingPtr extends GstControlBindingPtr { + + public GstARGBControlBindingPtr() { + } + + public GstARGBControlBindingPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstBinAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstBinAPI.java index dd3c09fd..084c14a5 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstBinAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstBinAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner * @@ -43,12 +44,12 @@ public interface GstBinAPI extends com.sun.jna.Library { @CallerOwnsReturn Element gst_bin_get_by_name(Bin bin, String name); @CallerOwnsReturn Element gst_bin_get_by_name_recurse_up(Bin bin, String name); @CallerOwnsReturn Element gst_bin_get_by_interface(Bin bin, GType iface); - Pointer gst_bin_iterate_elements(Bin bin); - Pointer gst_bin_iterate_sorted(Bin bin); - Pointer gst_bin_iterate_recurse(Bin bin); - Pointer gst_bin_iterate_sinks(Bin bin); - Pointer gst_bin_iterate_sources(Bin bin); - Pointer gst_bin_iterate_all_by_interface(Bin bin, GType iface); + GstIteratorPtr gst_bin_iterate_elements(Bin bin); + GstIteratorPtr gst_bin_iterate_sorted(Bin bin); + GstIteratorPtr gst_bin_iterate_recurse(Bin bin); + GstIteratorPtr gst_bin_iterate_sinks(Bin bin); + GstIteratorPtr gst_bin_iterate_sources(Bin bin); + GstIteratorPtr gst_bin_iterate_all_by_interface(Bin bin, GType iface); //Debugging void gst_debug_bin_to_dot_file (Bin bin, int details, String file_name); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java index 95434a37..770bddc7 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java @@ -24,12 +24,13 @@ import java.util.List; import org.freedesktop.gstreamer.Buffer; -import org.freedesktop.gstreamer.ClockTime; import org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.MiniObjectStruct; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; + import static org.freedesktop.gstreamer.lowlevel.GstAPI.GST_PADDING; /** @@ -105,7 +106,9 @@ protected List getFieldOrder() { void gst_buffer_unmap(Buffer buffer, MapInfoStruct info); int gst_buffer_n_memory(Buffer buffer); boolean gst_buffer_map_range(Buffer buffer, int idx, int length, MapInfoStruct info, int flags); - + GstMetaPtr gst_buffer_get_meta(Buffer buffer, GType gType); + int gst_buffer_get_n_meta(Buffer buffer,GType gType); + GstMetaPtr gst_buffer_iterate_meta(Buffer buffer, PointerByReference state); // re-introduces in gstreamer 1.9 int gst_buffer_get_flags(Buffer buffer); boolean gst_buffer_set_flags(Buffer buffer, int flags); @@ -155,9 +158,9 @@ public static final class BufferStruct extends com.sun.jna.Structure { public Pointer /* BufferPool */ pool; /* timestamp */ - public ClockTime pts; - public ClockTime dts; - public ClockTime duration; + public long pts; + public long dts; + public long duration; /* media specific offset */ public long offset; diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstBusAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstBusAPI.java index 11fca3ca..4076bf5d 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstBusAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstBusAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner @@ -22,9 +23,8 @@ import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.BusSyncReply; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Message; -import org.freedesktop.gstreamer.MessageType; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.message.MessageType; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GDestroyNotify; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; @@ -47,18 +47,18 @@ public interface GstBusAPI extends com.sun.jna.Library { @CallerOwnsReturn Message gst_bus_peek(Bus bus); @CallerOwnsReturn Message gst_bus_pop(Bus bus); @CallerOwnsReturn Message gst_bus_pop_filtered(Bus bus, MessageType types); - @CallerOwnsReturn Message gst_bus_timed_pop(Bus bus, ClockTime timeout); - @CallerOwnsReturn Message gst_bus_timed_pop_filtered(Bus bus, ClockTime timeout, MessageType types); + @CallerOwnsReturn Message gst_bus_timed_pop(Bus bus, long timeout); + @CallerOwnsReturn Message gst_bus_timed_pop_filtered(Bus bus, long timeout, MessageType types); /* polling the bus */ - @CallerOwnsReturn Message gst_bus_poll(Bus bus, MessageType events, /* GstClockTimeDiff */ long timeout); -// @CallerOwnsReturn Message gst_bus_poll(Bus bus, MessageType events, ClockTime timeout); + @CallerOwnsReturn Message gst_bus_poll(Bus bus, MessageType events, /* GstlongDiff */ long timeout); +// @CallerOwnsReturn Message gst_bus_poll(Bus bus, MessageType events, long timeout); void gst_bus_set_flushing(Bus ptr, int flushing); interface BusCallback extends GstCallback { - boolean callback(Bus bus, Message msg, Pointer data); + boolean callback(GstBusPtr bus, GstMessagePtr msg, Pointer data); } public interface BusSyncHandler extends GstCallback { - BusSyncReply callback(Bus bus, Message msg, Pointer userData); + BusSyncReply callback(GstBusPtr bus, GstMessagePtr msg, Pointer userData); } NativeLong gst_bus_add_watch(Bus bus, BusCallback function, Pointer data); void gst_bus_set_sync_handler(Bus bus, BusSyncHandler function, Pointer data, GDestroyNotify destroyCallback); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstBusPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstBusPtr.java new file mode 100644 index 00000000..93d80444 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstBusPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * GstBus pointer. + */ +public class GstBusPtr extends GstObjectPtr { + + public GstBusPtr() { + } + + public GstBusPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstClockAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstClockAPI.java index 862d5779..477ca09e 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstClockAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstClockAPI.java @@ -22,7 +22,6 @@ import org.freedesktop.gstreamer.Clock; import org.freedesktop.gstreamer.ClockID; import org.freedesktop.gstreamer.ClockReturn; -import org.freedesktop.gstreamer.ClockTime; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; import com.sun.jna.Pointer; @@ -35,38 +34,39 @@ public interface GstClockAPI extends com.sun.jna.Library { GstClockAPI GSTCLOCK_API = GstNative.load(GstClockAPI.class); GType gst_clock_get_type(); - ClockTime gst_clock_set_resolution(Clock clock, ClockTime resolution); - ClockTime gst_clock_get_resolution(Clock clock); - ClockTime gst_clock_get_time(Clock clock); - void gst_clock_set_calibration(Clock clock, ClockTime internal, ClockTime external, ClockTime rate_num, ClockTime rate_denom); + long gst_clock_set_resolution(Clock clock, long resolution); + long gst_clock_get_resolution(Clock clock); + long gst_clock_get_time(Clock clock); + void gst_clock_set_calibration(Clock clock, long internal, long external, long rate_num, long rate_denom); void gst_clock_get_calibration(Clock clock, long[] internal, long[] external, long[] rate_num, long[] rate_denom); /* master/slave clocks */ boolean gst_clock_set_master(Clock clock, Clock master); @CallerOwnsReturn Clock gst_clock_get_master(Clock clock); - boolean gst_clock_add_observation(Clock clock, ClockTime slave, ClockTime Master, double[] r_squared); + boolean gst_clock_add_observation(Clock clock, long slave, long Master, double[] r_squared); /* getting and adjusting internal time */ - ClockTime gst_clock_get_internal_time(Clock clock); - ClockTime gst_clock_adjust_unlocked(Clock clock, ClockTime internal); - ClockTime gst_clock_unadjust_unlocked(Clock clock, ClockTime external); + long gst_clock_get_internal_time(Clock clock); + long gst_clock_adjust_unlocked(Clock clock, long internal); + long gst_clock_unadjust_unlocked(Clock clock, long external); /* creating IDs that can be used to get notifications */ - @CallerOwnsReturn ClockID gst_clock_new_single_shot_id(Clock clock, ClockTime time); - @CallerOwnsReturn ClockID gst_clock_new_periodic_id(Clock clock, ClockTime start_time, ClockTime interval); + @CallerOwnsReturn ClockID gst_clock_new_single_shot_id(Clock clock, long time); + @CallerOwnsReturn ClockID gst_clock_new_periodic_id(Clock clock, long start_time, long interval); /* reference counting */ void gst_clock_id_ref(ClockID id); + void gst_clock_id_ref(GPointer id); void gst_clock_id_unref(ClockID id); - void gst_clock_id_unref(Pointer id); + void gst_clock_id_unref(GPointer id); /* operations on IDs */ int gst_clock_id_compare_func(ClockID id1, ClockID id2); - ClockTime gst_clock_id_get_time(ClockID id); - ClockReturn gst_clock_id_wait(ClockID id, /* GstClockTimeDiff * */ long[] jitter); + long gst_clock_id_get_time(ClockID id); + ClockReturn gst_clock_id_wait(ClockID id, /* GstlongDiff * */ long[] jitter); public static interface GstClockCallback { /** * @param clock The clock that triggered the callback @@ -74,7 +74,7 @@ public static interface GstClockCallback { * @param id The {@link ClockID} that expired * @return currently unused. */ - boolean callback(Clock clock, ClockTime time, ClockID id, Pointer user_data); + boolean callback(Clock clock, long time, ClockID id, Pointer user_data); } ClockReturn gst_clock_id_wait_async(ClockID id, GstClockCallback func, Pointer user_data); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstColorBalanceAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstColorBalanceAPI.java index 995fa95e..60e656a8 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstColorBalanceAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstColorBalanceAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Tamas Korodi * @@ -29,7 +30,7 @@ import java.util.List; public interface GstColorBalanceAPI extends Library { - GstColorBalanceAPI GSTCOLORBALANCE_API = GstNative.load("gstinterfaces", GstColorBalanceAPI.class); + GstColorBalanceAPI GSTCOLORBALANCE_API = GstNative.load("gstvideo", GstColorBalanceAPI.class); GType gst_color_balance_channel_get_type(); GType gst_color_balance_get_type(); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstContextAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstContextAPI.java new file mode 100644 index 00000000..f1a3bf49 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstContextAPI.java @@ -0,0 +1,29 @@ +package org.freedesktop.gstreamer.lowlevel; + +import org.freedesktop.gstreamer.Structure; + +/** + * GstContext API + * + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstcontext.html + * + * https://gitlab.freedesktop.org/gstreamer/gstreamer/blob/master/gst/gstcontext.h + * https://gitlab.freedesktop.org/gstreamer/gstreamer/blob/master/gst/gstcontext.c + */ +public interface GstContextAPI extends com.sun.jna.Library { + + GstContextAPI GSTCONTEXT_API = GstNative.load(GstContextAPI.class); + + /*@CallerOwnsReturn*/ GstContextPtr gst_context_new(String context_type, boolean persistent); + + String gst_context_get_context_type(GstContextPtr context); + + boolean gst_context_has_context_type(GstContextPtr context, String context_type); + + Structure gst_context_get_structure(GstContextPtr context); + + Structure gst_context_writable_structure(GstContextPtr context); + + boolean gst_context_is_persistent(GstContextPtr context); + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstContextPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstContextPtr.java new file mode 100644 index 00000000..dbda112b --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstContextPtr.java @@ -0,0 +1,14 @@ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +public class GstContextPtr extends GstMiniObjectPtr { + + public GstContextPtr() { + } + + public GstContextPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstControlBindingAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstControlBindingAPI.java new file mode 100644 index 00000000..f0bd7fc2 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstControlBindingAPI.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Library; + +/** + * GstControlBinding API + * + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstControlBinding.html + * https://gitlab.freedesktop.org/gstreamer/gstreamer/tree/master/libs/gst/controller + */ + +public interface GstControlBindingAPI extends Library { + + GstControlBindingAPI GSTCONTROLBINDING_API = GstNative.load(GstControlBindingAPI.class); + + + boolean gst_control_binding_sync_values(GstControlBindingPtr binding, + GstObjectPtr object, + long timestamp, + long lastSync); + + GValueAPI.GValue gst_control_binding_get_value(GstControlBindingPtr binding, + long timestamp); + + boolean gst_control_binding_get_g_value_array(GstControlBindingPtr binding, + long timestamp, + long internal, + int n_values, + GValueAPI.GValue[] values); + + void gst_control_binding_set_disabled(GstControlBindingPtr binding, + boolean disabled); + + boolean gst_control_binding_is_disabled(GstControlBindingPtr binding); + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstControlBindingPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstControlBindingPtr.java new file mode 100644 index 00000000..f9517d38 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstControlBindingPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstControlBindingPtr extends GstObjectPtr { + + public GstControlBindingPtr() { + } + + public GstControlBindingPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstControlSourceAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstControlSourceAPI.java index 405596a4..eacfb701 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstControlSourceAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstControlSourceAPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Levente Farkas + * Copyright (c) 2019 Neil C Smith * * This file is part of gstreamer-java. * @@ -18,109 +18,51 @@ package org.freedesktop.gstreamer.lowlevel; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.controller.ControlSource; -import org.freedesktop.gstreamer.lowlevel.GObjectAPI.GParamSpec; -import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; - -import com.sun.jna.Callback; import com.sun.jna.Library; import com.sun.jna.Pointer; -import com.sun.jna.ptr.DoubleByReference; -import java.util.Arrays; -import java.util.List; -import org.freedesktop.gstreamer.GstObject; -import static org.freedesktop.gstreamer.lowlevel.GstAPI.GST_PADDING; -import org.freedesktop.gstreamer.lowlevel.GstObjectAPI.GstObjectClass; +import com.sun.jna.Structure; /** - * GstControlSource methods and structures - * @see https://cgit.freedesktop.org/gstreamer/gstreamer/tree/gst/gstcontrolsource.h?h=1.8 + * GstControlSource API + * + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstControlBinding.html + * https://gitlab.freedesktop.org/gstreamer/gstreamer/tree/master/libs/gst/controller */ -public interface GstControlSourceAPI extends Library { - - GstControlSourceAPI GSTCONTROLSOURCE_API = GstNative.load("gstcontroller", GstControlSourceAPI.class); - - /** - * GstTimedValue: - * @timestamp: timestamp of the value change - * @value: the corresponding value - * - * Structure for saving a timestamp and a value. - */ - public static final class TimedValue extends com.sun.jna.Structure { - public static final String GTYPE_NAME = "GstTimedValue"; - - public volatile ClockTime timestamp; - public volatile double value; - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "timestamp", "value" - }); - } - } - - public static interface GstControlSourceGetValue extends Callback { - public boolean callback(ControlSource self, ClockTime timestamp, DoubleByReference value); - } - public static interface GstControlSourceGetValueArray extends Callback { - public boolean callback(ControlSource self, ClockTime timestamp, ClockTime interval, int n_values, DoubleByReference values); - } - public static interface GstControlSourceBind extends Callback { - public boolean callback(ControlSource self, GParamSpec pspec); - } - - /** - * GstControlSource: - * @get_value: Function for returning a value for a given timestamp - * @get_value_array: Function for returning a values array for a given timestamp - * - * The instance structure of #GstControlSource. - */ - public static final class GstControlSourceStruct extends com.sun.jna.Structure { - public volatile GstObject parent; +public interface GstControlSourceAPI extends Library { - /*< public >*/ - public volatile GstControlSourceGetValue get_value; /* Returns the value for a property at a given timestamp */ - public volatile GstControlSourceGetValueArray get_value_array; /* Returns values for a property in a given timespan */ + GstControlSourceAPI GSTCONTROLSOURCE_API = GstNative.load(GstControlSourceAPI.class); - /*< private >*/ - public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "parent", "get_value", "get_value_array", - "_gst_reserved" - }); - } - } + boolean gst_control_source_get_value(GstControlSourcePtr self, long timestamp, double[] value); + boolean gst_control_source_get_value_array(GstControlSourcePtr self, long timestamp, long interval, int n_values, double[] values); - /** - * GstControlSourceClass: - * @parent_class: Parent class - * - * The class structure of #GstControlSource. - */ - public static final class GstControlSourceClass extends com.sun.jna.Structure { - public volatile GstObjectClass parent_class; +// static class Direct implements GstControlSourceAPI { +// +// @Override +// public native boolean gst_control_source_get_value(GstControlSourcePtr self, long timestamp, double[] value); +// +// @Override +// public native boolean gst_control_source_get_value_array(GstControlSourcePtr self, long timestamp, long interval, int n_values, double[] values); +// +// } - /*< private >*/ - public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; - - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "parent_class", "_gst_reserved" - }); + @Structure.FieldOrder({"timestamp", "value"}) + public static final class GstTimedValue extends Structure { + + public volatile long timestamp; + public volatile double value; + + public GstTimedValue() { + super(); } - } - - GType gst_control_source_get_type(); + + public GstTimedValue(Pointer ptr) { + super(ptr); + } + + } + + - /* Functions */ - boolean gst_control_source_get_value(ControlSource self, ClockTime timestamp, GValue value); - boolean gst_control_source_get_value_array(ControlSource self, ClockTime timestamp, ClockTime interval, int n_values, DoubleByReference values); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstControlSourcePtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstControlSourcePtr.java new file mode 100644 index 00000000..7bedfbd0 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstControlSourcePtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstControlSourcePtr extends GstObjectPtr { + + public GstControlSourcePtr() { + } + + public GstControlSourcePtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstControllerAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstControllerAPI.java new file mode 100644 index 00000000..6f8ed29d --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstControllerAPI.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Library; +import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; + +/** + * GstController API functions + * + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstTimedValueControlSource.html + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstInterpolationControlSource.html + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstLFOControlSource.html + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstTriggerControlSource.html + * https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstDirectControlBinding.html + * + */ +public interface GstControllerAPI extends Library { + + GstControllerAPI GSTCONTROLLER_API = GstNative.load("gstcontroller", GstControllerAPI.class); + + @CallerOwnsReturn GstTriggerControlSourcePtr gst_trigger_control_source_new(); + + @CallerOwnsReturn GstInterpolationControlSourcePtr gst_interpolation_control_source_new(); + + @CallerOwnsReturn GstLFOControlSourcePtr gst_lfo_control_source_new(); + + @CallerOwnsReturn GstDirectControlBindingPtr gst_direct_control_binding_new( + GstObjectPtr object, + String property_name, + GstControlSourcePtr cs); + + @CallerOwnsReturn GstDirectControlBindingPtr gst_direct_control_binding_new_absolute( + GstObjectPtr object, + String property_name, + GstControlSourcePtr cs); + + @CallerOwnsReturn GstARGBControlBindingPtr gst_argb_control_binding_new( + GstObjectPtr object, + String property_name, + GstControlSourcePtr cs_a, + GstControlSourcePtr cs_r, + GstControlSourcePtr cs_g, + GstControlSourcePtr cs_b); + + // since 1.12 + @CallerOwnsReturn GstProxyControlBindingPtr gst_proxy_control_binding_new( + GstObjectPtr object, + String property_name, + GstObjectPtr ref_object, + String ref_property_name); + + + // GSequenceIter gst_timed_value_control_source_find_control_point_iter( + // GstTimedValueControlSourcePtr self, + // long timestamp); + + boolean gst_timed_value_control_source_set(GstTimedValueControlSourcePtr self, + long timestamp, + double value); + + boolean gst_timed_value_control_source_set_from_list( + GstTimedValueControlSourcePtr self, + GlibAPI.GSList timedvalues); + + // transfer container + // caller owns list, not contained values! + @CallerOwnsReturn GlibAPI.GList gst_timed_value_control_source_get_all( + GstTimedValueControlSourcePtr self); + + boolean gst_timed_value_control_source_unset(GstTimedValueControlSourcePtr self, + long timestamp); + + void gst_timed_value_control_source_unset_all(GstTimedValueControlSourcePtr self); + + int gst_timed_value_control_source_get_count(GstTimedValueControlSourcePtr self); + + void gst_timed_value_control_invalidate_cache(GstTimedValueControlSourcePtr self); + + // gst_control_point_copy + // gst_control_point_free + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstDeviceMonitorAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstDeviceMonitorAPI.java index b9ee797e..fd4f29cd 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstDeviceMonitorAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstDeviceMonitorAPI.java @@ -18,6 +18,7 @@ */ package org.freedesktop.gstreamer.lowlevel; +import com.sun.jna.Pointer; import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.device.DeviceMonitor; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; @@ -32,6 +33,7 @@ public interface GstDeviceMonitorAPI extends com.sun.jna.Library { GstDeviceMonitorAPI GSTDEVICEMONITOR_API = GstNative.load(GstDeviceMonitorAPI.class); @CallerOwnsReturn DeviceMonitor gst_device_monitor_new(); + @CallerOwnsReturn Pointer ptr_gst_device_monitor_new(); @CallerOwnsReturn Bus gst_device_monitor_get_bus(DeviceMonitor monitor); int gst_device_monitor_add_filter(DeviceMonitor monitor, String classes, Caps caps); boolean gst_device_monitor_remove_filter(DeviceMonitor monitor, int filterId); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstDirectControlBindingPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstDirectControlBindingPtr.java new file mode 100644 index 00000000..5ac7d805 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstDirectControlBindingPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstDirectControlBindingPtr extends GstControlBindingPtr { + + public GstDirectControlBindingPtr() { + } + + public GstDirectControlBindingPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java index dc653fbf..ecdb8e86 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner * @@ -26,17 +27,17 @@ import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.Caps; import org.freedesktop.gstreamer.Clock; -import org.freedesktop.gstreamer.ClockTime; import org.freedesktop.gstreamer.Element; import org.freedesktop.gstreamer.ElementFactory; -import org.freedesktop.gstreamer.Event; +import org.freedesktop.gstreamer.event.Event; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.message.Message; import org.freedesktop.gstreamer.Pad; -import org.freedesktop.gstreamer.Query; -import org.freedesktop.gstreamer.SeekType; +import org.freedesktop.gstreamer.query.Query; +import org.freedesktop.gstreamer.event.SeekType; import org.freedesktop.gstreamer.State; import org.freedesktop.gstreamer.StateChangeReturn; +import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; import org.freedesktop.gstreamer.lowlevel.GstObjectAPI.GstObjectClass; import org.freedesktop.gstreamer.lowlevel.GstObjectAPI.GstObjectStruct; @@ -58,23 +59,23 @@ public interface GstElementAPI extends com.sun.jna.Library { GType gst_element_get_type(); StateChangeReturn gst_element_set_state(Element elem, State state); StateChangeReturn gst_element_get_state(Element elem, State[] state, State[] pending, long timeout); - StateChangeReturn gst_element_get_state(Element elem, State[] state, State[] pending, ClockTime timeout); boolean gst_element_set_locked_state(Element element, boolean locked_state); boolean gst_element_sync_state_with_parent(Element elem); boolean gst_element_query_position(Element elem, Format fmt, long[] pos); boolean gst_element_query_duration(Element elem, Format fmt, long[] pos); boolean gst_element_query(Element elem, Query query); - boolean gst_element_seek(Element elem, double rate, Format format, int flags, - SeekType cur_type, long cur, SeekType stop_type, long stop); - boolean gst_element_seek_simple(Element elem, Format format, int flags, long pos); + boolean gst_element_seek(Element element, double rate, Format format, int flags, + SeekType start_type, long start, SeekType stop_type, long stop); + boolean gst_element_seek_simple(Element elem, Format format, int seek_flags, long seek_pos); boolean gst_element_link(Element elem1, Element elem2); + boolean gst_element_link_filtered(Element elem1, Element elem2, Caps filter); boolean gst_element_link_many(Element... elements); void gst_element_unlink_many(Element... elements); void gst_element_unlink(Element elem1, Element elem2); @CallerOwnsReturn Pad gst_element_get_pad(Element elem, String name); @CallerOwnsReturn Pad gst_element_get_static_pad(Element element, String name); // pads returned from get_request have to be freed via release_request_pad - Pad gst_element_get_request_pad(Element element, String name); + @CallerOwnsReturn Pad gst_element_get_request_pad(Element element, String name); void gst_element_release_request_pad(Element element, Pad pad); boolean gst_element_add_pad(Element elem, Pad pad); boolean gst_element_remove_pad(Element elem, @IncRef Pad pad); @@ -83,9 +84,9 @@ boolean gst_element_seek(Element elem, double rate, Format format, int flags, boolean gst_element_link_pads_filtered(Element src, String srcpadname, Element dest, String destpadname, Caps filter); - Pointer gst_element_iterate_pads(Element element); - Pointer gst_element_iterate_src_pads(Element element); - Pointer gst_element_iterate_sink_pads(Element element); + GstIteratorPtr gst_element_iterate_pads(Element element); + GstIteratorPtr gst_element_iterate_src_pads(Element element); + GstIteratorPtr gst_element_iterate_sink_pads(Element element); /* factory management */ ElementFactory gst_element_get_factory(Element element); @CallerOwnsReturn Bus gst_element_get_bus(Element element); @@ -96,10 +97,15 @@ boolean gst_element_link_pads_filtered(Element src, String srcpadname, Element d /* clocking */ Clock gst_element_get_clock(Element element); boolean gst_element_set_clock(Element element, Clock clock); - void gst_element_set_base_time(Element element, ClockTime time); - ClockTime gst_element_get_base_time(Element element); - void gst_element_set_start_time(Element element, ClockTime time); - ClockTime gst_element_get_start_time(Element element); + void gst_element_set_base_time(Element element, long time); + long gst_element_get_base_time(Element element); + void gst_element_set_start_time(Element element, long time); + long gst_element_get_start_time(Element element); + /* context */ + void gst_element_set_context(Element element, GstContextPtr context); + GList gst_element_get_contexts(Element element); + @CallerOwnsReturn GstContextPtr gst_element_get_context(Element element, String context_type); + @CallerOwnsReturn GstContextPtr gst_element_get_context_unlocked(Element element, String context_type); /** * GstElement: diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstEventAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstEventAPI.java index 518d3a2f..e6991067 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstEventAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstEventAPI.java @@ -23,12 +23,11 @@ import java.util.List; import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.EventType; +import org.freedesktop.gstreamer.event.Event; +import org.freedesktop.gstreamer.event.EventType; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.QOSType; -import org.freedesktop.gstreamer.SeekType; +import org.freedesktop.gstreamer.event.QOSType; +import org.freedesktop.gstreamer.event.SeekType; import org.freedesktop.gstreamer.Structure; import org.freedesktop.gstreamer.TagList; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; @@ -83,10 +82,8 @@ void gst_event_parse_buffer_size(Event event, Format[] format, long[] minsize, long[] maxsize, boolean[] async); /* QOS events */ - @CallerOwnsReturn Event gst_event_new_qos(QOSType type, double proportion, long diff, ClockTime timestamp); @CallerOwnsReturn Event gst_event_new_qos(QOSType type, double proportion, long diff, long timestamp); - Pointer ptr_gst_event_new_qos(QOSType type, double proportion, long diff, ClockTime timestamp); - void gst_event_parse_qos(Event event, QOSType[] type, double[] proportion, long[] diff, ClockTime[] timestamp); + Pointer ptr_gst_event_new_qos(QOSType type, double proportion, long diff, long timestamp); void gst_event_parse_qos(Event event, QOSType[] type, double[] proportion, long[] diff, long[] timestamp); /* seek event */ @@ -104,9 +101,9 @@ void gst_event_parse_seek(Event event, double[] rate, Format[] format, Pointer ptr_gst_event_new_navigation(@Invalidate Structure structure); /* latency event */ - @CallerOwnsReturn Event gst_event_new_latency(ClockTime latency); - Pointer ptr_gst_event_new_latency(ClockTime latency); - void gst_event_parse_latency(Event event, ClockTime[] latency); + @CallerOwnsReturn Event gst_event_new_latency(long latency); + Pointer ptr_gst_event_new_latency(long latency); + void gst_event_parse_latency(Event event, long[] latency); /* step event */ @CallerOwnsReturn Event gst_event_new_step(Format format, long amount, double rate, boolean flush, boolean intermediate); @@ -137,7 +134,7 @@ public static final class EventStruct extends com.sun.jna.Structure { public volatile MiniObjectStruct mini_object; /*< public >*/ /* with COW */ - public volatile int type; + public volatile EventType type; public volatile long timestamp; public volatile int seqnum; diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstInterpolationControlSourceAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstInterpolationControlSourceAPI.java index 395ce579..9c29a766 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstInterpolationControlSourceAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstInterpolationControlSourceAPI.java @@ -18,9 +18,8 @@ package org.freedesktop.gstreamer.lowlevel; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.controller.ControlSource; -import org.freedesktop.gstreamer.controller.InterpolationControlSource; +//import org.freedesktop.gstreamer.controller.ControlSource; +//import org.freedesktop.gstreamer.controller.InterpolationControlSource; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GSList; @@ -30,60 +29,62 @@ import java.util.Arrays; import java.util.List; -public interface GstInterpolationControlSourceAPI extends Library { - GstInterpolationControlSourceAPI GSTINTERPOLATIONCONTROLSOURCE_API - = GstNative.load("gstcontroller", GstInterpolationControlSourceAPI.class); - int GST_PADDING = GstAPI.GST_PADDING; - - public enum InterpolateMode { - NONE, - TRIGGER, - LINEAR, - QUADRATIC, - CUBIC, - USER; - } - - public static final class GstInterpolationControlSourceStruct extends com.sun.jna.Structure { - public volatile ControlSource parent; - - /* */ - public volatile Pointer /* GMutex */ lock; - public volatile Pointer /* GstInterpolationControlSourcePrivate */ priv; - public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; - - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "parent", "lock", "priv", - "_gst_reserved" - }); - } - } - - public static final class GstInterpolationControlSourceClass extends com.sun.jna.Structure { - public volatile ControlSource parent_class; - - /*< private >*/ - public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; +// @TODO review in line with https://gitlab.freedesktop.org/gstreamer/gstreamer/blob/master/libs/gst/controller/gstinterpolationcontrolsource.h - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "parent_class", "_gst_reserved" - }); - } - } - - GType gst_interpolation_control_source_get_type(); - - /* Functions */ - InterpolationControlSource gst_interpolation_control_source_new(); - boolean gst_interpolation_control_source_set_interpolation_mode(InterpolationControlSource self, InterpolateMode mode); - boolean gst_interpolation_control_source_set(InterpolationControlSource self, ClockTime timestamp, GValue value); - boolean gst_interpolation_control_source_set_from_list(InterpolationControlSource self, GSList timedvalues); - boolean gst_interpolation_control_source_unset(InterpolationControlSource self, ClockTime timestamp); - void gst_interpolation_control_source_unset_all(InterpolationControlSource self); - GList gst_interpolation_control_source_get_all(InterpolationControlSource self); - int gst_interpolation_control_source_get_count(InterpolationControlSource self); +public interface GstInterpolationControlSourceAPI extends Library { +// GstInterpolationControlSourceAPI GSTINTERPOLATIONCONTROLSOURCE_API +// = GstNative.load("gstcontroller", GstInterpolationControlSourceAPI.class); +// int GST_PADDING = GstAPI.GST_PADDING; +// +// public enum InterpolateMode { +// NONE, +// TRIGGER, +// LINEAR, +// QUADRATIC, +// CUBIC, +// USER; +// } +// +// public static final class GstInterpolationControlSourceStruct extends com.sun.jna.Structure { +// public volatile ControlSource parent; +// +// /* */ +// public volatile Pointer /* GMutex */ lock; +// public volatile Pointer /* GstInterpolationControlSourcePrivate */ priv; +// public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; +// +// @Override +// protected List getFieldOrder() { +// return Arrays.asList(new String[]{ +// "parent", "lock", "priv", +// "_gst_reserved" +// }); +// } +// } +// +// public static final class GstInterpolationControlSourceClass extends com.sun.jna.Structure { +// public volatile ControlSource parent_class; +// +// /*< private >*/ +// public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; +// +// @Override +// protected List getFieldOrder() { +// return Arrays.asList(new String[]{ +// "parent_class", "_gst_reserved" +// }); +// } +// } +// +// GType gst_interpolation_control_source_get_type(); +// +// /* Functions */ +// InterpolationControlSource gst_interpolation_control_source_new(); +// boolean gst_interpolation_control_source_set_interpolation_mode(InterpolationControlSource self, InterpolateMode mode); +// boolean gst_interpolation_control_source_set(InterpolationControlSource self, long timestamp, GValue value); +// boolean gst_interpolation_control_source_set_from_list(InterpolationControlSource self, GSList timedvalues); +// boolean gst_interpolation_control_source_unset(InterpolationControlSource self, long timestamp); +// void gst_interpolation_control_source_unset_all(InterpolationControlSource self); +// GList gst_interpolation_control_source_get_all(InterpolationControlSource self); +// int gst_interpolation_control_source_get_count(InterpolationControlSource self); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstInterpolationControlSourcePtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstInterpolationControlSourcePtr.java new file mode 100644 index 00000000..3dc8bea5 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstInterpolationControlSourcePtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstInterpolationControlSourcePtr extends GstTimedValueControlSourcePtr { + + public GstInterpolationControlSourcePtr() { + } + + public GstInterpolationControlSourcePtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstIteratorAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstIteratorAPI.java index b2022656..25e9aedc 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstIteratorAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstIteratorAPI.java @@ -1,7 +1,5 @@ /* - * Copyright (c) 2016 Neil C Smith - * Copyright (c) 2009 Levente Farkas - * Copyright (c) 2007, 2008 Wayne Meissner + * Copyright (c) 2021 Neil C Smith * * This file is part of gstreamer-java. * @@ -20,15 +18,14 @@ package org.freedesktop.gstreamer.lowlevel; -import com.sun.jna.Pointer; - /** * GstIterator functions */ public interface GstIteratorAPI extends com.sun.jna.Library { + GstIteratorAPI GSTITERATOR_API = GstNative.load(GstIteratorAPI.class); - void gst_iterator_free(Pointer iter); - int gst_iterator_next(Pointer iter, GValueAPI.GValue next); - void gst_iterator_resync(Pointer iter); + void gst_iterator_free(GstIteratorPtr iter); + int gst_iterator_next(GstIteratorPtr iter, GValueAPI.GValue next); + void gst_iterator_resync(GstIteratorPtr iter); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstIteratorPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstIteratorPtr.java new file mode 100644 index 00000000..adba914c --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstIteratorPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * GstIterator pointer. + */ +public class GstIteratorPtr extends GPointer { + + public GstIteratorPtr() { + } + + public GstIteratorPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstLFOControlSourceAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstLFOControlSourceAPI.java deleted file mode 100644 index de710b73..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/GstLFOControlSourceAPI.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2009 Levente Farkas - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -import org.freedesktop.gstreamer.controller.LFOControlSource; -import org.freedesktop.gstreamer.lowlevel.GstControlSourceAPI.GstControlSourceClass; -import org.freedesktop.gstreamer.lowlevel.GstControlSourceAPI.GstControlSourceStruct; - -import com.sun.jna.Library; -import com.sun.jna.Pointer; -import java.util.Arrays; -import java.util.List; - -public interface GstLFOControlSourceAPI extends Library { - GstLFOControlSourceAPI GSTLFOCONTROLSOURCE_API = GstNative.load("gstcontroller", GstLFOControlSourceAPI.class); - int GST_PADDING = GstAPI.GST_PADDING; - - public enum Waveform - { - SINE, - SQUARE, - SAW, - REVERSE_SAW, - TRIANGLE; - } - - public static final class GstLFOControlSourceStruct extends com.sun.jna.Structure { - public volatile GstControlSourceStruct parent; - - /* */ - public volatile Pointer /* GstLFOControlSourcePrivate */ priv; - public volatile Pointer /* GMutex */ lock; - public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; - - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "parent", "priv", "lock", - "_gst_reserved" - }); - } - } - - public static final class GstLFOControlSourceClass extends com.sun.jna.Structure { - public volatile GstControlSourceClass parent_class; - - /*< private >*/ - public volatile Pointer[] _gst_reserved = new Pointer[GST_PADDING]; - - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "parent_class", "_gst_reserved" - }); - } - } - - GType gst_lfo_control_source_get_type(); - GType gst_lfo_waveform_get_type(); - - /* Functions */ - LFOControlSource gst_lfo_control_source_new(); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstLFOControlSourcePtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstLFOControlSourcePtr.java new file mode 100644 index 00000000..23f857ec --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstLFOControlSourcePtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstLFOControlSourcePtr extends GstControlSourcePtr { + + public GstLFOControlSourcePtr() { + } + + public GstLFOControlSourcePtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMessageAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstMessageAPI.java index 0852d9d0..821c502d 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstMessageAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMessageAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner @@ -23,8 +24,8 @@ import org.freedesktop.gstreamer.Clock; import org.freedesktop.gstreamer.Format; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; -import org.freedesktop.gstreamer.MessageType; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.message.MessageType; import org.freedesktop.gstreamer.State; import org.freedesktop.gstreamer.Structure; import org.freedesktop.gstreamer.TagList; @@ -35,9 +36,10 @@ import org.freedesktop.gstreamer.lowlevel.annotations.Invalidate; import com.sun.jna.Pointer; +import com.sun.jna.Structure.FieldOrder; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; import com.sun.jna.ptr.PointerByReference; -import java.util.Arrays; -import java.util.List; /* * GstMessage functions @@ -45,11 +47,13 @@ public interface GstMessageAPI extends com.sun.jna.Library { GstMessageAPI GSTMESSAGE_API = GstNative.load(GstMessageAPI.class); + @FieldOrder({"mini_object", "type", "timestamp", "src", + "seqnum", "lock", "cond"}) public static final class MessageStruct extends com.sun.jna.Structure { public volatile MiniObjectStruct mini_object; - public volatile MessageType type; + public volatile int type; public volatile long timestamp; - public volatile GstObject src; + public volatile GstObjectPtr src; public volatile int seqnum; public volatile Pointer lock; @@ -63,38 +67,48 @@ public MessageStruct() { public MessageStruct(Pointer ptr) { useMemory(ptr); } - - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[]{ - "mini_object", - "type", "timestamp", "src", - "seqnum", - "lock", "cond" - }); + + int typeOffset() { + return fieldOffset("type"); + } + + int srcOffset() { + return fieldOffset("src"); } } GType gst_message_get_type(); String gst_message_type_get_name(MessageType type); void gst_message_parse_state_changed(Message msg, State[] old, State[] current, State[] pending); + void gst_message_parse_state_changed(GstMessagePtr msg, IntByReference old, IntByReference current, IntByReference pending); void gst_message_parse_tag(Message msg, PointerByReference tagList); + void gst_message_parse_tag(GstMessagePtr msg, PointerByReference tagList); void gst_message_parse_clock_provide(Message msg, PointerByReference clock, int[] reader); void gst_message_parse_new_clock(Message msg, PointerByReference clock); void gst_message_parse_error(Message msg, PointerByReference err, PointerByReference debug); + void gst_message_parse_error(GstMessagePtr msg, PointerByReference err, PointerByReference debug); void gst_message_parse_error(Message msg, GErrorStruct[] err, Pointer[] debug); void gst_message_parse_warning(Message msg, PointerByReference err, PointerByReference debug); + void gst_message_parse_warning(GstMessagePtr msg, PointerByReference err, PointerByReference debug); void gst_message_parse_warning(Message msg, GErrorStruct[] err, Pointer[] debug); void gst_message_parse_info(Message msg, PointerByReference err, PointerByReference debug); + void gst_message_parse_info(GstMessagePtr msg, PointerByReference err, PointerByReference debug); void gst_message_parse_info(Message msg, GErrorStruct[] err, Pointer[] debug); void gst_message_parse_buffering(Message msg, int[] percent); + void gst_message_parse_buffering(GstMessagePtr msg, IntByReference percent); void gst_message_parse_segment_start(Message message, Format[] format, long[] position); + void gst_message_parse_segment_start(GstMessagePtr msg, IntByReference format, LongByReference position); void gst_message_parse_segment_done(Message message, Format[] format, long[] position); + void gst_message_parse_segment_done(GstMessagePtr msg, IntByReference format, LongByReference position); void gst_message_parse_duration(Message message, Format[] format, long[] position); + void gst_message_parse_duration(GstMessagePtr msg, IntByReference format, LongByReference position); void gst_message_parse_async_start(Message message, boolean[] new_base_time); + boolean gst_message_parse_context_type(Message message, String[] context_type); + boolean gst_message_parse_context_type(GstMessagePtr msg, PointerByReference /*String*/ context_type); @CallerOwnsReturn Message gst_message_new_eos(GstObject src); Pointer ptr_gst_message_new_eos(GstObject src); + @CallerOwnsReturn GstMessagePtr gst_message_new_eos(GstObjectPtr src); @CallerOwnsReturn Message gst_message_new_error(GstObject src, GErrorStruct error, String debug); @CallerOwnsReturn Message gst_message_new_warning(GstObject src, GErrorStruct error, String debug); @CallerOwnsReturn Message gst_message_new_info(GstObject src, GErrorStruct error, String debug); @@ -121,4 +135,6 @@ protected List getFieldOrder() { Pointer ptr_gst_message_new_latency(GstObject source); @CallerOwnsReturn Message gst_message_new_custom(MessageType type, GstObject src, @Invalidate Structure structure); @ConstReturn Structure gst_message_get_structure(Message message); + Pointer ptr_gst_message_new_need_context(GstObject source, String context_type); + @CallerOwnsReturn Message gst_message_new_need_context(GstObject source, String context_type); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMessagePtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstMessagePtr.java new file mode 100644 index 00000000..c2d176ba --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMessagePtr.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * GstMessage pointer. + */ +public class GstMessagePtr extends GstMiniObjectPtr { + + private static final int TYPE_OFFSET; + private static final int SRC_OFFSET; + + static { + GstMessageAPI.MessageStruct struct = new GstMessageAPI.MessageStruct(); + TYPE_OFFSET = struct.typeOffset(); + SRC_OFFSET = struct.srcOffset(); + } + + + public GstMessagePtr() { + } + + public GstMessagePtr(Pointer ptr) { + super(ptr); + } + + public int getMessageType() { + return getPointer().getInt(TYPE_OFFSET); + } + + public GstObjectPtr getSource() { + Pointer raw = getPointer().getPointer(SRC_OFFSET); + return raw == null ? null : new GstObjectPtr(raw); + } + + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMetaAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstMetaAPI.java new file mode 100644 index 00000000..38c6b3ef --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMetaAPI.java @@ -0,0 +1,53 @@ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Library; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +/** + */ +public interface GstMetaAPI extends Library { + GstMetaAPI GST_META_API = GstNative.load(GstMetaAPI.class); + + + + @Structure.FieldOrder({"flags", "info"}) + class GstMetaStruct extends Structure { + public static final class ByValue extends GstMetaStruct implements Structure.ByValue { + } + + public int flags; + public GstMetaInfoStruct.ByReference info; + + int infoOffset() { + return fieldOffset("info"); + } + + } + + @Structure.FieldOrder({"api", "type", "size"}) + class GstMetaInfoStruct extends Structure { + public static class ByReference extends GstMetaInfoStruct implements Structure.ByReference { + } + + public GstMetaInfoStruct() { + } + + public GstMetaInfoStruct(Pointer p) { + super(p); + read(); + } + + public GType api; + public GType type; + public long size; + + int typeOffset() { + return fieldOffset("type"); + } + + } + + + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMetaPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstMetaPtr.java new file mode 100644 index 00000000..8a286ca6 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMetaPtr.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +/** + * Base GstMeta pointer + */ +public class GstMetaPtr extends GTypedPtr { + + private static final int INFO_OFFSET; + private static final int IMPL_TYPE_OFFSET; + + static { + INFO_OFFSET = new GstMetaAPI.GstMetaStruct().infoOffset(); + IMPL_TYPE_OFFSET = new GstMetaAPI.GstMetaInfoStruct().typeOffset(); + } + + public GstMetaPtr() { + } + + public GstMetaPtr(Pointer ptr) { + super(ptr); + } + + @Override + public GType getGType() { + // Quick getter for GType without allocation + Pointer metaInfo = getPointer().getPointer(INFO_OFFSET); + if (Native.SIZE_T_SIZE == 8) { + return GType.valueOf(metaInfo.getLong(IMPL_TYPE_OFFSET)); + } else if (Native.SIZE_T_SIZE == 4) { + return GType.valueOf(((long) metaInfo.getInt(IMPL_TYPE_OFFSET)) & 0xffffffffL); + } else { + throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); + } + } + + public GType getAPIGType() { + // Quick getter for GType without allocation + Pointer metaInfo = getPointer().getPointer(INFO_OFFSET); + if (Native.SIZE_T_SIZE == 8) { + return GType.valueOf(metaInfo.getLong(0)); + } else if (Native.SIZE_T_SIZE == 4) { + return GType.valueOf(((long) metaInfo.getInt(0)) & 0xffffffffL); + } else { + throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); + } + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMiniObjectAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstMiniObjectAPI.java index 0c5c4f98..dce15246 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstMiniObjectAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMiniObjectAPI.java @@ -21,6 +21,7 @@ package org.freedesktop.gstreamer.lowlevel; +import com.sun.jna.Callback; import java.util.Arrays; import java.util.List; @@ -38,9 +39,13 @@ public interface GstMiniObjectAPI extends com.sun.jna.Library { GstMiniObjectAPI GSTMINIOBJECT_API = GstNative.load(GstMiniObjectAPI.class); - void gst_mini_object_ref(MiniObject ptr); - void gst_mini_object_unref(MiniObject ptr); + void gst_mini_object_ref(GstMiniObjectPtr ptr); + void gst_mini_object_unref(GstMiniObjectPtr ptr); void gst_mini_object_unref(Pointer ptr); + void gst_mini_object_weak_ref(GstMiniObjectPtr ptr, GstMiniObjectNotify notify, + IntPtr data); + void gst_mini_object_weak_unref(GstMiniObjectPtr ptr, GstMiniObjectNotify notify, + IntPtr data); @CallerOwnsReturn MiniObject gst_mini_object_make_writable(@Invalidate MiniObject mini_object); @CallerOwnsReturn MiniObject gst_mini_object_copy(MiniObject mini_object); @@ -50,6 +55,12 @@ public interface GstMiniObjectAPI extends com.sun.jna.Library { Pointer gst_mini_object_get_qdata(MiniObject mini_object, GQuark quark); Pointer gst_mini_object_steal_qdata(MiniObject mini_object, GQuark quark); + public static interface GstMiniObjectNotify extends Callback { + + public void callback(IntPtr userData, Pointer obj); + + } + public static final class MiniObjectStruct extends com.sun.jna.Structure { public volatile GType type; public volatile int refcount; diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMiniObjectPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstMiniObjectPtr.java new file mode 100644 index 00000000..c2bffad8 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMiniObjectPtr.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +/** + * Base MiniObject pointer + */ +public class GstMiniObjectPtr extends GTypedPtr { + + public GstMiniObjectPtr() { + } + + public GstMiniObjectPtr(Pointer ptr) { + super(ptr); + } + + @Override + public GType getGType() { + // Quick getter for GType without allocation + // same as : new MiniObjectStruct(ptr).type + if (Native.SIZE_T_SIZE == 8) { + return GType.valueOf(getPointer().getLong(0)); + } else if (Native.SIZE_T_SIZE == 4) { + return GType.valueOf( ((long) getPointer().getInt(0)) & 0xffffffffL ); + } else { + throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); + } + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstNavigationAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstNavigationAPI.java index f0344d73..b961de5b 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstNavigationAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstNavigationAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Tamas Korodi * @@ -25,7 +26,7 @@ import com.sun.jna.Library; public interface GstNavigationAPI extends Library { - GstNavigationAPI GSTNAVIGATION_API = GstNative.load("gstinterfaces", GstNavigationAPI.class); + GstNavigationAPI GSTNAVIGATION_API = GstNative.load("gstvideo", GstNavigationAPI.class); GType gst_navigation_get_type(); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstObjectAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstObjectAPI.java index 59846a6b..a90db111 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstObjectAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstObjectAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner * @@ -28,8 +29,6 @@ import com.sun.jna.Pointer; import java.util.Arrays; import java.util.List; -import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.elements.BaseSrc; import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; /** @@ -41,9 +40,9 @@ public interface GstObjectAPI extends com.sun.jna.Library { GstObjectAPI GSTOBJECT_API = GstNative.load(GstObjectAPI.class); GType gst_object_get_type(); - void gst_object_ref(GstObject ptr); - void gst_object_unref(GstObject ptr); - void gst_object_ref_sink(GstObject ptr); + void gst_object_ref(GstObjectPtr ptr); + void gst_object_unref(GstObjectPtr ptr); + void gst_object_ref_sink(GstObjectPtr ptr); boolean gst_object_set_name(GstObject obj, String name); @FreeReturnValue String gst_object_get_name(GstObject obj); @@ -59,6 +58,17 @@ public interface GstObjectAPI extends com.sun.jna.Library { Pointer gst_implements_interface_cast(GstObject obj, NativeLong gtype); boolean gst_implements_interface_check(GstObject from, NativeLong type); + /* controller functions */ + long gst_object_suggest_next_sync(GstObjectPtr object); + boolean gst_object_sync_values(GstObjectPtr object, long timestamp); + boolean gst_object_has_active_control_bindings(GstObjectPtr object); + void gst_object_set_control_bindings_disabled(GstObjectPtr object, boolean disabled); + void gst_object_set_control_binding_disabled(GstObjectPtr object, String property_name, boolean disabled); + boolean gst_object_add_control_binding(GstObjectPtr object, GstControlBindingPtr binding); + GstControlBindingPtr gst_object_get_control_binding(GstObjectPtr object, String property_name); + boolean gst_object_remove_control_binding(GstObjectPtr object, GstControlBindingPtr binding); + + /** * GstObject: * @lock: object LOCK diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstObjectPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstObjectPtr.java new file mode 100644 index 00000000..e1d2edfb --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstObjectPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * Base GObject pointer + */ +public class GstObjectPtr extends GObjectPtr { + + public GstObjectPtr() { + } + + public GstObjectPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java index 3186ad91..cc4cd264 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2018 Antonio Morales * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2009 Levente Farkas @@ -24,7 +25,7 @@ import org.freedesktop.gstreamer.Buffer; import org.freedesktop.gstreamer.Caps; import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.Event; +import org.freedesktop.gstreamer.event.Event; import org.freedesktop.gstreamer.FlowReturn; import org.freedesktop.gstreamer.Pad; import org.freedesktop.gstreamer.PadDirection; @@ -39,6 +40,7 @@ import com.sun.jna.NativeLong; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.query.Query; /** * GstPad functions @@ -108,9 +110,9 @@ public interface GstPadAPI extends com.sun.jna.Library { @CallerOwnsReturn Caps gst_pad_query_caps(Pad pad, Caps caps); void gst_pad_fixate_caps(Pad pad, Caps caps); boolean gst_pad_query_accept_caps(Pad pad, Caps caps); - boolean gst_pad_set_caps(Pad pad, Caps caps); - @CallerOwnsReturn Caps gst_pad_peer_get_caps(Pad pad); - boolean gst_pad_peer_accept_caps(Pad pad, Caps caps); +// boolean gst_pad_set_caps(Pad pad, Caps caps); + @CallerOwnsReturn Caps gst_pad_peer_query_caps(Pad pad, Caps caps); + boolean gst_pad_peer_query_accept_caps(Pad pad, Caps caps); /* capsnego for connected pads */ @CallerOwnsReturn Caps gst_pad_get_allowed_caps(Pad pad); @@ -154,13 +156,15 @@ public static interface PadProbeCallback extends GstCallback { * @param destroy_data * @return */ - NativeLong gst_pad_add_probe(Pad pad, int mask, PadProbeCallback callback, + NativeLong gst_pad_add_probe(GstPadPtr pad, int mask, PadProbeCallback callback, Pointer user_data, GDestroyNotify destroy_data); - void gst_pad_remove_probe(Pad pad, NativeLong id); + void gst_pad_remove_probe(GstPadPtr pad, NativeLong id); Event gst_pad_probe_info_get_event(GstPadProbeInfo probeInfo); Buffer gst_pad_probe_info_get_buffer(GstPadProbeInfo probeInfo); + + Query gst_pad_probe_info_get_query(GstPadProbeInfo probeInfo); // NativeLong /* gulong */ gst_pad_add_data_probe(Pad pad, PadDataProbe handler, Pointer data); // diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstPadPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstPadPtr.java new file mode 100644 index 00000000..2716c53b --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstPadPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * Base GObject pointer + */ +public class GstPadPtr extends GstObjectPtr { + + public GstPadPtr() { + } + + public GstPadPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstPipelineAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstPipelineAPI.java index 485fbc81..944a0690 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstPipelineAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstPipelineAPI.java @@ -21,7 +21,6 @@ import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.Clock; -import org.freedesktop.gstreamer.ClockTime; import org.freedesktop.gstreamer.Pipeline; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; @@ -39,12 +38,12 @@ public interface GstPipelineAPI extends com.sun.jna.Library { @CallerOwnsReturn Bus gst_pipeline_get_bus(Pipeline pipeline); void gst_pipeline_set_auto_flush_bus(Pipeline pipeline, boolean flush); boolean gst_pipeline_get_auto_flush_bus(Pipeline pipeline); - void gst_pipeline_set_new_stream_time(Pipeline pipeline, ClockTime time); + void gst_pipeline_set_new_stream_time(Pipeline pipeline, long time); long gst_pipeline_get_last_stream_time(Pipeline pipeline); void gst_pipeline_use_clock(Pipeline pipeline, Clock clock); boolean gst_pipeline_set_clock(Pipeline pipeline, Clock clock); @CallerOwnsReturn Clock gst_pipeline_get_clock(Pipeline pipeline); void gst_pipeline_auto_clock(Pipeline pipeline); - void gst_pipeline_set_delay(Pipeline pipeline, ClockTime delay); + void gst_pipeline_set_delay(Pipeline pipeline, long delay); long gst_pipeline_get_delay(Pipeline pipeline); } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java index 0cd04d99..95c206ed 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java @@ -28,6 +28,7 @@ import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.lowlevel.annotations.Invalidate; /** * GstPromise methods and structures @@ -60,12 +61,12 @@ protected List getFieldOrder() { @CallerOwnsReturn Promise gst_promise_new(); @CallerOwnsReturn Pointer ptr_gst_promise_new(); - @CallerOwnsReturn Promise gst_promise_new_with_change_func(GstCallback callback); - @CallerOwnsReturn Pointer ptr_gst_promise_new_with_change_func(GstCallback callback); + @CallerOwnsReturn Promise gst_promise_new_with_change_func(GstCallback callback, Pointer userData, Pointer destroyNotify); + @CallerOwnsReturn Pointer ptr_gst_promise_new_with_change_func(GstCallback callback, Pointer userData, Pointer destroyNotify); PromiseResult gst_promise_wait(Promise promise); - void gst_promise_reply(Promise promise, Structure s); + void gst_promise_reply(Promise promise, @Invalidate Structure s); void gst_promise_interrupt(Promise promise); void gst_promise_expire(Promise promise); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstPropertyProbeAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstPropertyProbeAPI.java deleted file mode 100644 index cc1be91e..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/GstPropertyProbeAPI.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2009 Levente Farkas - * Copyright (c) 2008 Andres Colubri - * Copyright (c) 2008 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -import org.freedesktop.gstreamer.interfaces.PropertyProbe; -import org.freedesktop.gstreamer.lowlevel.GlibAPI.GList; - -import com.sun.jna.Library; -import com.sun.jna.Pointer; - -public interface GstPropertyProbeAPI extends Library { - GstPropertyProbeAPI GSTPROPERTYPROBE_API = GstNative.load("gstinterfaces", GstPropertyProbeAPI.class); - - GType gst_property_probe_get_type(); - - /* virtual class function wrappers */ - GList gst_property_probe_get_properties(PropertyProbe probe); - Pointer gst_property_probe_get_property(PropertyProbe probe, String name); - - void gst_property_probe_probe_property(PropertyProbe probe, GObjectAPI.GParamSpec pspec); - void gst_property_probe_probe_property_name(PropertyProbe probe, String name); - - boolean gst_property_probe_needs_probe(PropertyProbe probe, GObjectAPI.GParamSpec pspec); - boolean gst_property_probe_needs_probe_name(PropertyProbe probe, String name); - - Pointer gst_property_probe_get_values(PropertyProbe probe, GObjectAPI.GParamSpec pspec); - Pointer gst_property_probe_get_values_name(PropertyProbe probe, String name); - - Pointer gst_property_probe_probe_and_get_values(PropertyProbe probe, GObjectAPI.GParamSpec pspec); - Pointer gst_property_probe_probe_and_get_values_name(PropertyProbe probe, String name); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstProxyControlBindingPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstProxyControlBindingPtr.java new file mode 100644 index 00000000..ae11281b --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstProxyControlBindingPtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstProxyControlBindingPtr extends GstControlBindingPtr { + + public GstProxyControlBindingPtr() { + } + + public GstProxyControlBindingPtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstQueryAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstQueryAPI.java index 99a298ec..e3305349 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstQueryAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstQueryAPI.java @@ -24,10 +24,9 @@ import org.freedesktop.gstreamer.BufferPool; import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.ClockTime; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Query; -import org.freedesktop.gstreamer.QueryType; +import org.freedesktop.gstreamer.query.Query; +import org.freedesktop.gstreamer.query.QueryType; import org.freedesktop.gstreamer.Structure; import org.freedesktop.gstreamer.glib.GQuark; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; @@ -59,10 +58,10 @@ public interface GstQueryAPI extends com.sun.jna.Library { /* latency query */ @CallerOwnsReturn Query gst_query_new_latency(); @CallerOwnsReturn Pointer ptr_gst_query_new_latency(); - void gst_query_set_latency(Query query, boolean live, ClockTime min_latency, - ClockTime max_latency); - void gst_query_parse_latency(Query query, boolean[] live, ClockTime[] min_latency, - ClockTime[] max_latency); + void gst_query_set_latency(Query query, boolean live, long min_latency, + long max_latency); + void gst_query_parse_latency(Query query, boolean[] live, long[] min_latency, + long[] max_latency); /* convert query */ @CallerOwnsReturn Query gst_query_new_convert(Format src_format, /* gint64 */ long value, Format dest_format); @@ -109,7 +108,7 @@ public static final class QueryStruct extends com.sun.jna.Structure { public volatile GstMiniObjectAPI.MiniObjectStruct mini_object; /*< public > *//* with COW */ - public volatile int type; + public volatile QueryType type; public QueryStruct(Pointer ptr) { useMemory(ptr); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstSampleAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstSampleAPI.java index d5ca77ac..7e446052 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstSampleAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstSampleAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Christophe Lafolet * Copyright (c) 2015 Neil C Smith * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2009 Levente Farkas @@ -21,22 +22,23 @@ package org.freedesktop.gstreamer.lowlevel; -import org.freedesktop.gstreamer.Sample; -import org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.MiniObjectStruct; - -import com.sun.jna.Pointer; import java.util.Arrays; import java.util.List; + import org.freedesktop.gstreamer.Buffer; import org.freedesktop.gstreamer.Caps; +import org.freedesktop.gstreamer.Sample; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstSegmentStruct; +import org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.MiniObjectStruct; + +import com.sun.jna.Pointer; /** * GstSampleAPI functions and structure * @see https://cgit.freedesktop.org/gstreamer/gstreamer/tree/gst/gstsample.c?h=1.8 */ public interface GstSampleAPI extends com.sun.jna.Library { - GstSampleAPI GSTMESSAGE_API = GstNative.load(GstSampleAPI.class); + GstSampleAPI GSTSAMPLE_API = GstNative.load(GstSampleAPI.class); public static final class SampleStruct extends com.sun.jna.Structure { public volatile MiniObjectStruct mini_object; @@ -67,4 +69,7 @@ protected List getFieldOrder() { /*@CallerOwnsReturn*/ Caps gst_sample_get_caps(Sample sample); /*@CallerOwnsReturn*/ Buffer gst_sample_get_buffer(Sample sample); + void gst_sample_set_buffer(Sample sample, Buffer buffer); + void gst_sample_set_caps(Sample sample, Caps caps); + } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java index d5721789..bdb3185a 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java @@ -68,7 +68,7 @@ public interface GstStructureAPI extends com.sun.jna.Library { boolean gst_structure_get_date(Structure structure, String fieldname, PointerByReference value); boolean gst_structure_get_date(Structure structure, String fieldname, Pointer[] value); - //boolean gst_structure_get_clock_time(Structure structure, String fieldname, GstClockTime *value); + //boolean gst_structure_get_clock_time(Structure structure, String fieldname, Gstlong *value); String gst_structure_get_string(Structure structure, String fieldname); boolean gst_structure_get_enum(Structure structure, String fieldname, GType enumtype, int[] value); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstStructurePtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstStructurePtr.java new file mode 100644 index 00000000..c70cee79 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstStructurePtr.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +/** + * GstStructure pointer + */ +public class GstStructurePtr extends GTypedPtr { + + public GstStructurePtr() { + } + + public GstStructurePtr(Pointer ptr) { + super(ptr); + } + + @Override + public GType getGType() { + // Quick getter for GType without allocation + if (Native.SIZE_T_SIZE == 8) { + return GType.valueOf(getPointer().getLong(0)); + } else if (Native.SIZE_T_SIZE == 4) { + return GType.valueOf( ((long) getPointer().getInt(0)) & 0xffffffffL ); + } else { + throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); + } + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstTimedValueControlSourcePtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstTimedValueControlSourcePtr.java new file mode 100644 index 00000000..3c423f3d --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstTimedValueControlSourcePtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstTimedValueControlSourcePtr extends GstControlSourcePtr { + + public GstTimedValueControlSourcePtr() { + } + + public GstTimedValueControlSourcePtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstTriggerControlSourcePtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstTriggerControlSourcePtr.java new file mode 100644 index 00000000..6547d0b0 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstTriggerControlSourcePtr.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Pointer; + +/** + * + */ +public class GstTriggerControlSourcePtr extends GstTimedValueControlSourcePtr { + + public GstTriggerControlSourcePtr() { + } + + public GstTriggerControlSourcePtr(Pointer ptr) { + super(ptr); + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstTypes.java b/src/org/freedesktop/gstreamer/lowlevel/GstTypes.java index 737ea986..a4c15337 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstTypes.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstTypes.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * Copyright (c) 2009 Levente Farkas * Copyright (c) 2008 Andres Colubri @@ -18,73 +19,85 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.lowlevel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.glib.NativeObject.TypeRegistration; /** * */ public class GstTypes { + private static final Logger logger = Logger.getLogger(GstTypes.class.getName()); - private static final Map> gtypeNameMap - = new ConcurrentHashMap>(); +// private static final Map> gtypeNameMap +// = new ConcurrentHashMap>(); + private static final Map> TYPES + = new ConcurrentHashMap<>(); + + private GstTypes() { + } - private GstTypes() {} - /** * Register a class with its GType name */ - public static void registerType(Class cls, String gTypeName) { - gtypeNameMap.put(gTypeName, cls); +// public static void registerType(Class cls, String gTypeName) { +// gtypeNameMap.put(gTypeName, cls); +// } + public static void register(TypeRegistration registration) { + TYPES.putIfAbsent(registration.getGTypeName(), registration); } - + /** * Retrieve the class of a GType * * @param gType The type of Class * @return The Class of the desired type or null. */ - public static final Class classFor(final GType gType) { - final String gTypeName = gType.getTypeName(); + public static final TypeRegistration registrationFor(final GType gType) { + final String gTypeName = gType.getTypeName(); - // Is this GType still registered in the map ? - Class cls = gtypeNameMap.get(gTypeName); - if (cls != null) { - return cls; + // Is this GType still registered in the map ? + TypeRegistration reg = TYPES.get(gTypeName); + if (reg != null) { + return reg; } // Search for a parent class registration GType type = gType.getParentType(); while (!type.equals(GType.OBJECT) && !type.equals(GType.POINTER) && !type.equals(GType.INVALID)) { - cls = gtypeNameMap.get(type.getTypeName()); - if (cls != null) { + reg = TYPES.get(type.getTypeName()); + if (reg != null) { if (GstTypes.logger.isLoggable(Level.FINER)) { - GstTypes.logger.finer("Found type of " + gType + " = " + cls); + GstTypes.logger.finer("Found type of " + gType + " = " + reg.getJavaType()); } - + // The following line is an optimisation but not compatible with current implementation of GstTypes.typeFor() // Uncomment the following line after refactoring of GstTypes.typeFor() // gtypeNameMap.put(gTypeName, cls); - - return cls; + return reg; } - type = type.getParentType(); + type = type.getParentType(); } // No registered class found for this gType return null; } + + public static final Class classFor(final GType gType) { + TypeRegistration reg = registrationFor(gType); + return reg != null ? reg.getJavaType() : null; + } //TODO : need refactoring to take into account derived class public static final GType typeFor(Class cls) { - for (Map.Entry> e : gtypeNameMap.entrySet()) { - if (e.getValue().equals(cls)) { + for (Map.Entry> e : TYPES.entrySet()) { + if (e.getValue().getJavaType().equals(cls)) { return GType.valueOf(e.getKey()); } } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstURIAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstURIAPI.java deleted file mode 100644 index d1c83938..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/GstURIAPI.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010 DHoyt - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -import com.sun.jna.Library; -import com.sun.jna.Pointer; - -import org.freedesktop.gstreamer.URIType; -import org.freedesktop.gstreamer.lowlevel.GstNative; -import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; - -/** - * The URIHandler is an interface that is implemented by Source and Sink GstElement to simplify then handling of URI. - * An application can use the following functions to quickly get an element that handles the given URI for reading or - * writing (gst_element_make_from_uri()). - * Source and Sink plugins should implement this interface when possible. - */ -public interface GstURIAPI extends Library { - GstURIAPI GSTURI_API = GstNative.load(GstURIAPI.class); - - boolean gst_uri_protocol_is_valid(String protocol); - - boolean gst_uri_protocol_is_supported(URIType type, String protocol); - - boolean gst_uri_is_valid(String uri); - - boolean gst_uri_has_protocol(String uri, String protocol); - - @CallerOwnsReturn - Pointer gst_element_make_from_uri(URIType type, String uri, String name); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstValueAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstValueAPI.java index f065336b..026c851c 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstValueAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstValueAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner * @@ -19,10 +20,11 @@ package org.freedesktop.gstreamer.lowlevel; +import com.sun.jna.Pointer; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValue; /** - * GstStructure functions + * GstValue functions */ public interface GstValueAPI extends com.sun.jna.Library { GstValueAPI GSTVALUE_API = GstNative.load(GstValueAPI.class); @@ -44,4 +46,8 @@ public interface GstValueAPI extends com.sun.jna.Library { int gst_value_get_int_range_max(GValue value); int gst_value_list_get_size(GValue value); GValue gst_value_list_get_value(GValue value, int index); + + boolean gst_value_deserialize(GValue value, String src); + Pointer gst_value_serialize(GValue value); + } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java index a91d0b8d..f83ced11 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java @@ -1,4 +1,4 @@ -/* +/* * This file is part of gstreamer-java. * * This code is free software: you can redistribute it and/or modify it under @@ -21,19 +21,92 @@ import com.sun.jna.Library; import com.sun.jna.Pointer; -import org.freedesktop.gstreamer.ClockTime; +import com.sun.jna.Structure; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; +import org.freedesktop.gstreamer.video.VideoTimeCodeFlags; public interface GstVideoAPI extends Library { public final static GstVideoAPI GSTVIDEO_API = GstNative.load("gstvideo", GstVideoAPI.class); + @CallerOwnsReturn + Pointer gst_video_time_code_new_empty(); + void gst_video_time_code_free(Pointer gstVideoTimeCode); GValue gst_video_frame_rate(Pad pad); boolean gst_video_get_size(Pad pad, int [] width, int [] height); - + /* */ Pointer ptr_gst_video_event_new_downstream_force_key_unit( - ClockTime timestamp, ClockTime stream_time, ClockTime running_time, + long timestamp, long stream_time, long running_time, boolean all_headers, int count); Pointer ptr_gst_video_event_new_upstream_force_key_unit( - ClockTime running_time, boolean all_headers, int count); + long running_time, boolean all_headers, int count); + + GType gst_video_time_code_meta_api_get_type(); + + GType gst_video_crop_meta_api_get_type(); + + GType gst_video_gl_texture_upload_meta_api_get_type(); + + GType gst_video_meta_api_get_type(); + + GType gst_video_region_of_interest_meta_api_get_type(); + +// MetaInfo gst_video_time_code_meta_get_info(); + + + @Structure.FieldOrder({"meta", "tc"}) + class GstVideoTimeCodeMetaStruct extends Structure { + public GstMetaAPI.GstMetaStruct.ByValue meta; + public GstVideoTimeCodeStruct.ByValue tc; + + public GstVideoTimeCodeMetaStruct(Pointer p) { + super(p); + read(); + } + } + + @Structure.FieldOrder({"config", "hours", "minutes", "seconds", "frames", "field_count"}) + @Gst.Since(minor = 10) + class GstVideoTimeCodeStruct extends Structure { + public static class ByValue extends GstVideoTimeCodeStruct implements Structure.ByValue { + } + + public GstVideoTimeCodeConfigStruct.ByValue config; + public int hours; + public int minutes; + public int seconds; + public int frames; + public int field_count; + + public GstVideoTimeCodeStruct() { + } + + public GstVideoTimeCodeStruct(Pointer p) { + super(p); + read(); + } + } + + @Structure.FieldOrder({"fps_n", "fps_d", "flags", "latest_daily_jam"}) + @Gst.Since(minor = 10) + class GstVideoTimeCodeConfigStruct extends Structure { + + public static class ByValue extends GstVideoTimeCodeConfigStruct implements Structure.ByValue { + } + + public int fps_n; + public int fps_d; + public int flags; + public Pointer latest_daily_jam; + + public GstVideoTimeCodeConfigStruct() { + } + + public GstVideoTimeCodeConfigStruct(Pointer p) { + super(p); + read(); + } + } } diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstVideoOrientationAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstVideoOrientationAPI.java index 16b7ff59..1ff9c1d1 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstVideoOrientationAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstVideoOrientationAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Tamas Korodi * @@ -24,18 +25,18 @@ import com.sun.jna.Library; public interface GstVideoOrientationAPI extends Library { - GstVideoOrientationAPI GSTVIDEOORIENTATION_API = GstNative.load("gstinterfaces", GstVideoOrientationAPI.class); + GstVideoOrientationAPI GSTVIDEOORIENTATION_API = GstNative.load("gstvideo", GstVideoOrientationAPI.class); GType gst_video_orientation_get_type(); - /* vitrual class functions */ - boolean gst_video_orientation_get_hflip(VideoOrientation video_orientation, boolean flip); - - boolean gst_video_orientation_get_vflip(VideoOrientation video_orientation, boolean flip); - - boolean gst_video_orientation_get_hcenter(VideoOrientation video_orientation, int center); - - boolean gst_video_orientation_get_vcenter(VideoOrientation video_orientation, int center); + // @TODO need implementing to use pointers +// boolean gst_video_orientation_get_hflip(VideoOrientation video_orientation, boolean flip); +// +// boolean gst_video_orientation_get_vflip(VideoOrientation video_orientation, boolean flip); +// +// boolean gst_video_orientation_get_hcenter(VideoOrientation video_orientation, int center); +// +// boolean gst_video_orientation_get_vcenter(VideoOrientation video_orientation, int center); boolean gst_video_orientation_set_hflip(VideoOrientation video_orientation, boolean flip); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstVideoOverlayAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstVideoOverlayAPI.java index e6cb835f..ff6c127f 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstVideoOverlayAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstVideoOverlayAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2009 Tamas Korodi * @@ -19,7 +20,7 @@ package org.freedesktop.gstreamer.lowlevel; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.message.Message; import org.freedesktop.gstreamer.interfaces.VideoOverlay; import com.sun.jna.Library; @@ -31,9 +32,6 @@ public interface GstVideoOverlayAPI extends Library { GType gst_video_overlay_get_type(); - /* virtual class function wrappers */ - void gst_video_overlay_set_window_handle(VideoOverlay overlay, NativeLong xwindow_id); - void gst_video_overlay_set_window_handle(VideoOverlay overlay, Pointer xwindow_id); void gst_video_overlay_got_window_handle(VideoOverlay overlay, NativeLong xwindow_id); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java index fa926345..0e120683 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java @@ -20,8 +20,8 @@ import java.util.Arrays; import java.util.List; -import org.freedesktop.gstreamer.WebRTCSessionDescription; -import org.freedesktop.gstreamer.WebRTCSDPType; +import org.freedesktop.gstreamer.webrtc.WebRTCSessionDescription; +import org.freedesktop.gstreamer.webrtc.WebRTCSDPType; import org.freedesktop.gstreamer.SDPMessage; import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValueArray; diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstXOverlayAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstXOverlayAPI.java deleted file mode 100644 index 96420603..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/GstXOverlayAPI.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2009 Levente Farkas - * Copyright (c) 2009 Tamas Korodi - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -import org.freedesktop.gstreamer.interfaces.XOverlay; - -import com.sun.jna.Library; -import com.sun.jna.NativeLong; -import com.sun.jna.Pointer; - -public interface GstXOverlayAPI extends Library { - GstXOverlayAPI GSTXOVERLAY_API = GstNative.load("gstinterfaces", GstXOverlayAPI.class); - - GType gst_x_overlay_get_type(); - - /* virtual class function wrappers */ - void gst_x_overlay_set_window_handle(XOverlay overlay, NativeLong xwindow_id); - - void gst_x_overlay_set_window_handle(XOverlay overlay, Pointer xwindow_id); - - void gst_x_overlay_got_window_handle(XOverlay overlay, NativeLong xwindow_id); - - void gst_x_overlay_prepare_xwindow_id(XOverlay overlay); - - void gst_x_overlay_expose(XOverlay overlay); - - void gst_x_overlay_handle_events(XOverlay overlay, boolean handle_events); - - boolean gst_x_overlay_set_render_rectangle(XOverlay overlay, int x, int y, int width, int height); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/Handle.java b/src/org/freedesktop/gstreamer/lowlevel/Handle.java deleted file mode 100644 index 73368ad9..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/Handle.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2007 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -/** - * - */ -abstract public class Handle extends NativeValue { - - public Handle() { - } - abstract protected void invalidate(); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/IntegerEnum.java b/src/org/freedesktop/gstreamer/lowlevel/IntegerEnum.java deleted file mode 100644 index 7ec51d44..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/IntegerEnum.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2007 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -/** - * Interface for int enums that are non-contiguous (i.e. can't just use Enum.ordinal()) - * to find the native value. - */ -public interface IntegerEnum { - public int intValue(); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/MainLoop.java b/src/org/freedesktop/gstreamer/lowlevel/MainLoop.java index 52169768..a2eaf8f4 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/MainLoop.java +++ b/src/org/freedesktop/gstreamer/lowlevel/MainLoop.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.lowlevel; import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; @@ -29,37 +28,47 @@ import org.freedesktop.gstreamer.Gst; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.GMainContext; +import org.freedesktop.gstreamer.glib.GSource; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.glib.RefCountedObject; /** * The GLib main loop. */ public class MainLoop extends RefCountedObject { + private static final List bgTasks = new LinkedList(); - - /** + + private Thread bgThread; + + /** * Creates a new instance of {@code MainLoop} - * - *

This will create a new main loop on the default gstreamer main context. - * + * + *

+ * This will create a new main loop on the default gstreamer main context. + * */ public MainLoop() { - super(initializer(GLIB_API.g_main_loop_new(Gst.getMainContext(), false))); + this(Natives.initializer(GLIB_API.g_main_loop_new(Gst.getMainContext(), false))); } public MainLoop(GMainContext ctx) { - super(initializer(GLIB_API.g_main_loop_new(ctx, false))); + this(Natives.initializer(GLIB_API.g_main_loop_new(ctx, false))); } + /** * Creates a new instance of {@code MainLoop} - * - *

This variant is used internally. - * + * + *

+ * This variant is used internally. + * * @param init internal initialization data. */ - public MainLoop(Initializer init) { - super(init); + public MainLoop(Initializer init) { + super(new Handle(init.ptr, init.ownsHandle), init.needRef); } - + /** * Instructs a main loop to stop processing and return from {@link #run}. */ @@ -70,34 +79,34 @@ public void run() { } }); } - + /** * Enter a loop, processing all events. - *

The loop will continue processing events until {@link #quit} is - * called. + *

+ * The loop will continue processing events until {@link #quit} is called. */ public void run() { GLIB_API.g_main_loop_run(this); } - + /** * Returns whether this main loop is currently processing or not. - * + * * @return true if the main loop is currently being run. */ public boolean isRunning() { return GLIB_API.g_main_loop_is_running(this); } - + /** * Gets the main context for this main loop. - * + * * @return a main context. */ public GMainContext getMainContext() { return GLIB_API.g_main_loop_get_context(this); } - + /** * Runs the main loop in a background thread. */ @@ -112,11 +121,12 @@ public void run() { bgThread.setName("gmainloop"); bgThread.start(); } - + /** * Invokes a task on the main loop thread. - *

This method will wait until the task has completed before returning. - * + *

+ * This method will wait until the task has completed before returning. + * * @param r the task to invoke. */ public void invokeAndWait(Runnable r) { @@ -145,11 +155,13 @@ public boolean callback(Pointer source) { return false; } }; + /** * Invokes a task on the main loop thread. - *

This method returns immediately, without waiting for the task to + *

+ * This method returns immediately, without waiting for the task to * complete. - * + * * @param r the task to invoke. */ public void invokeLater(final Runnable r) { @@ -167,33 +179,28 @@ public void invokeLater(final Runnable r) { } } } - - //-------------------------------------------------------------------------- - // protected methods - // - /** - * Increases the reference count on the native {@code GMainLoop} - */ - protected void ref() { - GLIB_API.g_main_loop_ref(this); - } - - /** - * Decreases the reference count on the native {@code GMainLoop} - */ - protected void unref() { - GLIB_API.g_main_loop_unref(this); - } - - /** - * Frees the native {@code GMainLoop} - */ - protected void disposeNativeHandle(Pointer ptr) { - GLIB_API.g_main_loop_unref(ptr); + + private final static class Handle extends RefCountedObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GLIB_API.g_main_loop_unref(ptr); + } + + @Override + protected void ref() { + GLIB_API.g_main_loop_ref(getPointer()); + } + + @Override + protected void unref() { + GLIB_API.g_main_loop_unref(getPointer()); + } + } - - //-------------------------------------------------------------------------- - // Instance variables - // - private Thread bgThread; + } diff --git a/src/org/freedesktop/gstreamer/lowlevel/NativeObject.java b/src/org/freedesktop/gstreamer/lowlevel/NativeObject.java deleted file mode 100644 index 3b2e7fba..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/NativeObject.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (c) 2007 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -import java.lang.ref.WeakReference; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.freedesktop.gstreamer.GObject; -import org.freedesktop.gstreamer.Gst; -import org.freedesktop.gstreamer.MiniObject; -import org.freedesktop.gstreamer.lowlevel.annotations.HasSubtype; - -import com.sun.jna.Pointer; - -/** - * - */ -public abstract class NativeObject extends org.freedesktop.gstreamer.lowlevel.Handle { - private static final Logger logger = Logger.getLogger(NativeObject.class.getName()); - private static final Level LIFECYCLE = Level.FINE; - - // Use this to propagate low level pointer arguments up the constructor chain - protected static class Initializer { - public final Pointer ptr; - public final boolean needRef, ownsHandle; - public Initializer() { - this.ptr = null; - this.needRef = false; - this.ownsHandle = false; - } - public Initializer(Pointer ptr, boolean needRef, boolean ownsHandle) { - this.ptr = ptr; - this.needRef = needRef; - this.ownsHandle = ownsHandle; - } - } - protected static final Initializer defaultInit = new Initializer(); - - /* - * The default for new objects is to not need a refcount increase, and that - * they own the native object. Special cases can use the other constructor. - */ - protected static Initializer initializer(Pointer ptr) { - Initializer initializer = initializer(ptr, false, true); - return initializer; - } - protected static Initializer initializer(Pointer ptr, boolean needRef, boolean ownsHandle) { - if (ptr == null) { - throw new IllegalArgumentException("Invalid native pointer"); - } - return new Initializer(ptr, needRef, ownsHandle); - } - /** Creates a new instance of NativeObject */ - protected NativeObject(final Initializer init) { - logger.entering("NativeObject", "", new Object[] { init }); - if (init == null) { - throw new IllegalArgumentException("Initializer cannot be null"); - } - logger.log(LIFECYCLE, "Creating " + getClass().getSimpleName() + " (" + init.ptr + ")"); - nativeRef = new NativeRef(this); - this.handle = init.ptr; - this.ownsHandle.set(init.ownsHandle); - - // - // Only store this object in the map if we can tell when it has been disposed - // (i.e. must be at least a GObject - MiniObject and other NativeObject subclasses - // don't signal destruction, so it is impossible to know if the instance - // is stale or not - // - if (GObject.class.isAssignableFrom(getClass())) { - getInstanceMap().put(init.ptr, nativeRef); - } - - } - - abstract protected void disposeNativeHandle(Pointer ptr); - - public void dispose() { - logger.log(LIFECYCLE, "Disposing object " + getClass().getName() + " = " + handle); -// System.out.println("Disposing " + handle); - if (!disposed.getAndSet(true)) { - getInstanceMap().remove(handle, nativeRef); - if (ownsHandle.get()) { - disposeNativeHandle(handle); - } - valid.set(false); - } - } - - @Override - protected void invalidate() { - logger.log(LIFECYCLE, "Invalidating object " + this + " = " + handle()); - getInstanceMap().remove(handle(), nativeRef); - disposed.set(true); - ownsHandle.set(false); - valid.set(false); - } - - @Override - protected void finalize() throws Throwable { - try { - logger.log(LIFECYCLE, "Finalizing " + getClass().getSimpleName() + " (" + handle + ")"); -// System.out.println("Finalizing " + getClass().getSimpleName() + " (" + handle + ")"); - dispose(); - } finally { - super.finalize(); - } - } - @Override - protected Object nativeValue() { - return handle(); - } - protected Pointer handle() { - if (!valid.get() || disposed.get()) { - throw new IllegalStateException("Native object has been disposed"); - } - return handle; - } - public Pointer getNativeAddress() { - return handle; - } - protected boolean isDisposed() { - return disposed.get(); - } - protected static NativeObject instanceFor(Pointer ptr) { - WeakReference ref = getInstanceMap().get(ptr); - - // - // If the reference was there, but the object it pointed to had been collected, remove it from the map - // - if (ref != null && ref.get() == null) { - getInstanceMap().remove(ptr); - } - return ref != null ? ref.get() : null; - } - public static T objectFor(Pointer ptr, Class cls) { - return objectFor(ptr, cls, true); - } - public static T objectFor(Pointer ptr, Class cls, boolean needRef) { - return objectFor(ptr, cls, needRef, true); - } - public static T objectFor(Pointer ptr, Class cls, boolean needRef, boolean ownsHandle) { - return objectFor(ptr, cls, needRef ? 1 : 0, ownsHandle); - } - - public static T objectFor(Pointer ptr, Class cls, int refAdjust, boolean ownsHandle) { - logger.entering("NativeObject", "instanceFor", new Object[] { ptr, refAdjust, ownsHandle }); - - // Ignore null pointers - if (ptr == null) { - return null; - } - NativeObject obj = GObject.class.isAssignableFrom(cls) ? NativeObject.instanceFor(ptr) : null; - if (obj != null && cls.isInstance(obj)) { - if (refAdjust < 0) { - ((RefCountedObject) obj).unref(); // Lose the extra ref added by gstreamer - } - return cls.cast(obj); - } - - final GType gType = - GObject.class.isAssignableFrom(cls) ? GObject.getType(ptr) : - MiniObject.class.isAssignableFrom(cls) ? MiniObject.getType(ptr) : - null; // shall never appears - - // - // For a GObject, MiniObject, ..., use the GType field to find the most - // exact class match - // - if (gType != null) { - cls = NativeObject.classFor(ptr, gType, cls); - } - - try { - Constructor constructor = cls.getDeclaredConstructor(Initializer.class); - T retVal = constructor.newInstance(initializer(ptr, refAdjust > 0, ownsHandle)); - //retVal.initNativeHandle(ptr, refAdjust > 0, ownsHandle); - return retVal; - } catch (SecurityException ex) { - throw new RuntimeException(ex); - } catch (IllegalAccessException ex) { - throw new RuntimeException(ex); - } catch (InstantiationException ex) { - throw new RuntimeException(ex); - } catch (NoSuchMethodException ex) { - throw new RuntimeException(ex); - } catch (InvocationTargetException ex) { - throw new RuntimeException(ex); - } - - } - - @SuppressWarnings("unchecked") - protected static Class classFor(Pointer ptr, final GType gType, final Class defaultClass) { - Class cls = (Class)GstTypes.classFor(gType); - if (cls == null) cls = defaultClass; - - if (cls.isAnnotationPresent(HasSubtype.class)) { - cls = (Class)SubtypeMapper.subtypeFor(cls, ptr); - } - - return cls; - } - - @Override - public boolean equals(Object o) { - return o instanceof NativeObject && ((NativeObject) o).handle.equals(handle); - } - - @Override - public int hashCode() { - return handle.hashCode(); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + handle() + ")"; - } - - // - // No longer want to garbage collect this object - // - public void disown() { - logger.log(LIFECYCLE, "Disowning " + handle()); - ownsHandle.set(false); - } - - static { - // - // Add a shutdown task to cleanup any dangling object references, so - // Gst.deinit() can shutdown cleanly. Unreffing objects after gst_deinit() - // has been called could be asking for trouble. - // - Gst.addStaticShutdownTask(new Runnable() { - - public void run() { - System.gc(); - int gcCount = 20; - // Give the GC a chance to cleanup nicely - while (!getInstanceMap().isEmpty() && gcCount-- > 0) { - try { - Thread.sleep(10); - System.gc(); - } catch (InterruptedException ex) { - break; - } - } - for (Object o : getInstanceMap().values().toArray()) { - NativeObject obj = ((NativeRef) o).get(); - if (obj != null && !obj.disposed.get()) { -// System.out.println("Disposing " + obj); - obj.dispose(); - } - } - } - }); - } - private static final ConcurrentMap getInstanceMap() { - return StaticData.instanceMap; - } - static class NativeRef extends WeakReference { - public NativeRef(NativeObject obj) { - super(obj); - } - } - private final AtomicBoolean disposed = new AtomicBoolean(false); - private final AtomicBoolean valid = new AtomicBoolean(true); - private final Pointer handle; - protected final AtomicBoolean ownsHandle = new AtomicBoolean(false); - private final NativeRef nativeRef; - private static final class StaticData { - private static final ConcurrentMap instanceMap = new ConcurrentHashMap(); - static { - // - // Add a shutdown task to cleanup any dangling object references, so - // Gst.deinit() can shutdown cleanly. Unreffing objects after gst_deinit() - // has been called could be asking for trouble. - // - Gst.addStaticShutdownTask(new Runnable() { - - public void run() { - System.gc(); - int gcCount = 20; - // Give the GC a chance to cleanup nicely - while (!getInstanceMap().isEmpty() && gcCount-- > 0) { - try { - Thread.sleep(10); - System.gc(); - } catch (InterruptedException ex) { - break; - } - } - for (Object o : getInstanceMap().values().toArray()) { - NativeObject obj = ((NativeRef) o).get(); - if (obj != null && !obj.disposed.get()) { - // System.out.println("Disposing " + obj); - obj.dispose(); - } - } - } - }); - } - } -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/NativeValue.java b/src/org/freedesktop/gstreamer/lowlevel/NativeValue.java deleted file mode 100644 index 3521ef19..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/NativeValue.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2007 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -/** - * - */ -public abstract class NativeValue { - - protected NativeValue() { - } - abstract protected Object nativeValue(); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/RefCountedObject.java b/src/org/freedesktop/gstreamer/lowlevel/RefCountedObject.java deleted file mode 100644 index a1316308..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/RefCountedObject.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2007, 2008 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -/** - * A {@link NativeObject} that has an associated reference count - * @author wayne - */ -abstract public class RefCountedObject extends NativeObject { - /** Creates a new instance of RefCountedObject */ - protected RefCountedObject(Initializer init) { - super(init); - if (init.ownsHandle && init.needRef) { - ref(); - } - } - // overridden in subclasses - abstract protected void ref(); - abstract protected void unref(); -} diff --git a/src/org/freedesktop/gstreamer/lowlevel/ReferenceManager.java b/src/org/freedesktop/gstreamer/lowlevel/ReferenceManager.java index e2c802b8..41ba35bc 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/ReferenceManager.java +++ b/src/org/freedesktop/gstreamer/lowlevel/ReferenceManager.java @@ -22,6 +22,7 @@ import java.lang.ref.WeakReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.freedesktop.gstreamer.glib.RefCountedObject; /** * Manages keep alive links from one object to another. diff --git a/src/org/freedesktop/gstreamer/lowlevel/SubtypeMapper.java b/src/org/freedesktop/gstreamer/lowlevel/SubtypeMapper.java deleted file mode 100644 index 79ef7cae..00000000 --- a/src/org/freedesktop/gstreamer/lowlevel/SubtypeMapper.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2008 Wayne Meissner - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ - -package org.freedesktop.gstreamer.lowlevel; - -import java.util.HashMap; -import java.util.Map; - -import org.freedesktop.gstreamer.Event; -import org.freedesktop.gstreamer.EventType; -import org.freedesktop.gstreamer.Message; -import org.freedesktop.gstreamer.MessageType; -import org.freedesktop.gstreamer.Query; -import org.freedesktop.gstreamer.QueryType; -import org.freedesktop.gstreamer.event.BufferSizeEvent; -import org.freedesktop.gstreamer.event.CapsEvent; -import org.freedesktop.gstreamer.event.EOSEvent; -import org.freedesktop.gstreamer.event.FlushStartEvent; -import org.freedesktop.gstreamer.event.FlushStopEvent; -import org.freedesktop.gstreamer.event.LatencyEvent; -import org.freedesktop.gstreamer.event.NavigationEvent; -import org.freedesktop.gstreamer.event.QOSEvent; -import org.freedesktop.gstreamer.event.ReconfigureEvent; -import org.freedesktop.gstreamer.event.SeekEvent; -import org.freedesktop.gstreamer.event.SegmentEvent; -import org.freedesktop.gstreamer.event.StepEvent; -import org.freedesktop.gstreamer.event.StreamStartEvent; -import org.freedesktop.gstreamer.event.TagEvent; -import org.freedesktop.gstreamer.message.BufferingMessage; -import org.freedesktop.gstreamer.message.DurationChangedMessage; -import org.freedesktop.gstreamer.message.EOSMessage; -import org.freedesktop.gstreamer.message.ErrorMessage; -import org.freedesktop.gstreamer.message.InfoMessage; -import org.freedesktop.gstreamer.message.LatencyMessage; -import org.freedesktop.gstreamer.message.SegmentDoneMessage; -import org.freedesktop.gstreamer.message.StateChangedMessage; -import org.freedesktop.gstreamer.message.TagMessage; -import org.freedesktop.gstreamer.message.WarningMessage; -import org.freedesktop.gstreamer.query.AllocationQuery; -import org.freedesktop.gstreamer.query.ConvertQuery; -import org.freedesktop.gstreamer.query.DurationQuery; -import org.freedesktop.gstreamer.query.FormatsQuery; -import org.freedesktop.gstreamer.query.LatencyQuery; -import org.freedesktop.gstreamer.query.PositionQuery; -import org.freedesktop.gstreamer.query.SeekingQuery; -import org.freedesktop.gstreamer.query.SegmentQuery; - -import com.sun.jna.Pointer; - -/** - * Mapper for classes which have subtypes (e.g. Event, Message, Query). - *

- * This class will return the subtype of the super class that best matches the - * raw pointer passed in. - */ -@SuppressWarnings("serial") -class SubtypeMapper { - static Class subtypeFor(final Class defaultClass, final Pointer ptr) { - Mapper mapper = MapHolder.mappers.get(defaultClass); - Class cls = mapper != null ? mapper.subtypeFor(ptr) : null; - return cls != null ? cls : defaultClass; - } - private static final class MapHolder { - public static final Map, Mapper> mappers = new HashMap, Mapper>() {{ - put(Event.class, new EventMapper()); - put(Message.class, new MessageMapper()); - put(Query.class, new QueryMapper()); - }}; - } - private static interface Mapper { - public Class subtypeFor(Pointer ptr); - } - private static class EventMapper implements Mapper { - static class MapHolder { - private static final Map> typeMap - = new HashMap>() {{ - put(EventType.BUFFERSIZE, BufferSizeEvent.class); - put(EventType.EOS, EOSEvent.class); - put(EventType.CAPS, CapsEvent.class); - put(EventType.RECONFIGURE, ReconfigureEvent.class); - put(EventType.STREAM_START, StreamStartEvent.class); - put(EventType.LATENCY, LatencyEvent.class); - put(EventType.FLUSH_START, FlushStartEvent.class); - put(EventType.FLUSH_STOP, FlushStopEvent.class); - put(EventType.NAVIGATION, NavigationEvent.class); - put(EventType.SEGMENT, SegmentEvent.class); - put(EventType.SEEK, SeekEvent.class); - put(EventType.TAG, TagEvent.class); - put(EventType.QOS, QOSEvent.class); - put(EventType.STEP, StepEvent.class); - }}; - public static Class subtypeFor(Pointer ptr) { - GstEventAPI.EventStruct struct = new GstEventAPI.EventStruct(ptr); - EventType type = EventType.valueOf((Integer) struct.readField("type")); - Class eventClass = MapHolder.typeMap.get(type); - return eventClass != null ? eventClass : Event.class; - } - } - public Class subtypeFor(Pointer ptr) { - return MapHolder.subtypeFor(ptr); - } - } - private static class MessageMapper implements Mapper { - static class MapHolder { - private static final Map> typeMap - = new HashMap>() {{ - put(MessageType.EOS, EOSMessage.class); - put(MessageType.ERROR, ErrorMessage.class); - put(MessageType.BUFFERING, BufferingMessage.class); - put(MessageType.DURATION_CHANGED, DurationChangedMessage.class); - put(MessageType.INFO, InfoMessage.class); - put(MessageType.LATENCY, LatencyMessage.class); - put(MessageType.SEGMENT_DONE, SegmentDoneMessage.class); - put(MessageType.STATE_CHANGED, StateChangedMessage.class); - put(MessageType.TAG, TagMessage.class); - put(MessageType.WARNING, WarningMessage.class); - }}; - public static Class subtypeFor(Pointer ptr) { - GstMessageAPI.MessageStruct struct = new GstMessageAPI.MessageStruct(ptr); - MessageType type = (MessageType) struct.readField("type"); - Class messageClass = MapHolder.typeMap.get(type); - return messageClass != null ? messageClass : Message.class; - } - } - public Class subtypeFor(Pointer ptr) { - return MapHolder.subtypeFor(ptr); - } - } - private static class QueryMapper implements Mapper { - static class MapHolder { - private static final Map> typeMap - = new HashMap>() {{ - put(QueryType.ALLOCATION, AllocationQuery.class); - put(QueryType.CONVERT, ConvertQuery.class); - put(QueryType.DURATION, DurationQuery.class); - put(QueryType.FORMATS, FormatsQuery.class); - put(QueryType.LATENCY, LatencyQuery.class); - put(QueryType.POSITION, PositionQuery.class); - put(QueryType.SEEKING, SeekingQuery.class); - put(QueryType.SEGMENT, SegmentQuery.class); - }}; - public static Class subtypeFor(Pointer ptr) { - GstQueryAPI.QueryStruct struct = new GstQueryAPI.QueryStruct(ptr); - QueryType type = QueryType.valueOf((Integer) struct.readField("type")); - Class queryClass = typeMap.get(type); - return queryClass != null ? queryClass : Query.class; - } - } - public Class subtypeFor(Pointer ptr) { - return MapHolder.subtypeFor(ptr); - } - } -} diff --git a/src/org/freedesktop/gstreamer/message/BufferingMessage.java b/src/org/freedesktop/gstreamer/message/BufferingMessage.java index 976ad93b..1dce6879 100644 --- a/src/org/freedesktop/gstreamer/message/BufferingMessage.java +++ b/src/org/freedesktop/gstreamer/message/BufferingMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -20,15 +21,20 @@ package org.freedesktop.gstreamer.message; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; /** * This message can be posted by an element that * needs to buffer data before it can continue processing. {@code percent} should be a * value between 0 and 100. A value of 100 means that the buffering completed. - * - * When percent} is < 100 the application should PAUSE a PLAYING pipeline. When + *

+ * See upstream documentation + * at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-buffering + *

+ * When percent is < 100 the application should PAUSE a PLAYING pipeline. When * percent is 100, the application can set the pipeline (back) to PLAYING. * The application must be prepared to receive BUFFERING messages in the * PREROLLING state and may only set the pipeline to PLAYING after receiving a @@ -41,16 +47,17 @@ public class BufferingMessage extends Message { * Creates a new Buffering message. * @param init internal initialization data. */ - public BufferingMessage(Initializer init) { + BufferingMessage(Initializer init) { super(init); } /** * Creates a new Buffering message. * @param src The object originating the message. + * @param percent The buffering percent */ public BufferingMessage(GstObject src, int percent) { - this(initializer(GSTMESSAGE_API.ptr_gst_message_new_buffering(src, percent))); + this(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_buffering(src, percent))); } /** diff --git a/src/org/freedesktop/gstreamer/message/DurationChangedMessage.java b/src/org/freedesktop/gstreamer/message/DurationChangedMessage.java index 7d1f4772..38473465 100644 --- a/src/org/freedesktop/gstreamer/message/DurationChangedMessage.java +++ b/src/org/freedesktop/gstreamer/message/DurationChangedMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -16,30 +17,40 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.message; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; /** - * The duration of a pipeline has changed. The application can get the new - * duration with a duration query. + * A duration changed message. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-duration-changed + *

+ * This message is posted by elements that know the duration of a stream when + * the duration changes. This message is received by bins and is used to + * calculate the total duration of a pipeline. */ public class DurationChangedMessage extends Message { + /** * Creates a new DurationChanged message. + * * @param init internal initialization data. */ - public DurationChangedMessage(Initializer init) { + DurationChangedMessage(Initializer init) { super(init); } - + /** * Creates a new DurationChanged Message + * + * @param src The object originating the message. */ public DurationChangedMessage(GstObject src) { - this(initializer(GSTMESSAGE_API.ptr_gst_message_new_duration_changed(src))); + this(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_duration_changed(src))); } } diff --git a/src/org/freedesktop/gstreamer/message/EOSMessage.java b/src/org/freedesktop/gstreamer/message/EOSMessage.java index 8c5f62ee..523b76ba 100644 --- a/src/org/freedesktop/gstreamer/message/EOSMessage.java +++ b/src/org/freedesktop/gstreamer/message/EOSMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -16,33 +17,41 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.message; +import org.freedesktop.gstreamer.Bin; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; /** - * This message is generated and posted in the sink elements of a {@link org.freedesktop.gstreamer.Bin}. - * The bin will only forward the EOS message to the application if all sinks + * An End-of-Stream Message. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-eos + *

+ * This message is generated and posted in the sink elements of a {@link Bin}. + * The bin will only forward the EOS message to the application if all sinks * have posted an EOS message. */ public class EOSMessage extends Message { /** * Creates a new eos message. + * * @param init internal initialization data. */ - public EOSMessage(Initializer init) { + EOSMessage(Initializer init) { super(init); } - + /** * Creates a new eos message. + * * @param src The object originating the message. */ public EOSMessage(GstObject src) { - this(initializer(GSTMESSAGE_API.ptr_gst_message_new_eos(src))); + this(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_eos(src))); } } diff --git a/src/org/freedesktop/gstreamer/message/ErrorMessage.java b/src/org/freedesktop/gstreamer/message/ErrorMessage.java index 4e74ba24..9215a1aa 100644 --- a/src/org/freedesktop/gstreamer/message/ErrorMessage.java +++ b/src/org/freedesktop/gstreamer/message/ErrorMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -25,6 +26,10 @@ /** * This message is posted by element when a fatal event occurs. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-error + *

* The pipeline will probably (partially) stop. The application * receiving this message should stop the pipeline. */ @@ -35,7 +40,7 @@ public class ErrorMessage extends GErrorMessage { * * @param init internal initialization data. */ - public ErrorMessage(Initializer init) { + ErrorMessage(Initializer init) { super(init); } diff --git a/src/org/freedesktop/gstreamer/message/GErrorMessage.java b/src/org/freedesktop/gstreamer/message/GErrorMessage.java index 15074377..387ddfa6 100644 --- a/src/org/freedesktop/gstreamer/message/GErrorMessage.java +++ b/src/org/freedesktop/gstreamer/message/GErrorMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2025 Neil C Smith * Copyright (C) 2008 Wayne Meissner * * This file is part of gstreamer-java. @@ -19,15 +20,14 @@ package org.freedesktop.gstreamer.message; -import org.freedesktop.gstreamer.Message; import org.freedesktop.gstreamer.lowlevel.GstAPI; import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; /** - * Base class for ERROR, WARNING and INFO messages. + * Package private base class for ERROR, WARNING and INFO messages. */ -abstract public class GErrorMessage extends Message { +abstract class GErrorMessage extends Message { /** * Creates a new GError message. @@ -36,6 +36,7 @@ abstract public class GErrorMessage extends Message { GErrorMessage(Initializer init) { super(init); } + abstract GstAPI.GErrorStruct parseMessage(); /** @@ -49,7 +50,7 @@ public int getCode() { throw new NullPointerException("Could not parse message"); } int code = err.code; - GLIB_API.g_error_free(err); + GLIB_API.g_error_free(err.getPointer()); return code; } @@ -64,7 +65,7 @@ public String getMessage() { throw new NullPointerException("Could not parse message"); } String message = err.getMessage(); - GLIB_API.g_error_free(err); + GLIB_API.g_error_free(err.getPointer()); return message; } diff --git a/src/org/freedesktop/gstreamer/message/InfoMessage.java b/src/org/freedesktop/gstreamer/message/InfoMessage.java index f9ec4d3b..5941f331 100644 --- a/src/org/freedesktop/gstreamer/message/InfoMessage.java +++ b/src/org/freedesktop/gstreamer/message/InfoMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -16,7 +17,6 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.message; import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; @@ -24,26 +24,31 @@ /** * This message is posted by element to provide information to the application. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-info + *

*/ public class InfoMessage extends GErrorMessage { /** * Creates a new info message. - * + * * @param init internal initialization data. */ - public InfoMessage(Initializer init) { + InfoMessage(Initializer init) { super(init); } - + /** * Retrieves the GError structure contained in this message. - * + * * @return the GError contained in this message. */ @Override GErrorStruct parseMessage() { - GErrorStruct[] err = { null }; + GErrorStruct[] err = {null}; GSTMESSAGE_API.gst_message_parse_info(this, err, null); return err[0]; } diff --git a/src/org/freedesktop/gstreamer/message/LatencyMessage.java b/src/org/freedesktop/gstreamer/message/LatencyMessage.java index 7cf66b78..167afa2a 100644 --- a/src/org/freedesktop/gstreamer/message/LatencyMessage.java +++ b/src/org/freedesktop/gstreamer/message/LatencyMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -16,32 +17,37 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.message; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; /** * Message posted by elements when their latency requirements have changed. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-latency + *

*/ public class LatencyMessage extends Message { + /** * Creates a new Latency message. - * + * * @param init internal initialization data. */ - public LatencyMessage(Initializer init) { + LatencyMessage(Initializer init) { super(init); } - + /** * Creates a new Latency message. - * + * * @param source the object originating the message. */ public LatencyMessage(GstObject source) { - this(initializer(GSTMESSAGE_API.ptr_gst_message_new_latency(source))); + this(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_latency(source))); } } diff --git a/src/org/freedesktop/gstreamer/message/Message.java b/src/org/freedesktop/gstreamer/message/Message.java new file mode 100644 index 00000000..b051dd4e --- /dev/null +++ b/src/org/freedesktop/gstreamer/message/Message.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2007, 2008 Wayne Meissner + * Copyright (C) 2004 Wim Taymans + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.message; + +import java.util.EnumMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; +import org.freedesktop.gstreamer.GstObject; +import org.freedesktop.gstreamer.MiniObject; +import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstMessagePtr; +import org.freedesktop.gstreamer.lowlevel.ReferenceManager; +import org.freedesktop.gstreamer.lowlevel.annotations.HasSubtype; + +import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; + +/** + * Lightweight objects to signal the occurrence of pipeline events. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html + *

+ * Messages are implemented as a subclass of {@link MiniObject} with a generic + * {@link Structure} as the content. This allows for writing custom messages + * without requiring an API change while allowing a wide range of different + * types of messages. + *

+ * Messages are posted by objects in the pipeline and are passed to the + * application using the {@link Bus}. + * + * The basic use pattern of posting a message on a Bus is as follows: + * + * + * Posting a Message + * + * bus.post(new EOSMessage(source)); + * + * + * + * An {@link Element} usually posts messages on the bus provided by the parent + * container using {@link Element#postMessage postMessage()}. + */ +@HasSubtype +public class Message extends MiniObject { + + public static final String GTYPE_NAME = "GstMessage"; + + private static final Map> TYPE_MAP + = new EnumMap<>(MessageType.class); + + static { + TYPE_MAP.put(MessageType.EOS, EOSMessage::new); + TYPE_MAP.put(MessageType.ERROR, ErrorMessage::new); + TYPE_MAP.put(MessageType.BUFFERING, BufferingMessage::new); + TYPE_MAP.put(MessageType.DURATION_CHANGED, DurationChangedMessage::new); + TYPE_MAP.put(MessageType.INFO, InfoMessage::new); + TYPE_MAP.put(MessageType.LATENCY, LatencyMessage::new); + TYPE_MAP.put(MessageType.SEGMENT_DONE, SegmentDoneMessage::new); + TYPE_MAP.put(MessageType.STATE_CHANGED, StateChangedMessage::new); + TYPE_MAP.put(MessageType.TAG, TagMessage::new); + TYPE_MAP.put(MessageType.WARNING, WarningMessage::new); + TYPE_MAP.put(MessageType.NEED_CONTEXT, NeedContextMessage::new); + } + + private final Handle handle; + + /** + * Creates a new instance of Message. + * + * @param init internal initialization data. + */ + Message(Initializer init) { + this(new Handle(init.ptr.as(GstMessagePtr.class, GstMessagePtr::new), init.ownsHandle), init.needRef); + } + + Message(Handle handle, boolean needRef) { + super(handle, needRef); + this.handle = handle; + } + + /** + * Gets the Element that posted this message. + * + * @return the element that posted the message. + */ + public GstObject getSource() { + return Natives.objectFor(handle.getPointer().getSource(), GstObject.class, true, true); + } + + /** + * Gets the structure containing the data in this message. + * + * @return a structure. + */ + public Structure getStructure() { + return ReferenceManager.addKeepAliveReference(GSTMESSAGE_API.gst_message_get_structure(this), this); + } + + /** + * Gets the type of this message. + * + * @return the message type. + */ + public MessageType getType() { + return NativeEnum.fromInt(MessageType.class, MessageType.UNKNOWN, + handle.getPointer().getMessageType()); + } + + private static Message create(Initializer init) { + MessageType type = NativeEnum.fromInt(MessageType.class, + MessageType.UNKNOWN, + init.ptr.as(GstMessagePtr.class, GstMessagePtr::new).getMessageType() + ); + return TYPE_MAP.getOrDefault(type, Message::new).apply(init); + } + + public static class Types implements TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + Natives.registration(Message.class, GTYPE_NAME, Message::create) + ); + } + + } + + static class Handle extends MiniObject.Handle { + + Handle(GstMessagePtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected GstMessagePtr getPointer() { + return (GstMessagePtr) super.getPointer(); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/MessageType.java b/src/org/freedesktop/gstreamer/message/MessageType.java similarity index 77% rename from src/org/freedesktop/gstreamer/MessageType.java rename to src/org/freedesktop/gstreamer/message/MessageType.java index 67f65438..b0149178 100644 --- a/src/org/freedesktop/gstreamer/MessageType.java +++ b/src/org/freedesktop/gstreamer/message/MessageType.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2020 Neil C Smith * Copyright (C) 2009 Levente Farkas * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans @@ -17,21 +18,24 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ +package org.freedesktop.gstreamer.message; -package org.freedesktop.gstreamer; - -import java.util.HashMap; -import java.util.Map; - -import org.freedesktop.gstreamer.lowlevel.EnumMapper; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; - -import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.device.Device; +import org.freedesktop.gstreamer.device.DeviceProvider; +import org.freedesktop.gstreamer.glib.NativeEnum; /** * The different message types that are available. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#GstMessageType + *

*/ -public enum MessageType implements IntegerEnum { +// use NativeEnum not NativeFlags - from upstream definition +// FIXME: 2.0: Make it NOT flags, just a regular 1,2,3,4.. enumeration +public enum MessageType implements NativeEnum { /** * An undefined message */ @@ -44,17 +48,17 @@ public enum MessageType implements IntegerEnum { */ EOS(1 << 0), /** - * An error occured. Whe the application receives an error message it should - * stop playback of the pipeline and not assume that more data will be - * played. + * An error occurred. When the application receives an error message it + * should stop playback of the pipeline and not assume that more data will + * be played. */ ERROR(1 << 1), /** - * A warning occured. + * A warning occurred. */ WARNING(1 << 2), /** - * An info message occured. + * An info message occurred. */ INFO(1 << 3), /** @@ -74,7 +78,8 @@ public enum MessageType implements IntegerEnum { */ STATE_CHANGED(1 << 6), /** - * an element changed state in a streaming thread. This message is deprecated. + * an element changed state in a streaming thread. This message is + * deprecated. */ STATE_DIRTY(1 << 7), /** @@ -211,77 +216,58 @@ public enum MessageType implements IntegerEnum { /** * Message indicating a {@link GObject} property has changed (Since 1.10) */ + @Gst.Since(minor = 10) PROPERTY_NOTIFY(EXTENDED.intValue() + 3), /** - * Message indicating a new {@link GstStreamCollection} is available (Since 1.10) + * Message indicating a new {@link GstStreamCollection} is available (Since + * 1.10) */ - STREAM_COLLECTION(EXTENDED.intValue()+4), + @Gst.Since(minor = 10) + STREAM_COLLECTION(EXTENDED.intValue() + 4), /** - * Message indicating the active selection of {@link GstStreams} has changed (Since 1.10) + * Message indicating the active selection of {@link GstStreams} has changed + * (Since 1.10) */ + @Gst.Since(minor = 10) STREAMS_SELECTED(EXTENDED.intValue() + 5), /** - * Message indicating to request the application to try to play the given URL(s). Useful if for example a HTTP 302/303 response is received with a non-HTTP URL inside. (Since 1.10) + * Message indicating to request the application to try to play the given + * URL(s). Useful if for example a HTTP 302/303 response is received with a + * non-HTTP URL inside. (Since 1.10) */ + @Gst.Since(minor = 10) REDIRECT(EXTENDED.intValue() + 6), + /** + * Message indicating a {@link Device} was changed by a + * {@link DeviceProvider} (Since 1.16) + */ + @Gst.Since(minor = 16) + DEVICE_CHANGED(EXTENDED.intValue() + 7), + /** + * Message sent by elements to request the running time from the pipeline + * when an instant rate change should be applied (which may be in the past + * when the answer arrives). (Since 1.18) + */ + @Gst.Since(minor = 18) + INSTANT_RATE_REQUEST(EXTENDED.intValue() + 8), /** * mask for all of the above messages. */ ANY(~0); - - MessageType(int type) { + + private final int type; + + private MessageType(int type) { this.type = type; } - + /** * Gets the native integer value for this type. - * + * * @return the native gstreamer value. */ + @Override public int intValue() { return type; } - - /** - * Gets the name of this message type. - * - * @return the name of the message type. - */ - public String getName() { - if(name == null) { - name = GSTMESSAGE_API.gst_message_type_get_name(this); - } - return name; - } - - /** - * Gets a MessageType that corresponds to the native integer value. - * - * @param type the native value of the type. - * @return a MessageType. - */ - public static final MessageType valueOf(int type) { - return EnumMapper.getInstance().valueOf(type, MessageType.class); - } - - /** - * Gets a MessageType that corresponds to the name - * - * @param name the gstreamer name of the type. - * @return a MessageType. - */ - public static final MessageType forName(String name) { - MessageType type = MapHolder.typeMap.get(name); - return type != null ? type : UNKNOWN; - } - private final int type; - private String name; - private static final class MapHolder { - private static final Map typeMap = new HashMap(); - static { - for (MessageType t : MessageType.values()) { - typeMap.put(t.getName(), t); - } - } - } } diff --git a/src/org/freedesktop/gstreamer/message/NeedContextMessage.java b/src/org/freedesktop/gstreamer/message/NeedContextMessage.java new file mode 100644 index 00000000..7529ceca --- /dev/null +++ b/src/org/freedesktop/gstreamer/message/NeedContextMessage.java @@ -0,0 +1,60 @@ +/* + * + * Copyright (c) 2019 Christophe Lafolet + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.message; + +import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; + +import org.freedesktop.gstreamer.GstObject; +import org.freedesktop.gstreamer.glib.Natives; + +/** + * Message indicating that an element wants a specific context. + */ +public class NeedContextMessage extends Message { + + /** + * Creates a new Need-Context message. + * + * @param init internal initialization data. + */ + NeedContextMessage(Initializer init) { + super(init); + } + + /** + * Creates a new Need-Context message. + * + * @param src the object originating the message. + */ + public NeedContextMessage(GstObject src, String context_type) { + this(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_need_context(src, context_type))); + } + + /** + * Gets the context type contained in this message. + * + * @return the context type. + */ + public String getContextType() { + String context_type[] = new String[1]; + boolean isOk = GSTMESSAGE_API.gst_message_parse_context_type(this, context_type); + return isOk ? context_type[0] : null; + } + +} diff --git a/src/org/freedesktop/gstreamer/message/SegmentDoneMessage.java b/src/org/freedesktop/gstreamer/message/SegmentDoneMessage.java index 1ea045e2..76d3256d 100644 --- a/src/org/freedesktop/gstreamer/message/SegmentDoneMessage.java +++ b/src/org/freedesktop/gstreamer/message/SegmentDoneMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -21,13 +22,17 @@ import org.freedesktop.gstreamer.Format; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; /** * This message is posted by elements that finish playback of a segment as a * result of a segment seek. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-segment-done + *

* This message is received by the application after all elements that posted a segment_start * have posted the segment_done. */ @@ -38,7 +43,7 @@ public class SegmentDoneMessage extends Message { * * @param init internal initialization data. */ - public SegmentDoneMessage(Initializer init) { + SegmentDoneMessage(Initializer init) { super(init); } @@ -50,7 +55,7 @@ public SegmentDoneMessage(Initializer init) { * @param position the position of the segment being done */ public SegmentDoneMessage(GstObject src, Format format, long position) { - this(initializer(GSTMESSAGE_API.ptr_gst_message_new_segment_done(src, format, position))); + this(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_segment_done(src, format, position))); } /** diff --git a/src/org/freedesktop/gstreamer/message/StateChangedMessage.java b/src/org/freedesktop/gstreamer/message/StateChangedMessage.java index dbb139f9..3d2a186a 100644 --- a/src/org/freedesktop/gstreamer/message/StateChangedMessage.java +++ b/src/org/freedesktop/gstreamer/message/StateChangedMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -20,13 +21,17 @@ package org.freedesktop.gstreamer.message; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; import org.freedesktop.gstreamer.State; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; /** * A state change message. *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-state-changed + *

* This message is posted whenever an element changes its state. */ public class StateChangedMessage extends Message { @@ -35,7 +40,7 @@ public class StateChangedMessage extends Message { * Creates a new Buffering message. * @param init internal initialization data. */ - public StateChangedMessage(Initializer init) { + StateChangedMessage(Initializer init) { super(init); } @@ -48,7 +53,7 @@ public StateChangedMessage(Initializer init) { * @param pending the pending (target) state. */ public StateChangedMessage(GstObject src, State old, State current, State pending) { - super(initializer(GSTMESSAGE_API.ptr_gst_message_new_state_changed(src, old, current, pending))); + super(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_state_changed(src, old, current, pending))); } /** diff --git a/src/org/freedesktop/gstreamer/message/TagMessage.java b/src/org/freedesktop/gstreamer/message/TagMessage.java index 958307f9..d7ef3bab 100644 --- a/src/org/freedesktop/gstreamer/message/TagMessage.java +++ b/src/org/freedesktop/gstreamer/message/TagMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -16,30 +17,36 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.message; import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.Message; import org.freedesktop.gstreamer.TagList; import com.sun.jna.ptr.PointerByReference; +import org.freedesktop.gstreamer.glib.Natives; import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; /** * This message is posted by elements that have discovered new tags. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-tag + *

*/ public class TagMessage extends Message { /** - * Creates a new Buffering message. + * Creates a new Tag message. + * * @param init internal initialization data. */ - public TagMessage(Initializer init) { + TagMessage(Initializer init) { super(init); } - + /** - * Creates a new Buffering message. + * Creates a new Tag message. + * * @param src The object originating the message. * @param tagList the tag list for this message. *

@@ -47,17 +54,17 @@ public TagMessage(Initializer init) { * it again after adding it to this message. */ public TagMessage(GstObject src, TagList tagList) { - this(initializer(GSTMESSAGE_API.ptr_gst_message_new_tag(src, tagList))); + this(Natives.initializer(GSTMESSAGE_API.ptr_gst_message_new_tag(src, tagList))); } - + /** * Gets the list of tags contained in this message. - * + * * @return the list of tags in this message. */ public TagList getTagList() { PointerByReference list = new PointerByReference(); GSTMESSAGE_API.gst_message_parse_tag(this, list); - return objectFor(list.getValue(), TagList.class, false, true); + return Natives.objectFor(list.getValue(), TagList.class, false, true); } } diff --git a/src/org/freedesktop/gstreamer/message/WarningMessage.java b/src/org/freedesktop/gstreamer/message/WarningMessage.java index ada4378a..9e5b51df 100644 --- a/src/org/freedesktop/gstreamer/message/WarningMessage.java +++ b/src/org/freedesktop/gstreamer/message/WarningMessage.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 2004 Wim Taymans * @@ -16,7 +17,6 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.message; import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; @@ -24,25 +24,31 @@ /** * This message is posted by element when a warning notice is required. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstMessage.html#gst-message-new-warning + *

*/ public class WarningMessage extends GErrorMessage { + /** * Creates a new warning message. - * + * * @param init internal initialization data. */ - public WarningMessage(Initializer init) { + WarningMessage(Initializer init) { super(init); } - + /** * Retrieves the GError structure contained in this message. - * + * * @return the GError contained in this message. */ @Override GErrorStruct parseMessage() { - GErrorStruct[] err = { null }; + GErrorStruct[] err = {null}; GSTMESSAGE_API.gst_message_parse_warning(this, err, null); return err[0]; } diff --git a/src/org/freedesktop/gstreamer/query/AllocationQuery.java b/src/org/freedesktop/gstreamer/query/AllocationQuery.java index f27b9a87..1031d245 100644 --- a/src/org/freedesktop/gstreamer/query/AllocationQuery.java +++ b/src/org/freedesktop/gstreamer/query/AllocationQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 Neil C Smith * Copyright (c) 2016 Christophe Lafolet * * This file is part of gstreamer-java. @@ -19,46 +20,80 @@ import org.freedesktop.gstreamer.BufferPool; import org.freedesktop.gstreamer.Caps; -import org.freedesktop.gstreamer.Query; import org.freedesktop.gstreamer.Structure; import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.Natives; +/** + * An allocation query for querying allocation properties. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-allocation + *

+ * + */ public class AllocationQuery extends Query { + /** * This constructor is for internal use only. + * * @param init initialization data. */ - public AllocationQuery(Initializer init) { - // special case : no ref shall be added - // the allocationQuery is an in/out parameter during query notification - // so, we shall keep query writable for add methods - super(initializer(init.ptr, false, true)); + AllocationQuery(Initializer init) { + super(init); } + /** + * Create a new allocation query. + * + * @param caps the negotiated {@link Caps} + * @param need_pool return a pool. + */ public AllocationQuery(Caps caps, boolean need_pool) { - this(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_allocation(caps, need_pool))); + this(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_allocation(caps, need_pool))); } + /** + * Whether a {@link BufferPool} is needed. + * + * @return true if BufferPool needed + */ public boolean isPoolNeeded() { - boolean[] need_pool = {false}; - GstQueryAPI.GSTQUERY_API.gst_query_parse_allocation(this, null, need_pool); - return need_pool[0]; + boolean[] need_pool = {false}; + GstQueryAPI.GSTQUERY_API.gst_query_parse_allocation(this, null, need_pool); + return need_pool[0]; } + /** + * Get the requested {@link Caps} + * + * @return requested Caps + */ public Caps getCaps() { - Pointer[] ptr = new Pointer[1]; - GstQueryAPI.GSTQUERY_API.gst_query_parse_allocation(this, ptr, null); - return new Caps(new Initializer(ptr[0], false, true)); + Pointer[] ptr = new Pointer[1]; + GstQueryAPI.GSTQUERY_API.gst_query_parse_allocation(this, ptr, null); +// return new Caps(new Initializer(ptr[0], false, true)); + return Natives.objectFor(ptr[0], Caps.class, false, true); } - public void addAllocationMeta(GType api, Structure params) { - GstQueryAPI.GSTQUERY_API.gst_query_add_allocation_meta(this, api, params); + // @TODO how best not to expose GType? + void addAllocationMeta(GType api, Structure params) { + GstQueryAPI.GSTQUERY_API.gst_query_add_allocation_meta(this, api, params); } - public void addBufferPool(BufferPool pool, int size, int min_buffers, int max_buffers) { - GstQueryAPI.GSTQUERY_API.gst_query_add_allocation_pool(this, pool, size, min_buffers, max_buffers); + /** + * Set the pool parameters of the query. + * + * @param pool the {@link BufferPool} + * @param size the buffer size + * @param min_buffers the min buffers + * @param max_buffers the max buffers + */ + public void addAllocationPool(BufferPool pool, int size, int min_buffers, int max_buffers) { + GstQueryAPI.GSTQUERY_API.gst_query_add_allocation_pool(this, pool, size, min_buffers, max_buffers); } } diff --git a/src/org/freedesktop/gstreamer/query/ConvertQuery.java b/src/org/freedesktop/gstreamer/query/ConvertQuery.java index 9bf64da2..25cb7fba 100644 --- a/src/org/freedesktop/gstreamer/query/ConvertQuery.java +++ b/src/org/freedesktop/gstreamer/query/ConvertQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,42 +19,87 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.query; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; /** - * Convert values between formats + * A convert query used to ask for a conversion between one format and another. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-convert + *

*/ public class ConvertQuery extends Query { - public ConvertQuery(Initializer init) { + ConvertQuery(Initializer init) { super(init); } + + /** + * Construct a new convert query object. + * + * @param srcFormat the source {@link Format} for the new query + * @param value the value to convert + * @param destFormat the target {@link Format} + */ public ConvertQuery(Format srcFormat, long value, Format destFormat) { - this(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_convert(srcFormat, value, destFormat))); + this(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_convert(srcFormat, value, destFormat))); } + + /** + * Answer a convert query by setting the requested values. + * + * @param srcFormat the source {@link Format} + * @param srcValue the source value + * @param dstFormat the destination {@link Format} + * @param dstValue the destination value + */ public void setConvert(Format srcFormat, long srcValue, Format dstFormat, long dstValue) { GstQueryAPI.GSTQUERY_API.gst_query_set_convert(this, srcFormat, srcValue, dstFormat, dstValue); } + + /** + * Get the source {@link Format} of this query. + * + * @return source Format + */ public Format getSourceFormat() { Format[] fmt = new Format[1]; GstQueryAPI.GSTQUERY_API.gst_query_parse_convert(this, fmt, null, null, null); return fmt[0]; } + + /** + * Get the destination {@link Format} of this query. + * + * @return destination Format + */ public Format getDestinationFormat() { Format[] fmt = new Format[1]; GstQueryAPI.GSTQUERY_API.gst_query_parse_convert(this, null, null, fmt, null); return fmt[0]; } + + /** + * Get the source value of this query. + * + * @return source value + */ public long getSourceValue() { long[] value = new long[1]; GstQueryAPI.GSTQUERY_API.gst_query_parse_convert(this, null, value, null, null); return value[0]; } + + /** + * Get the destination value of this query. + * + * @return destination value + */ public long getDestinationValue() { long[] value = new long[1]; GstQueryAPI.GSTQUERY_API.gst_query_parse_convert(this, null, null, null, value); diff --git a/src/org/freedesktop/gstreamer/query/DurationQuery.java b/src/org/freedesktop/gstreamer/query/DurationQuery.java index f7c70846..7ca88d7d 100644 --- a/src/org/freedesktop/gstreamer/query/DurationQuery.java +++ b/src/org/freedesktop/gstreamer/query/DurationQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,41 +19,51 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.query; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; /** - * Used to query the total duration of a stream. + * A duration query used to get the total length of a stream. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-duration + *

+ * @see Format */ public class DurationQuery extends Query { - public DurationQuery(Initializer init) { + DurationQuery(Initializer init) { super(init); } + /** - * Constructs a new stream duration query object to query in the given format. - * A duration query will give the total length of the stream. - * + * Constructs a new stream duration query object to query in the given + * format. A duration query will give the total length of the stream. + * * @param format the {@link Format} for this duration query. */ public DurationQuery(Format format) { - super(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_duration(format))); + super(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_duration(format))); } + /** - * Answers a duration query by setting the requested value in the given format. + * Answers a duration query by setting the requested value in the given + * format. + * * @param format the {@link Format} for the duration * @param duration the duration of the stream */ public void setDuration(Format format, long duration) { GstQueryAPI.GSTQUERY_API.gst_query_set_duration(this, format, duration); } + /** * Gets the format of this duration query. - * + * * @return The {@link Format} of the duration value. */ public Format getFormat() { @@ -62,8 +73,9 @@ public Format getFormat() { } /** - * Gets the duration answer for this duration query. - * + * Gets the duration answer for this query, in the format available from + * {@link #getFormat() } + * * @return The total duration. */ public long getDuration() { @@ -71,10 +83,10 @@ public long getDuration() { GstQueryAPI.GSTQUERY_API.gst_query_parse_duration(this, null, duration); return duration[0]; } - + /** * Gets the duration as a user-readable string. - * + * * @return A string representing the duration. */ @Override diff --git a/src/org/freedesktop/gstreamer/query/FormatsQuery.java b/src/org/freedesktop/gstreamer/query/FormatsQuery.java index 49b10fd8..d6eb2cae 100644 --- a/src/org/freedesktop/gstreamer/query/FormatsQuery.java +++ b/src/org/freedesktop/gstreamer/query/FormatsQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,53 +19,60 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.query; import java.util.AbstractList; import java.util.List; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; /** - * Used for querying formats of the stream. + * Used for querying formats of the stream. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-formats + *

+ * @see Format */ public class FormatsQuery extends Query { /** - * Constructs a new query object for querying formats of the stream. + * Constructs a new query object for querying formats of the stream. */ public FormatsQuery() { - this(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_formats())); + this(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_formats())); } - public FormatsQuery(Initializer init) { + + FormatsQuery(Initializer init) { super(init); } - + /** - * Sets the formats query result fields. - * + * Sets the formats query result fields. + * * @param formats the formats to set. */ public void setFormats(Format... formats) { GstQueryAPI.GSTQUERY_API.gst_query_set_formats(this, formats.length, formats); } - + /** * Gets the number of formats in this query. - * + * * @return the number of formats in this query. */ public int getCount() { - int[] count = { 0 }; + int[] count = {0}; GstQueryAPI.GSTQUERY_API.gst_query_parse_n_formats(this, count); return count[0]; } - + /** * Gets a format at {@code index}. + * * @param index the index of the format to retrieve. * @return the format. */ @@ -73,11 +81,11 @@ public Format getFormat(int index) { GstQueryAPI.GSTQUERY_API.gst_query_parse_nth_format(this, index, fmt); return fmt[0]; } - + /** * Gets all formats in this query. - * - * @return a {@code List} of {@link Format}. + * + * @return a {@link List} of {@link Format}. */ public List getFormats() { final int count = getCount(); @@ -90,7 +98,7 @@ public Format get(int index) { @Override public int size() { return count; - } + } }; } } diff --git a/src/org/freedesktop/gstreamer/query/LatencyQuery.java b/src/org/freedesktop/gstreamer/query/LatencyQuery.java index 5e01cada..0180dd17 100644 --- a/src/org/freedesktop/gstreamer/query/LatencyQuery.java +++ b/src/org/freedesktop/gstreamer/query/LatencyQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,44 +19,48 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.query; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; /** - * - * @author wayne + * Used for querying the latency of the stream. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-latency + *

*/ public class LatencyQuery extends Query { - public LatencyQuery(Initializer init) { + LatencyQuery(Initializer init) { super(init); } - + /** - * Constructs a new query stream position query object. A position query is - * used to query the current position of playback in the streams, in some format. + * Constructs a new query stream position query object. A position query is + * used to query the current position of playback in the streams, in some + * format. */ public LatencyQuery() { - super(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_latency())); + super(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_latency())); } - + /** * Answers a latency query. - * + * * @param live if there is a live element upstream * @param minLatency the minimal latency of the live element * @param maxLatency the maximal latency of the live element */ - public void setLatency(boolean live, ClockTime minLatency, ClockTime maxLatency) { + public void setLatency(boolean live, long minLatency, long maxLatency) { GstQueryAPI.GSTQUERY_API.gst_query_set_latency(this, live, minLatency, maxLatency); } + /** * Gets whether the element has a live element upstream or not. - * + * * @return true if the element has a live element upstream. */ public boolean isLive() { @@ -63,37 +68,37 @@ public boolean isLive() { GstQueryAPI.GSTQUERY_API.gst_query_parse_latency(this, live, null, null); return live[0]; } - + /** * Gets the minimum latency of the live element. - * + * * @return The minimum latency of the live element. */ - public ClockTime getMinimumLatency() { - ClockTime[] latency = new ClockTime[1]; + public long getMinimumLatency() { + long[] latency = new long[1]; GstQueryAPI.GSTQUERY_API.gst_query_parse_latency(this, null, latency, null); return latency[0]; } - + /** * Gets the maximum latency of the live element. - * + * * @return The maximum latency of the live element. */ - public ClockTime getMaximumLatency() { - ClockTime[] latency = new ClockTime[1]; + public long getMaximumLatency() { + long[] latency = new long[1]; GstQueryAPI.GSTQUERY_API.gst_query_parse_latency(this, null, null, latency); return latency[0]; } - + /** * Gets the latency as a user-readable string. - * + * * @return A string representing the latency. */ @Override public String toString() { - return String.format("latency:[live=%b, min=%s, max=%s]", + return String.format("latency:[live=%b, min=%s, max=%s]", isLive(), getMinimumLatency(), getMaximumLatency()); } diff --git a/src/org/freedesktop/gstreamer/query/PositionQuery.java b/src/org/freedesktop/gstreamer/query/PositionQuery.java index 9c340892..42b8f63b 100644 --- a/src/org/freedesktop/gstreamer/query/PositionQuery.java +++ b/src/org/freedesktop/gstreamer/query/PositionQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,45 +19,51 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.query; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; /** * Used to query an element for the current position in the stream. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-position + *

*/ public class PositionQuery extends Query { - - public PositionQuery(Initializer init) { + + PositionQuery(Initializer init) { super(init); } - + /** - * Constructs a new query stream position query object. A position query is - * used to query the current position of playback in the streams, in some format. - * + * Constructs a new query stream position query object. A position query is + * used to query the current position of playback in the streams, in some + * format. + * * @param format the default {@link Format} for the new query */ public PositionQuery(Format format) { - super(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_position(format))); + super(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_position(format))); } - + /** - * Answers a position query by setting the requested value in the given format. - * + * Answers a position query by setting the requested value in the given + * format. + * * @param format the requested {@link Format} * @param position the position to set in the answer */ public void setPosition(Format format, long position) { GstQueryAPI.GSTQUERY_API.gst_query_set_position(this, format, position); } - + /** * Gets the {@link Format} of this position query. - * + * * @return The format of the query. */ public Format getFormat() { @@ -64,10 +71,10 @@ public Format getFormat() { GstQueryAPI.GSTQUERY_API.gst_query_parse_position(this, fmt, null); return fmt[0]; } - + /** * Gets the position in terms of the {@link Format} of the query. - * + * * @return the position. */ public long getPosition() { @@ -75,10 +82,10 @@ public long getPosition() { GstQueryAPI.GSTQUERY_API.gst_query_parse_position(this, null, pos); return pos[0]; } - + /** * Gets the position as a user-readable string. - * + * * @return A string representation of the position. */ @Override diff --git a/src/org/freedesktop/gstreamer/query/Query.java b/src/org/freedesktop/gstreamer/query/Query.java new file mode 100644 index 00000000..f0e2f40d --- /dev/null +++ b/src/org/freedesktop/gstreamer/query/Query.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 Neil C Smith + * Copyright (C) 2008 Wayne Meissner + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.query; + +import java.util.EnumMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; +import org.freedesktop.gstreamer.Element; +import org.freedesktop.gstreamer.MiniObject; +import org.freedesktop.gstreamer.Pad; +import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; +import org.freedesktop.gstreamer.lowlevel.ReferenceManager; +import org.freedesktop.gstreamer.lowlevel.annotations.HasSubtype; + +import static org.freedesktop.gstreamer.lowlevel.GstQueryAPI.GSTQUERY_API; + +/** + * Base query type. Queries can be performed on {@link Pad} and {@link Element}. + * Some queries might need a running pipeline to work. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html + */ +@HasSubtype +public class Query extends MiniObject { + + public static final String GTYPE_NAME = "GstQuery"; + + private static final Map> TYPE_MAP + = new EnumMap<>(QueryType.class); + + static { + TYPE_MAP.put(QueryType.ALLOCATION, AllocationQuery::new); + TYPE_MAP.put(QueryType.CONVERT, ConvertQuery::new); + TYPE_MAP.put(QueryType.DURATION, DurationQuery::new); + TYPE_MAP.put(QueryType.FORMATS, FormatsQuery::new); + TYPE_MAP.put(QueryType.LATENCY, LatencyQuery::new); + TYPE_MAP.put(QueryType.POSITION, PositionQuery::new); + TYPE_MAP.put(QueryType.SEEKING, SeekingQuery::new); + TYPE_MAP.put(QueryType.SEGMENT, SegmentQuery::new); + } + + /** + * Internally used constructor. Do not use. + * + * @param init internal initialization data. + */ + Query(Initializer init) { + super(init); + } + + /** + * Get the structure of this query. + * + * @return The structure of this Query. + */ + public Structure getStructure() { + return ReferenceManager.addKeepAliveReference(GSTQUERY_API.gst_query_get_structure(this), this); + } + + private static Query create(Initializer init) { + GstQueryAPI.QueryStruct struct = new GstQueryAPI.QueryStruct(init.ptr.getPointer()); + QueryType type = (QueryType) struct.readField("type"); + return TYPE_MAP.getOrDefault(type, Query::new).apply(init); + } + + public static class Types implements TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + Natives.registration(Query.class, GTYPE_NAME, Query::create) + ); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/query/QueryType.java b/src/org/freedesktop/gstreamer/query/QueryType.java new file mode 100644 index 00000000..6cd710cb --- /dev/null +++ b/src/org/freedesktop/gstreamer/query/QueryType.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019 Neil C Smith + * Copyright (C) 2008 Wayne Meissner + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * 2005 Wim Taymans + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.query; + +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeEnum; + +/** + * Standard predefined Query types + */ +public enum QueryType implements NativeEnum { + + /** + * invalid query type + */ + UNKNOWN(0, 0), + /** + * current position in stream + */ + POSITION(10, Flags.BOTH), + /** + * total duration of the stream + */ + DURATION(20, Flags.BOTH), + /** + * latency of stream + */ + LATENCY(30, Flags.BOTH), + /** + * current jitter of stream + */ + JITTER(40, Flags.BOTH), + /** + * current rate of the stream + */ + RATE(50, Flags.BOTH), + /** + * seeking capabilities + */ + SEEKING(60, Flags.BOTH), + /** + * segment start/stop positions + */ + SEGMENT(70, Flags.BOTH), + /** + * convert values between formats + */ + CONVERT(80, Flags.BOTH), + /** + * query supported formats for convert + */ + FORMATS(90, Flags.BOTH), + /** + * query available media for efficient seeking + */ + BUFFERING(110, Flags.BOTH), + /** + * a custom application or element defined query + */ + CUSTOM(120, Flags.BOTH), + /** + * query the URI of the source or sink + */ + URI(130, Flags.BOTH), + /** + * the buffer allocation properties + */ + ALLOCATION(140, Flags.DOWNSTREAM | Flags.SERIALIZED), + /** + * the scheduling properties + */ + SCHEDULING(150, Flags.UPSTREAM), + /** + * the accept caps query + */ + ACCEPT_CAPS(160, Flags.BOTH), + /** + * the caps query + */ + CAPS(170, Flags.BOTH), + /** + * wait till all serialized data is consumed downstream + */ + DRAIN(180, Flags.DOWNSTREAM | Flags.SERIALIZED), + /** + * query the pipeline-local context from downstream or upstream (since 1.2) + */ + CONTEXT(190, Flags.BOTH), + + /** + * the bitrate query (since 1.16) + */ + @Gst.Since(minor = 16) + BITRATE(200, Flags.DOWNSTREAM); + + private static final int SHIFT = 8; + + private final int value; + + private QueryType(int num, int flags) { + this.value = (num << SHIFT) | flags; + } + + private QueryType(int value) { + this.value = value; + } + + /** + * Gets the integer value of the enum. + * + * @return the integer value for this enum. + */ + public int intValue() { + return value; + } + + private static final class Flags { + + public static final int UPSTREAM = 1 << 0; + public static final int DOWNSTREAM = 1 << 1; + public static final int SERIALIZED = 1 << 2; + public static final int BOTH = UPSTREAM | DOWNSTREAM; + } +} diff --git a/src/org/freedesktop/gstreamer/query/SeekingQuery.java b/src/org/freedesktop/gstreamer/query/SeekingQuery.java index 1255b043..6229d86a 100644 --- a/src/org/freedesktop/gstreamer/query/SeekingQuery.java +++ b/src/org/freedesktop/gstreamer/query/SeekingQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,35 +19,41 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.query; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; /** * Used for querying the seeking properties of the stream. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-seeking + *

*/ public class SeekingQuery extends Query { - public SeekingQuery(Initializer init) { + SeekingQuery(Initializer init) { super(init); } - + /** - * Constructs a new query object for querying seeking properties of the stream. - * + * Constructs a new query object for querying seeking properties of the + * stream. + * * @param format the default {@link Format} for the new query. */ public SeekingQuery(Format format) { - this(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_seeking(format))); + this(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_seeking(format))); } - + /** * Sets the seeking query result fields. - * - * @param format the format to set for the {@code start} and {@code end} values. + * + * @param format the format to set for the {@code start} and {@code end} + * values. * @param seekable the seekable flag to set * @param start the start of the segment. * @param end the end of the segment. @@ -54,47 +61,47 @@ public SeekingQuery(Format format) { public void setSeeking(Format format, boolean seekable, long start, long end) { GstQueryAPI.GSTQUERY_API.gst_query_set_seeking(this, format, seekable, start, end); } - + /** * Checks if the stream is seekable. - * + * * @return {@code true} if the stream is seekable. */ public boolean isSeekable() { - boolean[] value = { false }; + boolean[] value = {false}; GstQueryAPI.GSTQUERY_API.gst_query_parse_seeking(this, null, value, null, null); return value[0]; } - + /** * Gets the {@link Format} of the start and end values for the segment. - * + * * @return the format of the start and end values. */ public Format getFormat() { - Format[] value = { Format.UNDEFINED }; + Format[] value = {Format.UNDEFINED}; GstQueryAPI.GSTQUERY_API.gst_query_parse_seeking(this, value, null, null, null); return value[0]; } - + /** * Gets the start of the segment. - * + * * @return the start of the segment. */ public long getStart() { - long[] value = { 0 }; + long[] value = {0}; GstQueryAPI.GSTQUERY_API.gst_query_parse_seeking(this, null, null, value, null); return value[0]; } - + /** * Gets the end of the segment. - * + * * @return the end of the segment. */ public long getEnd() { - long[] value = { 0 }; + long[] value = {0}; GstQueryAPI.GSTQUERY_API.gst_query_parse_seeking(this, null, null, null, value); return value[0]; } diff --git a/src/org/freedesktop/gstreamer/query/SegmentQuery.java b/src/org/freedesktop/gstreamer/query/SegmentQuery.java index 42209cf0..31b09fff 100644 --- a/src/org/freedesktop/gstreamer/query/SegmentQuery.java +++ b/src/org/freedesktop/gstreamer/query/SegmentQuery.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019 Neil C Smith * Copyright (C) 2008 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans @@ -18,44 +19,50 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - package org.freedesktop.gstreamer.query; import org.freedesktop.gstreamer.Format; -import org.freedesktop.gstreamer.Query; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; /** - * Used to discover information about the currently configured segment for playback. + * Used to discover information about the currently configured segment for + * playback. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstQuery.html#gst-query-new-segment + *

*/ public class SegmentQuery extends Query { - public SegmentQuery(Initializer init) { + SegmentQuery(Initializer init) { super(init); } + /** * Constructs a new segment query object. * * @param format the {@link Format} for the new query. */ public SegmentQuery(Format format) { - this(initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_segment(format))); + this(Natives.initializer(GstQueryAPI.GSTQUERY_API.ptr_gst_query_new_segment(format))); } - + /** - * Answers a segment query by setting the requested values. + * Answers a segment query by setting the requested values. *

- * The normal playback segment of a pipeline is 0 to duration at the default rate of - * 1.0. If a seek was performed on the pipeline to play a different + * The normal playback segment of a pipeline is 0 to duration at the default + * rate of 1.0. If a seek was performed on the pipeline to play a different * segment, this query will return the range specified in the last seek. * - * {@code startValue} and {@code stopValue} will respectively contain the configured - * playback range start and stop values expressed in format. - * The values are always between 0 and the duration of the media and - * {@code startValue <= stopValue}. {@code rate} will contain the playback rate. For - * negative rates, playback will actually happen from {@code stopValue} to - * {@code startValue}. - * + * {@code startValue} and {@code stopValue} will respectively contain the + * configured playback range start and stop values expressed in format. The + * values are always between 0 and the duration of the media and + * {@code startValue <= stopValue}. {@code rate} will contain the playback + * rate. For negative rates, playback will actually happen from + * {@code stopValue} to {@code startValue}. + * * @param rate the rate of the segment. * @param format the {@link Format} of the segment values. * @param startValue the start value. @@ -64,10 +71,10 @@ public SegmentQuery(Format format) { public void setSegment(double rate, Format format, long startValue, long stopValue) { GstQueryAPI.GSTQUERY_API.gst_query_set_segment(this, rate, format, startValue, stopValue); } - + /** * Gets the rate of the segment Query. - * + * * @return the rate of the segment. */ public double getRate() { @@ -75,10 +82,10 @@ public double getRate() { GstQueryAPI.GSTQUERY_API.gst_query_parse_segment(this, rate, null, null, null); return rate[0]; } - + /** * Gets the format of the start and stop values in the segment query. - * + * * @return The format for the start and stop values. */ public Format getFormat() { @@ -86,10 +93,10 @@ public Format getFormat() { GstQueryAPI.GSTQUERY_API.gst_query_parse_segment(this, null, fmt, null, null); return fmt[0]; } - + /** * Gets the start of the playback range. - * + * * @return the start of the playback range. */ public long getStart() { @@ -97,10 +104,10 @@ public long getStart() { GstQueryAPI.GSTQUERY_API.gst_query_parse_segment(this, null, null, value, null); return value[0]; } - + /** * Gets the end of the playback range. - * + * * @return the end of the playback range. */ public long getEnd() { diff --git a/src/org/freedesktop/gstreamer/video/Video.java b/src/org/freedesktop/gstreamer/video/Video.java new file mode 100644 index 00000000..149217b3 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/Video.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.video; + +import java.util.stream.Stream; +import org.freedesktop.gstreamer.glib.NativeObject; + +import static org.freedesktop.gstreamer.glib.Natives.registration; + +/** + * Utility class for GStreamer Video library. + */ +public final class Video { + + private Video() { + } + + /** + * TypeProvider implementation for GStreamer Video library. + */ + public static class Types implements NativeObject.TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + registration(VideoTimeCodeMeta.class, VideoTimeCodeMeta.GTYPE_NAME, + VideoTimeCodeMeta::new) + ); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCode.java b/src/org/freedesktop/gstreamer/video/VideoTimeCode.java new file mode 100644 index 00000000..28b053a5 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCode.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.video; + +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI; +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI.GstVideoTimeCodeStruct; + + +/** + * A representation of a SMPTE time code. + * + * See https://gstreamer.freedesktop.org/documentation/video/gstvideotimecode.html + */ +@Gst.Since(minor = 10) +public class VideoTimeCode extends NativeObject { + + private final GstVideoTimeCodeStruct timeCodeStruct; + private final VideoTimeCodeConfig timeCodeConfig; + +// public VideoTimeCode(){ +// this(Natives.initializer(GstVideoAPI.GSTVIDEO_API.gst_video_time_code_new_empty())); +// } + + VideoTimeCode(GstVideoTimeCodeStruct struct) { + this(struct, new Handle(new GPointer(struct.getPointer()), false)); + } + + private VideoTimeCode(GstVideoTimeCodeStruct struct, Handle handle) { + super(handle); + this.timeCodeStruct = struct; + timeCodeConfig = new VideoTimeCodeConfig(timeCodeStruct.config); + } + + public VideoTimeCodeConfig getConfig() { + return timeCodeConfig; + } + + /** + * Hours field, must be less than 24. + * + * @return number of hours + */ + public int getHours() { + return timeCodeStruct.hours; + } + + /** + * Minutes field, must be less than 60. + * + * @return number of minutes + */ + public int getMinutes() { + return timeCodeStruct.minutes; + } + + /** + * Second field, must be less than 60. + * + * @return number of seconds + */ + public int getSeconds() { + return timeCodeStruct.seconds; + } + + /** + * Frames field. + * + * @return number of seconds + */ + public int getFrames() { + return timeCodeStruct.frames; + } + + @Override + public String toString() { + return "GstVideoTimeCode{" + getHours() + ":" + getMinutes() + ":" + getSeconds() + ":" + getFrames() + ", timeconfig=" + timeCodeConfig + "}"; + } + + @Override + public void disown() { + timeCodeConfig.disown(); + super.disown(); + } + + private static final class Handle extends NativeObject.Handle{ + + /** + * Construct a Handle for the supplied native reference. + * + * @param ptr native reference + * @param ownsReference whether the Handle owns the native reference and + */ + public Handle(GPointer ptr, boolean ownsReference) { + super(ptr, ownsReference); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GstVideoAPI.GSTVIDEO_API.gst_video_time_code_free(ptr.getPointer()); + } + + @Override + protected GPointer getPointer() { + return super.getPointer(); + } + + + } +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCodeConfig.java b/src/org/freedesktop/gstreamer/video/VideoTimeCodeConfig.java new file mode 100644 index 00000000..3a0cd737 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCodeConfig.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.video; + +import java.util.EnumSet; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI.GstVideoTimeCodeConfigStruct; + +/** + * The configuration of the time code. + *

+ * See upstream documentation at + * + * https://gstreamer.freedesktop.org/documentation/video/gstvideotimecode.html#GstVideoTimeCodeConfig + */ +@Gst.Since(minor = 10) +public class VideoTimeCodeConfig extends NativeObject { + + private final GstVideoTimeCodeConfigStruct timeCodeConfig; + + VideoTimeCodeConfig(GstVideoTimeCodeConfigStruct struct) { + this(struct, new Handle(new GPointer(struct.getPointer()), false)); + } + + private VideoTimeCodeConfig(GstVideoTimeCodeConfigStruct struct, Handle handle) { + super(handle); + timeCodeConfig = struct; + } + + /** + * The corresponding {@link VideoTimeCodeFlags}. + * + * @return return flags for current timecode + */ + public EnumSet getFlags() { + return NativeFlags.fromInt(VideoTimeCodeFlags.class, timeCodeConfig.flags); + } + + /** + * Numerator of the frame rate. + * + * @return numerator + */ + public int getNumerator() { + return timeCodeConfig.fps_n; + } + + /** + * Denominator of the frame rate. + * + * @return denominator + */ + public int getDenominator() { + return timeCodeConfig.fps_d; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("GstVideoTimeCodeConfig{"); + sb.append("flags=").append(getFlags()) + .append(", numerator=").append(getNumerator()) + .append(", denominator=").append(getDenominator()) + .append('}'); + return sb.toString(); + } + + private static final class Handle extends NativeObject.Handle { + + /** + * Construct a Handle for the supplied native reference. + * + * @param ptr native reference + * @param ownsReference whether the Handle owns the native reference and + */ + public Handle(GPointer ptr, boolean ownsReference) { + super(ptr, ownsReference); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + // usually video timecode config will be released together with video timecode + // GlibAPI.GLIB_API.g_free(ptr.getPointer()); + } + + @Override + protected GPointer getPointer() { + return super.getPointer(); + } + + } +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCodeFlags.java b/src/org/freedesktop/gstreamer/video/VideoTimeCodeFlags.java new file mode 100644 index 00000000..804552c4 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCodeFlags.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.video; + +import org.freedesktop.gstreamer.glib.NativeFlags; + +/** + * Flags related to the time code information. For drop frame, only 30000/1001 + * and 60000/1001 frame rates are supported. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/video/gstvideotimecode.html#GstVideoTimeCodeFlags + */ +public enum VideoTimeCodeFlags implements NativeFlags { +// /** +// * No flags +// */ +// GST_VIDEO_TIME_CODE_FLAGS_NONE(0), // No flags + /** + * Whether we have drop frame rate + */ + GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME(1), + /** + * Whether we have interlaced video + */ + GST_VIDEO_TIME_CODE_FLAGS_INTERLACED(2); + + + private final int value; + + VideoTimeCodeFlags(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCodeMeta.java b/src/org/freedesktop/gstreamer/video/VideoTimeCodeMeta.java new file mode 100644 index 00000000..ea683655 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCodeMeta.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.video; + +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.Meta; + +import static org.freedesktop.gstreamer.lowlevel.GstVideoAPI.GstVideoTimeCodeMetaStruct; + + /** + * Extra buffer metadata describing the GstVideoTimeCode of the frame. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/video/gstvideometa.html#GstVideoTimeCodeMeta + */ +@Gst.Since(minor = 10) +public class VideoTimeCodeMeta extends Meta { + + /** + * Meta.API for VideoTimeCodeMeta. + */ + public static final API API = + new API(VideoTimeCodeMeta.class, "GstVideoTimeCodeMetaAPI"); + + /** + * Underlying GType name. + */ + public static final String GTYPE_NAME = "GstVideoTimeCodeMeta"; + + private final VideoTimeCode timeCode; + + VideoTimeCodeMeta(Initializer init) { + super(init); + GstVideoTimeCodeMetaStruct metaStruct = + new GstVideoTimeCodeMetaStruct(init.ptr.getPointer()); + timeCode = new VideoTimeCode(metaStruct.tc); + } + + /** + * Retrieve the time code attached to frame. + * + * @return time code + */ + public VideoTimeCode getTimeCode() { + return timeCode; + } + + @Override + public void disown() { + timeCode.disown(); + super.disown(); + } + +} diff --git a/src/org/freedesktop/gstreamer/webrtc/WebRTC.java b/src/org/freedesktop/gstreamer/webrtc/WebRTC.java new file mode 100644 index 00000000..0195e9a6 --- /dev/null +++ b/src/org/freedesktop/gstreamer/webrtc/WebRTC.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer.webrtc; + +import java.util.stream.Stream; +import org.freedesktop.gstreamer.glib.NativeObject; +import static org.freedesktop.gstreamer.glib.Natives.registration; + +/** + * + * @author Neil C Smith - https://www.neilcsmith.net + */ +public class WebRTC { + + private WebRTC() { + } + + public static class Types implements NativeObject.TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + registration(WebRTCSessionDescription.class, + WebRTCSessionDescription.GTYPE_NAME, + WebRTCSessionDescription::new), + registration(WebRTCBin.class, + WebRTCBin.GTYPE_NAME, + WebRTCBin::new)); + + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/elements/WebRTCBin.java b/src/org/freedesktop/gstreamer/webrtc/WebRTCBin.java similarity index 76% rename from src/org/freedesktop/gstreamer/elements/WebRTCBin.java rename to src/org/freedesktop/gstreamer/webrtc/WebRTCBin.java index 589d8667..c3198102 100644 --- a/src/org/freedesktop/gstreamer/elements/WebRTCBin.java +++ b/src/org/freedesktop/gstreamer/webrtc/WebRTCBin.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2018 Antonio Morales * * This file is part of gstreamer-java. @@ -15,36 +16,35 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ - -package org.freedesktop.gstreamer.elements; +package org.freedesktop.gstreamer.webrtc; import org.freedesktop.gstreamer.Bin; import org.freedesktop.gstreamer.Element; +import org.freedesktop.gstreamer.Gst; import org.freedesktop.gstreamer.Promise; -import org.freedesktop.gstreamer.State; import org.freedesktop.gstreamer.Structure; -import org.freedesktop.gstreamer.GstObject; -import org.freedesktop.gstreamer.WebRTCSessionDescription; -import org.freedesktop.gstreamer.WebRTCPeerConnectionState; +import org.freedesktop.gstreamer.glib.NativeEnum; import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; -import com.sun.jna.Pointer; -import com.sun.jna.ptr.PointerByReference; - /** - * WebRTCBin is an abstraction over gstreamers webrtcbin element - * It is structured to mimic the RTCPeerConnection API that is available in web browsers + * WebRTCBin is an abstraction over gstreamers webrtcbin element It is + * structured to mimic the RTCPeerConnection API that is available in web + * browsers + * * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection * - * @see https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/blob/master/ext/webrtc/gstwebrtcbin.c + * @see + * https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/blob/master/ext/webrtc/gstwebrtcbin.c * available since Gstreamer 1.14 */ +@Gst.Since(minor = 14) public class WebRTCBin extends Bin { + public static final String GST_NAME = "webrtcbin"; - public static final String GTYPE_NAME = "GstWebrtcBin"; + public static final String GTYPE_NAME = "GstWebRTCBin"; - public WebRTCBin(Initializer init) { + WebRTCBin(Initializer init) { super(init); } @@ -53,13 +53,15 @@ public WebRTCBin(String name) { } /** - * Signal emitted when this {@link WebRTCBin} is ready to do negotiation to setup a WebRTC connection - * Good starting point to have the WebRTCBin send an offer to potential clients + * Signal emitted when this {@link WebRTCBin} is ready to do negotiation to + * setup a WebRTC connection Good starting point to have the WebRTCBin send + * an offer to potential clients */ public static interface ON_NEGOTIATION_NEEDED { + /** - * @param elem the original webrtc bin that had the callback attached to - */ + * @param elem the original webrtc bin that had the callback attached to + */ public void onNegotiationNeeded(Element elem); } @@ -67,9 +69,11 @@ public static interface ON_NEGOTIATION_NEEDED { * Signal emmited when this {@link WebRTCBin} gets a new ice candidate */ public static interface ON_ICE_CANDIDATE { + /** - * @param sdpMLineIndex the zero-based index of the m-line attribute within the SDP to which the candidate should be associated to - * @param candidate the ICE candidate + * @param sdpMLineIndex the zero-based index of the m-line attribute + * within the SDP to which the candidate should be associated to + * @param candidate the ICE candidate */ public void onIceCandidate(int sdpMLineIndex, String candidate); } @@ -78,6 +82,7 @@ public static interface ON_ICE_CANDIDATE { * Signal emitted when this {@link WebRTCBin} creates an offer */ public static interface CREATE_OFFER { + /** * @param a @WebRTCSessionDescription of the offer */ @@ -88,6 +93,7 @@ public static interface CREATE_OFFER { * Signal emitted when this {@link WebRTCBin} creates an answer */ public static interface CREATE_ANSWER { + /** * @param a @WebRTCSessionDescription of the answer */ @@ -96,6 +102,7 @@ public static interface CREATE_ANSWER { /** * Adds a listener for the on-negotiation-needed signal. + * * @param listener */ public void connect(final ON_NEGOTIATION_NEEDED listener) { @@ -109,6 +116,7 @@ public void callback(Element elem) { /** * Adds a listener for the on-ice-candidate signal. + * * @param listener */ public void connect(final ON_ICE_CANDIDATE listener) { @@ -121,9 +129,11 @@ public void callback(Element elem, int sdpMLineIndex, String candidate) { } /** - * Create an offer that can be sent to other clients to setup a WebRTC connection. + * Create an offer that can be sent to other clients to setup a WebRTC + * connection. *

- * In most cases {@link #setLocalDescription} should be called after an answer is created + * In most cases {@link #setLocalDescription} should be called after an + * answer is created * * @param listener callback that is called when a offer is created */ @@ -132,7 +142,7 @@ public void createOffer(final CREATE_OFFER listener) { @SuppressWarnings("unused") public void onChange(Promise promise) { Structure reply = promise.getReply(); - WebRTCSessionDescription description = (WebRTCSessionDescription)reply.getValue("offer"); + WebRTCSessionDescription description = (WebRTCSessionDescription) reply.getValue("offer"); listener.onOfferCreated(description); promise.dispose(); } @@ -141,11 +151,13 @@ public void onChange(Promise promise) { } /** - * Create an answer in response to an offer received in order for the WebRTC signaling protocol to start. + * Create an answer in response to an offer received in order for the WebRTC + * signaling protocol to start. *

* Should be called after {@link #setRemoteDescription} is called *

- * In most cases {@link #setLocalDescription} should be called after an answer is created + * In most cases {@link #setLocalDescription} should be called after an + * answer is created * * @param listener callback that is called when an answer is created. */ @@ -154,7 +166,7 @@ public void createAnswer(final CREATE_ANSWER listener) { @SuppressWarnings("unused") public void onChange(Promise promise) { Structure reply = promise.getReply(); - WebRTCSessionDescription description = (WebRTCSessionDescription)reply.getValue("answer"); + WebRTCSessionDescription description = (WebRTCSessionDescription) reply.getValue("answer"); listener.onAnswerCreated(description); promise.dispose(); } @@ -165,18 +177,20 @@ public void onChange(Promise promise) { /** * Adds a remote ice candidate to the bin * - * @param sdpMLineIndex the zero-based index of the m-line attribute within the SDP to which the candidate should be associated to - * @param candidate the ICE candidate + * @param sdpMLineIndex the zero-based index of the m-line attribute within + * the SDP to which the candidate should be associated to + * @param candidate the ICE candidate */ public void addIceCandidate(int sdpMLineIndex, String candidate) { emit("add-ice-candidate", sdpMLineIndex, candidate); } /** - * Sets the local description for the WebRTC connection. - * Should be called after {@link #createOffer} or {@link #createAnser} is called. + * Sets the local description for the WebRTC connection. Should be called + * after {@link #createOffer} or {@link #createAnser} is called. * - * @param description the {@link WebRTCSessionDescription} to set for the local description + * @param description the {@link WebRTCSessionDescription} to set for the + * local description */ public void setLocalDescription(WebRTCSessionDescription description) { Promise promise = new Promise(); @@ -188,10 +202,11 @@ public void setLocalDescription(WebRTCSessionDescription description) { } /** - * Sets the remote description for the WebRTC connection. - * Shoud be called after receiving an offer or answer from other clients. - * - * @param description the {@link WebRTCSessionDescription} to set for the remote description + * Sets the remote description for the WebRTC connection. Shoud be called + * after receiving an offer or answer from other clients. + * + * @param description the {@link WebRTCSessionDescription} to set for the + * remote description */ public void setRemoteDescription(WebRTCSessionDescription description) { Promise promise = new Promise(); @@ -203,7 +218,8 @@ public void setRemoteDescription(WebRTCSessionDescription description) { } /** - * Sets the stun-server property for this {@link WebRTCBin} which is use to gather ICE data + * Sets the stun-server property for this {@link WebRTCBin} + * which is use to gather ICE data * * @param server STUN server url */ @@ -217,11 +233,13 @@ public void setStunServer(String server) { * @return the url for the STUN server */ public String getStunServer() { - return (String)get("stun-server"); + return (String) get("stun-server"); } /** - * Sets the turn-server property for this {@link WebRTCBin} which is used whenever a direct peer-to-peer connection can be established + * Sets the turn-server property for this {@link WebRTCBin} + * which is used whenever a direct peer-to-peer connection can be + * established * * @param server TURN server url */ @@ -235,16 +253,26 @@ public void setTurnServer(String server) { * @return the url for the TURN server */ public String getTurnServer() { - return (String)get("turn-server"); + return (String) get("turn-server"); } /** * Retrieve the connection state this {@link WebRTCBin} is currently in * - * @return a {@link WebRTCPeerConnectionState} describing the connection state + * @return a {@link WebRTCPeerConnectionState} describing the connection + * state */ public WebRTCPeerConnectionState getConnectionState() { - return (WebRTCPeerConnectionState)get("connection-state"); + return NativeEnum.fromInt(WebRTCPeerConnectionState.class, (Integer) get("connection-state")); + } + + /** + * Retrieve ICE gathering state this {@link WebRTCBin} is currently in + * + * @return a {@link WebRTCICEGatheringState} describing gathering state + */ + public WebRTCICEGatheringState getICEGatheringState() { + return NativeEnum.fromInt(WebRTCICEGatheringState.class, (Integer) get("ice-gathering-state")); } /** @@ -253,7 +281,7 @@ public WebRTCPeerConnectionState getConnectionState() { * @return the local {@link WebRTCSessionDescription} */ public WebRTCSessionDescription getLocalDescription() { - WebRTCSessionDescription description = (WebRTCSessionDescription)get("local-description"); + WebRTCSessionDescription description = (WebRTCSessionDescription) get("local-description"); description.disown(); return description; } @@ -264,7 +292,7 @@ public WebRTCSessionDescription getLocalDescription() { * @return the remote {@link WebRTCSessionDescription} */ public WebRTCSessionDescription getRemoteDescription() { - WebRTCSessionDescription description = (WebRTCSessionDescription)get("remote-description"); + WebRTCSessionDescription description = (WebRTCSessionDescription) get("remote-description"); description.disown(); return description; } diff --git a/src/org/freedesktop/gstreamer/webrtc/WebRTCICEGatheringState.java b/src/org/freedesktop/gstreamer/webrtc/WebRTCICEGatheringState.java new file mode 100644 index 00000000..6e50a993 --- /dev/null +++ b/src/org/freedesktop/gstreamer/webrtc/WebRTCICEGatheringState.java @@ -0,0 +1,29 @@ +package org.freedesktop.gstreamer.webrtc; + +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeEnum; + +/** + * The ICE gathering state of WebRTC peer + * Available since GStreamer 1.14 + */ +@Gst.Since(minor = 14) +public enum WebRTCICEGatheringState implements NativeEnum { + /** New gathering */ + NEW(0), + /** Gathering in progress */ + GATHERING(1), + /** Gathering completed */ + COMPLETE(2); + + private final int value; + + private WebRTCICEGatheringState(int value) { + this.value = value; + } + + @Override + public int intValue() { + return this.value; + } +} diff --git a/src/org/freedesktop/gstreamer/WebRTCPeerConnectionState.java b/src/org/freedesktop/gstreamer/webrtc/WebRTCPeerConnectionState.java similarity index 65% rename from src/org/freedesktop/gstreamer/WebRTCPeerConnectionState.java rename to src/org/freedesktop/gstreamer/webrtc/WebRTCPeerConnectionState.java index b41b93cf..90d16f8e 100644 --- a/src/org/freedesktop/gstreamer/WebRTCPeerConnectionState.java +++ b/src/org/freedesktop/gstreamer/webrtc/WebRTCPeerConnectionState.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019 Neil C Smith * Copyright (c) 2018 Antonio Morales * * This file is part of gstreamer-java. @@ -14,27 +15,39 @@ * this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.webrtc; -import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeEnum; /** * The state of a WebRTC peer connection * Available since GStreamer 1.14 */ -public enum WebRTCPeerConnectionState { +@Gst.Since(minor = 14) +public enum WebRTCPeerConnectionState implements NativeEnum { /** New WebRTC connection */ - NEW, + NEW(0), /** A WebRTC connection is being made */ - CONNECTING, + CONNECTING(1), /** A WebRTC connection has been made */ - CONNECTED, + CONNECTED(2), /** A WebRTC connection has been disconnected */ - DISCONNECTED, + DISCONNECTED(3), /** Attempt to make a WebRTC connection failed */ - FAILED, + FAILED(4), /** A WebRTC connection has been closed */ - CLOSED, - /** Unknown result */ - @DefaultEnumValue UNKNOWN; + CLOSED(5); + + private final int value; + + private WebRTCPeerConnectionState(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + } diff --git a/src/org/freedesktop/gstreamer/WebRTCSDPType.java b/src/org/freedesktop/gstreamer/webrtc/WebRTCSDPType.java similarity index 80% rename from src/org/freedesktop/gstreamer/WebRTCSDPType.java rename to src/org/freedesktop/gstreamer/webrtc/WebRTCSDPType.java index f363360c..3bf016f9 100644 --- a/src/org/freedesktop/gstreamer/WebRTCSDPType.java +++ b/src/org/freedesktop/gstreamer/webrtc/WebRTCSDPType.java @@ -15,23 +15,27 @@ * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.webrtc; -import org.freedesktop.gstreamer.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeEnum; /** * The type of a {@link WebRTCSessionDescription} - * + *

* @see https://w3c.github.io/webrtc-pc/#rtcsdptype * Available since GStreamer 1.12 */ -public enum WebRTCSDPType implements IntegerEnum { +@Gst.Since(minor = 12) +public enum WebRTCSDPType implements NativeEnum { OFFER(1), PRANSWER(2), ANSWER(3), ROLLBACK(4); + + private final int value; - WebRTCSDPType(int value) { + private WebRTCSDPType(int value) { this.value = value; } @@ -39,8 +43,8 @@ public enum WebRTCSDPType implements IntegerEnum { * Gets the integer value of the enum * @return the integer value for this enum. */ + @Override public int intValue() { return value; } - private final int value; } diff --git a/src/org/freedesktop/gstreamer/webrtc/WebRTCSessionDescription.java b/src/org/freedesktop/gstreamer/webrtc/WebRTCSessionDescription.java new file mode 100644 index 00000000..bb24a648 --- /dev/null +++ b/src/org/freedesktop/gstreamer/webrtc/WebRTCSessionDescription.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2018 Vinicius Tona + * Copyright (c) 2018 Antonio Morales + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License version 3 only, as published by the Free Software Foundation. + * + * This code 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 version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License version 3 along with + * this work. If not, see . + */ +package org.freedesktop.gstreamer.webrtc; + +import static org.freedesktop.gstreamer.lowlevel.GstWebRTCSessionDescriptionAPI.GSTWEBRTCSESSIONDESCRIPTION_API; + +import org.freedesktop.gstreamer.lowlevel.GstWebRTCSessionDescriptionAPI; +import org.freedesktop.gstreamer.glib.NativeObject; + +import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.SDPMessage; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import static org.freedesktop.gstreamer.lowlevel.GstSDPMessageAPI.GSTSDPMESSAGE_API; + +/** + * Wraps a GstWebRTCSessionDescription + *

+ * See https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/blob/master/gst-libs/gst/webrtc/rtcsessiondescription.h + */ +public class WebRTCSessionDescription extends NativeObject { + + public static final String GTYPE_NAME = "GstWebRTCSessionDescription"; + + private GstWebRTCSessionDescriptionAPI.WebRTCSessionDescriptionStruct sessionDescriptionStruct; + + /** + * Internally used constructor. Do not use. + * + * @param init internal initialization data. + */ + WebRTCSessionDescription(Initializer init) { + super(new Handle(init.ptr, init.ownsHandle)); + sessionDescriptionStruct = + new GstWebRTCSessionDescriptionAPI.WebRTCSessionDescriptionStruct(init.ptr.getPointer()); + } + + /** + * Creates a new instance of WebRTCSessionDescription. + * + * @param type The {@link WebRTCSDPType} type of the session description + * @param sdpMessage The {@link SDPMessage} of the session description + */ + public WebRTCSessionDescription(WebRTCSDPType type, SDPMessage sdpMessage) { + this(Natives.initializer(GSTWEBRTCSESSIONDESCRIPTION_API.ptr_gst_webrtc_session_description_new(type, sdpMessage))); + } + + /** + * Gets the SDPMessage from the WebRTCSessionDescription. + * + * @return the {@link SDPMessage} for the WebRTCSessionDescription + */ + public SDPMessage getSDPMessage() { + SDPMessage originalSDP = (SDPMessage) sessionDescriptionStruct.readField("sdp"); + // making a copy of the SDPMessage since the original SDPMessage in the struct belongs to WebRTCSessionDescription. + // Once WebRTCSessionDescription is disposed it would also dispose of SDPMessage leading to any objects with a reference + // to the original SDPMessage to be invalid and potentially lead to runtime errors. + + // return originalSDP.copy(true); + Pointer[] ptr = new Pointer[1]; + GSTSDPMESSAGE_API.gst_sdp_message_copy(originalSDP, ptr); + originalSDP.invalidate(); + return Natives.objectFor(ptr[0], SDPMessage.class, false, true); + } + + + private static final class Handle extends NativeObject.Handle { + + public Handle(GPointer ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GSTWEBRTCSESSIONDESCRIPTION_API.gst_webrtc_session_description_free(ptr.getPointer()); + } + + } +} diff --git a/test/org/freedesktop/gstreamer/BinTest.java b/test/org/freedesktop/gstreamer/BinTest.java index 4c72ed51..1024c289 100644 --- a/test/org/freedesktop/gstreamer/BinTest.java +++ b/test/org/freedesktop/gstreamer/BinTest.java @@ -19,6 +19,7 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.GError; import java.util.ArrayList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/test/org/freedesktop/gstreamer/BufferFieldsTest.java b/test/org/freedesktop/gstreamer/BufferFieldsTest.java index 293ec96f..87b433e7 100644 --- a/test/org/freedesktop/gstreamer/BufferFieldsTest.java +++ b/test/org/freedesktop/gstreamer/BufferFieldsTest.java @@ -4,74 +4,67 @@ import static org.junit.Assert.*; /** - *

Copyright (C) 2018 Robert Forsman, Ericsson SATV - * $Author thoth $ - * $Date 3/8/18 $ + *

+ * Copyright (C) 2018 Robert Forsman, Ericsson SATV $Author thoth $ $Date 3/8/18 + * $ */ -public class BufferFieldsTest -{ +public class BufferFieldsTest { + @BeforeClass public static void setUpClass() throws Exception { - Gst.init("BufferFieldsTest", new String[] {}); + Gst.init("BufferFieldsTest"); } - + @AfterClass public static void tearDownClass() throws Exception { Gst.deinit(); } - + private Buffer buf; - + @Before - public void setUp() - { + public void setUp() { buf = new Buffer(12); } - + @Test - public void setPTS() - { + public void setPTS() { buf.setPresentationTimestamp(ClockTime.fromMicros(5004003)); - ClockTime val = buf.getPresentationTimestamp(); - assertEquals(5004003, val.toMicros()); + long val = buf.getPresentationTimestamp(); + assertEquals(5004003, ClockTime.toMicros(val)); } - + @Test - public void setDTS() - { + public void setDTS() { buf.setDecodeTimestamp(ClockTime.fromMicros(9001004)); - ClockTime val = buf.getDecodeTimestamp(); - assertEquals(9001004, val.toMicros()); + long val = buf.getDecodeTimestamp(); + assertEquals(9001004, ClockTime.toMicros(val)); } - + @Test - public void setDuration() - { + public void setDuration() { buf.setDuration(ClockTime.fromMicros(4006008)); - ClockTime val = buf.getDuration(); - assertEquals(4006008, val.toMicros()); + long val = buf.getDuration(); + assertEquals(4006008, ClockTime.toMicros(val)); } - + @Test - public void setOffset() - { + public void setOffset() { buf.setOffset(2009006); long val = buf.getOffset(); assertEquals(2009006, val); } - + @Test - public void setOffsetEnd() - { + public void setOffsetEnd() { buf.setOffsetEnd(7005003); long val = buf.getOffsetEnd(); assertEquals(7005003, val); } - + @Test // cannot test on GStreamer 1.8 - public void setFlags() - { + public void setFlags() { // assertTrue(buf.setFlags(7)); // int val = buf.getFlags(); // assertEquals(7, val); diff --git a/test/org/freedesktop/gstreamer/BufferProbeTester.java b/test/org/freedesktop/gstreamer/BufferProbeTester.java new file mode 100644 index 00000000..86acfe58 --- /dev/null +++ b/test/org/freedesktop/gstreamer/BufferProbeTester.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 John Cortell + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +/** + * Utility class for unit testing API that operates on a Buffer. + *

+ * Call {@link BufferTester#test(Consumer)} and pass a callback which will + * perform the test on a Buffer it is supplied. The callback runs in a Pad data + * probe. The buffer is produced by a simple, ephemeral pipeline that is fed by + * a video test source. + */ +public class BufferProbeTester { + + public static void test(Consumer callback) { + test(callback, "videotestsrc ! videoconvert ! fakesink name=sink"); + } + + public static void test(Consumer callback, String pipelineDescription) { + test(callback, pipelineDescription, 0); + } + + public static void test(Consumer callback, String pipelineDescription, int skipFrames) { + assertNotNull("Pipeline description can not be null", pipelineDescription); + assertFalse("Pipeline description can not be empty", pipelineDescription.isEmpty()); + Pipeline pipe = (Pipeline) Gst.parseLaunch(pipelineDescription); + assertNotNull("Unable to create Pipeline from pipeline description: ", pipe); + + Element sink = pipe.getElementByName("sink"); + Pad pad = sink.getStaticPad("sink"); + BufferProbe probe = new BufferProbe(callback, skipFrames); + pad.addDataProbe(probe); + + pipe.play(); + + // Wait for the sample to arrive and for the client supplied test function to + // complete + try { + probe.await(5000); + } catch (Exception ex) { + fail("Unexpected exception waiting for buffer\n" + ex); + } finally { + pipe.stop(); + } + + // If the test threw an exception on the sample listener thread, throw it here + // (on the main thread) + if (probe.exception != null) { + throw new AssertionError(probe.exception); + } + } + + private static class BufferProbe implements Pad.DATA_PROBE { + + private final int skipFrames; + private final CountDownLatch latch; + private final Consumer callback; + + private Throwable exception; + private int counter = 0; + + BufferProbe(Consumer callback) { + this(callback, 0); + } + + BufferProbe(Consumer callback, int skip) { + this.callback = callback; + skipFrames = skip; + latch = new CountDownLatch(1); + } + + @Override + public PadProbeReturn dataReceived(Pad pad, Buffer buffer) { + if (latch.getCount() > 0) { + if (counter < skipFrames) { + counter++; + return PadProbeReturn.OK; + } + try { + // Run the client's test logic on the buffer (only once) + try { + callback.accept(buffer); + } catch (Throwable exc) { + exception = exc; + } + } finally { + latch.countDown(); + } + } + return PadProbeReturn.OK; + } + + void await(long millis) throws InterruptedException, TimeoutException { + if (!latch.await(millis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException(); + } + } + } + +} diff --git a/test/org/freedesktop/gstreamer/BusTest.java b/test/org/freedesktop/gstreamer/BusTest.java index 6a944f1c..951c8714 100644 --- a/test/org/freedesktop/gstreamer/BusTest.java +++ b/test/org/freedesktop/gstreamer/BusTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -16,22 +17,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with gstreamer-java. If not, see . */ - package org.freedesktop.gstreamer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; - -import com.sun.jna.Platform; - -import org.freedesktop.gstreamer.lowlevel.GstMessageAPI; +import org.freedesktop.gstreamer.lowlevel.GlibAPI; import org.freedesktop.gstreamer.lowlevel.GstAPI.GErrorStruct; import org.freedesktop.gstreamer.message.EOSMessage; -import org.freedesktop.gstreamer.message.StateChangedMessage; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.message.MessageType; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -39,16 +35,19 @@ import org.junit.Test; import static org.freedesktop.gstreamer.lowlevel.GstElementAPI.GSTELEMENT_API; +import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; +import static org.junit.Assert.*; + +public class BusTest { -public class BusTest { public BusTest() { } @BeforeClass public static void setUpClass() throws Exception { - Gst.init("BusTest", new String[] {}); + Gst.init("BusTest"); } - + @AfterClass public static void tearDownClass() throws Exception { Gst.deinit(); @@ -61,19 +60,18 @@ public void setUp() throws Exception { @After public void tearDown() throws Exception { } + @Test public void endOfStream() { - final TestPipe pipe = new TestPipe("endOfStream"); - + final TestPipe pipe = new TestPipe("endOfStream"); + final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalSource = new AtomicReference(); - Bus.EOS eosSignal = new Bus.EOS() { + final AtomicReference signalSource = new AtomicReference<>(); - public void endOfStream(GstObject source) { - signalFired.set(true); - signalSource.set(source); - pipe.quit(); - } + Bus.EOS eosSignal = (GstObject source) -> { + signalFired.set(true); + signalSource.set(source); + pipe.quit(); }; pipe.play(); pipe.getBus().connect(eosSignal); @@ -81,7 +79,8 @@ public void endOfStream(GstObject source) { // For the pipeline to post an EOS message, all sink elements must post it // for (Element elem : pipe.pipe.getSinks()) { - GSTELEMENT_API.gst_element_post_message(elem, GstMessageAPI.GSTMESSAGE_API.gst_message_new_eos(elem)); + GSTELEMENT_API.gst_element_post_message(elem, + GSTMESSAGE_API.gst_message_new_eos(elem)); } pipe.run(); pipe.getBus().disconnect(eosSignal); @@ -89,126 +88,136 @@ public void endOfStream(GstObject source) { assertTrue("EOS signal not received", signalFired.get()); pipe.dispose(); } + @Test public void stateChanged() { final TestPipe pipe = new TestPipe("stateChanged"); final AtomicBoolean signalFired = new AtomicBoolean(false); - - Bus.STATE_CHANGED stateChanged = new Bus.STATE_CHANGED() { - - public void stateChanged(GstObject source, State old, State current, State pending) { - if (pending == State.PLAYING || current == State.PLAYING) { - signalFired.set(true); - pipe.quit(); - } + + Bus.STATE_CHANGED stateChanged = (GstObject source, State old, State current, State pending) -> { + if (pending == State.PLAYING || current == State.PLAYING) { + signalFired.set(true); + pipe.quit(); } - }; pipe.getBus().connect(stateChanged); - GSTELEMENT_API.gst_element_post_message(pipe.pipe, - new StateChangedMessage(pipe.pipe, State.READY, State.PLAYING, State.VOID_PENDING)); - pipe.run(); + pipe.play().run(); pipe.getBus().disconnect(stateChanged); assertTrue("STATE_CHANGED signal not received", signalFired.get()); pipe.dispose(); } + @Test + public void asyncDone() { + final TestPipe pipe = new TestPipe("asyncDone"); + final AtomicBoolean signalFired = new AtomicBoolean(false); + + Bus.ASYNC_DONE asyncDone = (GstObject source) -> { + signalFired.set(true); + pipe.quit(); + }; + pipe.getBus().connect(asyncDone); + pipe.play().run(); + pipe.getBus().disconnect(asyncDone); + assertTrue("ASYNC_DONE message not received", signalFired.get()); + pipe.dispose(); + } + @Test public void errorMessage() { final TestPipe pipe = new TestPipe("errorMessage"); - + final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalSource = new AtomicReference(); - Bus.ERROR errorSignal = new Bus.ERROR() { + final AtomicReference signalSource = new AtomicReference<>(); - public void errorMessage(GstObject source, int code, String message) { - signalFired.set(true); - signalSource.set(source); - pipe.quit(); - } + Bus.ERROR errorSignal = (GstObject source, int code, String message) -> { + signalFired.set(true); + signalSource.set(source); + pipe.quit(); }; pipe.getBus().connect(errorSignal); - - GErrorStruct msg = new GErrorStruct(); - GSTELEMENT_API.gst_element_post_message(pipe.src, GstMessageAPI.GSTMESSAGE_API.gst_message_new_error(pipe.src, msg, "testing error messages")); + + GErrorStruct msg = GlibAPI.GLIB_API.g_error_new(1, 1, "MSG"); + GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTMESSAGE_API.gst_message_new_error(pipe.src, msg, "testing error messages")); pipe.play().run(); pipe.getBus().disconnect(errorSignal); pipe.dispose(); assertTrue("ERROR signal not received", signalFired.get()); assertEquals("Incorrect source object on signal", pipe.src, signalSource.get()); + GlibAPI.GLIB_API.g_error_free(msg.getPointer()); } + @Test public void warningMessage() { final TestPipe pipe = new TestPipe("warningMessage"); - + final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalSource = new AtomicReference(); - Bus.WARNING signal = new Bus.WARNING() { + final AtomicReference signalSource = new AtomicReference<>(); - public void warningMessage(GstObject source, int code, String message) { - signalFired.set(true); - signalSource.set(source); - pipe.quit(); - } + Bus.WARNING signal = (GstObject source, int code, String message) -> { + signalFired.set(true); + signalSource.set(source); + pipe.quit(); }; pipe.getBus().connect(signal); - - GErrorStruct msg = new GErrorStruct(); + + GErrorStruct msg = GlibAPI.GLIB_API.g_error_new(1, 1, "MSG"); pipe.play(); - GSTELEMENT_API.gst_element_post_message(pipe.src, GstMessageAPI.GSTMESSAGE_API.gst_message_new_warning(pipe.src, msg, "testing warning messages")); + GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTMESSAGE_API.gst_message_new_warning(pipe.src, msg, "testing warning messages")); pipe.run(); pipe.getBus().disconnect(signal); pipe.dispose(); assertTrue("WARNING signal not received", signalFired.get()); assertEquals("Incorrect source object on signal", pipe.src, signalSource.get()); + GlibAPI.GLIB_API.g_error_free(msg.getPointer()); } + @Test public void infoMessage() { - if (Platform.isWindows()) { - return; // This test does not work on windows - gst_message_new_info() doesn't exist. - } final TestPipe pipe = new TestPipe("infoMessage"); - + final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalSource = new AtomicReference(); - Bus.INFO signal = new Bus.INFO() { + final AtomicReference signalSource = new AtomicReference<>(); - public void infoMessage(GstObject source, int code, String message) { - signalFired.set(true); - signalSource.set(source); - pipe.quit(); - } + Bus.INFO signal = (GstObject source, int code, String message) -> { + signalFired.set(true); + signalSource.set(source); + pipe.quit(); }; pipe.getBus().connect(signal); - - GErrorStruct msg = new GErrorStruct(); + + GErrorStruct msg = GlibAPI.GLIB_API.g_error_new(1, 1, "MSG"); pipe.play(); - GSTELEMENT_API.gst_element_post_message(pipe.src, GstMessageAPI.GSTMESSAGE_API.gst_message_new_info(pipe.src, msg, "testing warning messages")); + GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTMESSAGE_API.gst_message_new_info(pipe.src, msg, "testing info messages")); pipe.run(); pipe.getBus().disconnect(signal); pipe.dispose(); assertTrue("INFO signal not received", signalFired.get()); assertEquals("Incorrect source object on signal", pipe.src, signalSource.get()); + GlibAPI.GLIB_API.g_error_free(msg.getPointer()); } + @Test public void bufferingData() { final TestPipe pipe = new TestPipe("bufferingData"); - + final AtomicBoolean signalFired = new AtomicBoolean(false); final AtomicInteger signalValue = new AtomicInteger(-1); - final AtomicReference signalSource = new AtomicReference(); + final AtomicReference signalSource = new AtomicReference<>(); final int PERCENT = 95; - Bus.BUFFERING signal = new Bus.BUFFERING() { - public void bufferingData(GstObject source, int percent) { - signalFired.set(true); - signalValue.set(percent); - signalSource.set(source); - pipe.quit(); - } + Bus.BUFFERING signal = (GstObject source, int percent) -> { + signalFired.set(true); + signalValue.set(percent); + signalSource.set(source); + pipe.quit(); }; pipe.getBus().connect(signal); - GSTELEMENT_API.gst_element_post_message(pipe.src, GstMessageAPI.GSTMESSAGE_API.gst_message_new_buffering(pipe.src, PERCENT)); + GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTMESSAGE_API.gst_message_new_buffering(pipe.src, PERCENT)); pipe.play().run(); pipe.getBus().disconnect(signal); pipe.dispose(); @@ -216,75 +225,262 @@ public void bufferingData(GstObject source, int percent) { assertEquals("Wrong percent value received for signal", PERCENT, signalValue.get()); assertEquals("Incorrect source object on signal", pipe.src, signalSource.get()); } + @Test public void tagsFound() { final TestPipe pipe = new TestPipe("tagsFound"); - - final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalSource = new AtomicReference(); - Bus.TAG signal = new Bus.TAG() { - public void tagsFound(GstObject source, TagList tagList) { - signalFired.set(true); - signalSource.set(source); - pipe.quit(); - } + final AtomicBoolean signalFired = new AtomicBoolean(false); + final AtomicReference signalSource = new AtomicReference<>(); + Bus.TAG signal = (GstObject source, TagList tagList) -> { + signalFired.set(true); + signalSource.set(source); + pipe.quit(); }; pipe.getBus().connect(signal); - + TagList tagList = new TagList(); - GSTELEMENT_API.gst_element_post_message(pipe.src, GstMessageAPI.GSTMESSAGE_API.gst_message_new_tag(pipe.src, tagList)); + GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTMESSAGE_API.gst_message_new_tag(pipe.src, tagList)); pipe.play().run(); pipe.getBus().disconnect(signal); pipe.dispose(); assertTrue("TAG signal not received", signalFired.get()); assertEquals("Incorrect source object on signal", pipe.src, signalSource.get()); } - @Test public void anyMessage() { - final TestPipe pipe = new TestPipe("anyMessage"); - - + + @Test + public void durationChanged() { + final TestPipe pipe = new TestPipe("testDurationChanged"); + final AtomicBoolean signalFired = new AtomicBoolean(false); + final AtomicReference signalSource = new AtomicReference<>(null); + Bus.DURATION_CHANGED signal = (GstObject source) -> { + signalFired.set(true); + signalSource.set(source); + pipe.quit(); + }; + pipe.getBus().connect(signal); + GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTMESSAGE_API.gst_message_new_duration_changed(pipe.src)); + pipe.play().run(); + pipe.getBus().disconnect(signal); + pipe.dispose(); + assertTrue("DURATION signal not received", signalFired.get()); + assertEquals("Incorrect source object on signal", pipe.src, signalSource.get()); + } + + @Test + public void segmentDone() { + final TestPipe pipe = new TestPipe("segmentDone"); final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalSource = new AtomicReference(); - Bus.MESSAGE listener = new Bus.MESSAGE() { + final AtomicReference formatReceived = new AtomicReference<>(null); + final AtomicLong positionReceived = new AtomicLong(0); - public void busMessage(Bus bus, Message msg) { - signalFired.set(true); - signalSource.set(msg.getSource()); - pipe.quit(); - } + Bus.SEGMENT_DONE segmentDone = (source, format, position) -> { + signalFired.set(true); + formatReceived.set(format); + positionReceived.set(position); + }; + + pipe.getBus().connect(segmentDone); + + final long POSITION = 0xdeadbeef; + GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTMESSAGE_API.gst_message_new_segment_done(pipe.src, Format.TIME, POSITION)); + pipe.run(); + + assertTrue("No segment done message received", signalFired.get()); + assertEquals("Wrong format", Format.TIME, formatReceived.get()); + assertEquals("Wrong position", POSITION, positionReceived.get()); + pipe.dispose(); + } + + @Test + public void anyMessage() { + final TestPipe pipe = new TestPipe("anyMessage"); + + final AtomicReference firstMessage = new AtomicReference<>(); + + Bus.MESSAGE listener = (Bus bus, Message msg) -> { + firstMessage.compareAndSet(null, msg); + pipe.quit(); }; + pipe.getBus().connect(listener); - // - // For the pipeline to post an EOS message, all sink elements must post it - // - for (Element elem : pipe.pipe.getSinks()) { - GSTELEMENT_API.gst_element_post_message(elem, GstMessageAPI.GSTMESSAGE_API.gst_message_new_eos(elem)); - } pipe.play().run(); pipe.getBus().disconnect(listener); - - assertTrue("EOS signal not received", signalFired.get()); pipe.dispose(); + + Message message = firstMessage.getAndSet(null); + assertNotNull("No message received", message); + GCTracker gc = new GCTracker(message); + message = null; + assertTrue("Message not garbage collected", gc.waitGC()); + assertTrue("Message not destroyed", gc.waitDestroyed()); } - @Test public void postMessage() { + + @Test + public void postMessage() { final TestPipe pipe = new TestPipe(); - - - final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalSource = new AtomicReference(); - Bus.MESSAGE listener = new Bus.MESSAGE() { - public void busMessage(Bus bus, Message msg) { - signalFired.set(true); - signalSource.set(msg.getSource()); - pipe.quit(); - } + final AtomicBoolean signalFired = new AtomicBoolean(false); + final AtomicReference signalSource = new AtomicReference<>(); + + Bus.MESSAGE listener = (Bus bus, Message msg) -> { + signalFired.set(true); + signalSource.set(msg.getSource()); + pipe.quit(); }; + pipe.getBus().connect(listener); - pipe.getBus().post(new EOSMessage(pipe.src)); + Message message = new EOSMessage(pipe.src); + pipe.getBus().post(message); pipe.run(); assertTrue("Message not posted", signalFired.get()); assertEquals("Wrong source in message", pipe.src, signalSource.get()); + pipe.dispose(); + + GCTracker gc = new GCTracker(message); + message = null; + assertTrue("Message not garbage collected", gc.waitGC()); + assertTrue("Message not destroyed", gc.waitDestroyed()); + } + + @Test + public void syncHandler() { + final TestPipe pipe = new TestPipe("syncHandler"); + + final AtomicReference firstMessageSync = new AtomicReference<>(); + final AtomicReference firstMessageAsync = new AtomicReference<>(); + + BusSyncHandler syncHandler = (Message message) -> { + firstMessageSync.compareAndSet(null, message); + return BusSyncReply.PASS; + }; + + Bus.MESSAGE listener = (Bus bus, Message msg) -> { + firstMessageAsync.compareAndSet(null, msg); + pipe.quit(); + }; + + pipe.getBus().setSyncHandler(syncHandler); + pipe.getBus().connect(listener); + pipe.play().run(); + pipe.getBus().disconnect(listener); + pipe.dispose(); + + Message message = firstMessageSync.getAndSet(null); + Message asyncMessage = firstMessageAsync.getAndSet(null); + assertNotNull("No message received", message); + assertTrue("Sync and listeners messages not equal", message == asyncMessage); + GCTracker gc = new GCTracker(message); + message = null; + asyncMessage = null; + assertTrue("Message not garbage collected", gc.waitGC()); + assertTrue("Message not destroyed", gc.waitDestroyed()); + } + + @Test + public void syncHandlerRemoval() { + final TestPipe pipe = new TestPipe("syncHandlerRemoval"); + + final AtomicReference firstMessageSync = new AtomicReference<>(); + final AtomicReference firstMessageAsync = new AtomicReference<>(); + + BusSyncHandler syncHandler = (Message message) -> { + firstMessageSync.compareAndSet(null, message); + return BusSyncReply.PASS; + }; + + Bus.MESSAGE listener = (Bus bus, Message msg) -> { + firstMessageAsync.compareAndSet(null, msg); + pipe.quit(); + }; + + pipe.getBus().setSyncHandler(syncHandler); + pipe.getBus().connect(listener); + pipe.getBus().setSyncHandler(null); + pipe.play().run(); + pipe.getBus().disconnect(listener); + pipe.dispose(); + + assertNull("Removed sync handler received message", + firstMessageSync.getAndSet(null)); + Message message = firstMessageAsync.getAndSet(null); + assertNotNull("No message received", message); + GCTracker gc = new GCTracker(message); + message = null; + assertTrue("Message not garbage collected", gc.waitGC()); + assertTrue("Message not destroyed", gc.waitDestroyed()); } + + @Test + public void listenerRemoval() { + final TestPipe pipe = new TestPipe("checkListenerRemoval"); + + final AtomicReference firstMessage = new AtomicReference<>(null); + final AtomicBoolean stateChangedFired = new AtomicBoolean(false); + + Bus.MESSAGE listener = (Bus bus, Message msg) -> { + firstMessage.compareAndSet(null, msg); + pipe.quit(); + }; + + Bus.STATE_CHANGED stateListener = (GstObject source, State old, State current, State pending) -> { + stateChangedFired.set(true); + }; + + pipe.getBus().connect(listener); + pipe.getBus().connect(stateListener); + pipe.getBus().disconnect(stateListener); + pipe.play().run(); + pipe.getBus().disconnect(listener); + pipe.dispose(); + + Message message = firstMessage.getAndSet(null); + assertNotNull("No message received", message); + + assertFalse("State changed fired after removal", stateChangedFired.get()); + + GCTracker gc = new GCTracker(message); + message = null; + assertTrue("Message not garbage collected", gc.waitGC()); + assertTrue("Message not destroyed", gc.waitDestroyed()); + } + + @Test + public void extendedMessageIssue202() { + final TestPipe pipe = new TestPipe("issue202"); + + final AtomicBoolean signalFired = new AtomicBoolean(false); + + Bus.MESSAGE msgListener = (Bus bus, Message msg) -> { + signalFired.set(true); + }; + + Bus.ERROR errListener = (GstObject source, int code, String message) -> { + // @TODO If used as flags, DEVICE_REMOVED and ERROR overlap. + // but an exception will be thrown in the executor and logged + // rather than this method being called - need a way to fail with + // executor exceptions in tests? + }; + + pipe.getBus().connect(errListener); + pipe.getBus().connect(msgListener); + + for (Element elem : pipe.pipe.getSources()) { + GSTELEMENT_API.gst_element_post_message(elem, + GSTMESSAGE_API + .gst_message_new_custom(MessageType.DEVICE_REMOVED, + elem, null) + ); + } + pipe.play().run(); + pipe.getBus().disconnect(msgListener); + pipe.getBus().disconnect(errListener); + + assertTrue("Custom message not received", signalFired.get()); + pipe.dispose(); + } + } diff --git a/test/org/freedesktop/gstreamer/ClockTest.java b/test/org/freedesktop/gstreamer/ClockTest.java new file mode 100644 index 00000000..3cd0b5b6 --- /dev/null +++ b/test/org/freedesktop/gstreamer/ClockTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + * + */ +package org.freedesktop.gstreamer; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + */ +public class ClockTest { + + @BeforeClass + public static void beforeClass() { + Gst.init(Gst.getVersion()); + } + + @AfterClass + public static void afterClass() { + Gst.deinit(); + } + + @Test + public void calibrationTest() { + Pipeline pipe = (Pipeline) Gst.parseLaunch("autovideosrc ! autovideosink"); + Clock clock = pipe.getClock(); + Clock.Calibration cal1 = clock.getCalibration(); + System.out.println(cal1); + assertEquals(0, cal1.internal()); + assertEquals(0, cal1.external()); + assertEquals(1, cal1.rateNum()); + assertEquals(1, cal1.rateDenom()); + clock.setCalibration(-100, 1000, 8, 5); + + Clock.Calibration cal2 = clock.getCalibration(); + System.out.println(cal2); + assertEquals(-100, cal2.internal()); + assertEquals(1000, cal2.external()); + assertEquals(8, cal2.rateNum()); + assertEquals(5, cal2.rateDenom()); + + Clock.Calibration cal3 = clock.getCalibration(); + assertEquals(cal2, cal3); + assertEquals(cal2.hashCode(), cal3.hashCode()); + assertNotEquals(cal1, cal3); + + } + + +} diff --git a/test/org/freedesktop/gstreamer/ClockTimeTest.java b/test/org/freedesktop/gstreamer/ClockTimeTest.java index 0d9fbb91..834b765f 100644 --- a/test/org/freedesktop/gstreamer/ClockTimeTest.java +++ b/test/org/freedesktop/gstreamer/ClockTimeTest.java @@ -59,46 +59,46 @@ public void tearDown() { @Test public void toSeconds() { final long TIME = TimeUnit.SECONDS.toNanos(0xdeadbeef); - ClockTime time = ClockTime.valueOf(TIME, TimeUnit.NANOSECONDS); assertEquals("toSeconds returned incorrect value", - TimeUnit.NANOSECONDS.toSeconds(TIME), time.toSeconds()); - assertEquals("convertTo returned incorrect value", - TimeUnit.NANOSECONDS.toSeconds(TIME), time.convertTo(TimeUnit.SECONDS)); + TimeUnit.NANOSECONDS.toSeconds(TIME), ClockTime.toSeconds(TIME)); } @Test public void toMillis() { final long TIME = TimeUnit.SECONDS.toNanos(0xdeadbeef); - ClockTime time = ClockTime.valueOf(TIME, TimeUnit.NANOSECONDS); assertEquals("toMillis returned incorrect value", - TimeUnit.NANOSECONDS.toMillis(TIME), time.toMillis()); - assertEquals("convertTo returned incorrect value", - TimeUnit.NANOSECONDS.toMillis(TIME), time.convertTo(TimeUnit.MILLISECONDS)); + TimeUnit.NANOSECONDS.toMillis(TIME), ClockTime.toMillis(TIME)); } @Test public void toMicros() { final long TIME = TimeUnit.SECONDS.toNanos(0xdeadbeef); - ClockTime time = ClockTime.valueOf(TIME, TimeUnit.NANOSECONDS); assertEquals("toMillis returned incorrect value", - TimeUnit.NANOSECONDS.toMicros(TIME), time.toMicros()); - assertEquals("convertTo returned incorrect value", - TimeUnit.NANOSECONDS.toMicros(TIME), time.convertTo(TimeUnit.MICROSECONDS)); + TimeUnit.NANOSECONDS.toMicros(TIME), ClockTime.toMicros(TIME)); } - @Test public void toNanos() { - final long TIME = TimeUnit.SECONDS.toNanos(0xdeadbeef); - ClockTime time = ClockTime.valueOf(TIME, TimeUnit.NANOSECONDS); - assertEquals("toNanos returned incorrect value", - TimeUnit.NANOSECONDS.toNanos(TIME), time.toNanos()); - assertEquals("convertTo returned incorrect value", - TimeUnit.NANOSECONDS.toNanos(TIME), time.convertTo(TimeUnit.NANOSECONDS)); - } - @Test public void compareTo() { - // Collections.sort uses compareTo() - List list = new ArrayList(); - list.add(ClockTime.valueOf(2, TimeUnit.SECONDS)); - list.add(ClockTime.valueOf(3, TimeUnit.SECONDS)); - list.add(ClockTime.valueOf(1, TimeUnit.SECONDS)); - Collections.sort(list); - assertEquals("list not sorted correctly", 1, list.get(0).toSeconds()); - assertEquals("list not sorted correctly", 2, list.get(1).toSeconds()); - assertEquals("list not sorted correctly", 3, list.get(2).toSeconds()); + @Test public void toStringRepresentation() { + long hours = 3; + long minutes = 27; + long seconds = 13; + long time = TimeUnit.HOURS.toNanos(hours) + + TimeUnit.MINUTES.toNanos(minutes) + + TimeUnit.SECONDS.toNanos(seconds); + assertEquals("ClockTime.toString() incorrect", "03:27:13", ClockTime.toString(time)); } - +// @Test public void toNanos() { +// final long TIME = TimeUnit.SECONDS.toNanos(0xdeadbeef); +// ClockTime time = ClockTime.valueOf(TIME, TimeUnit.NANOSECONDS); +// assertEquals("toNanos returned incorrect value", +// TimeUnit.NANOSECONDS.toNanos(TIME), time.toNanos()); +// assertEquals("convertTo returned incorrect value", +// TimeUnit.NANOSECONDS.toNanos(TIME), time.convertTo(TimeUnit.NANOSECONDS)); +// } +// @Test public void compareTo() { +// // Collections.sort uses compareTo() +// List list = new ArrayList(); +// list.add(ClockTime.valueOf(2, TimeUnit.SECONDS)); +// list.add(ClockTime.valueOf(3, TimeUnit.SECONDS)); +// list.add(ClockTime.valueOf(1, TimeUnit.SECONDS)); +// Collections.sort(list); +// assertEquals("list not sorted correctly", 1, list.get(0).toSeconds()); +// assertEquals("list not sorted correctly", 2, list.get(1).toSeconds()); +// assertEquals("list not sorted correctly", 3, list.get(2).toSeconds()); +// } +// } \ No newline at end of file diff --git a/test/org/freedesktop/gstreamer/ContextTest.java b/test/org/freedesktop/gstreamer/ContextTest.java new file mode 100644 index 00000000..6a8ebe35 --- /dev/null +++ b/test/org/freedesktop/gstreamer/ContextTest.java @@ -0,0 +1,42 @@ +package org.freedesktop.gstreamer; + +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstContextAPI; +import org.freedesktop.gstreamer.lowlevel.GstContextPtr; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ContextTest { + + @BeforeClass + public static void setUpClass() throws Exception { + Gst.init("test"); + } + + @AfterClass + public static void tearDownClass() throws Exception { + Gst.deinit(); + } + + @Test + public void testConstruction() { + GstContextAPI contextApi = GstContextAPI.GSTCONTEXT_API; + String contextType = "whatever"; + try (Context context = new Context(contextType)) { + GstContextPtr gstContextPtr = Natives.getPointer(context).as(GstContextPtr.class, GstContextPtr::new); + + // Context type. + Assert.assertEquals(contextType, context.getContextType()); + Assert.assertTrue(contextApi.gst_context_has_context_type(gstContextPtr, contextType)); + Assert.assertFalse(contextApi.gst_context_has_context_type(gstContextPtr, contextType + ".something-else")); + + // Default is persistent. + Assert.assertTrue(contextApi.gst_context_is_persistent(gstContextPtr)); + + Assert.assertNotNull(context.getWritableStructure()); + } + } + +} diff --git a/test/org/freedesktop/gstreamer/ElementFactoryTest.java b/test/org/freedesktop/gstreamer/ElementFactoryTest.java index 13949603..45273e86 100644 --- a/test/org/freedesktop/gstreamer/ElementFactoryTest.java +++ b/test/org/freedesktop/gstreamer/ElementFactoryTest.java @@ -26,7 +26,7 @@ import java.lang.ref.WeakReference; import java.util.List; -import org.freedesktop.gstreamer.ElementFactory.ElementFactoryListType; +import org.freedesktop.gstreamer.ElementFactory.ListType; import org.freedesktop.gstreamer.PluginFeature.Rank; import org.freedesktop.gstreamer.elements.DecodeBin; import org.freedesktop.gstreamer.elements.PlayBin; @@ -156,7 +156,6 @@ public void testCreatePlaybin() { assertTrue("Element not a subclass of PlayBin", e instanceof PlayBin); } @Test - @Ignore public void testGarbageCollection() throws Throwable { ElementFactory factory = ElementFactory.find("fakesrc"); assertNotNull("Could not locate fakesrc factory", factory); @@ -193,8 +192,8 @@ public void getStaticPadTemplates() { @Test public void listGetElement() { - List list = ElementFactory.listGetElement(ElementFactoryListType.ANY, - Rank.GST_RANK_NONE); + List list = ElementFactory.listGetElements(ListType.ANY, + Rank.NONE); assertNotNull("List of factories is null", list); assertTrue("No factories found", !list.isEmpty()); // System.out.println("Factories >>>"); @@ -204,28 +203,27 @@ public void listGetElement() { // System.out.println("<<<"); } - @Test - public void filterList() { - List list = ElementFactory.listGetElement(ElementFactoryListType.ENCODER, - Rank.GST_RANK_NONE); - assertNotNull("List of factories is null", list); - assertTrue("No factories found", !list.isEmpty()); - List filterList = ElementFactory.listFilter(list, new Caps("video/x-h263"), - PadDirection.SRC, false); - - assertNotNull("List of factories is null", filterList); - assertTrue("No factories found", !filterList.isEmpty()); -// System.out.println("Filtered factories >>>"); -// for (ElementFactory fact : filterList) { -// System.out.println(fact.getName()); -// } -// System.out.println("<<<"); - } +// @Test +// public void filterList() { +// List list = ElementFactory.listGetElements(ListType.ENCODER, +// Rank.NONE); +// assertNotNull("List of factories is null", list); +// assertTrue("No factories found", !list.isEmpty()); +// List filterList = ElementFactory.listFilter(list, new Caps("video/x-h263"), +// PadDirection.SRC, false); +// +// assertNotNull("List of factories is null", filterList); +// assertTrue("No factories found", !filterList.isEmpty()); +//// System.out.println("Filtered factories >>>"); +//// for (ElementFactory fact : filterList) { +//// System.out.println(fact.getName()); +//// } +//// System.out.println("<<<"); +// } @Test public void filterList2() { - List list = ElementFactory.listGetElementFilter( - ElementFactoryListType.ENCODER, Rank.GST_RANK_NONE, new Caps("video/x-h263"), + List list = ElementFactory.listGetElementsFilter(ListType.ENCODER, Rank.NONE, new Caps("video/x-h263"), PadDirection.SRC, false); assertNotNull("List of factories is null", list); assertTrue("No factories found", !list.isEmpty()); @@ -236,4 +234,22 @@ ElementFactoryListType.ENCODER, Rank.GST_RANK_NONE, new Caps("video/x-h263"), // } // System.out.println("<<<"); } + + @Test + public void testMetaData() { + ElementFactory f = ElementFactory.find("fakesink"); + String klass = f.getKlass(); + String longName = f.getLongName(); + String description = f.getDescription(); + String author = f.getAuthor(); + assertNotNull("Klass is null", klass); + assertNotNull("Long name is null", longName); + assertNotNull("Description is null", description); + assertNotNull("Author is null", author); + System.out.println("FakeSink MetaData"); + System.out.println("Klass : " + f.getKlass()); + System.out.println("Long Name : " + f.getLongName()); + System.out.println("Description : " + f.getDescription()); + System.out.println("Author : " + f.getAuthor()); + } } diff --git a/test/org/freedesktop/gstreamer/ElementTest.java b/test/org/freedesktop/gstreamer/ElementTest.java index 81838599..dc6e5710 100644 --- a/test/org/freedesktop/gstreamer/ElementTest.java +++ b/test/org/freedesktop/gstreamer/ElementTest.java @@ -24,10 +24,13 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.message.Message; import org.freedesktop.gstreamer.message.TagMessage; import org.junit.After; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -119,4 +122,25 @@ public void busMessage(Bus bus, Message msg) { pipe.run(); assertTrue("Message not posted", signalFired.get()); } -} \ No newline at end of file + @Test public void testContext() { + Element element = ElementFactory.make("fakesrc", "fs"); + Assert.assertEquals(1, element.getRefCount()); + + Context context = new Context("test"); + Assert.assertEquals(1, context.getRefCount()); + element.setContext(context); + Assert.assertEquals(2, context.getRefCount()); + + Context anotherContext = element.getContext("test"); + Assert.assertEquals(2, anotherContext.getRefCount()); + Assert.assertNotNull(anotherContext); + Assert.assertEquals(context.getContextType(), anotherContext.getContextType()); + + Assert.assertNull(element.getContext("test-something-else")); + + element.dispose(); + Assert.assertEquals(0, element.getRefCount()); + Assert.assertEquals(1, context.getRefCount()); + Assert.assertEquals(1, anotherContext.getRefCount()); + } +} diff --git a/test/org/freedesktop/gstreamer/GCTracker.java b/test/org/freedesktop/gstreamer/GCTracker.java index 407e28e8..3bc10593 100644 --- a/test/org/freedesktop/gstreamer/GCTracker.java +++ b/test/org/freedesktop/gstreamer/GCTracker.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2007, 2008 Wayne Meissner + * Copyright (C) 2020 Neil C Smith + * Copyright (C) 2008 Wayne Meissner * * This file is part of gstreamer-java. * @@ -16,36 +17,87 @@ * You should have received a copy of the GNU Lesser General Public License * along with gstreamer-java. If not, see . */ - package org.freedesktop.gstreamer; import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import org.freedesktop.gstreamer.lowlevel.GObjectAPI; +import com.sun.jna.Pointer; +import org.freedesktop.gstreamer.glib.GObject; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstMiniObjectPtr; import org.freedesktop.gstreamer.lowlevel.IntPtr; -import com.sun.jna.Pointer; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import static org.freedesktop.gstreamer.lowlevel.GObjectAPI.*; +import static org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.*; /** - * Keeps track of GObject derived objects. + * Tracks Java and/or native disposal of GObject and MiniObject derived objects. */ -class GCTracker { +public class GCTracker { + + private static final Map> OBJ_MAP + = Collections.synchronizedMap(new HashMap>()); + + private final WeakReference ref; + private final AtomicBoolean destroyed = new AtomicBoolean(false); - private static Map> objectMap = - Collections.synchronizedMap(new HashMap>()); + public GCTracker(GObject obj) { + int identityHashCode = System.identityHashCode(this); + ref = new WeakReference<>(obj); + OBJ_MAP.put(identityHashCode, new WeakReference<>(this)); + GOBJECT_API.g_object_weak_ref(obj, GOBJECT_NOTIFY, new IntPtr(identityHashCode)); + } - private static final GObjectAPI.GWeakNotify notify = new GObjectAPI.GWeakNotify() { + public GCTracker(MiniObject obj) { + int identityHashCode = System.identityHashCode(this); + ref = new WeakReference<>(obj); + OBJ_MAP.put(identityHashCode, new WeakReference<>(this)); + GSTMINIOBJECT_API.gst_mini_object_weak_ref( + Natives.getPointer(obj).as(GstMiniObjectPtr.class, GstMiniObjectPtr::new), + GSTMINIOBJECT_NOTIFY, + new IntPtr(identityHashCode)); + } + + public boolean waitGC() { + return waitGC(ref); + } + + public boolean waitDestroyed() { + for (int i = 0; !destroyed.get() && i < 10; ++i) { + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + } + } + return destroyed.get(); + } + + private static final GWeakNotify GOBJECT_NOTIFY = new GWeakNotify() { @Override public void callback(IntPtr id, Pointer obj) { int identityHashCode = id.intValue(); - WeakReference trackerRef = objectMap.get(identityHashCode); + WeakReference trackerRef = OBJ_MAP.get(identityHashCode); GCTracker tracker = trackerRef.get(); - if(tracker != null) { + if (tracker != null) { + tracker.destroyed.set(true); + } + } + }; + + private static final GstMiniObjectNotify GSTMINIOBJECT_NOTIFY + = new GstMiniObjectNotify() { + + @Override + public void callback(IntPtr id, Pointer obj) { + int identityHashCode = id.intValue(); + WeakReference trackerRef = OBJ_MAP.get(identityHashCode); + GCTracker tracker = trackerRef.get(); + if (tracker != null) { tracker.destroyed.set(true); } } @@ -62,28 +114,5 @@ public static boolean waitGC(WeakReference ref) { } return ref.get() == null; } - - private final WeakReference ref; - private final AtomicBoolean destroyed = new AtomicBoolean(false); - - public GCTracker(GObject obj) { - int identityHashCode = System.identityHashCode(this); - ref = new WeakReference(obj); - objectMap.put(identityHashCode, new WeakReference(this)); - GObjectAPI.GOBJECT_API.g_object_weak_ref(obj, notify, new IntPtr(identityHashCode)); - } - - public boolean waitGC() { - return waitGC(ref); - } - public boolean waitDestroyed() { - for (int i = 0; !destroyed.get() && i < 10; ++i) { - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - } - } - return destroyed.get(); - } } diff --git a/test/org/freedesktop/gstreamer/GarbageCollectionEDTTest.java b/test/org/freedesktop/gstreamer/GarbageCollectionEDTTest.java new file mode 100644 index 00000000..f3c0d521 --- /dev/null +++ b/test/org/freedesktop/gstreamer/GarbageCollectionEDTTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2007 Wayne Meissner + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer; + +import java.lang.ref.WeakReference; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * + */ +public class GarbageCollectionEDTTest { + + public GarbageCollectionEDTTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + System.setProperty("glib.reapOnEDT", "true"); + Gst.init("test", new String[]{}); + } + + @AfterClass + public static void tearDownClass() throws Exception { +// Gst.deinit(); + } + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testElement() throws Exception { + + Element e = ElementFactory.make("fakesrc", "test element"); + GCTracker tracker = new GCTracker(e); + e = null; + assertTrue("Element not garbage collected", tracker.waitGC()); + assertTrue("GObject not destroyed", tracker.waitDestroyed()); + } + + @Test + public void testBin() throws Exception { + Bin bin = new Bin("test"); + Element e1 = ElementFactory.make("fakesrc", "source"); + Element e2 = ElementFactory.make("fakesink", "sink"); + bin.addMany(e1, e2); + + assertEquals("source not returned", e1, bin.getElementByName("source")); + assertEquals("sink not returned", e2, bin.getElementByName("sink")); + WeakReference binRef = new WeakReference(bin); + bin = null; + assertTrue("Bin not garbage collected", GCTracker.waitGC(binRef)); + WeakReference e1Ref = new WeakReference(e1); + WeakReference e2Ref = new WeakReference(e2); + e1 = null; + e2 = null; + + assertTrue("First Element not garbage collected", GCTracker.waitGC(e1Ref)); + assertTrue("Second Element not garbage collected", GCTracker.waitGC(e2Ref)); + + } + + @Test + public void testBinRetrieval() throws Exception { + Bin bin = new Bin("test"); + Element e1 = ElementFactory.make("fakesrc", "source"); + Element e2 = ElementFactory.make("fakesink", "sink"); + bin.addMany(e1, e2); + int id1 = System.identityHashCode(e1); + int id2 = System.identityHashCode(e2); + + e1 = null; + e2 = null; + System.gc(); + Thread.sleep(10); + // Should return the same object that was put into the bin + assertEquals("source ID does not match", id1, System.identityHashCode(bin.getElementByName("source"))); + assertEquals("sink ID does not match", id2, System.identityHashCode(bin.getElementByName("sink"))); + } + + @Test + public void pipeline() { + Pipeline pipe = new Pipeline("test"); + GCTracker pipeTracker = new GCTracker(pipe); + pipe = null; + assertTrue("Pipe not garbage collected", pipeTracker.waitGC()); + System.out.println("checking if pipeline is destroyed"); + assertTrue("Pipe not destroyed", pipeTracker.waitDestroyed()); + } + + @Test + public void pipelineBus() { + Pipeline pipe = new Pipeline("test"); + Bus bus = pipe.getBus(); + GCTracker busTracker = new GCTracker(bus); + GCTracker pipeTracker = new GCTracker(pipe); + + pipe = null; + bus = null; + assertTrue("Bus not garbage collected", busTracker.waitGC()); + assertTrue("Bus not destroyed", busTracker.waitDestroyed()); + assertTrue("Pipe not garbage collected", pipeTracker.waitGC()); + assertTrue("Pipe not destroyed", pipeTracker.waitDestroyed()); + + } + + @Test + public void busWithListeners() { + Pipeline pipe = new Pipeline("test"); + Bus bus = pipe.getBus(); + bus.connect(new Bus.EOS() { + + public void endOfStream(GstObject source) { + } + }); + + GCTracker busTracker = new GCTracker(bus); + GCTracker pipeTracker = new GCTracker(pipe); + bus = null; + pipe = null; + assertTrue("Bus not garbage collected", busTracker.waitGC()); + assertTrue("Bus not destroyed", busTracker.waitDestroyed()); + assertTrue("Pipe not garbage collected", pipeTracker.waitGC()); + assertTrue("Pipe not destroyed", pipeTracker.waitDestroyed()); + } +} diff --git a/test/org/freedesktop/gstreamer/GarbageCollectionTest.java b/test/org/freedesktop/gstreamer/GarbageCollectionTest.java index 01d36e11..44f0524a 100644 --- a/test/org/freedesktop/gstreamer/GarbageCollectionTest.java +++ b/test/org/freedesktop/gstreamer/GarbageCollectionTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import java.lang.ref.WeakReference; +import java.util.List; import org.junit.After; import org.junit.AfterClass; @@ -76,16 +77,45 @@ public void testBin() throws Exception { assertEquals("source not returned", e1, bin.getElementByName("source")); assertEquals("sink not returned", e2, bin.getElementByName("sink")); - WeakReference binRef = new WeakReference(bin); + GCTracker binTracker = new GCTracker(bin); bin = null; - assertTrue("Bin not garbage collected", GCTracker.waitGC(binRef)); - WeakReference e1Ref = new WeakReference(e1); - WeakReference e2Ref = new WeakReference(e2); + assertTrue("Bin not garbage collected", binTracker.waitGC()); + assertTrue("Bin not destroyed", binTracker.waitDestroyed()); + GCTracker e1Tracker = new GCTracker(e1); + GCTracker e2Tracker = new GCTracker(e2); e1 = null; e2 = null; - assertTrue("First Element not garbage collected", GCTracker.waitGC(e1Ref)); - assertTrue("Second Element not garbage collected", GCTracker.waitGC(e2Ref)); + assertTrue("First Element not garbage collected", e1Tracker.waitGC()); + assertTrue("First Element not destroyed", e1Tracker.waitDestroyed()); + assertTrue("Second Element not garbage collected", e2Tracker.waitGC()); + assertTrue("Second Element not destroyed", e2Tracker.waitDestroyed()); + + } + + @Test + public void testBinParsed() throws Exception { + Bin bin = Gst.parseBinFromDescription("fakesrc name=source ! fakesink name=sink", false); + int binRefCount = bin.getRefCount(); + List children = bin.getElements(); + assertEquals("Iteration increased Bin refcount", binRefCount, bin.getRefCount()); + assertEquals("Wrong number of child elements", 2, children.size()); + Element e1 = children.get(0); + Element e2 = children.get(1); + GCTracker binTracker = new GCTracker(bin); + bin = null; + assertTrue("Bin not garbage collected", binTracker.waitGC()); + assertTrue("Bin not destroyed", binTracker.waitDestroyed()); + GCTracker e1Tracker = new GCTracker(e1); + GCTracker e2Tracker = new GCTracker(e2); + children = null; + e1 = null; + e2 = null; + + assertTrue("First Element not garbage collected", e1Tracker.waitGC()); + assertTrue("First Element not destroyed", e1Tracker.waitDestroyed()); + assertTrue("Second Element not garbage collected", e2Tracker.waitGC()); + assertTrue("Second Element not destroyed", e2Tracker.waitDestroyed()); } @Test @@ -130,14 +160,9 @@ public void pipelineBus() { } @Test - @Ignore("See comment below.") public void busWithListeners() { Pipeline pipe = new Pipeline("test"); Bus bus = pipe.getBus(); - // Reasoning for @Igore: - // - // #connect increments the native reference count this in turn prevents - // the native part of the BUS to get out of scope. bus.connect(new Bus.EOS() { public void endOfStream(GstObject source) { diff --git a/test/org/freedesktop/gstreamer/GstTypesTest.java b/test/org/freedesktop/gstreamer/GstTypesTest.java index 0588fa47..a543bb52 100644 --- a/test/org/freedesktop/gstreamer/GstTypesTest.java +++ b/test/org/freedesktop/gstreamer/GstTypesTest.java @@ -18,6 +18,8 @@ */ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GObjectPtr; import static org.junit.Assert.assertEquals; import org.freedesktop.gstreamer.lowlevel.GType; @@ -64,7 +66,10 @@ public void unregisteredClassTest() { GType elementType = GType.valueOf(Element.GTYPE_NAME); // check a unregistered class which derived from Element Element anElement = ElementFactory.make("avidemux", "avidemux"); - assertEquals(Element.class, GstTypes.classFor(anElement.getType())); +// assertEquals(Element.class, GstTypes.classFor(anElement.getType())); + assertEquals(Element.class, GstTypes.classFor( + Natives.getPointer(anElement) + .as(GObjectPtr.class, GObjectPtr::new).getGType())); // verify GType has not changed for Element.class assertEquals(elementType, GstTypes.typeFor(Element.class)); diff --git a/test/org/freedesktop/gstreamer/InitTest.java b/test/org/freedesktop/gstreamer/InitTest.java index b9fbff07..2efc9f98 100644 --- a/test/org/freedesktop/gstreamer/InitTest.java +++ b/test/org/freedesktop/gstreamer/InitTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -37,8 +38,22 @@ public InitTest() { } @Test public void testInit() { - String[] args = Gst.init("InitTest", new String[] { "--gst-plugin-spew" }); + Version available = Gst.getVersion(); + Version notAvailable = Version.of(available.getMajor(), available.getMinor() + 2); + try { + Gst.init(notAvailable); + assertTrue("Version check exception not thrown!", false); + } catch (GstException ex) { + System.out.println("Expected init failure"); + System.out.println(ex); + } + String[] args = Gst.init(available, "InitTest", "--gst-debug-no-color"); assertTrue(args.length == 0); + + assertTrue(Gst.testVersion(available.getMajor(), available.getMinor())); + assertTrue(Gst.testVersion(available.getMajor(), available.getMinor() - 2)); + assertTrue(!Gst.testVersion(notAvailable.getMajor(), notAvailable.getMinor())); + Gst.deinit(); } @BeforeClass diff --git a/test/org/freedesktop/gstreamer/MessageTest.java b/test/org/freedesktop/gstreamer/MessageTest.java index 7b8cbbd6..ebf9df1a 100644 --- a/test/org/freedesktop/gstreamer/MessageTest.java +++ b/test/org/freedesktop/gstreamer/MessageTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (c) 2008 Wayne Meissner * @@ -17,23 +18,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with gstreamer-java. If not, see . */ - package org.freedesktop.gstreamer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; - -import org.freedesktop.gstreamer.lowlevel.GstMessageAPI; -import org.freedesktop.gstreamer.lowlevel.GstElementAPI; -import org.freedesktop.gstreamer.lowlevel.GstTagListAPI; import org.freedesktop.gstreamer.message.BufferingMessage; import org.freedesktop.gstreamer.message.DurationChangedMessage; import org.freedesktop.gstreamer.message.EOSMessage; import org.freedesktop.gstreamer.message.LatencyMessage; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.message.MessageType; import org.freedesktop.gstreamer.message.SegmentDoneMessage; import org.freedesktop.gstreamer.message.StateChangedMessage; import org.freedesktop.gstreamer.message.TagMessage; @@ -41,12 +35,17 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; +import static org.freedesktop.gstreamer.lowlevel.GstElementAPI.GSTELEMENT_API; +import static org.freedesktop.gstreamer.lowlevel.GstMessageAPI.GSTMESSAGE_API; +import static org.freedesktop.gstreamer.lowlevel.GstTagListAPI.GSTTAGLIST_API; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + /** * - * @author wayne */ public class MessageTest { @@ -55,9 +54,9 @@ public MessageTest() { @BeforeClass public static void setUpClass() throws Exception { - Gst.init("BusTest", new String[] {}); + Gst.init("MessageTest"); } - + @AfterClass public static void tearDownClass() throws Exception { Gst.deinit(); @@ -70,23 +69,29 @@ public void setUp() { @After public void tearDown() { } - - @Test public void gst_message_new_eos() { + + @Test + public void gst_message_new_eos() { Element fakesink = ElementFactory.make("fakesink", "sink"); - Message msg = GstMessageAPI.GSTMESSAGE_API.gst_message_new_eos(fakesink); + Message msg = GSTMESSAGE_API.gst_message_new_eos(fakesink); assertTrue("gst_message_new_eos did not return an instance of EOSMessage", msg instanceof EOSMessage); } - @Test public void EOSMessage_getSource() { + + @Test + public void EOSMessage_getSource() { Element fakesink = ElementFactory.make("fakesink", "sink"); - Message msg = GstMessageAPI.GSTMESSAGE_API.gst_message_new_eos(fakesink); + Message msg = GSTMESSAGE_API.gst_message_new_eos(fakesink); assertEquals("Wrong source in message", fakesink, msg.getSource()); } - @Test public void postEOS() { + + @Test + public void postEOS() { final TestPipe pipe = new TestPipe(); final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalMessage = new AtomicReference(null); + final AtomicReference signalMessage = new AtomicReference<>(null); pipe.getBus().connect("message::eos", new Bus.MESSAGE() { + @Override public void busMessage(Bus bus, Message msg) { signalFired.set(true); signalMessage.set(msg); @@ -94,7 +99,7 @@ public void busMessage(Bus bus, Message msg) { } }); pipe.play(); - GstElementAPI.GSTELEMENT_API.gst_element_post_message(pipe.sink, new EOSMessage(pipe.sink)); + GSTELEMENT_API.gst_element_post_message(pipe.sink, new EOSMessage(pipe.sink)); pipe.run(); Message msg = signalMessage.get(); @@ -104,20 +109,26 @@ public void busMessage(Bus bus, Message msg) { assertEquals("Wrong source in message", pipe.pipe, msg.getSource()); pipe.dispose(); } - @Test public void gst_message_new_percent() { + + @Test + public void gst_message_new_percent() { Element fakesink = ElementFactory.make("fakesink", "sink"); - Message msg = GstMessageAPI.GSTMESSAGE_API.gst_message_new_buffering(fakesink, 55); + Message msg = GSTMESSAGE_API.gst_message_new_buffering(fakesink, 55); assertTrue("gst_message_new_eos did not return an instance of BufferingMessage", msg instanceof BufferingMessage); } - @Test public void BufferingMessage_getPercent() { + + @Test + public void BufferingMessage_getPercent() { Element fakesink = ElementFactory.make("fakesink", "sink"); - BufferingMessage msg = (BufferingMessage) GstMessageAPI.GSTMESSAGE_API.gst_message_new_buffering(fakesink, 55); + BufferingMessage msg = (BufferingMessage) GSTMESSAGE_API.gst_message_new_buffering(fakesink, 55); assertEquals("Wrong source in message", 55, msg.getPercent()); } - @Test public void postBufferingMessage() { + + @Test + public void postBufferingMessage() { final TestPipe pipe = new TestPipe(); final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalMessage = new AtomicReference(null); + final AtomicReference signalMessage = new AtomicReference<>(null); pipe.getBus().connect("message::buffering", new Bus.MESSAGE() { public void busMessage(Bus bus, Message msg) { @@ -127,7 +138,7 @@ public void busMessage(Bus bus, Message msg) { } }); final int PERCENT = 55; - GstElementAPI.GSTELEMENT_API.gst_element_post_message(pipe.sink, new BufferingMessage(pipe.src, PERCENT)); + GSTELEMENT_API.gst_element_post_message(pipe.sink, new BufferingMessage(pipe.src, PERCENT)); pipe.run(); Message msg = signalMessage.get(); assertNotNull("No message available on bus", msg); @@ -138,25 +149,28 @@ public void busMessage(Bus bus, Message msg) { pipe.dispose(); } - @Test public void gst_message_new_duration() { + @Test + public void gst_message_new_duration() { Element fakesink = ElementFactory.make("fakesink", "sink"); - Message msg = GstMessageAPI.GSTMESSAGE_API.gst_message_new_duration_changed(fakesink); + Message msg = GSTMESSAGE_API.gst_message_new_duration_changed(fakesink); assertTrue("gst_message_new_duration did not return an instance of DurationMessage", msg instanceof DurationChangedMessage); } - @Test public void postDurationMessage() { + @Test + public void postDurationMessage() { final TestPipe pipe = new TestPipe(); final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalMessage = new AtomicReference(null); + final AtomicReference signalMessage = new AtomicReference<>(null); pipe.getBus().connect("message::duration-changed", new Bus.MESSAGE() { + @Override public void busMessage(Bus bus, Message msg) { signalFired.set(true); signalMessage.set(msg); pipe.quit(); } }); - GstElementAPI.GSTELEMENT_API.gst_element_post_message(pipe.src, new DurationChangedMessage(pipe.src)); + GSTELEMENT_API.gst_element_post_message(pipe.src, new DurationChangedMessage(pipe.src)); pipe.play().run(); Message msg = signalMessage.get(); assertNotNull("No message available on bus", msg); @@ -165,43 +179,53 @@ public void busMessage(Bus bus, Message msg) { assertEquals("Wrong source in message", pipe.src, msg.getSource()); pipe.dispose(); } - @Test public void gst_message_new_tag() { + + @Test + public void gst_message_new_tag() { Element src = ElementFactory.make("fakesrc", "src"); - Message msg = GstMessageAPI.GSTMESSAGE_API.gst_message_new_tag(src, new TagList()); + Message msg = GSTMESSAGE_API.gst_message_new_tag(src, new TagList()); assertTrue("gst_message_new_tag did not return an instance of TagMessage", msg instanceof TagMessage); } - -// @Ignore - @Test public void TagMessage_getTagList() { + + @Test + public void TagMessage_getTagList() { Element src = ElementFactory.make("fakesrc", "src"); TagList tl = new TagList(); final String MAGIC = "fubar"; - GstTagListAPI.GSTTAGLIST_API.gst_tag_list_add(tl, TagMergeMode.APPEND, "artist", MAGIC); - TagMessage msg = (TagMessage) GstMessageAPI.GSTMESSAGE_API.gst_message_new_tag(src, tl); + GSTTAGLIST_API.gst_tag_list_add(tl, TagMergeMode.APPEND, "artist", MAGIC); + TagMessage msg = (TagMessage) GSTMESSAGE_API.gst_message_new_tag(src, tl); tl = msg.getTagList(); assertEquals("Wrong artist in tag list", MAGIC, tl.getString("artist", 0)); } - @Test public void gst_message_new_state_changed() { + + @Test + public void gst_message_new_state_changed() { Element src = ElementFactory.make("fakesrc", "src"); - Message msg = GstMessageAPI.GSTMESSAGE_API.gst_message_new_state_changed(src, State.READY, State.PLAYING, State.VOID_PENDING); + Message msg = GSTMESSAGE_API.gst_message_new_state_changed(src, State.READY, State.PLAYING, State.VOID_PENDING); assertTrue("gst_message_new_state_changed did not return an instance of StateChangedMessage", msg instanceof StateChangedMessage); } - @Test public void constructStateChanged() { + + @Test + public void constructStateChanged() { Element src = ElementFactory.make("fakesrc", "src"); new StateChangedMessage(src, State.READY, State.PLAYING, State.VOID_PENDING); } - @Test public void StateChanged_get() { + + @Test + public void StateChanged_get() { Element src = ElementFactory.make("fakesrc", "src"); - StateChangedMessage msg = (StateChangedMessage) GstMessageAPI.GSTMESSAGE_API.gst_message_new_state_changed(src, State.READY, State.PLAYING, State.VOID_PENDING); + StateChangedMessage msg = (StateChangedMessage) GSTMESSAGE_API.gst_message_new_state_changed(src, State.READY, State.PLAYING, State.VOID_PENDING); assertEquals("Wrong old state", State.READY, msg.getOldState()); assertEquals("Wrong new state", State.PLAYING, msg.getNewState()); assertEquals("Wrong pending state", State.VOID_PENDING, msg.getPendingState()); } - @Test public void postStateChangedMessage() { + + @Test + public void postStateChangedMessage() { final TestPipe pipe = new TestPipe(); final AtomicBoolean signalFired = new AtomicBoolean(false); final AtomicReference signalMessage = new AtomicReference(null); - + pipe.getBus().connect("message::state-changed", new Bus.MESSAGE() { public void busMessage(Bus bus, Message msg) { @@ -210,7 +234,7 @@ public void busMessage(Bus bus, Message msg) { pipe.quit(); } }); - GstElementAPI.GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTELEMENT_API.gst_element_post_message(pipe.src, new StateChangedMessage(pipe.src, State.READY, State.PLAYING, State.VOID_PENDING)); pipe.run(); Message msg = signalMessage.get(); @@ -222,27 +246,35 @@ public void busMessage(Bus bus, Message msg) { assertEquals("Wrong pending state", State.VOID_PENDING, smsg.getPendingState()); pipe.dispose(); } - @Test public void gst_message_new_segment_done() { + + @Test + public void gst_message_new_segment_done() { Element src = ElementFactory.make("fakesrc", "src"); - Message msg = GstMessageAPI.GSTMESSAGE_API.gst_message_new_segment_done(src, Format.TIME, 0xdeadbeef); - assertTrue("gst_message_new_segment_done did not return an instance of SegmentDoneMessage", + Message msg = GSTMESSAGE_API.gst_message_new_segment_done(src, Format.TIME, 0xdeadbeef); + assertTrue("gst_message_new_segment_done did not return an instance of SegmentDoneMessage", msg instanceof SegmentDoneMessage); } - @Test public void constructSegmentDone() { + + @Test + public void constructSegmentDone() { Element src = ElementFactory.make("fakesrc", "src"); new SegmentDoneMessage(src, Format.TIME, 0xdeadbeef); } - @Test public void parseSegmentDone() { + + @Test + public void parseSegmentDone() { Element src = ElementFactory.make("fakesrc", "src"); - SegmentDoneMessage msg = (SegmentDoneMessage) GstMessageAPI.GSTMESSAGE_API.gst_message_new_segment_done(src, Format.TIME, 0xdeadbeef); + SegmentDoneMessage msg = (SegmentDoneMessage) GSTMESSAGE_API.gst_message_new_segment_done(src, Format.TIME, 0xdeadbeef); assertEquals("Wrong format", Format.TIME, msg.getFormat()); assertEquals("Wrong position", 0xdeadbeef, msg.getPosition()); } - @Test public void postSegmentDoneMessage() { + + @Test + public void postSegmentDoneMessage() { final TestPipe pipe = new TestPipe(); final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalMessage = new AtomicReference(null); - + final AtomicReference signalMessage = new AtomicReference<>(null); + pipe.getBus().connect("message::segment-done", new Bus.MESSAGE() { public void busMessage(Bus bus, Message msg) { @@ -252,7 +284,7 @@ public void busMessage(Bus bus, Message msg) { } }); final int POSITION = 0xdeadbeef; - GstElementAPI.GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTELEMENT_API.gst_element_post_message(pipe.src, new SegmentDoneMessage(pipe.src, Format.TIME, POSITION)); pipe.run(); Message msg = signalMessage.get(); @@ -263,20 +295,23 @@ public void busMessage(Bus bus, Message msg) { assertEquals("Wrong position", POSITION, smsg.getPosition()); pipe.dispose(); } - @Test public void postLatencyMessage() { + + @Test + public void postLatencyMessage() { final TestPipe pipe = new TestPipe(); final AtomicBoolean signalFired = new AtomicBoolean(false); - final AtomicReference signalMessage = new AtomicReference(null); - + final AtomicReference signalMessage = new AtomicReference<>(null); + pipe.getBus().connect("message::latency", new Bus.MESSAGE() { + @Override public void busMessage(Bus bus, Message msg) { signalFired.set(true); signalMessage.set(msg); pipe.quit(); } }); - GstElementAPI.GSTELEMENT_API.gst_element_post_message(pipe.src, + GSTELEMENT_API.gst_element_post_message(pipe.src, new LatencyMessage(pipe.src)); pipe.run(); Message msg = signalMessage.get(); diff --git a/test/org/freedesktop/gstreamer/PadProbeTypeTest.java b/test/org/freedesktop/gstreamer/PadProbeTypeTest.java new file mode 100644 index 00000000..aef67595 --- /dev/null +++ b/test/org/freedesktop/gstreamer/PadProbeTypeTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package org.freedesktop.gstreamer; + +import java.lang.reflect.Field; +import java.util.Set; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.lowlevel.GstPadAPI; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class PadProbeTypeTest { + + + @Test + public void testCombinations() throws Exception { + for (Field field : PadProbeType.class.getFields()) { + if (Set.class.isAssignableFrom(field.getType())) { + Set flags = (Set) field.get(null); +// System.out.println(field.getName() + " : " + flags); + Field nativeField = GstPadAPI.class.getField("GST_PAD_PROBE_TYPE_" + field.getName()); + assertEquals(NativeFlags.toInt(flags), nativeField.get(null)); + } + } + } + + +} diff --git a/test/org/freedesktop/gstreamer/PadTest.java b/test/org/freedesktop/gstreamer/PadTest.java index 656a8b3f..86dd1fab 100644 --- a/test/org/freedesktop/gstreamer/PadTest.java +++ b/test/org/freedesktop/gstreamer/PadTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2007 Wayne Meissner * * This file is part of gstreamer-java. @@ -16,53 +17,55 @@ * You should have received a copy of the GNU Lesser General Public License * along with gstreamer-java. If not, see . */ - package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.event.Event; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import org.freedesktop.gstreamer.event.FlushStopEvent; import org.freedesktop.gstreamer.event.TagEvent; +import org.freedesktop.gstreamer.query.AllocationQuery; +import org.freedesktop.gstreamer.query.Query; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; /** * */ public class PadTest { - + public PadTest() { } - + @BeforeClass public static void setUpClass() throws Exception { - Gst.init("test", new String[] {}); + Gst.init("test", new String[]{}); } - + @AfterClass public static void tearDownClass() throws Exception { Gst.deinit(); } - + @Before public void setUp() throws Exception { } - + @After public void tearDown() throws Exception { } - - // Does not work yet (<- this comment was here in the 0.10 code) - @Ignore("Didn't work in the 0.10 either by the comment so still ignored") + @Test public void getPad() throws Exception { Element src = ElementFactory.make("fakesrc", "src"); @@ -73,17 +76,16 @@ public void getPad() throws Exception { assertNotNull("Could not get sink pad", sinkPad); src = null; sink = null; - WeakReference srcRef = new WeakReference(srcPad); - WeakReference sinkRef = new WeakReference(sinkPad); + WeakReference srcRef = new WeakReference<>(srcPad); + WeakReference sinkRef = new WeakReference<>(sinkPad); srcPad = null; sinkPad = null; assertTrue("Src pad not garbage collected", GCTracker.waitGC(srcRef)); assertTrue("Sink pad not garbage collected", GCTracker.waitGC(sinkRef)); } + @Test - public void padLink() - throws Exception - { + public void padLink() throws Exception { Element src = ElementFactory.make("fakesrc", "src"); Element sink = ElementFactory.make("fakesink", "src"); Pad srcPad = src.getStaticPad("src"); @@ -91,13 +93,10 @@ public void padLink() srcPad.link(sinkPad); } - @Ignore("This seems to fail because gst1.0 doesn't actually send the event because pads " + - "are now created in FLUSHING state") @Test public void addEventProbe() { Element elem = ElementFactory.make("identity", "src"); - TagList taglist = new TagList(); - Event ev = new TagEvent(taglist); + Event ev = new TagEvent(new TagList()); Pad sink = elem.getStaticPad("sink"); @@ -111,52 +110,231 @@ public PadProbeReturn eventReceived(Pad pad, Event event) { } }; + sink.setActive(true); + sink.sendEvent(new FlushStopEvent()); + sink.addEventProbe(event_probe); sink.sendEvent(ev); assertEquals("event_prober.probeEvent() was not called", ev, e.get()); sink.removeEventProbe(event_probe); - Event ev2 = new TagEvent(taglist); + Event ev2 = new TagEvent(new TagList()); sink.sendEvent(ev2); assertNotSame("event_prober.probeEvent() should not have been called", ev2, e.get()); } + + @Test + public void addEventProbe_Remove() { + Element elem = ElementFactory.make("identity", "src"); + Event ev = new TagEvent(new TagList()); + + Pad sink = elem.getStaticPad("sink"); + + final AtomicReference e = new AtomicReference(); + + Pad.EVENT_PROBE event_probe = new Pad.EVENT_PROBE() { + + public PadProbeReturn eventReceived(Pad pad, Event event) { + e.set(event); + return PadProbeReturn.REMOVE; + } + }; + + sink.setActive(true); + sink.sendEvent(new FlushStopEvent()); + + sink.addEventProbe(event_probe); + sink.sendEvent(ev); + assertEquals("event_prober.probeEvent() was not called", ev, e.get()); + + Event ev2 = new TagEvent(new TagList()); + sink.sendEvent(ev2); + assertNotSame("event_prober.probeEvent() should not have been called", ev2, e.get()); + WeakReference probeRef = new WeakReference<>(event_probe); + event_probe = null; + assertTrue("Removed probe not collected", GCTracker.waitGC(probeRef)); + + } @Test - public void addDataProbe() { + public void addProbe_Event() { + Element elem = ElementFactory.make("identity", "src"); + Event ev = new TagEvent(new TagList()); + + Pad sink = elem.getStaticPad("sink"); + + final AtomicReference e = new AtomicReference<>(); + + Pad.PROBE probe = (Pad pad, PadProbeInfo info) -> { + assertTrue("Info type does not include event downstream", + info.getType().contains(PadProbeType.EVENT_DOWNSTREAM)); + e.set(info.getEvent()); + return PadProbeReturn.OK; + }; + + sink.setActive(true); + sink.sendEvent(new FlushStopEvent()); + + sink.addProbe(PadProbeType.EVENT_BOTH, probe); + sink.sendEvent(ev); + assertEquals("Probe (Event) was not called", ev, e.get()); + + sink.removeProbe(probe); + + Event ev2 = new TagEvent(new TagList()); + sink.sendEvent(ev2); + assertNotSame("Probe (Event) should not have been called", ev2, e.get()); + } + + @Test + public void addProbe_EventRemove() { + Element elem = ElementFactory.make("identity", "src"); + Event ev = new TagEvent(new TagList()); + + Pad sink = elem.getStaticPad("sink"); + + final AtomicReference e = new AtomicReference<>(); + + Pad.PROBE probe = (Pad pad, PadProbeInfo info) -> { + assertTrue("Info type does not include event downstream", + info.getType().contains(PadProbeType.EVENT_DOWNSTREAM)); + e.set(info.getEvent()); + return PadProbeReturn.REMOVE; + }; + + sink.setActive(true); + sink.sendEvent(new FlushStopEvent()); + + sink.addProbe(PadProbeType.EVENT_BOTH, probe); + sink.sendEvent(ev); + assertEquals("Probe (Event) was not called", ev, e.get()); + + Event ev2 = new TagEvent(new TagList()); + sink.sendEvent(ev2); + assertNotSame("Probe (Event) should not have been called", ev2, e.get()); + + WeakReference probeRef = new WeakReference<>(probe); + probe = null; + assertTrue("Removed probe not collected", GCTracker.waitGC(probeRef)); + Event ev3 = new TagEvent(new TagList()); + sink.sendEvent(ev3); + assertNotSame("Probe (Event) should not have been called", ev3, e.get()); + } + + @Test + public void addDataProbe() { + Element elem = ElementFactory.make("identity", "src"); - TagList taglist = new TagList(); Buffer buf = new Buffer(3); Buffer buf2 = new Buffer(2); final AtomicReference b = new AtomicReference(); - + Pad src = elem.getStaticPad("src"); - + Pad.DATA_PROBE data_probe = new Pad.DATA_PROBE() { - + @Override public PadProbeReturn dataReceived(Pad pad, Buffer buffer) { b.set(buffer); return PadProbeReturn.OK; } }; - + elem.play(); - + // add a dataprobe src.addDataProbe(data_probe); // push data FlowReturn res = src.push(buf); - assertEquals("event_prober.probeEvent() was not called", buf, b.get()); - + assertEquals("data_prober.probeData() was not called", buf, b.get()); + // remove the dataprobe - src.removeDataProbe(data_probe); - + src.removeDataProbe(data_probe); + + // push data + res = src.push(buf2); + assertNotSame("data_prober.probeData() should not have been called", buf2, b.get()); + + elem.stop(); + + } + + @Test + public void addProbe_Data() { + + Element elem = ElementFactory.make("identity", "src"); + Buffer buf = new Buffer(3); + Buffer buf2 = new Buffer(2); + final AtomicReference b = new AtomicReference<>(); + + Pad src = elem.getStaticPad("src"); + + Pad.PROBE probe = (Pad pad, PadProbeInfo info) -> { + assertTrue("Info type does not include buffer", + info.getType().contains(PadProbeType.BUFFER)); + // These cause assertion messages to be logged by GStreamer + // assertTrue(info.getEvent() == null); + // assertTrue(info.getQuery() == null); + b.set(info.getBuffer()); + return PadProbeReturn.OK; + }; + + elem.play(); + + // add a dataprobe + src.addProbe(PadProbeType.BUFFER, probe); + // push data - res = src.push(buf2); - assertNotSame("event_prober.probeEvent() should not have been called", buf2, b.get()); + FlowReturn res = src.push(buf); + assertEquals("Probe (Data) was not called", buf, b.get()); + + // remove the dataprobe + src.removeProbe(probe); + + // push data + res = src.push(buf2); + assertNotSame("Probe (Data) should not have been called", buf2, b.get()); + + elem.stop(); + } + + @Test + public void addProbe_Idle() { + + Element elem = ElementFactory.make("identity", "src"); + final AtomicBoolean called = new AtomicBoolean(); + + Pad src = elem.getStaticPad("src"); + + Pad.PROBE probe = (Pad pad, PadProbeInfo info) -> { + called.set(true); + return PadProbeReturn.REMOVE; + }; + + src.addProbe(PadProbeType.IDLE, probe); + + assertTrue("Idle probe not called", called.get()); + + WeakReference probeRef = new WeakReference<>(probe); + + probe = null; + + assertTrue("Idle probe not collected", GCTracker.waitGC(probeRef)); + + } + + @Test + public void addProbe_Query() { + ProbeTester.test(PadProbeType.QUERY_BOTH, info -> { + Query q = info.getQuery(); + return q instanceof AllocationQuery; + }); + + } + } diff --git a/test/org/freedesktop/gstreamer/PipelineTest.java b/test/org/freedesktop/gstreamer/PipelineTest.java index c19f4621..f38f37e3 100644 --- a/test/org/freedesktop/gstreamer/PipelineTest.java +++ b/test/org/freedesktop/gstreamer/PipelineTest.java @@ -20,6 +20,8 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.glib.GError; +import org.freedesktop.gstreamer.glib.GObject; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -27,8 +29,10 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GObjectAPI.GObjectStruct; +import org.freedesktop.gstreamer.lowlevel.GObjectPtr; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -76,17 +80,19 @@ public boolean waitRefCnt(GObjectStruct struct, int refcnt) throws InterruptedEx @Test public void testPipelineGC() throws Exception { - Pipeline p = new Pipeline(); - int refcnt = new GObjectStruct(p).ref_count; - + Pipeline p = new Pipeline("test pipeline"); + int refcnt = new GObjectStruct((GObjectPtr) Natives.getPointer(p)).ref_count; assertEquals("Refcount should be 1", refcnt, 1); + WeakReference pref = new WeakReference(p); + p = null; + assertTrue("pipe not disposed", GCTracker.waitGC(pref)); } @Test public void testBusGC() throws Exception { Pipeline pipe = new Pipeline("test playbin"); pipe.play(); Bus bus = pipe.getBus(); - GObjectStruct struct = new GObjectStruct(bus); + GObjectStruct struct = new GObjectStruct((GObjectPtr) Natives.getPointer(bus)); int refcnt = struct.ref_count; assertTrue(refcnt > 1); // reget the Bus - should return the same object and not increment ref count diff --git a/test/org/freedesktop/gstreamer/PluginFeatureTest.java b/test/org/freedesktop/gstreamer/PluginFeatureTest.java index 93aee6fa..db4c3fee 100644 --- a/test/org/freedesktop/gstreamer/PluginFeatureTest.java +++ b/test/org/freedesktop/gstreamer/PluginFeatureTest.java @@ -27,7 +27,7 @@ public class PluginFeatureTest { @BeforeClass public static void setUpClass() throws Exception { Gst.init("PluginTest", new String[] {}); - decodebinFeature = Registry.get().findPluginFeature("decodebin"); + decodebinFeature = Registry.get().lookupFeature("decodebin"); } @AfterClass diff --git a/test/org/freedesktop/gstreamer/PluginTest.java b/test/org/freedesktop/gstreamer/PluginTest.java index 04cbfd1b..25aa9fea 100644 --- a/test/org/freedesktop/gstreamer/PluginTest.java +++ b/test/org/freedesktop/gstreamer/PluginTest.java @@ -28,7 +28,7 @@ public class PluginTest { @BeforeClass public static void setUpClass() throws Exception { Gst.init("PluginTest", new String[] {}); - playbackPlugin = Plugin.load("playback"); + playbackPlugin = Plugin.loadByName("playback"); } @AfterClass @@ -61,7 +61,7 @@ public void testGetFilename() { @Test public void testGetVersion() { - assertTrue(playbackPlugin.getVersion().matches("\\d+\\.\\d+\\.\\d+")); + assertTrue(playbackPlugin.getVersion().matches("^(?:\\d+\\.)*\\d+$")); } @Test diff --git a/test/org/freedesktop/gstreamer/ProbeTester.java b/test/org/freedesktop/gstreamer/ProbeTester.java new file mode 100644 index 00000000..a8f9e5c9 --- /dev/null +++ b/test/org/freedesktop/gstreamer/ProbeTester.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 John Cortell + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer; + +import java.util.Set; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; + +/** + * Utility class for unit testing API that operates on a Probe. + *

+ * Call {@link ProbeTester#test(Consumer)} and pass a callback which will + * perform the test on a PadProbeInfo it is supplied. The callback runs in a Pad + * probe. The test uses a simple, ephemeral pipeline that is fed by a video test + * source (or custom pipeline). + *

+ * The callback is a {@link Predicate} and should return false if the input info + * doesn't match that required by the test. Test exceptions should be thrown as + * normal. This is to allow the probe to be called repeatedly until the input + * info matches that expected. If the probe never matches the test will time + * out. + */ +public class ProbeTester { + + /** + * Run a probe test on a simple test pipeline. The callback will be called + * by the probe until it returns true, allowing for probe callbacks to be + * ignored. If the callback never returns true the test will timeout and + * fail. + *

+ * The pipeline is videotestsrc ! fakesink name=sink. The probe + * will be attached to the sink pad of the sink element. + * + * @param mask PadProbeType mask to use when attaching probe to sink pad + * @param callback probe callback + */ + public static void test(Set mask, Predicate callback) { + test("videotestsrc ! fakesink name=sink", mask, callback); + } + + /** + * Run a probe test on a simple test pipeline. The callback will be called + * by the probe until it returns true, allowing for probe callbacks to be + * ignored. If the callback never returns true the test will timeout and + * fail. + *

+ * The pipeline must have a sink element named sink. The probe will be + * attached to the sink pad of the sink element. + * + * @param pipeline custom pipeline with named sink element + * @param mask PadProbeType mask to use when attaching probe to sink pad + * @param callback probe callback + */ + public static void test(String pipeline, Set mask, Predicate callback) { + assertNotNull("Pipeline description can not be null", pipeline); + assertFalse("Pipeline description can not be empty", pipeline.isEmpty()); + Pipeline pipe = (Pipeline) Gst.parseLaunch(pipeline); + assertNotNull("Unable to create Pipeline from pipeline description: ", pipe); + + Element sink = pipe.getElementByName("sink"); + Pad pad = sink.getStaticPad("sink"); + PadProbe probe = new PadProbe(callback); + pad.addProbe(mask, probe); + + pipe.play(); + + // Wait for the probe to complete + try { + probe.await(5000); + } catch (TimeoutException ex) { + fail("Timed out waiting for probe condition\n" + ex); + } catch (Exception ex) { + fail("Unexpected exception waiting for probe\n" + ex); + } finally { + pipe.stop(); + } + + // If the test threw an exception on the sample listener thread, throw it here + // (on the main thread) + if (probe.exception != null) { + throw new AssertionError(probe.exception); + } + } + + private static class PadProbe implements Pad.PROBE { + + private final CountDownLatch latch; + private final Predicate callback; + + private Throwable exception; + + PadProbe(Predicate callback) { + this.callback = callback; + latch = new CountDownLatch(1); + } + + @Override + public PadProbeReturn probeCallback(Pad pad, PadProbeInfo info) { + if (latch.getCount() > 0) { + try { + if (callback.test(info)) { + latch.countDown(); + } + } catch (Throwable exc) { + exception = exc; + latch.countDown(); + } + } + return PadProbeReturn.OK; + } + + void await(long millis) throws InterruptedException, TimeoutException { + if (!latch.await(millis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException(); + } + } + + } + +} diff --git a/test/org/freedesktop/gstreamer/PromiseTest.java b/test/org/freedesktop/gstreamer/PromiseTest.java index 516ad6ca..26dd292f 100644 --- a/test/org/freedesktop/gstreamer/PromiseTest.java +++ b/test/org/freedesktop/gstreamer/PromiseTest.java @@ -1,4 +1,6 @@ /* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2019 Kezhu Wang * Copyright (c) 2018 Antonio Morales * * This file is part of gstreamer-java. @@ -18,13 +20,18 @@ */ package org.freedesktop.gstreamer; +import java.util.concurrent.atomic.AtomicBoolean; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GPointer; import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.util.TestAssumptions; import org.junit.BeforeClass; import org.junit.AfterClass; -import org.junit.Before; import org.junit.Test; public class PromiseTest { @@ -34,7 +41,7 @@ public PromiseTest() { @BeforeClass public static void setUpClass() throws Exception { - Gst.init("PromiseTest", new String[] {}); + Gst.init(Gst.getVersion(), "PromiseTest"); } @AfterClass @@ -44,9 +51,8 @@ public static void tearDownClass() throws Exception { @Test public void testReply() { - if (Gst.getVersion().getMinor() < 14) { - return; - } + TestAssumptions.requireGstVersion(1, 14); + Promise promise = new Promise(); promise.reply(null); @@ -58,9 +64,8 @@ public void testReply() { @Test public void testInterrupt() { - if (Gst.getVersion().getMinor() < 14) { - return; - } + TestAssumptions.requireGstVersion(1, 14); + Promise promise = new Promise(); promise.interrupt(); @@ -71,9 +76,8 @@ public void testInterrupt() { @Test public void testExpire() { - if (Gst.getVersion().getMinor() < 14) { - return; - } + TestAssumptions.requireGstVersion(1, 14); + Promise promise = new Promise(); promise.expire(); @@ -82,18 +86,72 @@ public void testExpire() { assertEquals("promise reply state not correct", promiseStatus, PromiseResult.EXPIRED); } + @Test + public void testInvalidateReply() { + TestAssumptions.requireGstVersion(1, 14); + + Promise promise = new Promise(); + Structure data = new Structure("data"); + + assertTrue(Natives.ownsReference(data)); + promise.reply(data); + assertFalse(Natives.ownsReference(data)); + assertFalse(Natives.validReference(data)); + } + @Test public void testReplyData() { - if (Gst.getVersion().getMinor() < 14) { - return; - } + TestAssumptions.requireGstVersion(1, 14); + Promise promise = new Promise(); Structure data = new Structure("data", "test", GType.UINT, 1); + GPointer pointer = Natives.getPointer(data); promise.reply(data); assertEquals("promise state not in replied", promise.waitResult(), PromiseResult.REPLIED); Structure result = promise.getReply(); - assertTrue("result of promise does not match reply", result.isEqual(data)); + assertEquals("result of promise does not match reply", pointer, Natives.getPointer(result)); + } + + @Test + public void testDispose() { + TestAssumptions.requireGstVersion(1, 14); + + Promise promise = new Promise(); + promise.interrupt(); + promise.dispose(); + } + + @Test + public void testDisposeWithChangeFunc() { + TestAssumptions.requireGstVersion(1, 14); + + Promise promise = new Promise(new Promise.PROMISE_CHANGE() { + @Override + public void onChange(Promise promise) { + } + }); + promise.interrupt(); + promise.dispose(); + } + + @Test + public void testChangeFunctionGC() { + TestAssumptions.requireGstVersion(1, 14); + + final AtomicBoolean onChangeFired = new AtomicBoolean(false); + + Promise promise = new Promise(new Promise.PROMISE_CHANGE() { + @Override + public void onChange(Promise promise) { + onChangeFired.set(true); + } + }); + System.gc(); + System.gc(); + promise.interrupt(); + assertTrue("Promise Change callback GC'd", onChangeFired.get()); + promise.dispose(); } } diff --git a/test/org/freedesktop/gstreamer/PropertyTypeTest.java b/test/org/freedesktop/gstreamer/PropertyTypeTest.java new file mode 100644 index 00000000..dd73633e --- /dev/null +++ b/test/org/freedesktop/gstreamer/PropertyTypeTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2020 Peter Körner + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer; + +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.util.TestAssumptions; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class PropertyTypeTest { + + private Element audiotestsrc; + private Element filesink; + private Element convert; + + @BeforeClass + public static void setUpClass() { + Gst.init(Gst.getVersion(), "PropertyTypeTest"); + } + + @AfterClass + public static void tearDownClass() { + Gst.deinit(); + } + + @Before + public void createElements() { + audiotestsrc = ElementFactory.make("audiotestsrc", null); + filesink = ElementFactory.make("filesink", null); + convert = ElementFactory.make("audioconvert", null); + } + + @After + public void disposeElements() { + audiotestsrc.dispose(); + filesink.dispose(); + convert.dispose(); + } + + @Test + public void setBool() { + audiotestsrc.set("do-timestamp", true); + assertEquals(true, audiotestsrc.get("do-timestamp")); + + audiotestsrc.set("do-timestamp", false); + assertEquals(false, audiotestsrc.get("do-timestamp")); + + audiotestsrc.setAsString("do-timestamp", "true"); + assertEquals("true", audiotestsrc.getAsString("do-timestamp")); + } + + @Test + public void setDouble() { + audiotestsrc.set("freq", 42.23); + assertEquals(42.23, audiotestsrc.get("freq")); + + audiotestsrc.set("freq", 5_000_000.); + assertEquals(5_000_000., audiotestsrc.get("freq")); + + audiotestsrc.setAsString("freq", "42.23"); + assertEquals(42.23, audiotestsrc.get("freq")); + } + + @Test + public void setInt() { + audiotestsrc.set("num-buffers", 0); + assertEquals(0, audiotestsrc.get("num-buffers")); + + audiotestsrc.set("num-buffers", 42); + assertEquals(42, audiotestsrc.get("num-buffers")); + + audiotestsrc.set("num-buffers", 2147483647); + assertEquals(2147483647, audiotestsrc.get("num-buffers")); + } + + @Test + public void setUInt() { + audiotestsrc.set("blocksize", 0); + assertEquals(0, audiotestsrc.get("blocksize")); + + audiotestsrc.set("blocksize", 42); + assertEquals(42, audiotestsrc.get("blocksize")); + + int maxUnsignedInt = Integer.parseUnsignedInt("4294967295"); + audiotestsrc.set("blocksize", maxUnsignedInt); + assertEquals(maxUnsignedInt, audiotestsrc.get("blocksize")); + } + + @Test + public void setLong() { + audiotestsrc.set("timestamp-offset", 0L); + assertEquals(0L, audiotestsrc.get("timestamp-offset")); + + audiotestsrc.set("timestamp-offset", 42L); + assertEquals(42L, audiotestsrc.get("timestamp-offset")); + + audiotestsrc.set("timestamp-offset", -42L); + assertEquals(-42L, audiotestsrc.get("timestamp-offset")); + + audiotestsrc.set("timestamp-offset", 9223372036854775807L); + assertEquals(9223372036854775807L, audiotestsrc.get("timestamp-offset")); + + audiotestsrc.set("timestamp-offset", -9223372036854775808L); + assertEquals(-9223372036854775808L, audiotestsrc.get("timestamp-offset")); + } + + @Test + public void setULong() { + filesink.set("max-bitrate", 0L); + assertEquals(0L, filesink.get("max-bitrate")); + + long maxUnsignedLong = Long.parseUnsignedLong("18446744073709551615"); + filesink.set("max-bitrate", maxUnsignedLong); + assertEquals(maxUnsignedLong, filesink.get("max-bitrate")); + + filesink.set("max-bitrate", 42L); + assertEquals(42L, filesink.get("max-bitrate")); + + filesink.setAsString("max-bitrate", "18446744073709551615"); + assertEquals(maxUnsignedLong, filesink.get("max-bitrate")); + } + + @Test + public void setString() { + filesink.set("location", ""); + assertEquals("", filesink.get("location")); + + filesink.set("location", null); + assertNull(filesink.get("location")); + + filesink.set("location", "foobar"); + assertEquals("foobar", filesink.get("location")); + } + + @Test + public void setEnum() { + audiotestsrc.set("wave", 8); + assertEquals(8, audiotestsrc.get("wave")); + + audiotestsrc.setAsString("wave", "Silence"); + assertEquals(4, audiotestsrc.get("wave")); + + // unfortunately returned strings do not seem to be consistent across + // different OS / builds - comment out pending a fix or removal + // assertEquals("Silence", audiotestsrc.getAsString("wave")); + + audiotestsrc.setAsString("wave", "square"); + assertEquals(1, audiotestsrc.get("wave")); + // assertEquals("Square", audiotestsrc.getAsString("wave")); + + audiotestsrc.setAsString("wave", "red-noise"); + assertEquals(10, audiotestsrc.get("wave")); + String redNoise = audiotestsrc.getAsString("wave"); + // assertEquals("Red (brownian) noise", redNoise); + audiotestsrc.setAsString("wave", redNoise); + assertEquals(10, audiotestsrc.get("wave")); + + // invalid value + audiotestsrc.set("wave", -256); + assertEquals(0, audiotestsrc.get("wave")); + + // native enum + audiotestsrc.set("wave", AudioTestSrcWave.SILENCE); + assertEquals(AudioTestSrcWave.SILENCE.intValue(), audiotestsrc.get("wave")); + // assertEquals("Silence", audiotestsrc.getAsString("wave")); + + } + + @Test(expected = IllegalArgumentException.class) + public void setEnumInvalidString() { + audiotestsrc.setAsString("wave", "FOO"); + } + + @Test + public void setValueArrayFromString() { + TestAssumptions.requireGstVersion(1, 14); + + convert.setAsString("mix-matrix", "<<(float)0.25, (float)0.45>,<(float)0.65, (float)0.85>>"); + String mixMatrix = convert.getAsString("mix-matrix"); + assertTrue(mixMatrix.contains("0.2")); + assertTrue(mixMatrix.contains("0.4")); + assertTrue(mixMatrix.contains("0.6")); + assertTrue(mixMatrix.contains("0.8")); + } + + private static enum AudioTestSrcWave implements NativeEnum{ + SINE(0), + SQUARE(1), + SAW(2), + TRIANGLE(3), + SILENCE(4), + WHITE_NOISE(5), + PINK_NOISE(6), + SINE_TABLE(7), + TICKS(8), + GAUSSIAN_NOISE(9), + RED_NOISE(10), + BLUE_NOISE(11), + VIOLET_NOISE(12); + + private final int value; + + private AudioTestSrcWave(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } + } + +} diff --git a/test/org/freedesktop/gstreamer/QueryTest.java b/test/org/freedesktop/gstreamer/QueryTest.java index 2038a59a..5acb048d 100644 --- a/test/org/freedesktop/gstreamer/QueryTest.java +++ b/test/org/freedesktop/gstreamer/QueryTest.java @@ -20,6 +20,8 @@ package org.freedesktop.gstreamer; +import org.freedesktop.gstreamer.query.Query; +import org.freedesktop.gstreamer.query.QueryType; import static org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.GSTMINIOBJECT_API; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -28,6 +30,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import org.freedesktop.gstreamer.glib.Natives; import org.freedesktop.gstreamer.lowlevel.GstQueryAPI; import org.freedesktop.gstreamer.query.AllocationQuery; @@ -129,31 +132,33 @@ public void gst_query_new_latency() { @Test public void latencyIsLive() { LatencyQuery query = new LatencyQuery(); - query.setLatency(true, ClockTime.ZERO, ClockTime.ZERO); + query.setLatency(true, 0, 0); assertTrue("isLive not set to true", query.isLive()); - query.setLatency(false, ClockTime.ZERO, ClockTime.ZERO); + query.setLatency(false, 0, 0); assertFalse("isLive not set to true", query.isLive()); } @Test public void getMinimumLatency() { LatencyQuery query = new LatencyQuery(); - final ClockTime MIN = ClockTime.fromMillis(13000); - query.setLatency(false, MIN, ClockTime.valueOf(~0L, TimeUnit.SECONDS)); +// final ClockTime MIN = ClockTime.fromMillis(13000); + final long MIN = TimeUnit.MILLISECONDS.toNanos(13000); + query.setLatency(false, MIN, ~0); assertEquals("Min latency not set", MIN, query.getMinimumLatency()); } @Test public void getMaximumLatency() { LatencyQuery query = new LatencyQuery(); - final ClockTime MAX = ClockTime.fromMillis(123000); - query.setLatency(false, ClockTime.ZERO, MAX); +// final ClockTime MAX = ClockTime.fromMillis(123000); + final long MAX = TimeUnit.MILLISECONDS.toNanos(123000); + query.setLatency(false, 0, MAX); assertEquals("Min latency not set", MAX, query.getMaximumLatency()); } @Test public void latencyQueryToString() { LatencyQuery query = new LatencyQuery(); - ClockTime minLatency = ClockTime.fromMillis(13000); - ClockTime maxLatency = ClockTime.fromMillis(200000); + long minLatency = TimeUnit.MILLISECONDS.toNanos(13000); //ClockTime.fromMillis(13000); + long maxLatency = TimeUnit.MILLISECONDS.toNanos(200000);//ClockTime.fromMillis(200000); query.setLatency(true, minLatency, maxLatency); String s = query.toString(); assertTrue("toString() did not return isLive", s.contains("live=true")); @@ -163,20 +168,21 @@ public void latencyQueryToString() { @Test public void segmentQuery() { SegmentQuery query = new SegmentQuery(Format.TIME); - ClockTime end = ClockTime.fromMillis(1000); - query.setSegment(1.0, Format.TIME, 0, end.toNanos()); +// ClockTime end = ClockTime.fromMillis(1000); + long end = TimeUnit.MILLISECONDS.toNanos(1000); + query.setSegment(1.0, Format.TIME, 0, end); assertEquals("Format not set correctly", Format.TIME, query.getFormat()); assertEquals("Start time not set correctly", 0, query.getStart()); - assertEquals("End time not set correctly", end.toNanos(), query.getEnd()); + assertEquals("End time not set correctly", end, query.getEnd()); } @Test public void seekingQuery() { SeekingQuery query = new SeekingQuery(Format.TIME); - ClockTime start = ClockTime.ZERO; - ClockTime end = ClockTime.fromMillis(1000); - query.setSeeking(Format.TIME, true, start.toNanos(), end.toNanos()); + long start = 0; + long end = TimeUnit.MILLISECONDS.toNanos(1000); + query.setSeeking(Format.TIME, true, start, end); assertEquals("Format not set", Format.TIME, query.getFormat()); - assertEquals("Start time not set", start.toNanos(), query.getStart()); - assertEquals("End time not set", end.toNanos(), query.getEnd()); + assertEquals("Start time not set", start, query.getStart()); + assertEquals("End time not set", end, query.getEnd()); } @Test public void formatsQuery() { Query query = GstQueryAPI.GSTQUERY_API.gst_query_new_formats(); @@ -202,16 +208,17 @@ public void latencyQueryToString() { Query query = new SegmentQuery(Format.TIME); assertTrue("New query is not writable", query.isWritable()); // Bumping the ref count makes this instance non writable - GSTMINIOBJECT_API.gst_mini_object_ref(query); +// GSTMINIOBJECT_API.gst_mini_object_ref(query); + Natives.ref(query); assertFalse("Query with multiple references should not be writable", query.isWritable()); // Now get a new reference that is writable query = query.makeWritable(); assertTrue("Query not writable after makeWritable", query.isWritable()); } - @Test public void testQueryTypeGetName() { - assertEquals(QueryType.JITTER.getName(), "jitter"); - } +// @Test public void testQueryTypeGetName() { +// assertEquals(QueryType.JITTER.getName(), "jitter"); +// } @Test public void gst_query_new_allocation() { diff --git a/test/org/freedesktop/gstreamer/RegistryTest.java b/test/org/freedesktop/gstreamer/RegistryTest.java index c59f1cac..99e5522d 100644 --- a/test/org/freedesktop/gstreamer/RegistryTest.java +++ b/test/org/freedesktop/gstreamer/RegistryTest.java @@ -81,7 +81,7 @@ public boolean accept(Plugin plugin) { filterCalled[0] = true; return plugin.getName().equals(PLUGIN); } - }, true); + }); assertFalse("No plugins found", plugins.isEmpty()); assertTrue("PluginFilter not called", filterCalled[0]); assertEquals("Plugin list should contain 1 item", 1, plugins.size()); @@ -106,7 +106,7 @@ public void testGetPluginFeatureListByPlugin() { @Test public void testFindPluginFeature() { - PluginFeature f = Registry.get().findPluginFeature("decodebin"); + PluginFeature f = Registry.get().lookupFeature("decodebin"); assertNotNull(f); } diff --git a/test/org/freedesktop/gstreamer/SampleTest.java b/test/org/freedesktop/gstreamer/SampleTest.java new file mode 100644 index 00000000..de9f2df2 --- /dev/null +++ b/test/org/freedesktop/gstreamer/SampleTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 John Cortell + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer; + +import static org.junit.Assert.*; + +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.util.TestAssumptions; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SampleTest { + + public SampleTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + Gst.init(Gst.getVersion(), "SampleTest"); + } + + @AfterClass + public static void tearDownClass() throws Exception { + Gst.deinit(); + } + + @Test + public void testGetCaps() { + SampleTester.test((Sample sample) -> { + Caps caps = sample.getCaps(); + Structure struct = caps.getStructure(0); + String name = struct.getName(); + assertEquals("video/x-raw", name); + }); + } + + @Test + public void testGetBuffer() { + SampleTester.test((Sample sample) -> { + Buffer buffer = sample.getBuffer(); + assertEquals(1, buffer.getMemoryCount()); + }); + } + + @Test + public void testSetBuffer() { + // since gst 1.16, the sample is recycled and keep a reference on the last buffer received + TestAssumptions.requireGstVersion(1, 16); + + SampleTester.test((Sample sample) -> { + + Buffer buffer = sample.getBuffer(); + + int refCount = buffer.getRefCount(); + + assertEquals(2, sample.getRefCount()); + + // make sample writable + Natives.unref(sample); + + // force sample to release the buffer + sample.setBuffer(null); + + Natives.ref(sample); + + assertEquals(2, sample.getRefCount()); + + assertEquals(refCount - 1, buffer.getRefCount()); + }); + } + + @Test + public void testSampleTester() { + try { + SampleTester.test(sample -> { + throw new IllegalStateException(); + }); + } catch (Throwable t) { + assertTrue(t instanceof AssertionError); + assertTrue(t.getCause() instanceof IllegalStateException); + return; + } + fail("No exception thrown"); + } + +} diff --git a/test/org/freedesktop/gstreamer/SampleTester.java b/test/org/freedesktop/gstreamer/SampleTester.java new file mode 100644 index 00000000..132004d8 --- /dev/null +++ b/test/org/freedesktop/gstreamer/SampleTester.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 John Cortell + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +import org.freedesktop.gstreamer.elements.AppSink; + +/** + * Utility class for unit testing API that operates on a Sample. + *

+ * Call {@link SampleTester#test(Consumer)} and pass a callback which will + * perform the test on a Sample it is supplied. The callback runs on the + * AppSink.NEW_SAMPLE thread. The sample is produced by a simple, ephemeral + * pipeline that is fed by a video test source. + */ +public class SampleTester { + + public static void test(Consumer callback) { + test(callback, "videotestsrc ! videoconvert ! appsink name=myappsink"); + } + + public static void test(Consumer callback, String pipelineDescription) { + test(callback, pipelineDescription, 0); + } + + public static void test(Consumer callback, String pipelineDescription, int skipFrames) { + assertNotNull("Pipeline description can not be null", pipelineDescription); + assertFalse("Pipeline description can not be empty", pipelineDescription.isEmpty()); + Pipeline pipe = (Pipeline) Gst.parseLaunch(pipelineDescription); + assertNotNull("Unable to create Pipeline from pipeline description: ", pipe); + + AppSink appSink = (AppSink) pipe.getElementByName("myappsink"); + appSink.set("emit-signals", true); + + NewSampleListener sampleListener = new NewSampleListener(callback, skipFrames); + appSink.connect(sampleListener); + + pipe.play(); + + // Wait for the sample to arrive and for the client supplied test function to + // complete + try { + sampleListener.await(5000); + } catch (Exception ex) { + fail("Unexpected exception waiting for sample\n" + ex); + } finally { + pipe.stop(); + appSink.disconnect(sampleListener); + } + + // If the test threw an exception on the sample listener thread, throw it here + // (on the main thread) + if (sampleListener.exception != null) { + throw new AssertionError(sampleListener.exception); + } + } + + private static class NewSampleListener implements AppSink.NEW_SAMPLE { + + private final Consumer callback; + private final int skipFrames; + private final CountDownLatch latch; + + private Throwable exception; + private int counter = 0; + + NewSampleListener(Consumer callback) { + this(callback, 0); + } + + NewSampleListener(Consumer callback, int skip) { + this.callback = callback; + skipFrames = skip; + latch = new CountDownLatch(1); + } + + @Override + public FlowReturn newSample(AppSink appSink) { + if (latch.getCount() > 0) { + Sample sample = appSink.pullSample(); + if (counter < skipFrames) { + counter++; + sample.dispose(); + return FlowReturn.OK; + } + try { + // Run the client's test logic on the sample (only once) + try { + callback.accept(sample); + } catch (Throwable exc) { + exception = exc; + } + } finally { + latch.countDown(); + sample.dispose(); + } + } + return FlowReturn.OK; + } + + void await(long millis) throws InterruptedException, TimeoutException { + if (!latch.await(millis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException(); + } + } + } + +} diff --git a/test/org/freedesktop/gstreamer/StructureTest.java b/test/org/freedesktop/gstreamer/StructureTest.java index 3cf5d816..96583f3a 100644 --- a/test/org/freedesktop/gstreamer/StructureTest.java +++ b/test/org/freedesktop/gstreamer/StructureTest.java @@ -1,23 +1,47 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2009 Levente Farkas + * Copyright (C) 2009 Tamas Korodi + * Copyright (C) 2007 Wayne Meissner + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ package org.freedesktop.gstreamer; -import java.util.List; -import static org.junit.Assert.*; - +import org.freedesktop.gstreamer.glib.GCancellable; import org.freedesktop.gstreamer.lowlevel.GType; import org.freedesktop.gstreamer.lowlevel.GValueAPI; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.util.Arrays; +import java.util.List; +import org.freedesktop.gstreamer.util.TestAssumptions; + +import static org.junit.Assert.*; + public class StructureTest { - private Structure structure; - + + private Structure structure; + @BeforeClass public static void setUpClass() throws Exception { - Gst.init("StructureTest", new String[] {}); + Gst.init(Gst.getVersion(), "StructureTest"); } - + @AfterClass public static void tearDownClass() throws Exception { Gst.deinit(); @@ -25,32 +49,33 @@ public static void tearDownClass() throws Exception { @Before public void setUp() { - structure = new Structure("nazgul"); + structure = new Structure("nazgul"); } - - @Test - public void testGetName() { - assertEquals("nazgul", structure.getName()); - } - - @Test - public void testGetValue() { - structure.setValue("uint", GType.UINT, 9); - assertEquals(9, structure.getValue("uint")); - - try { - structure.getValue("noexist"); - fail("Structure.InvalidFieldException should have been thrown"); - } catch (Structure.InvalidFieldException e) {} - - structure.setDouble("double", 9.0); - assertEquals(9.0, structure.getValue("double")); - - structure.setValue("bool", GType.BOOLEAN, true); - assertEquals(true, structure.getValue("bool")); - - } - + + @Test + public void testGetName() { + assertEquals("nazgul", structure.getName()); + } + + @Test + public void testGetValue() { + structure.setValue("uint", GType.UINT, 9); + assertEquals(9, structure.getValue("uint")); + + try { + structure.getValue("noexist"); + fail("Structure.InvalidFieldException should have been thrown"); + } catch (Structure.InvalidFieldException e) { + } + + structure.setDouble("double", 9.0); + assertEquals(9.0, structure.getValue("double")); + + structure.setValue("bool", GType.BOOLEAN, true); + assertEquals(true, structure.getValue("bool")); + + } + @Test public void testGetValues() { GValueAPI.GValueArray ar = new GValueAPI.GValueArray(2); @@ -71,16 +96,16 @@ public void testGetValues() { } catch (Structure.InvalidFieldException ex) { } } - - @Test - public void testGetInteger() { - structure.setInteger("int", 9); - assertEquals(9, structure.getInteger("int")); - - structure.setInteger("int", -9); - assertEquals(-9, structure.getInteger("int")); - } - + + @Test + public void testGetInteger() { + structure.setInteger("int", 9); + assertEquals(9, structure.getInteger("int")); + + structure.setInteger("int", -9); + assertEquals(-9, structure.getInteger("int")); + } + @Test public void testGetIntegers() { GValueAPI.GValueArray ar = new GValueAPI.GValueArray(2); @@ -92,28 +117,28 @@ public void testGetIntegers() { assertTrue(in == ints); assertEquals(32, ints[0]); assertEquals(-49, ints[1]); - + in = new int[1]; ints = structure.getIntegers("integers", in); assertFalse(in == ints); assertEquals(32, ints[0]); assertEquals(-49, ints[1]); - + structure.setInteger("single_integer", 18); int[] single = structure.getIntegers("single_integer", in); assertTrue(in == single); assertEquals(18, single[0]); } - @Test - public void testGetDouble() { - structure.setDouble("double", 9.0); - assertEquals(9.0, structure.getDouble("double"), 0); - - structure.setDouble("double", -9.0); - assertEquals(-9.0, structure.getDouble("double"), 0); - } - + @Test + public void testGetDouble() { + structure.setDouble("double", 9.0); + assertEquals(9.0, structure.getDouble("double"), 0); + + structure.setDouble("double", -9.0); + assertEquals(-9.0, structure.getDouble("double"), 0); + } + @Test public void testGetDoubles() { GValueAPI.GValueArray ar = new GValueAPI.GValueArray(2); @@ -125,30 +150,90 @@ public void testGetDoubles() { assertTrue(in == doubles); assertEquals(3.25, doubles[0], 0.001); assertEquals(79.6, doubles[1], 0.001); - + in = new double[1]; doubles = structure.getDoubles("doubles", in); assertFalse(in == doubles); assertEquals(3.25, doubles[0], 0.001); assertEquals(79.6, doubles[1], 0.001); - + structure.setDouble("single_double", 18.2); double[] single = structure.getDoubles("single_double", in); assertTrue(in == single); assertEquals(18.2, single[0], 0.001); } - @Test - public void testFraction() { - structure.setFraction("fraction", 10, 1); + @Test + public void testFraction() { + structure.setFraction("fraction", 10, 1); - assertEquals(true, structure.hasField("fraction")); + assertEquals(true, structure.hasField("fraction")); - assertEquals(10, structure.getFraction("fraction").getNumerator()); - assertEquals(1, structure.getFraction("fraction").getDenominator()); + assertEquals(10, structure.getFraction("fraction").getNumerator()); + assertEquals(1, structure.getFraction("fraction").getDenominator()); + + structure.setFraction("fraction", 17, 10); + assertEquals(17, structure.getFraction("fraction").getNumerator()); + assertEquals(10, structure.getFraction("fraction").getDenominator()); + } - structure.setFraction("fraction", 17, 10); - assertEquals(17, structure.getFraction("fraction").getNumerator()); - assertEquals(10, structure.getFraction("fraction").getDenominator()); - } + @Test + public void testValueListInteger() { + Caps caps = Caps.fromString("audio/x-raw,rate={44100,48000}"); + List rates = caps.getStructure(0).getValues(Integer.class, "rate"); + assertEquals(Arrays.asList(44100, 48000), rates); + } + + @Test + public void testValueListStrings() { + Caps caps = Caps.fromString("video/x-raw,format={RGB, BGR, RGBx, BGRx}"); + List formats = caps.getStructure(0).getValues(String.class, "format"); + assertEquals(Arrays.asList("RGB", "BGR", "RGBx", "BGRx"), formats); + } + + @Test(expected = Structure.InvalidFieldException.class) + public void testValueListChecksType() { + Caps caps = Caps.fromString("video/x-raw,format={RGB, BGR, RGBx, BGRx}"); + caps.getStructure(0).getValues(Integer.class, "format"); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetMistypedObject() { + GCancellable notACapsInstance = new GCancellable(); + structure.setObject("whatever", Caps.GTYPE_NAME, notACapsInstance); + } + + @Test + public void testSetUntypedObject() { + GCancellable anyKindOfObject = new GCancellable(); + structure.setObject("whatever", GType.OBJECT.getTypeName(), anyKindOfObject); + Object value = structure.getValue("whatever"); + Assert.assertSame(anyKindOfObject, value); + } + + @Test + public void testSetObject() { + GCancellable anyKindOfObject = new GCancellable(); + structure.setObject("whatever", GCancellable.GTYPE_NAME, anyKindOfObject); + Object value = structure.getValue("whatever"); + Assert.assertSame(anyKindOfObject, value); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetNullObject() { + structure.setObject("whatever", GCancellable.GTYPE_NAME, null); + Object value = structure.getValue("whatever"); + Assert.assertNull(value); + } + + @Test + public void testIssue173() { + TestAssumptions.requireGstVersion(1, 16); + TestAssumptions.requireElement("srtsink"); + + Element srtsink = ElementFactory.make("srtsink", "srtsink"); + srtsink.set("uri", "srt://:8888/"); + Object stats = srtsink.get("stats"); + assertTrue(stats instanceof Structure); + } } diff --git a/test/org/freedesktop/gstreamer/TestPipe.java b/test/org/freedesktop/gstreamer/TestPipe.java index 245bc73d..bcedf04f 100644 --- a/test/org/freedesktop/gstreamer/TestPipe.java +++ b/test/org/freedesktop/gstreamer/TestPipe.java @@ -71,7 +71,4 @@ public void dispose() { pipe.stop(); } - protected void finalize() { - dispose(); - } } diff --git a/test/org/freedesktop/gstreamer/WebRTCBinTest.java b/test/org/freedesktop/gstreamer/WebRTCBinTest.java new file mode 100644 index 00000000..ca833168 --- /dev/null +++ b/test/org/freedesktop/gstreamer/WebRTCBinTest.java @@ -0,0 +1,27 @@ +package org.freedesktop.gstreamer; + +import org.freedesktop.gstreamer.glib.NativeEnum; +import org.freedesktop.gstreamer.webrtc.WebRTCICEGatheringState; +import org.freedesktop.gstreamer.webrtc.WebRTCPeerConnectionState; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class WebRTCBinTest { + @Test + public void connectionStateTest() { + assertEquals(NativeEnum.fromInt(WebRTCPeerConnectionState.class, 0), WebRTCPeerConnectionState.NEW); + assertEquals(NativeEnum.fromInt(WebRTCPeerConnectionState.class, 1), WebRTCPeerConnectionState.CONNECTING); + assertEquals(NativeEnum.fromInt(WebRTCPeerConnectionState.class, 2), WebRTCPeerConnectionState.CONNECTED); + assertEquals(NativeEnum.fromInt(WebRTCPeerConnectionState.class, 3), WebRTCPeerConnectionState.DISCONNECTED); + assertEquals(NativeEnum.fromInt(WebRTCPeerConnectionState.class, 4), WebRTCPeerConnectionState.FAILED); + assertEquals(NativeEnum.fromInt(WebRTCPeerConnectionState.class, 5), WebRTCPeerConnectionState.CLOSED); + } + + @Test + public void iceGatheringStateTest() { + assertEquals(NativeEnum.fromInt(WebRTCICEGatheringState.class, 0), WebRTCICEGatheringState.NEW); + assertEquals(NativeEnum.fromInt(WebRTCICEGatheringState.class, 1), WebRTCICEGatheringState.GATHERING); + assertEquals(NativeEnum.fromInt(WebRTCICEGatheringState.class, 2), WebRTCICEGatheringState.COMPLETE); + } +} diff --git a/test/org/freedesktop/gstreamer/controller/InterpolationControlSourceTest.java b/test/org/freedesktop/gstreamer/controller/InterpolationControlSourceTest.java new file mode 100644 index 00000000..8c573b8c --- /dev/null +++ b/test/org/freedesktop/gstreamer/controller/InterpolationControlSourceTest.java @@ -0,0 +1,159 @@ +package org.freedesktop.gstreamer.controller; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.freedesktop.gstreamer.ClockTime; +import org.freedesktop.gstreamer.ControlBinding; +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.Element; +import org.freedesktop.gstreamer.ElementFactory; +import org.freedesktop.gstreamer.GCTracker; +import org.freedesktop.gstreamer.Gst; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + */ +public class InterpolationControlSourceTest { + + public InterpolationControlSourceTest() { + } + + @BeforeClass + public static void setUpClass() { + Gst.init("InterpolationControlSourceTest"); + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of setMode method, of class InterpolationControlSource. + */ + @Test + public void testMode() { + InterpolationControlSource controller = new InterpolationControlSource(); + InterpolationMode[] modes = InterpolationMode.values(); + for (InterpolationMode mode : modes) { + controller.setMode(mode); + assertEquals(mode, controller.getMode()); + } + } + + @Test + public void testSetValue() { + List timedValue + = Collections.singletonList( + new ControlSource.TimedValue(ClockTime.fromSeconds(10), 0.5) + ); + InterpolationControlSource controller = new InterpolationControlSource(); + controller.set(timedValue.get(0).timestamp, timedValue.get(0).value); + assertEquals(timedValue, controller.getAll()); + } + + @Test + public void testSetValues() { + List timedValues + = Stream.of( + new ControlSource.TimedValue(ClockTime.fromSeconds(0), 0.0), + new ControlSource.TimedValue(ClockTime.fromSeconds(1), 0.5), + new ControlSource.TimedValue(ClockTime.fromSeconds(2), 0.2), + new ControlSource.TimedValue(ClockTime.fromSeconds(4), 0.8) + ).collect(Collectors.toList()); + InterpolationControlSource controller = new InterpolationControlSource(); + controller.setFromList(timedValues); + assertEquals(timedValues, controller.getAll()); + } + + @Test + public void testLinearInterpolation() { + List timedValues + = Stream.of( + new ControlSource.TimedValue(ClockTime.fromSeconds(0), 0.0), + new ControlSource.TimedValue(ClockTime.fromSeconds(1), 1.0) + ).collect(Collectors.toList()); + InterpolationControlSource controller = new InterpolationControlSource(); + controller.setMode(InterpolationMode.LINEAR); + controller.setFromList(timedValues); + + Element volume = ElementFactory.make("volume", "volume"); + volume.addControlBinding(DirectControlBinding.create(volume, "volume", controller)); + volume.syncValues(0); + assertEquals(0, ((Double) volume.get("volume")).doubleValue(), 0.001); + volume.syncValues(ClockTime.fromMillis(500)); + assertEquals(5, ((Double) volume.get("volume")).doubleValue(), 0.001); + volume.syncValues(ClockTime.fromSeconds(1)); + assertEquals(10, ((Double) volume.get("volume")).doubleValue(), 0.001); + + } + + @Test + public void testLinearInterpolationAbsolute() { + List timedValues + = Stream.of( + new ControlSource.TimedValue(ClockTime.fromSeconds(0), 0.0), + new ControlSource.TimedValue(ClockTime.fromSeconds(1), 5.0) + ).collect(Collectors.toList()); + InterpolationControlSource controller = new InterpolationControlSource(); + controller.setMode(InterpolationMode.LINEAR); + controller.setFromList(timedValues); + + Element volume = ElementFactory.make("volume", "volume"); + ControlBinding binding = DirectControlBinding.createAbsolute(volume, "volume", controller); + + assertEquals(2.5, + (Double) binding.getValue(ClockTime.fromMillis(500)), + 0.01); + + Object[] values = new Object[3]; + binding.getValueArray(0, ClockTime.fromMillis(500), values); + assertEquals(0, (Double) values[0], 0.01); + assertEquals(2.5, (Double) values[1], 0.01); + assertEquals(5, (Double) values[2], 0.01); + + volume.addControlBinding(binding); + volume.syncValues(0); + assertEquals(0, ((Double) volume.get("volume")), 0.001); + volume.syncValues(ClockTime.fromMillis(500)); + assertEquals(2.5, ((Double) volume.get("volume")), 0.001); + volume.syncValues(ClockTime.fromSeconds(1)); + assertEquals(5, ((Double) volume.get("volume")), 0.001); + + } + + @Test + public void testGC() { + InterpolationControlSource controller = new InterpolationControlSource(); + Element volume = ElementFactory.make("volume", "volume"); + ControlBinding binding = DirectControlBinding.create(volume, "volume", controller); + volume.addControlBinding(binding); + + GCTracker tracker = new GCTracker(controller); + controller = null; + binding = null; + volume = null; + + assertTrue("Controller not garbage collected", tracker.waitGC()); + assertTrue("Controller not destroyed", tracker.waitDestroyed()); + + } + +} diff --git a/test/org/freedesktop/gstreamer/controller/TriggerControlSourceTest.java b/test/org/freedesktop/gstreamer/controller/TriggerControlSourceTest.java new file mode 100644 index 00000000..f6e41ed8 --- /dev/null +++ b/test/org/freedesktop/gstreamer/controller/TriggerControlSourceTest.java @@ -0,0 +1,95 @@ +package org.freedesktop.gstreamer.controller; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.freedesktop.gstreamer.ClockTime; +import org.freedesktop.gstreamer.ControlBinding; +import org.freedesktop.gstreamer.ControlSource; +import org.freedesktop.gstreamer.Element; +import org.freedesktop.gstreamer.ElementFactory; +import org.freedesktop.gstreamer.GCTracker; +import org.freedesktop.gstreamer.Gst; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + */ +public class TriggerControlSourceTest { + + public TriggerControlSourceTest() { + } + + @BeforeClass + public static void setUpClass() { + Gst.init("InterpolationControlSourceTest"); + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + + @Test + public void testTolerance() { + List timedValues + = Stream.of( + new ControlSource.TimedValue(ClockTime.fromSeconds(0), 0.5), + new ControlSource.TimedValue(ClockTime.fromSeconds(2), 1.0) + ).collect(Collectors.toList()); + TriggerControlSource controller = new TriggerControlSource(); + controller.setFromList(timedValues); + + Element volume = ElementFactory.make("volume", "volume"); + volume.addControlBinding(DirectControlBinding.create(volume, "volume", controller)); + volume.syncValues(0); + assertEquals(5, ((Double) volume.get("volume")), 0.001); + volume.set("volume", 0); + volume.syncValues(ClockTime.fromSeconds(1)); + assertEquals(0, ((Double) volume.get("volume")), 0.001); + volume.syncValues(ClockTime.fromSeconds(2)); + assertEquals(10, ((Double) volume.get("volume")), 0.001); + + controller.setTolerance(ClockTime.fromMillis(500)); + volume.syncValues(ClockTime.fromMillis(450)); + assertEquals(5, ((Double) volume.get("volume")), 0.001); + volume.set("volume", 0); + volume.syncValues(ClockTime.fromMillis(550)); + assertEquals(0, ((Double) volume.get("volume")), 0.001); + volume.syncValues(ClockTime.fromMillis(1650)); + assertEquals(10, ((Double) volume.get("volume")), 0.001); + + } + + @Test + public void testGC() { + TriggerControlSource controller = new TriggerControlSource(); + Element volume = ElementFactory.make("volume", "volume"); + ControlBinding binding = DirectControlBinding.create(volume, "volume", controller); + volume.addControlBinding(binding); + + GCTracker tracker = new GCTracker(controller); + controller = null; + binding = null; + volume = null; + + assertTrue("Controller not garbage collected", tracker.waitGC()); + assertTrue("Controller not destroyed", tracker.waitDestroyed()); + + } + +} diff --git a/test/org/freedesktop/gstreamer/elements/PlayBinTest.java b/test/org/freedesktop/gstreamer/elements/PlayBinTest.java new file mode 100644 index 00000000..41714e4c --- /dev/null +++ b/test/org/freedesktop/gstreamer/elements/PlayBinTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021 Neil C Smith + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer.elements; + +import java.net.URI; +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.freedesktop.gstreamer.Caps; +import org.freedesktop.gstreamer.ElementFactory; +import org.freedesktop.gstreamer.GCTracker; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.util.TestAssumptions; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Tests for PlayBin. + */ +public class PlayBinTest { + + @BeforeClass + public static void setUpClass() throws Exception { + Gst.init(Gst.getVersion(), "PlayBinTest"); + } + + @AfterClass + public static void tearDownClass() throws Exception { + Gst.deinit(); + } + + @Test + public void testFlags() { + PlayBin playbin = new PlayBin("playbin"); + Set defaultFlags = EnumSet.of( + PlayFlags.SOFT_COLORBALANCE, + PlayFlags.DEINTERLACE, + PlayFlags.SOFT_VOLUME, + PlayFlags.TEXT, + PlayFlags.AUDIO, + PlayFlags.VIDEO + ); + Set flags = playbin.getFlags(); + assertEquals("PlayBin flags not expected defaults", defaultFlags, flags); + + flags.add(PlayFlags.VIS); + flags.remove(PlayFlags.DEINTERLACE); + playbin.setFlags(flags); + + flags = playbin.getFlags(); + assertTrue("VIS flag not set", flags.contains(PlayFlags.VIS)); + assertFalse("Deinterlace not removed from playbin flags", + flags.contains(PlayFlags.DEINTERLACE)); + + playbin.dispose(); + + } + + @Test + public void testSourceSetupSignal() throws Exception { + + PlayBin playbin = new PlayBin("playbin", URI.create("appsrc:/")); + AtomicReference sourceRef = new AtomicReference<>(null); + playbin.connect((PlayBin.SOURCE_SETUP) ((p, e) -> { + if (e instanceof AppSrc) { + AppSrc appSrc = (AppSrc) e; + appSrc.setCaps(Caps.fromString("video/x-raw, format=xRGB, width=640, height=480")); + sourceRef.set(appSrc); + } + })); + playbin.setVideoSink(ElementFactory.make("fakesink", "videosink")); + playbin.play(); + playbin.getState(200, TimeUnit.MILLISECONDS); + + AppSrc src = sourceRef.getAndSet(null); + assertNotNull(src); + + GCTracker sourceTracker = new GCTracker(src); + GCTracker playbinTracker = new GCTracker(playbin); + + playbin.stop(); + + src = null; + playbin = null; + + assertTrue("AppSrc not garbage collected", sourceTracker.waitGC()); + assertTrue("AppSrc not destroyed", sourceTracker.waitDestroyed()); + assertTrue("PlayBin not garbage collected", playbinTracker.waitGC()); + assertTrue("PlayBin not destroyed", playbinTracker.waitDestroyed()); + + } + + @Test + public void testElementSetupSignal() throws Exception { + + TestAssumptions.requireGstVersion(1, 10); + PlayBin playbin = new PlayBin("playbin", URI.create("appsrc:/")); + AtomicReference sourceRef = new AtomicReference<>(null); + playbin.connect((PlayBin.ELEMENT_SETUP) ((p, e) -> { + if (e instanceof AppSrc) { + AppSrc appSrc = (AppSrc) e; + sourceRef.set(appSrc); + } + })); + playbin.setVideoSink(ElementFactory.make("fakesink", "videosink")); + playbin.play(); + playbin.getState(200, TimeUnit.MILLISECONDS); + + AppSrc src = sourceRef.getAndSet(null); + assertNotNull(src); + + GCTracker sourceTracker = new GCTracker(src); + GCTracker playbinTracker = new GCTracker(playbin); + + playbin.stop(); + + src = null; + playbin = null; + + assertTrue("AppSrc not garbage collected", sourceTracker.waitGC()); + assertTrue("AppSrc not destroyed", sourceTracker.waitDestroyed()); + assertTrue("PlayBin not garbage collected", playbinTracker.waitGC()); + assertTrue("PlayBin not destroyed", playbinTracker.waitDestroyed()); + + } + +} diff --git a/test/org/freedesktop/gstreamer/EventTest.java b/test/org/freedesktop/gstreamer/event/EventTest.java similarity index 89% rename from test/org/freedesktop/gstreamer/EventTest.java rename to test/org/freedesktop/gstreamer/event/EventTest.java index 1d2c43d6..6a6ba13a 100644 --- a/test/org/freedesktop/gstreamer/EventTest.java +++ b/test/org/freedesktop/gstreamer/event/EventTest.java @@ -17,8 +17,11 @@ * along with gstreamer-java. If not, see . */ -package org.freedesktop.gstreamer; +package org.freedesktop.gstreamer.event; +import org.freedesktop.gstreamer.event.QOSType; +import org.freedesktop.gstreamer.event.Event; +import org.freedesktop.gstreamer.event.EventType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -26,7 +29,15 @@ import static org.junit.Assert.fail; import java.lang.ref.WeakReference; +import java.util.EnumSet; import java.util.concurrent.TimeUnit; +import org.freedesktop.gstreamer.Caps; +import org.freedesktop.gstreamer.ClockTime; +import org.freedesktop.gstreamer.Format; +import org.freedesktop.gstreamer.GCTracker; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.TagList; import org.freedesktop.gstreamer.event.BufferSizeEvent; import org.freedesktop.gstreamer.event.CapsEvent; @@ -87,7 +98,7 @@ public static void tearDownClass() throws Exception { } @Test public void createSegmentEvent() throws Exception { GstAPI.GstSegmentStruct struct = new GstAPI.GstSegmentStruct(); - struct.flags = SegmentFlags.NONE; + struct.flags = 0; struct.rate = 1.0; struct.applied_rate = 1.0; struct.format = Format.TIME; @@ -121,13 +132,13 @@ public static void tearDownClass() throws Exception { assertTrue("gst_event_new_flush_stop returned a non-FLUSH_STOP event", ev instanceof FlushStopEvent); } @Test public void gst_event_new_latency() { - Event ev = GSTEVENT_API.gst_event_new_latency(ClockTime.ZERO); + Event ev = GSTEVENT_API.gst_event_new_latency(0); assertNotNull("gst_event_new_latency returned null", ev); assertTrue("gst_event_new_latency returned a non-LATENCY event", ev instanceof LatencyEvent); } @Test public void gst_event_new_new_segment() { GstAPI.GstSegmentStruct struct = new GstAPI.GstSegmentStruct(); - struct.flags = SegmentFlags.NONE; + struct.flags = 0; struct.rate = 1.0; struct.applied_rate = 1.0; struct.format = Format.TIME; @@ -136,23 +147,24 @@ public static void tearDownClass() throws Exception { assertTrue("gst_event_new_latency returned a non-NEWSEGMENT event", ev instanceof SegmentEvent); } @Test public void getLatency() { - final ClockTime MAGIC = ClockTime.valueOf(0xdeadbeef, TimeUnit.NANOSECONDS); +// final ClockTime MAGIC = ClockTime.valueOf(0xdeadbeef, TimeUnit.NANOSECONDS); + long MAGIC = 0xdeadbeef; LatencyEvent ev = new LatencyEvent(MAGIC); assertEquals("Incorrect latency returned", MAGIC, ev.getLatency()); } @Test public void NewSegment_getRate() { final double RATE = (double) 0xdeadbeef; - SegmentEvent ev = new SegmentEvent(new GstAPI.GstSegmentStruct(SegmentFlags.NONE, RATE, RATE, Format.TIME, 0, 0, 0, 0, 0, 0, 0)); + SegmentEvent ev = new SegmentEvent(new GstAPI.GstSegmentStruct(0, RATE, RATE, Format.TIME, 0, 0, 0, 0, 0, 0, 0)); assertEquals("Incorrect rate returned from getRate", RATE, ev.getSegment().rate, 0.0); } @Test public void NewSegment_getStart() { final long START = 0xdeadbeefL; - SegmentEvent ev = new SegmentEvent(new GstAPI.GstSegmentStruct(SegmentFlags.NONE, 0.1, 0.1, Format.TIME, 0, 0, START, -1L, 0, 0, 0)); + SegmentEvent ev = new SegmentEvent(new GstAPI.GstSegmentStruct(0, 0.1, 0.1, Format.TIME, 0, 0, START, -1L, 0, 0, 0)); assertEquals("Incorrect rate returned from getStart", START, ev.getSegment().start); } @Test public void NewSegment_getStop() { final long STOP = 0xdeadbeefL; - SegmentEvent ev = new SegmentEvent(new GstAPI.GstSegmentStruct(SegmentFlags.NONE, 0.1, 0.1, Format.TIME, 0, 0, 0L, STOP, 0, 0, 0)); + SegmentEvent ev = new SegmentEvent(new GstAPI.GstSegmentStruct(0, 0.1, 0.1, Format.TIME, 0, 0, 0L, STOP, 0, 0, 0)); assertEquals("Incorrect rate returned from getRate", STOP, ev.getSegment().stop); } @Test public void gst_event_new_tag() { @@ -171,7 +183,7 @@ public static void tearDownClass() throws Exception { assertTrue("Event ref not collected after TagList is unreferenced", GCTracker.waitGC(evRef)); } @Test public void Event_testGC() throws Exception { - Event ev = new LatencyEvent(ClockTime.NONE); + Event ev = new LatencyEvent(100); @SuppressWarnings("unused") Structure s = ev.getStructure(); WeakReference evRef = new WeakReference(ev); @@ -227,13 +239,13 @@ public static void tearDownClass() throws Exception { assertEquals("Wrong difference", DIFF, ev.getDifference()); } @Test public void QOS_getTimestamp() { - final ClockTime STAMP = ClockTime.valueOf(0xdeadbeef, TimeUnit.NANOSECONDS); + final long STAMP = 0xdeadbeef; QOSEvent ev = new QOSEvent(QOSType.THROTTLE, 0d, 0, STAMP); assertEquals("Wrong timestamp", STAMP, ev.getTimestamp()); } @Test public void QOS_getType() { - final ClockTime STAMP = ClockTime.valueOf(0xdeadbeef, TimeUnit.NANOSECONDS); + final long STAMP = 0xdeadbeef; QOSEvent ev = new QOSEvent(QOSType.THROTTLE, 0d, 0, STAMP); assertEquals("Wrong QOSType", QOSType.THROTTLE, ev.getType()); } @@ -245,40 +257,40 @@ public void QOS_getType() { } @Test public void Seek_getFormat() { for (Format FORMAT : new Format[] { Format.TIME, Format.BYTES }) { - SeekEvent ev = new SeekEvent(1.0, FORMAT, 0, + SeekEvent ev = new SeekEvent(1.0, FORMAT, EnumSet.noneOf(SeekFlags.class), SeekType.SET, 0, SeekType.SET, 0); assertEquals("Wrong format in SeekEvent", FORMAT, ev.getFormat()); } } @Test public void Seek_getStartType() { for (SeekType TYPE : new SeekType[] { SeekType.SET, SeekType.END }) { - SeekEvent ev = new SeekEvent(1.0, Format.TIME, 0, + SeekEvent ev = new SeekEvent(1.0, Format.TIME, EnumSet.noneOf(SeekFlags.class), TYPE, 0, SeekType.NONE, 0); assertEquals("Wrong startType in SeekEvent", TYPE, ev.getStartType()); } } @Test public void Seek_getStopType() { for (SeekType TYPE : new SeekType[] { SeekType.SET, SeekType.END }) { - SeekEvent ev = new SeekEvent(1.0, Format.TIME, 0, + SeekEvent ev = new SeekEvent(1.0, Format.TIME, EnumSet.noneOf(SeekFlags.class), SeekType.NONE, 0, TYPE, 0); assertEquals("Wrong stopType in SeekEvent", TYPE, ev.getStopType()); } } @Test public void Seek_getStart() { final long START = 0xdeadbeef; - SeekEvent ev = new SeekEvent(1.0, Format.TIME, 0, + SeekEvent ev = new SeekEvent(1.0, Format.TIME, EnumSet.noneOf(SeekFlags.class), SeekType.SET, START, SeekType.SET, -1); assertEquals("Wrong start in SeekEvent", START, ev.getStart()); } @Test public void Seek_getStop() { final long STOP = 0xdeadbeef; - SeekEvent ev = new SeekEvent(1.0, Format.TIME, 0, + SeekEvent ev = new SeekEvent(1.0, Format.TIME, EnumSet.noneOf(SeekFlags.class), SeekType.SET, 0, SeekType.SET, STOP); assertEquals("Wrong stop in SeekEvent", STOP, ev.getStop()); } @Test public void Seek_rateZero() { try { - new SeekEvent(0.0, Format.TIME, 0, + new SeekEvent(0.0, Format.TIME, EnumSet.noneOf(SeekFlags.class), SeekType.SET, 0, SeekType.SET, -1); fail("A rate of 0.0 should throw an exception"); } catch (IllegalArgumentException ex) { diff --git a/test/org/freedesktop/gstreamer/event/ForceKeyUnitTest.java b/test/org/freedesktop/gstreamer/event/ForceKeyUnitTest.java deleted file mode 100644 index 7723d321..00000000 --- a/test/org/freedesktop/gstreamer/event/ForceKeyUnitTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014 Tom Greenwood - * - * This file is part of gstreamer-java. - * - * This code is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 3 for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with this work. If not, see . - */ -package org.freedesktop.gstreamer.event; - -import org.freedesktop.gstreamer.event.ForceKeyUnit; -import static org.junit.Assert.*; - -import org.freedesktop.gstreamer.Bus; -import org.freedesktop.gstreamer.ClockTime; -import org.freedesktop.gstreamer.Element; -import org.freedesktop.gstreamer.Gst; -import org.freedesktop.gstreamer.lowlevel.GstEventAPI; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -public class ForceKeyUnitTest -{ - @BeforeClass - public static void init() throws Exception - { - Gst.init("ForceKeyUnitTest", new String[] {}); - } - - @AfterClass - public static void deinit() throws Exception - { - Gst.deinit(); - } - - @Test - public void testUpstreamEventHasUpstreamType() - { - final ForceKeyUnit event = new ForceKeyUnit(ClockTime.NONE, true, 0); - } - - @Test - public void testUpstreamEventHasDownstreamType() - { - final ForceKeyUnit event = new ForceKeyUnit(ClockTime.fromMicros(1000), ClockTime.fromMicros(1000), ClockTime.NONE, true, 0); - } -} diff --git a/test/org/freedesktop/gstreamer/lowlevel/GValueTest.java b/test/org/freedesktop/gstreamer/lowlevel/GValueTest.java index 7cb08603..e48044b0 100644 --- a/test/org/freedesktop/gstreamer/lowlevel/GValueTest.java +++ b/test/org/freedesktop/gstreamer/lowlevel/GValueTest.java @@ -98,6 +98,8 @@ private void testGValueArray(GValueArray gva) throws Exception { assertEquals("value mismatch", 5, gva.getValue(0)); assertEquals("value mismatch", 5.0, gva.getValue(1)); assertEquals("value mismatch", "omanipadmihoom", gva.getValue(2)); + + gva.free(); } @Test public void testInitSet() throws Exception { diff --git a/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java b/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java index 89fe4592..54422e14 100644 --- a/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java +++ b/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java @@ -17,12 +17,9 @@ import org.junit.Test; import com.sun.jna.Structure; +import java.util.Arrays; import org.freedesktop.gstreamer.Gst; -/** - * - * @author Neil C Smith - */ public class LowLevelStructureTest { private final static Logger LOG = Logger.getLogger(LowLevelStructureTest.class.getName()); @@ -88,9 +85,14 @@ private void testStruct(Class struct) { } try { - Method getFieldOrder = inst.getClass().getDeclaredMethod("getFieldOrder"); - getFieldOrder.setAccessible(true); - fields = (List) getFieldOrder.invoke(inst); + Structure.FieldOrder fieldOrder = inst.getClass().getAnnotation(Structure.FieldOrder.class); + if (fieldOrder != null) { + fields = Arrays.asList(fieldOrder.value()); + } else { + Method getFieldOrder = inst.getClass().getDeclaredMethod("getFieldOrder"); + getFieldOrder.setAccessible(true); + fields = (List) getFieldOrder.invoke(inst); + } } catch (Exception ex) { LOG.log(Level.SEVERE, "Can't find getFieldOrder() method", ex); assertTrue(false); @@ -110,7 +112,7 @@ private void testFields(Structure inst, List expectedFields) { } private static void initStructList() { - structs = new ArrayList>(); + structs = new ArrayList<>(); structs.add(BaseSinkAPI.GstBaseSinkStruct.class); structs.add(BaseSinkAPI.GstBaseSinkClass.class); @@ -156,20 +158,24 @@ private static void initStructList() { structs.add(GstColorBalanceAPI.ColorBalanceChannelStruct.class); - structs.add(GstControlSourceAPI.TimedValue.class); - structs.add(GstControlSourceAPI.GstControlSourceStruct.class); - structs.add(GstControlSourceAPI.GstControlSourceClass.class); +// structs.add(GstControlSourceAPI.TimedValue.class); +// structs.add(GstControlSourceAPI.GstControlSourceStruct.class); +// structs.add(GstControlSourceAPI.GstControlSourceClass.class); structs.add(GstElementAPI.GstElementStruct.class); structs.add(GstElementAPI.GstElementClass.class); structs.add(GstEventAPI.EventStruct.class); - - structs.add(GstInterpolationControlSourceAPI.GstInterpolationControlSourceStruct.class); - structs.add(GstInterpolationControlSourceAPI.GstInterpolationControlSourceClass.class); - - structs.add(GstLFOControlSourceAPI.GstLFOControlSourceStruct.class); - structs.add(GstLFOControlSourceAPI.GstLFOControlSourceClass.class); + structs.add(GstVideoAPI.GstVideoTimeCodeMetaStruct.class); + structs.add(GstVideoAPI.GstVideoTimeCodeStruct.class); + structs.add(GstMetaAPI.GstMetaInfoStruct.class); + structs.add(GstMetaAPI.GstMetaStruct.class); + +// structs.add(GstInterpolationControlSourceAPI.GstInterpolationControlSourceStruct.class); +// structs.add(GstInterpolationControlSourceAPI.GstInterpolationControlSourceClass.class); +// +// structs.add(GstLFOControlSourceAPI.GstLFOControlSourceStruct.class); +// structs.add(GstLFOControlSourceAPI.GstLFOControlSourceClass.class); structs.add(GstMessageAPI.MessageStruct.class); diff --git a/test/org/freedesktop/gstreamer/lowlevel/ReferenceManagerTest.java b/test/org/freedesktop/gstreamer/lowlevel/ReferenceManagerTest.java index 2403c8e4..cf3ff2f4 100644 --- a/test/org/freedesktop/gstreamer/lowlevel/ReferenceManagerTest.java +++ b/test/org/freedesktop/gstreamer/lowlevel/ReferenceManagerTest.java @@ -70,7 +70,7 @@ public boolean waitGC(Reference ref) throws InterruptedExcepti @Test public void testReference() throws Exception { Object ref = new Object(); - Caps target = new Caps("video/x-raw-rgb"); + Caps target = new Caps("video/x-raw"); ReferenceManager.addKeepAliveReference(ref, target); WeakReference targetRef = new WeakReference(target); target = null; @@ -81,7 +81,7 @@ public boolean waitGC(Reference ref) throws InterruptedExcepti @Test public void testMultipleReferences() throws Exception { Object ref1 = new Object(); Object ref2 = new Object(); - Caps target = new Caps("video/x-raw-rgb"); + Caps target = new Caps("video/x-raw"); ReferenceManager.addKeepAliveReference(ref1, target); ReferenceManager.addKeepAliveReference(ref2, target); WeakReference targetRef = new WeakReference(target); diff --git a/test/org/freedesktop/gstreamer/util/TestAssumptions.java b/test/org/freedesktop/gstreamer/util/TestAssumptions.java new file mode 100644 index 00000000..20c1fffa --- /dev/null +++ b/test/org/freedesktop/gstreamer/util/TestAssumptions.java @@ -0,0 +1,33 @@ +package org.freedesktop.gstreamer.util; + +import org.freedesktop.gstreamer.ElementFactory; +import org.freedesktop.gstreamer.Gst; +import org.junit.Assume; + +/** + * These Assumptions skip Tests if the environmental Properties do not meet the Assumption. + */ +public class TestAssumptions { + /** + * Assume a GStreamer-Installation of at least the specified Version, ignore the Test if not met. + * + * @param major Required Major Version + * @param minor Required Minor Version + */ + public static void requireGstVersion(int major, int minor) { + Assume.assumeTrue(Gst.testVersion(major, minor)); + } + + /** + * Assume a GStreamer installation has the required element. + * + * @param elementType element type + */ + public static void requireElement(String elementType) { + ElementFactory factory = null; + try { + factory = ElementFactory.find(elementType); + } catch (Exception ex) {} + Assume.assumeNotNull(factory); + } +} diff --git a/test/org/freedesktop/gstreamer/video/VideoCropMetaTest.java b/test/org/freedesktop/gstreamer/video/VideoCropMetaTest.java new file mode 100644 index 00000000..134f0639 --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoCropMetaTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + * + */ +package org.freedesktop.gstreamer.video; + +import org.freedesktop.gstreamer.BufferProbeTester; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstMetaPtr; +import org.freedesktop.gstreamer.util.TestAssumptions; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + + + +public class VideoCropMetaTest { + + @BeforeClass + public static void beforeClass() { + Gst.init(Gst.getVersion()); + } + + @AfterClass + public static void afterClass() { + Gst.deinit(); + } + + @Test + public void testIterateWithCrop() { + TestAssumptions.requireGstVersion(1, 14); + TestAssumptions.requireElement("fakevideosink"); + BufferProbeTester.test(buffer -> { + buffer.iterateMeta().forEachRemaining(meta -> { + GstMetaPtr ptr = Natives.getPointer(meta).as(GstMetaPtr.class, GstMetaPtr::new); + System.out.println(ptr.getGType().getTypeName()); + System.out.println(ptr.getAPIGType().getTypeName()); + }); + + }, "videotestsrc ! videocrop top=10 left=10 bottom=50 right=50 ! fakevideosink name=sink"); + } + +} diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeConfigTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeConfigTest.java new file mode 100644 index 00000000..8a9d9f9a --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeConfigTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + * + */ +package org.freedesktop.gstreamer.video; + +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI; + +public class VideoTimeCodeConfigTest { + private GstVideoAPI.GstVideoTimeCodeConfigStruct origStruct; + private VideoTimeCodeConfig codeConfig; + + + @Before + public void setUp() { + origStruct = new GstVideoAPI.GstVideoTimeCodeConfigStruct(); + origStruct.fps_d = 25; + origStruct.fps_n = 1; + origStruct.flags = VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME.intValue(); + origStruct.write(); + + codeConfig = new VideoTimeCodeConfig(origStruct); + } + + @Test + public void testGetTimeCodeFlags() { + assertEquals(origStruct.flags, NativeFlags.toInt(codeConfig.getFlags())); + } + + @Test + public void testGetFramerateNumerator() { + assertEquals(origStruct.fps_n, codeConfig.getNumerator()); + } + + @Test + public void testGetFramerateDenominator() { + assertEquals(origStruct.fps_d, codeConfig.getDenominator()); + } +} \ No newline at end of file diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeFlagsTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeFlagsTest.java new file mode 100644 index 00000000..4f3752ad --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeFlagsTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + * + */ +package org.freedesktop.gstreamer.video; + +import java.util.Arrays; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.junit.Assert.assertEquals; + + +@RunWith(Parameterized.class) +public class VideoTimeCodeFlagsTest { + + private final VideoTimeCodeFlags flags; + private final int intValue; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[][]{ +// {VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_NONE, 0}, + {VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 1}, + {VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_INTERLACED, 2} + }); + } + + public VideoTimeCodeFlagsTest(VideoTimeCodeFlags flags, int intValue) { + this.flags = flags; + this.intValue = intValue; + } + + @Test + public void testIntValue() { + assertEquals(intValue,flags.intValue()); + } +} \ No newline at end of file diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeMetaTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeMetaTest.java new file mode 100644 index 00000000..17f9f75b --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeMetaTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2025 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + * + */ +package org.freedesktop.gstreamer.video; + +import org.freedesktop.gstreamer.Buffer; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.SampleTester; +import org.freedesktop.gstreamer.util.TestAssumptions; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.freedesktop.gstreamer.video.VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class VideoTimeCodeMetaTest { + + @BeforeClass + public static void beforeClass() { + Gst.init(Gst.getVersion()); + } + + @AfterClass + public static void afterClass() { + Gst.deinit(); + } + + @Test + public void testVideoWithoutTimeCodeMeta() { + // method containsMetadata is available since 1.14 + TestAssumptions.requireGstVersion(1, 14); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + assertFalse("Default video not contains timecode metadata", buffer.hasMeta(VideoTimeCodeMeta.API)); + }, "videotestsrc do-timestamp=true ! appsink name=myappsink"); + } + + @Test + public void testVideoTimeCodeMetaPal() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1, 10); + TestAssumptions.requireElement("timecodestamper"); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + try (VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API)) { + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00:00 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(0, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // PAL standard 25/1 + assertEquals(25, timeCodeConfig.getNumerator()); + assertEquals(1, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().isEmpty()); + } + }, "videotestsrc ! video/x-raw,framerate=25/1 ! timecodestamper ! videoconvert ! appsink name=myappsink"); + } + + @Test + public void testVideoTimeCodeNTSCDrop() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1, 10); + TestAssumptions.requireElement("timecodestamper"); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + try (VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API)) { + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00;00 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(0, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // NTSC drop standard 30000/1001 + assertEquals(30000, timeCodeConfig.getNumerator()); + assertEquals(1001, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().contains(GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME)); + } + }, "videotestsrc ! video/x-raw,framerate=30000/1001 ! timecodestamper drop-frame=true ! videoconvert ! appsink name=myappsink"); + } + + /** + * Handle last frame of first minute + */ + @Test + public void testVideoTimeCodeNTSCDropFrame() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1, 10); + TestAssumptions.requireElement("timecodestamper"); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + try (VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API)) { + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00;29 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(29, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // NTSC drop standard 30000/1001 + assertEquals(30000, timeCodeConfig.getNumerator()); + assertEquals(1001, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().contains(GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME)); + } + }, "videotestsrc ! video/x-raw,framerate=30000/1001 ! videoconvert ! timecodestamper drop-frame=true ! videoconvert ! appsink name=myappsink", 29); + } + + @Test + public void testVideoTimeCodeNTSCNonDrop() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1, 10); + TestAssumptions.requireElement("timecodestamper"); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + try (VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API)) { + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00:00 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(0, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // NTSC drop standard 30/1 + assertEquals(30, timeCodeConfig.getNumerator()); + assertEquals(1, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().isEmpty()); + } + }, "videotestsrc ! video/x-raw,framerate=30/1 ! timecodestamper ! videoconvert ! appsink name=myappsink"); + } +} diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeTest.java new file mode 100644 index 00000000..bca8fa5e --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * This file is part of gstreamer-java. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + * + */ +package org.freedesktop.gstreamer.video; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI; + + +public class VideoTimeCodeTest { + + + private GstVideoAPI.GstVideoTimeCodeStruct timeCodeStruct; + private GstVideoAPI.GstVideoTimeCodeConfigStruct.ByValue configStruct; + + private VideoTimeCode timeCode; + + @Before + public void setUp() { + timeCodeStruct = new GstVideoAPI.GstVideoTimeCodeStruct(); + configStruct = new GstVideoAPI.GstVideoTimeCodeConfigStruct.ByValue(); + + // 01:02:03:04 + timeCodeStruct.hours = 1; + timeCodeStruct.minutes = 2; + timeCodeStruct.seconds = 3; + timeCodeStruct.frames = 4; + + timeCodeStruct.field_count = 55; + + // config + timeCodeStruct.config = configStruct; + + timeCodeStruct.write(); + timeCode = new VideoTimeCode(timeCodeStruct); + + + } + + @Test + public void testGetTCConfig() { + assertNotNull(timeCode.getConfig()); + } + + @Test + public void testGetHours() { + assertEquals(1,timeCode.getHours()); + } + + @Test + public void testGetMinutes() { + assertEquals(2,timeCode.getMinutes()); + } + + @Test + public void testGetSeconds() { + assertEquals(3,timeCode.getSeconds()); + } + + @Test + public void testGetFrames() { + assertEquals(4,timeCode.getFrames()); + } +}