blob: f38f6b28eafd471c3fe2a234fdbc9607e245c1cc [file]
/*
* Copyright 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jni_zero.NativeMethods;
/** Java wrapper for a C++ MediaStreamInterface. */
public class MediaStream {
private static final String TAG = "MediaStream";
public final List<AudioTrack> audioTracks = new ArrayList<>();
public final List<VideoTrack> videoTracks = new ArrayList<>();
public final List<VideoTrack> preservedVideoTracks = new ArrayList<>();
final NativeLifecycleLock lifecycleLock;
@CalledByNative
public MediaStream(long nativeStream) {
this.lifecycleLock = new NativeLifecycleLock("MediaStream", nativeStream);
}
public boolean addTrack(AudioTrack track) {
return lifecycleLock.call(
nativeStream ->
track.lifecycleLock.call(
nativeTrack -> {
if (MediaStreamJni.get().addAudioTrackToNativeStream(nativeStream, nativeTrack)) {
audioTracks.add(track);
return true;
}
return false;
}));
}
public boolean addTrack(VideoTrack track) {
return lifecycleLock.call(
nativeStream ->
track.lifecycleLock.call(
nativeTrack -> {
if (MediaStreamJni.get().addVideoTrackToNativeStream(nativeStream, nativeTrack)) {
videoTracks.add(track);
return true;
}
return false;
}));
}
// Tracks added in addTrack() call will be auto released once MediaStream.dispose()
// is called. If video track need to be preserved after MediaStream is destroyed it
// should be added to MediaStream using addPreservedTrack() call.
public boolean addPreservedTrack(VideoTrack track) {
return lifecycleLock.call(
nativeStream ->
track.lifecycleLock.call(
nativeTrack -> {
if (MediaStreamJni.get().addVideoTrackToNativeStream(nativeStream, nativeTrack)) {
preservedVideoTracks.add(track);
return true;
}
return false;
}));
}
public boolean removeTrack(AudioTrack track) {
return lifecycleLock.callOrDefault(
nativeStream -> {
audioTracks.remove(track);
return track.lifecycleLock.callOrDefault(
nativeTrack -> MediaStreamJni.get().removeAudioTrack(nativeStream, nativeTrack),
false);
},
false);
}
public boolean removeTrack(VideoTrack track) {
return lifecycleLock.callOrDefault(
nativeStream -> {
videoTracks.remove(track);
preservedVideoTracks.remove(track);
return track.lifecycleLock.callOrDefault(
nativeTrack -> MediaStreamJni.get().removeVideoTrack(nativeStream, nativeTrack),
false);
},
false);
}
@CalledByNative
public void dispose() {
lifecycleLock.dispose(
nativeStream -> {
// Remove and release previously added audio and video tracks.
while (!audioTracks.isEmpty()) {
AudioTrack track = audioTracks.get(0 /* index */);
removeTrack(track);
track.dispose();
}
while (!videoTracks.isEmpty()) {
VideoTrack track = videoTracks.get(0 /* index */);
removeTrack(track);
track.dispose();
}
// Remove, but do not release preserved video tracks.
while (!preservedVideoTracks.isEmpty()) {
removeTrack(preservedVideoTracks.get(0 /* index */));
}
JniCommon.nativeReleaseRef(nativeStream);
});
}
public String getId() {
return lifecycleLock.call(nativeStream -> MediaStreamJni.get().getId(nativeStream));
}
@Override
public String toString() {
return "[" + getId() + ":A=" + audioTracks.size() + ":V=" + videoTracks.size() + "]";
}
@CalledByNative
void addNativeAudioTrack(long nativeTrack) {
audioTracks.add(new AudioTrack(nativeTrack));
}
@CalledByNative
void addNativeVideoTrack(long nativeTrack) {
videoTracks.add(new VideoTrack(nativeTrack));
}
@CalledByNative
void removeAudioTrack(long nativeTrack) {
removeMediaStreamTrack(audioTracks, nativeTrack);
}
@CalledByNative
void removeVideoTrack(long nativeTrack) {
removeMediaStreamTrack(videoTracks, nativeTrack);
}
/** Returns a pointer to webrtc::MediaStreamInterface. */
long getNativeMediaStream() {
return lifecycleLock.getNativePointer();
}
private static void removeMediaStreamTrack(
List<? extends MediaStreamTrack> tracks, long nativeTrack) {
final Iterator<? extends MediaStreamTrack> it = tracks.iterator();
while (it.hasNext()) {
MediaStreamTrack track = it.next();
if (track.lifecycleLock.callOrDefault(ptr -> ptr == nativeTrack, false)) {
track.dispose();
it.remove();
return;
}
}
Logging.e(TAG, "Couldn't not find track");
}
@NativeMethods
interface Natives {
boolean addAudioTrackToNativeStream(long stream, long nativeAudioTrack);
boolean addVideoTrackToNativeStream(long stream, long nativeVideoTrack);
boolean removeAudioTrack(long stream, long nativeAudioTrack);
boolean removeVideoTrack(long stream, long nativeVideoTrack);
String getId(long stream);
}
}