diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index a9f167c5..99ca8e90 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -34,6 +34,17 @@ runs: key: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-${{ hashFiles('webrtc-jni/pom.xml') }} restore-keys: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}- + - name: Derive the FFmpeg version from the pom + run: echo "FFMPEG_VERSION=$(sed -n 's|.*\([^<]*\).*|\1|p' webrtc-java-media/pom.xml)" >> "$GITHUB_ENV" + shell: bash + + - name: Set up FFmpeg cache + uses: actions/cache@v4 + with: + path: ~/ffmpeg + key: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}-${{ hashFiles('webrtc-java-media/pom.xml') }} + restore-keys: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}- + - name: Set up Maven cache uses: actions/cache@v4 with: diff --git a/.github/actions/prepare-linux/action.yml b/.github/actions/prepare-linux/action.yml index 6fbf9f30..23f07c40 100644 --- a/.github/actions/prepare-linux/action.yml +++ b/.github/actions/prepare-linux/action.yml @@ -31,7 +31,8 @@ runs: - name: Install required packages run: | sudo apt update - sudo apt install -y binutils cmake git locales lsb-release ninja-build pipewire pipewire-pulse pkg-config python3 python3-setuptools rsync unzip wget xz-utils + # nasm is what FFmpeg assembles its x86 code with. + sudo apt install -y binutils cmake git locales lsb-release nasm ninja-build pipewire pipewire-pulse pkg-config python3 python3-setuptools rsync unzip wget xz-utils # Chromium Clang to be used with the clang toolchain file #curl -s https://raw.githubusercontent.com/chromium/chromium/main/tools/clang/scripts/update.py | python3 - --output-dir=/opt/clang diff --git a/.github/actions/prepare-macos/action.yml b/.github/actions/prepare-macos/action.yml index 818e04ea..5ef69300 100644 --- a/.github/actions/prepare-macos/action.yml +++ b/.github/actions/prepare-macos/action.yml @@ -9,6 +9,9 @@ runs: run: | # Required on macos-14 brew install python-setuptools + # nasm is what FFmpeg assembles its x86 code with, which the Intel + # build needs even though the runner itself is Apple Silicon. + brew install nasm shell: bash - name: Select Xcode version diff --git a/.github/actions/prepare-windows/action.yml b/.github/actions/prepare-windows/action.yml index ff9b023a..dd0e96a5 100644 --- a/.github/actions/prepare-windows/action.yml +++ b/.github/actions/prepare-windows/action.yml @@ -38,3 +38,16 @@ runs: run: | choco install ninja shell: powershell + + - name: Install MSYS2 and the FFmpeg build tools + run: | + # FFmpeg's configure and makefiles are shell scripts, so building it + # needs a POSIX shell even with MSVC, and that shell has to carry make + # and nasm. Git for Windows' bash carries neither. + if (-not (Test-Path "C:\msys64\usr\bin\bash.exe")) { + choco install msys2 --no-progress -y + } + C:\msys64\usr\bin\bash.exe -lc "pacman -Sy --noconfirm && pacman -S --needed --noconfirm make nasm diffutils pkgconf" + # Where the media module's CMake looks for it. + "MSYS2_ROOT=C:\msys64" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + shell: powershell diff --git a/.github/actions/release/action.yml b/.github/actions/release/action.yml index fa20ed67..0c6fe4af 100644 --- a/.github/actions/release/action.yml +++ b/.github/actions/release/action.yml @@ -25,6 +25,17 @@ runs: key: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-${{ hashFiles('webrtc-jni/pom.xml') }} restore-keys: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}- + - name: Derive the FFmpeg version from the pom + run: echo "FFMPEG_VERSION=$(sed -n 's|.*\([^<]*\).*|\1|p' webrtc-java-media/pom.xml)" >> "$GITHUB_ENV" + shell: bash + + - name: Set up FFmpeg cache + uses: actions/cache@v4 + with: + path: ~/ffmpeg + key: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}-${{ hashFiles('webrtc-java-media/pom.xml') }} + restore-keys: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}- + - name: Set up Maven cache uses: actions/cache@v4 with: diff --git a/.github/actions/test-natives/action.yml b/.github/actions/test-natives/action.yml index 3866de00..02459615 100644 --- a/.github/actions/test-natives/action.yml +++ b/.github/actions/test-natives/action.yml @@ -76,13 +76,50 @@ runs: - name: Install the natives into the local repository run: | VERSION=$(sed -n '0,//s|.*\([^<]*\).*|\1|p' pom.xml) - JAR=$(ls natives/webrtc-java-*.jar | head -n 1) + JAR=$(find natives -path '*webrtc-jni*' -name 'webrtc-java-*.jar' | head -n 1) echo "Installing $JAR as webrtc-java:$VERSION:${{ inputs.classifier }}" mvn -B -q install:install-file -Dfile="$JAR" -DgroupId=dev.onvoid.webrtc \ -DartifactId=webrtc-java -Dversion="$VERSION" -Dpackaging=jar \ -Dclassifier=${{ inputs.classifier }} shell: bash + # Both native libraries go into the directory the media module's tests + # already read from, which is where its own build collects them. That is + # what puts them on the class path, and it is also what makes the reactor + # below work: webrtc-java's natives are a classifier artifact of a module + # that is in that reactor, and Maven answers a dependency on such a thing + # with the module's output directory, which holds no native library at + # all. Coming from a directory instead, they are simply found. + - name: Unpack the natives where the tests look for them + run: | + MEDIA_JAR=$(find natives -name 'webrtc-java-media-*-${{ inputs.classifier }}.jar' | head -n 1) + WEBRTC_JAR=$(find natives -path '*webrtc-jni*' -name 'webrtc-java-*.jar' | head -n 1) + + if [ -z "$MEDIA_JAR" ]; then + echo "No webrtc-java-media natives for ${{ inputs.classifier }} in the artifact" >&2 + exit 1 + fi + + MEDIA_JAR="$PWD/$MEDIA_JAR" + WEBRTC_JAR="$PWD/$WEBRTC_JAR" + + mkdir -p webrtc-java-media/target/natives + cd webrtc-java-media/target/natives + # jar rather than unzip: the JDK is set up above, and unzip is not on + # every runner image. + "$JAVA_HOME/bin/jar" xf "$MEDIA_JAR" + "$JAVA_HOME/bin/jar" xf "$WEBRTC_JAR" + rm -rf META-INF + ls -l + shell: bash + + # One reactor for both, so that webrtc-java-media resolves webrtc-java + # from it. Testing rather than installing: webrtc attaches the host + # natives from webrtc-jni/target during its package phase, and on a runner + # that only downloaded them there is no such directory. + # + # The native build is off: these natives were cross compiled by another + # job, and this runner is here to run them, not to build them again. - name: Test - run: mvn -B -pl webrtc test -P${{ inputs.profile }} + run: mvn -B -pl webrtc,webrtc-java-media test -DskipNativeBuild -P${{ inputs.profile }} shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c63b2fe..4de02cd7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + # The media module builds FFmpeg from its third-party submodule. + submodules: true - id: prepare name: Prepare build @@ -53,7 +56,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: natives-${{ matrix.platform.name }} - path: webrtc-jni/target/webrtc-java-*.jar + path: | + webrtc-jni/target/webrtc-java-*.jar + webrtc-java-media/target/webrtc-java-media-*.jar if-no-files-found: error build-linux: @@ -72,6 +77,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + # The media module builds FFmpeg from its third-party submodule. + submodules: true - id: prepare name: Prepare build @@ -91,7 +99,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: natives-${{ matrix.platform.name }} - path: webrtc-jni/target/webrtc-java-*.jar + path: | + webrtc-jni/target/webrtc-java-*.jar + webrtc-java-media/target/webrtc-java-media-*.jar if-no-files-found: error build-macos: @@ -108,6 +118,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + # The media module builds FFmpeg from its third-party submodule. + submodules: true - id: prepare-build name: Prepare build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0684e52f..33085969 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -148,6 +148,8 @@ jobs: uses: actions/checkout@v6 with: fetch-tags: true + # The media module builds FFmpeg from its third-party submodule. + submodules: true - name: Get tag from current branch id: tag-selector @@ -189,6 +191,8 @@ jobs: uses: actions/checkout@v6 with: fetch-tags: true + # The media module builds FFmpeg from its third-party submodule. + submodules: true - name: Get tag from current branch id: tag-selector @@ -227,6 +231,8 @@ jobs: uses: actions/checkout@v6 with: fetch-tags: true + # The media module builds FFmpeg from its third-party submodule. + submodules: true - name: Get tag from current branch id: tag-selector diff --git a/NOTICE b/NOTICE index d8f064b2..b726bb05 100644 --- a/NOTICE +++ b/NOTICE @@ -6,6 +6,19 @@ EXTERNAL COMPONENTS This product bundles WebRTC, which is available under a "3-clause BSD" license. For details, see https://webrtc.github.io/webrtc-org/license. +WebRTC is linked statically into the webrtc-java platform jars, together with +the third-party code it builds in, among them libvpx, libyuv, OpenH264, libaom, +dav1d, Opus, BoringSSL, Abseil and, for H.264 decoding, Chromium's FFmpeg under +the GNU Lesser General Public License version 2.1 or later. Each platform jar +carries their licenses and WebRTC's patent grant under META-INF/licenses/webrtc. + +The webrtc-java-media platform jars bundle FFmpeg (https://ffmpeg.org), which is +available under the GNU Lesser General Public License version 2.1 or later. It is +built from an unmodified FFmpeg release, without --enable-gpl and without +--enable-nonfree, and linked dynamically, so it can be replaced. Each platform +jar carries the license text and a notice naming the FFmpeg version and where +its source is, under META-INF/licenses/ffmpeg. + DOCUMENTATION WebRTC 1.0: Real-time Communication Between Browsers diff --git a/README.md b/README.md index 6a93e40e..8a3a0e1e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ The library provides a comprehensive set of Java classes that map to the WebRTC - **Cross-platform support** - Works on Windows, macOS, and Linux (x64, ARM, ARM64) - **Media capabilities** - Audio and video capture from cameras and microphones - **Desktop capture** - Screen and application window sharing +- **Media file playback** - Send video and audio files over a peer connection in place of a camera and microphone, with the optional FFmpeg-based `webrtc-java-media` module - **Data channels** - Bidirectional peer-to-peer data exchange - **Statistics API** - Detailed metrics for monitoring connection quality - **Simple integration** - Available as a Maven dependency @@ -28,6 +29,7 @@ For more detailed information, check out the documentation: - [Quickstart](https://jrtc.dev/guide/get-started) - Get up and running quickly with webrtc-java - [Guides](https://jrtc.dev/guide/) - Comprehensive documentation on using the library - [Examples](https://jrtc.dev/guide/examples) - Sample code demonstrating various features +- [Media Files](https://jrtc.dev/guide/media/media-files) - Sending video and audio files with the media module - [Build Notes](https://jrtc.dev/guide/build) - Instructions for building the library from source ## License diff --git a/docs/guide/examples.md b/docs/guide/examples.md index ae9191cb..a6636a84 100644 --- a/docs/guide/examples.md +++ b/docs/guide/examples.md @@ -54,22 +54,44 @@ This example is particularly useful for applications that need to implement scre ## Media File -The [`MediaFileExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/media/java/dev/onvoid/webrtc/examples/MediaFileExample.java) demonstrates how to send a media file over a peer connection, in place of a camera and a microphone. See the [Media Files](/guide/media/media-files) guide for the API it uses. +The [`MediaFileExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/MediaFileExample.java) demonstrates how to send a media file over a peer connection, in place of a camera and a microphone. See the [Media Files](/guide/media/media-files) guide for the API it uses. **Key features demonstrated:** -- Opening a media file with a `MediaFileSource` +- Opening a media file, or a live RTSP stream, with a `MediaFileSource` - Reading what the source contains from its `MediaInfo` - Creating audio and video tracks from the media sources it feeds - Adding those tracks to a peer connection - Following playback through a `MediaPlayerListener` -This example is useful for applications that stream pre-recorded media, or that need a dependable stand-in for a camera in testing. +This example is useful for applications that stream pre-recorded media, relay an IP camera into WebRTC, or need a dependable stand-in for a camera in testing. -::: warning -This example needs the `webrtc-java-media` module, which is opt-in while it is being brought up on every platform, so it is built and run with the `with-media-extension` profile: +::: info +This example needs the `webrtc-java-media` module, which builds FFmpeg from the `third-party/ffmpeg` submodule. Make sure the submodule is checked out before building, as the [Media Files](/guide/media/media-files) guide describes. + +```bash +mvn exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.MediaFileExample" -D"exec.args=movie.mp4" +mvn exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.MediaFileExample" -D"exec.args=rtsp://camera.local/stream1" +``` +::: + +## Media File Player + +The [`MediaFilePlayerExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/MediaFilePlayerExample.java) is a Swing application that sends a media file, or a live RTSP stream, from one peer connection to another in the same process, then shows the received video and plays the received audio in sync. Start and Stop buttons set up and tear down the whole session. + +**Key features demonstrated:** +- Connecting two peer connections in one application, without a signaling server +- Using one factory with a dummy audio layer to send the file, and another with the platform audio layer to play what is received +- Keeping audio and video in sync by sending both tracks in the same media stream +- Raising the video sender's `maxBitrate` above WebRTC's default, so a high-resolution file is not held to a fraction of its size +- Rendering received video frames in a Swing component with `VideoBufferConverter` +- Showing the source's format, the playback position, and live receive metrics (codec, resolution, frame rate, bitrate, packet loss, jitter, audio level) read from `getStats()` +- Releasing senders, peer connections, tracks and the source in order on Stop + +::: info +Like the [Media File](#media-file) example, this one needs the `webrtc-java-media` module. The source argument is optional; a file can also be chosen from the window, or a stream URL typed into it. A stream URL's credentials are never shown, and the Loop box is disabled while a live stream plays, since it has no length to start over from. ```bash -mvn -Pwith-media-extension exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.MediaFileExample" -D"exec.args=movie.mp4" +mvn exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.MediaFilePlayerExample" -D"exec.args=movie.mp4" ``` ::: diff --git a/docs/guide/index.md b/docs/guide/index.md index 6e1b913e..b851497e 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -7,6 +7,7 @@ This section provides detailed guides for various features of the webrtc-java li - [Media Devices](/guide/media/media-devices) - Working with audio and video devices - [Bitrate and Framerate Constraints](/guide/media/constraints) - Controlling media quality - [Send-only and Receive-only](/guide/media/directionality) - Configure transceiver directions (send-only, receive-only or inactive) +- [Media Files](/guide/media/media-files) - Sending video and audio files instead of a camera and microphone ## Audio diff --git a/docs/guide/media/media-files.md b/docs/guide/media/media-files.md index 56874a1e..c0fc63b8 100644 --- a/docs/guide/media/media-files.md +++ b/docs/guide/media/media-files.md @@ -7,22 +7,19 @@ This guide explains how to send a media file over a peer connection instead of a - Reading what a source contains with `MediaReader` and `MediaInfo` - Controlling playback and following it with a listener - Feeding your own media sources with `MediaPlayer` +- Playing a live stream from an IP camera or media server over RTSP Sending a file is a common need: a test pattern instead of a webcam, a pre-recorded briefing, a video that has to reach several participants. Without help, an application has to bring its own decoder and push I420 frames into a `CustomVideoSource` itself. The media module removes that work by decoding with [FFmpeg](https://ffmpeg.org) inside the library. Decoding happens entirely in native code. Frames never travel through Java: the module hands decoded pictures straight to the native side of a `CustomVideoSource`, and 10 ms chunks to a `CustomAudioSource`. They are paced in real time and carry the presentation times of the file, so what a receiver gets keeps the timing of the media rather than the timing of a Java thread. -::: warning Opt-in while it is being brought up -The media module is not part of the default build yet, and its native library has so far been built for `windows-x86_64` only. Build it with the `with-media-extension` profile, as described below. -::: - ## Adding the Module -The module builds FFmpeg from a submodule pinned to a release tag, so the submodule has to be present: +The module is part of the normal build, and it builds FFmpeg from a submodule pinned to a release tag, so the submodule has to be present: ```shell git submodule update --init --depth 1 webrtc-java-media/third-party/ffmpeg -mvn install -Pwith-media-extension +mvn install ``` Building FFmpeg needs `make` and `nasm`. On Windows they come from MSYS2: @@ -34,7 +31,7 @@ C:\msys64\usr\bin\bash -lc "pacman -S --needed make nasm diffutils pkgconf" Maven still runs from an ordinary shell; the build enters MSYS2 and the Visual Studio environment on its own. The first build compiles FFmpeg, which takes a while; later builds reuse the install directory. -Once installed, depend on it alongside `webrtc-java`: +Once installed, depend on it alongside `webrtc-java`. It takes two entries: one for the Java API, and one for the natives of the platform you are running on. ```xml @@ -42,9 +39,19 @@ Once installed, depend on it alongside `webrtc-java`: webrtc-java-media 0.19.0-SNAPSHOT + + dev.onvoid.webrtc + webrtc-java-media + 0.19.0-SNAPSHOT + windows-x86_64 + ``` -The classifier jar carries the module's native library together with the FFmpeg shared libraries it uses. Applications that do not use this module never download FFmpeg. +The classifier jar carries the module's native library together with the FFmpeg shared libraries it uses, so applications that do not use this module never download FFmpeg. Replace the classifier with the platform you are building for: `windows-x86_64`, `windows-aarch64`, `linux-x86_64`, `linux-aarch64`, `linux-aarch32`, `macos-x86_64` or `macos-aarch64`. + +::: info +Unlike `webrtc-java`, which brings its natives along by itself, this module cannot: the natives are built by the module rather than by a separate one, so a dependency on them would have nothing to resolve against on a first build. Asking for them explicitly is the price of that. +::: ## Sending a File @@ -52,7 +59,7 @@ The classifier jar carries the module's native library together with the FFmpeg ```java // Import required classes -import dev.onvoid.webrtc.media.ffmpeg.MediaFileSource; +import dev.onvoid.webrtc.media.player.MediaFileSource; import dev.onvoid.webrtc.media.audio.AudioTrack; import dev.onvoid.webrtc.media.video.VideoTrack; import java.nio.file.Path; @@ -95,8 +102,8 @@ PeerConnectionFactory factory = new PeerConnectionFactory(audioModule); ```java // Import required classes -import dev.onvoid.webrtc.media.ffmpeg.MediaInfo; -import dev.onvoid.webrtc.media.ffmpeg.MediaReader; +import dev.onvoid.webrtc.media.player.MediaInfo; +import dev.onvoid.webrtc.media.player.MediaReader; try (MediaReader reader = new MediaReader(Path.of("movie.mp4"))) { MediaInfo info = reader.getInfo(); @@ -143,8 +150,8 @@ A seek lands on the keyframe at or before the position asked for, which is how f ```java // Import required classes -import dev.onvoid.webrtc.media.ffmpeg.MediaPlayerListener; -import dev.onvoid.webrtc.media.ffmpeg.MediaPlayerState; +import dev.onvoid.webrtc.media.player.MediaPlayerListener; +import dev.onvoid.webrtc.media.player.MediaPlayerState; source.setListener(new MediaPlayerListener() { @@ -167,6 +174,8 @@ source.setListener(new MediaPlayerListener() { ::: warning Every call arrives on the player's own thread, and that thread is the one decoding the media. A listener must return promptly, and must not wait on the player. + +Closing the player, or the `MediaFileSource`, from a listener is fine: it takes effect at once, and the native player is released on another thread once the listener has returned. ::: ## Feeding Your Own Media Sources @@ -175,8 +184,8 @@ Every call arrives on the player's own thread, and that thread is the one decodi ```java // Import required classes -import dev.onvoid.webrtc.media.ffmpeg.MediaPlayer; -import dev.onvoid.webrtc.media.ffmpeg.MediaReader; +import dev.onvoid.webrtc.media.player.MediaPlayer; +import dev.onvoid.webrtc.media.player.MediaReader; import dev.onvoid.webrtc.media.video.CustomVideoSource; CustomVideoSource videoSource = new CustomVideoSource(); @@ -191,8 +200,33 @@ player.play(); ::: info The player takes over the reader it is given. That reader must not be used or closed afterwards; closing the player releases it. + +It also keeps the native side of the media sources it feeds alive until it is closed, so disposing of a source or its track while the player runs is safe. ::: +## Playing a Live Stream + +A source can also be a live stream behind an `rtsp://` URL, which is what IP cameras, video recorders and most media servers offer. Everything above works the same way; only the URL differs: + +```java +MediaFileSource source = new MediaFileSource("rtsp://camera.local:554/stream1"); +``` + +Credentials go into the URL, as `rtsp://user:password@camera.local/stream1`. FFmpeg first asks the server for RTP over UDP, and falls back to RTP interleaved on the RTSP connection over TCP if the server refuses UDP or no UDP packets arrive, which is what gets a stream through most firewalls. + +A network source can stall or disappear, so every operation that waits on a source has a time limit: opening it, and then each read during playback. It is 10 seconds unless you pass another: + +```java +// Import required classes +import java.time.Duration; + +MediaFileSource source = new MediaFileSource("rtsp://camera.local/stream1", Duration.ofSeconds(5)); +``` + +An unreachable server makes the constructor throw an `IOException` once that time is up. A stream that stops sending during playback is reported to the listener's `onError`, and the player is left `PAUSED`. Closing a player never waits for the time limit: it breaks off whatever the player is waiting for at once. + +A live stream has no end and no length: its `MediaInfo` reports a duration of 0, looping does not apply, and seeking is reported as an error. Pausing stops reading from the server, but the server keeps sending; resuming carries on from where playback paused, which leaves it behind real time by as long as it was paused. To get back to live, close the source and open it again. + ## Closing Closing a `MediaFileSource` stops playback and releases the player along with both media sources. Release the senders a peer connection handed out, and the tracks, before that: @@ -212,21 +246,25 @@ The FFmpeg build is deliberately small, and carries only what this module plays: | | | | --- | --- | | **Containers** | MP4 and MOV, Matroska and WebM, AVI, MPEG-TS, FLV, WAV, MP3, Ogg, FLAC, AAC | -| **Video** | H.264, H.265/HEVC, VP8, VP9, MPEG-4, MJPEG | -| **Audio** | AAC, MP3, Opus, Vorbis, FLAC, PCM | +| **Video** | H.264, H.265/HEVC, VP8, VP9, MPEG-4 (including Xvid and DivX), Microsoft MPEG-4 v1 to v3, MJPEG | +| **Audio** | AAC, MP3, MP2, AC-3, Opus, Vorbis, FLAC, PCM, MS and IMA ADPCM | Audio of any rate or layout is resampled to what WebRTC takes, which is 48 kHz 16-bit PCM in mono or stereo. Video that decodes to I420 — almost all 8-bit H.264, VP8, VP9 and MPEG-4 — reaches the encoder without being copied; anything else is converted first. -Only local files play today. FFmpeg demuxes network sources just as well, so the same code will cover http, rtsp and rtmp once those protocols are turned on in the build. +Sources can be local files, or live streams over RTSP (`rtsp://`), with RTP over UDP or TCP. Protocols that need TLS, such as `rtsps://` and `https://`, are not part of the build, and neither are HLS, DASH and RTMP. A source cannot reach any other protocol either, even one FFmpeg uses internally, so a URL passed on from a user cannot make the module fetch something else. ## Licensing The module uses FFmpeg under the LGPL version 2.1 or later. It is configured without `--enable-gpl` and without `--enable-nonfree`, and FFmpeg is linked dynamically and shipped as separate files inside the platform jar, so its libraries may be replaced with your own build, as the LGPL requires. The wrapper code is licensed under the Apache License 2.0 like the rest of webrtc-java. +Each platform jar carries the LGPL text and a notice under `META-INF/licenses/ffmpeg`, naming the FFmpeg release the libraries are built from, unmodified, and where its source is. If you redistribute your application with these jars, keep those files with them. + +Some of the formats FFmpeg decodes, such as H.264, H.265/HEVC and AAC, may be covered by patents in some countries. Whether your use of them needs a patent license is for you to determine. + ## Complete Example See `MediaFileExample` in the `webrtc-examples` module, which opens a file, reports what it contains, creates tracks, adds them to a peer connection and follows playback to the end. ```shell -mvn -Pwith-media-extension -pl webrtc-examples compile +mvn -pl webrtc-examples compile ``` diff --git a/docs/guide/video/custom-video-source.md b/docs/guide/video/custom-video-source.md index 9753ac13..0b3da97e 100644 --- a/docs/guide/video/custom-video-source.md +++ b/docs/guide/video/custom-video-source.md @@ -78,6 +78,7 @@ When pushing video frames, you need to consider the following: ### Resolution - Common resolutions: 320x240, 640x480, 1280x720, 1920x1080 - Higher resolutions require more bandwidth and processing power +- Push frames at their full size: when the bitrate cannot carry them, the source scales them down (and, if asked, drops some) before they reach the encoder, the way a camera source does, and scales back up once the bandwidth allows. Local sinks on the track see the adapted frames too. ### Frame Rate - Common frame rates: 15, 24, 30, 60 fps diff --git a/docs/index.md b/docs/index.md index 443f50ed..3e3d6dc6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,6 +26,8 @@ features: details: Thin JNI layer with minimal overhead, delivering near-native performance with minimal context switching between Java and native code. - title: Audio and video streaming details: Audio and video capture from cameras and microphones devices, with support for custom media sources for flexible streaming solutions. + - title: Media File Playback + details: Send video and audio files over a peer connection. The optional media module decodes with FFmpeg in native code and paces playback in real time, keeping audio and video in sync. - title: Screen Sharing details: Share application windows or the full desktop with minimal setup; integrate screen capture streams like any other media stream. - title: Data Channels diff --git a/pom.xml b/pom.xml index b8e6770a..90d3a69d 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,7 @@ webrtc-jni webrtc + webrtc-java-media webrtc-examples @@ -354,19 +355,6 @@ webrtc.macos.aarch64 - - - with-media-extension - - webrtc-java-media - - diff --git a/webrtc-examples/pom.xml b/webrtc-examples/pom.xml index cf9437d3..5f0118f9 100644 --- a/webrtc-examples/pom.xml +++ b/webrtc-examples/pom.xml @@ -21,6 +21,27 @@ + + + org.codehaus.mojo + exec-maven-plugin + 3.5.0 + + + ${project.basedir}/../webrtc-java-media/target/natives + + + + org.apache.maven.plugins maven-compiler-plugin @@ -65,6 +86,12 @@ ${project.version} + + ${project.groupId} + webrtc-java-media + ${project.version} + + org.eclipse.jetty jetty-server @@ -95,74 +122,4 @@ - - - - with-media-extension - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - compile-media-example - compile - - compile - - - 17 - - ${project.basedir}/src/media/java - - ${project.build.directory}/media-classes - - - - - - - - org.codehaus.mojo - exec-maven-plugin - 3.5.0 - - - ${project.build.directory}/media-classes - - - - - - - - - ${project.groupId} - webrtc-java-media - ${project.version} - - - - \ No newline at end of file diff --git a/webrtc-examples/src/media/java/dev/onvoid/webrtc/examples/MediaFileExample.java b/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/MediaFileExample.java similarity index 89% rename from webrtc-examples/src/media/java/dev/onvoid/webrtc/examples/MediaFileExample.java rename to webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/MediaFileExample.java index cdbc3b05..ccc6a71d 100644 --- a/webrtc-examples/src/media/java/dev/onvoid/webrtc/examples/MediaFileExample.java +++ b/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/MediaFileExample.java @@ -32,10 +32,10 @@ import dev.onvoid.webrtc.media.audio.AudioDeviceModule; import dev.onvoid.webrtc.media.audio.AudioLayer; import dev.onvoid.webrtc.media.audio.AudioTrack; -import dev.onvoid.webrtc.media.ffmpeg.MediaFileSource; -import dev.onvoid.webrtc.media.ffmpeg.MediaInfo; -import dev.onvoid.webrtc.media.ffmpeg.MediaPlayerListener; -import dev.onvoid.webrtc.media.ffmpeg.MediaPlayerState; +import dev.onvoid.webrtc.media.player.MediaFileSource; +import dev.onvoid.webrtc.media.player.MediaInfo; +import dev.onvoid.webrtc.media.player.MediaPlayerListener; +import dev.onvoid.webrtc.media.player.MediaPlayerState; import dev.onvoid.webrtc.media.video.VideoTrack; import java.util.List; @@ -46,7 +46,7 @@ *

* This example shows how to: *