diff --git a/docs/guide/get-started.md b/docs/guide/get-started.md index 99ed5cd5..969b362d 100644 --- a/docs/guide/get-started.md +++ b/docs/guide/get-started.md @@ -298,6 +298,7 @@ audioTrack.dispose(); // Dispose of sources videoSource.dispose(); +audioSource.dispose(); // Close peer connection and release resources peerConnection.close(); diff --git a/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/PeerConnectionExample.java b/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/PeerConnectionExample.java index 52c411ed..ee7679de 100644 --- a/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/PeerConnectionExample.java +++ b/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/PeerConnectionExample.java @@ -98,6 +98,7 @@ public static void main(String[] args) { private static class LocalPeer implements PeerConnectionObserver { private final RTCPeerConnection peerConnection; + private final AudioTrackSource audioSource; private final AudioTrack audioTrack; private final VideoTrack videoTrack; private final RTCRtpSender audioSender; @@ -124,7 +125,7 @@ public LocalPeer(PeerConnectionFactory factory) { audioOptions.autoGainControl = true; audioOptions.noiseSuppression = true; - AudioTrackSource audioSource = factory.createAudioSource(audioOptions); + audioSource = factory.createAudioSource(audioOptions); audioTrack = factory.createAudioTrack("audio0", audioSource); VideoDeviceSource videoSource = new VideoDeviceSource(); @@ -160,6 +161,11 @@ public void dispose() { if (peerConnection != null) { peerConnection.close(); } + // AudioTrackSource is ref-counted and not owned by the audio + // track; the application must dispose it once no longer needed. + if (audioSource != null) { + audioSource.dispose(); + } } // PeerConnectionObserver implementation. diff --git a/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/web/connection/PeerConnectionManager.java b/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/web/connection/PeerConnectionManager.java index 167f8b87..5707f857 100644 --- a/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/web/connection/PeerConnectionManager.java +++ b/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/web/connection/PeerConnectionManager.java @@ -69,6 +69,7 @@ public class PeerConnectionManager implements PeerConnectionSignalingHandler { private final PeerConnectionFactory factory; private final RTCPeerConnection peerConnection; private final List senders = new ArrayList<>(); + private final List audioSources = new ArrayList<>(); private Consumer onLocalDescriptionCreated; private Consumer onIceCandidateGenerated; @@ -120,6 +121,10 @@ public void addTrack(MediaStreamTrack track, List streamIds) { public AudioTrack createAudioTrack(AudioOptions options, String label) { AudioTrackSource audioSource = factory.createAudioSource(options); + // Keep the source around so it can be disposed in close(); it is + // ref-counted and not owned by the audio track. + audioSources.add(audioSource); + return factory.createAudioTrack(label, audioSource); } @@ -156,6 +161,11 @@ public void close() { } senders.clear(); + for (AudioTrackSource audioSource : audioSources) { + audioSource.dispose(); + } + audioSources.clear(); + if (peerConnection != null) { peerConnection.close(); } diff --git a/webrtc-jni/src/main/cpp/include/JNI_AudioTrackSource.h b/webrtc-jni/src/main/cpp/include/JNI_AudioTrackSource.h new file mode 100644 index 00000000..34cacdff --- /dev/null +++ b/webrtc-jni/src/main/cpp/include/JNI_AudioTrackSource.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class dev_onvoid_webrtc_media_audio_AudioTrackSource */ + +#ifndef _Included_dev_onvoid_webrtc_media_audio_AudioTrackSource +#define _Included_dev_onvoid_webrtc_media_audio_AudioTrackSource +#ifdef __cplusplus +extern "C" { +#endif + /* + * Class: dev_onvoid_webrtc_media_audio_AudioTrackSource + * Method: dispose + * Signature: ()V + */ + JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioTrackSource_dispose + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioTrackSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioTrackSource.cpp new file mode 100644 index 00000000..f5b37d0e --- /dev/null +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioTrackSource.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2019 Alex Andres + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNI_AudioTrackSource.h" +#include "JavaUtils.h" + +#include "api/media_stream_interface.h" + +#include "rtc_base/logging.h" + +JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioTrackSource_dispose +(JNIEnv * env, jobject caller) +{ + webrtc::AudioSourceInterface * source = GetHandle(env, caller); + CHECK_HANDLE(source); + + webrtc::RefCountReleaseStatus status = source->Release(); + + if (status != webrtc::RefCountReleaseStatus::kDroppedLastRef) { + RTC_LOG(LS_WARNING) << "Native object was not deleted. A reference is still around somewhere."; + } + + SetHandle(env, caller, nullptr); + + source = nullptr; +} diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioTrackSource.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioTrackSource.java index f25ee9b5..edcc13a0 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioTrackSource.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioTrackSource.java @@ -29,4 +29,11 @@ protected AudioTrackSource() { } + /** + * Disposes of the native resources held by this audio source. + * This method should be called when the audio source is no longer needed + * to prevent memory leaks. + */ + public native void dispose(); + } diff --git a/webrtc/src/test/java/dev/onvoid/webrtc/media/MediaSourceTests.java b/webrtc/src/test/java/dev/onvoid/webrtc/media/MediaSourceTests.java index 4a1a7f1b..e6e977f3 100644 --- a/webrtc/src/test/java/dev/onvoid/webrtc/media/MediaSourceTests.java +++ b/webrtc/src/test/java/dev/onvoid/webrtc/media/MediaSourceTests.java @@ -20,6 +20,7 @@ import dev.onvoid.webrtc.TestBase; import dev.onvoid.webrtc.media.audio.AudioOptions; +import dev.onvoid.webrtc.media.audio.AudioTrack; import dev.onvoid.webrtc.media.audio.AudioTrackSource; import org.junit.jupiter.api.Test; @@ -32,6 +33,21 @@ void audioSourceStateAfterCreation() { AudioTrackSource audioSource = factory.createAudioSource(audioOptions); assertEquals(MediaSource.State.LIVE, audioSource.getState()); + + audioSource.dispose(); + } + + @Test + void audioSourceDisposeAfterTrackDispose() { + // Regression test: AudioTrackSource used to have no dispose(), so the + // native AudioSourceInterface reference obtained from + // createAudioSource() could never be released by the application. + AudioOptions audioOptions = new AudioOptions(); + AudioTrackSource audioSource = factory.createAudioSource(audioOptions); + AudioTrack audioTrack = factory.createAudioTrack("audio0", audioSource); + + audioTrack.dispose(); + audioSource.dispose(); } }