PeerConnection shutdown-time fixes
- TCPPort::~TCPPort() was leaking incoming_ sockets; now they are deleted. - PeerConnection::RemoveStream() now removes streams even if the PeerConnection::IsClosed(). Previously such streams would never get removed. - Gave MediaStreamTrackInterface a virtual destructor to ensure deletes on base pointers are dispatched virtually. - VideoTrack.dispose() delegates to super.dispose() (instead of leaking) - PeerConnection.dispose() now removes streams before disposing of them. - MediaStream.dispose() now removes tracks before disposing of them. - VideoCapturer.dispose() only unowned VideoCapturers (mirroring C++ API) - AppRTCDemo.disconnectAndExit() now correctly .dispose()s its VideoSource and PeerConnectionFactory. - CHECK that Release()d objects are deleted when expected to (i.e. no ref-cycles or missing .dispose() calls) in the Java API. - Create & Return webrtc::Traces at factory birth/death to be able to assert that _all_ threads started during the test are collected by the end. - Name threads attached to the JVM more informatively for debugging. - Removed a bunch of unnecessary scoped_refptr instances in peerconnection_jni.cc whose only job was messing with refcounts. RISK=P2 TESTED=AppRTCDemo can be ended and restarted just fine instead of crashing on camera unavailability. No more post-app-exit logcat lines. PCTest.java now asserts that all threads are collected before exit. BUG=2183 R=wu@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2005004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4534 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
a5506690b4
commit
32001ef124
@ -56,8 +56,11 @@
|
||||
#undef JNIEXPORT
|
||||
#define JNIEXPORT __attribute__((visibility("default")))
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
||||
#include "talk/app/webrtc/peerconnectioninterface.h"
|
||||
@ -116,6 +119,18 @@ using webrtc::VideoRendererInterface;
|
||||
} \
|
||||
}
|
||||
|
||||
// Helper that calls ptr->Release() and logs a useful message if that didn't
|
||||
// actually delete *ptr because of extra refcounts.
|
||||
#define CHECK_RELEASE(ptr) \
|
||||
do { \
|
||||
int count = (ptr)->Release(); \
|
||||
if (count != 0) { \
|
||||
LOG(LS_ERROR) << "Refcount unexpectedly not 0: " << (ptr) \
|
||||
<< ": " << count; \
|
||||
} \
|
||||
CHECK(!count, "Unexpected refcount"); \
|
||||
} while (0)
|
||||
|
||||
namespace {
|
||||
|
||||
static JavaVM* g_jvm = NULL; // Set in JNI_OnLoad().
|
||||
@ -123,6 +138,22 @@ static JavaVM* g_jvm = NULL; // Set in JNI_OnLoad().
|
||||
static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
|
||||
static pthread_key_t g_jni_ptr; // Key for per-thread JNIEnv* data.
|
||||
|
||||
// Return thread ID as a string.
|
||||
static std::string GetThreadId() {
|
||||
char buf[21]; // Big enough to hold a kuint64max plus terminating NULL.
|
||||
CHECK(snprintf(buf, sizeof(buf), "%llu", syscall(__NR_gettid)) <= sizeof(buf),
|
||||
"Thread id is bigger than uint64??");
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Return the current thread's name.
|
||||
static std::string GetThreadName() {
|
||||
char name[17];
|
||||
CHECK(prctl(PR_GET_NAME, name) == 0, "prctl(PR_GET_NAME) failed");
|
||||
name[16] = '\0';
|
||||
return std::string(name);
|
||||
}
|
||||
|
||||
static void ThreadDestructor(void* unused) {
|
||||
jint status = g_jvm->DetachCurrentThread();
|
||||
CHECK(status == JNI_OK, "Failed to detach thread: " << status);
|
||||
@ -144,7 +175,13 @@ static JNIEnv* AttachCurrentThreadIfNeeded() {
|
||||
#else
|
||||
JNIEnv* env;
|
||||
#endif
|
||||
CHECK(!g_jvm->AttachCurrentThread(&env, NULL), "Failed to attach thread");
|
||||
char* name = strdup((GetThreadName() + " - " + GetThreadId()).c_str());
|
||||
JavaVMAttachArgs args;
|
||||
args.version = JNI_VERSION_1_6;
|
||||
args.name = name;
|
||||
args.group = NULL;
|
||||
CHECK(!g_jvm->AttachCurrentThread(&env, &args), "Failed to attach thread");
|
||||
free(name);
|
||||
CHECK(env, "AttachCurrentThread handed back NULL!");
|
||||
jni = reinterpret_cast<JNIEnv*>(env);
|
||||
CHECK(!pthread_setspecific(g_jni_ptr, jni), "pthread_setspecific");
|
||||
@ -512,8 +549,10 @@ class PCOJava : public PeerConnectionObserver {
|
||||
ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
|
||||
*j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id));
|
||||
CHECK_EXCEPTION(jni(), "error during NewObject");
|
||||
jfieldID audio_tracks_id = GetFieldID(
|
||||
jni(), *j_media_stream_class_, "audioTracks", "Ljava/util/List;");
|
||||
jfieldID audio_tracks_id = GetFieldID(jni(),
|
||||
*j_media_stream_class_,
|
||||
"audioTracks",
|
||||
"Ljava/util/LinkedList;");
|
||||
ScopedLocalRef<jobject> audio_tracks(jni(), GetObjectField(
|
||||
jni(), *j_stream, audio_tracks_id));
|
||||
jmethodID add = GetMethodID(jni(),
|
||||
@ -531,8 +570,10 @@ class PCOJava : public PeerConnectionObserver {
|
||||
ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
|
||||
*j_video_track_class_, j_video_track_ctor_, (jlong)track, *id));
|
||||
CHECK_EXCEPTION(jni(), "error during NewObject");
|
||||
jfieldID video_tracks_id = GetFieldID(
|
||||
jni(), *j_media_stream_class_, "videoTracks", "Ljava/util/List;");
|
||||
jfieldID video_tracks_id = GetFieldID(jni(),
|
||||
*j_media_stream_class_,
|
||||
"videoTracks",
|
||||
"Ljava/util/LinkedList;");
|
||||
ScopedLocalRef<jobject> video_tracks(jni(), GetObjectField(
|
||||
jni(), *j_stream, video_tracks_id));
|
||||
jmethodID add = GetMethodID(jni(),
|
||||
@ -569,13 +610,18 @@ class PCOJava : public PeerConnectionObserver {
|
||||
ScopedLocalRef<jobject> j_channel(jni(), jni()->NewObject(
|
||||
*j_data_channel_class_, j_data_channel_ctor_, (jlong)channel));
|
||||
CHECK_EXCEPTION(jni(), "error during NewObject");
|
||||
// Channel is now owned by Java object, and will be freed from
|
||||
// DataChannel.dispose().
|
||||
channel->AddRef();
|
||||
|
||||
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel",
|
||||
"(Lorg/webrtc/DataChannel;)V");
|
||||
jni()->CallVoidMethod(*j_observer_global_, m, *j_channel);
|
||||
|
||||
// Channel is now owned by Java object, and will be freed from
|
||||
// DataChannel.dispose(). Important that this be done _after_ the
|
||||
// CallVoidMethod above as Java code might call back into native code and be
|
||||
// surprised to see a refcount of 2.
|
||||
int bumped_count = channel->AddRef();
|
||||
CHECK(bumped_count == 2, "Unexpected refcount OnDataChannel");
|
||||
|
||||
CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
|
||||
}
|
||||
|
||||
@ -1013,25 +1059,20 @@ extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
return -1;
|
||||
g_class_reference_holder = new ClassReferenceHolder(jni);
|
||||
|
||||
webrtc::Trace::CreateTrace();
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) {
|
||||
webrtc::Trace::ReturnTrace();
|
||||
delete g_class_reference_holder;
|
||||
g_class_reference_holder = NULL;
|
||||
CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()");
|
||||
}
|
||||
|
||||
static talk_base::scoped_refptr<DataChannelInterface> ExtractNativeDC(
|
||||
JNIEnv* jni, jobject j_dc) {
|
||||
static DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) {
|
||||
jfieldID native_dc_id = GetFieldID(jni,
|
||||
GetObjectClass(jni, j_dc), "nativeDataChannel", "J");
|
||||
jlong j_d = GetLongField(jni, j_dc, native_dc_id);
|
||||
return talk_base::scoped_refptr<DataChannelInterface>(
|
||||
reinterpret_cast<DataChannelInterface*>(j_d));
|
||||
return reinterpret_cast<DataChannelInterface*>(j_d);
|
||||
}
|
||||
|
||||
JOW(jlong, DataChannel_registerObserverNative)(
|
||||
@ -1079,7 +1120,7 @@ JOW(jboolean, DataChannel_sendNative)(JNIEnv* jni, jobject j_dc,
|
||||
}
|
||||
|
||||
JOW(void, DataChannel_dispose)(JNIEnv* jni, jobject j_dc) {
|
||||
ExtractNativeDC(jni, j_dc)->Release();
|
||||
CHECK_RELEASE(ExtractNativeDC(jni, j_dc));
|
||||
}
|
||||
|
||||
JOW(void, Logging_nativeEnableTracing)(
|
||||
@ -1096,7 +1137,7 @@ JOW(void, Logging_nativeEnableTracing)(
|
||||
}
|
||||
|
||||
JOW(void, PeerConnection_freePeerConnection)(JNIEnv*, jclass, jlong j_p) {
|
||||
reinterpret_cast<PeerConnectionInterface*>(j_p)->Release();
|
||||
CHECK_RELEASE(reinterpret_cast<PeerConnectionInterface*>(j_p));
|
||||
}
|
||||
|
||||
JOW(void, PeerConnection_freeObserver)(JNIEnv*, jclass, jlong j_p) {
|
||||
@ -1105,7 +1146,7 @@ JOW(void, PeerConnection_freeObserver)(JNIEnv*, jclass, jlong j_p) {
|
||||
}
|
||||
|
||||
JOW(void, MediaSource_free)(JNIEnv*, jclass, jlong j_p) {
|
||||
reinterpret_cast<MediaSourceInterface*>(j_p)->Release();
|
||||
CHECK_RELEASE(reinterpret_cast<MediaSourceInterface*>(j_p));
|
||||
}
|
||||
|
||||
JOW(void, VideoCapturer_free)(JNIEnv*, jclass, jlong j_p) {
|
||||
@ -1117,43 +1158,31 @@ JOW(void, VideoRenderer_free)(JNIEnv*, jclass, jlong j_p) {
|
||||
}
|
||||
|
||||
JOW(void, MediaStreamTrack_free)(JNIEnv*, jclass, jlong j_p) {
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p)->Release();
|
||||
CHECK_RELEASE(reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
||||
}
|
||||
|
||||
JOW(jboolean, MediaStream_nativeAddAudioTrack)(
|
||||
JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
|
||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
||||
talk_base::scoped_refptr<AudioTrackInterface> track(
|
||||
return reinterpret_cast<MediaStreamInterface*>(pointer)->AddTrack(
|
||||
reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
|
||||
return stream->AddTrack(track);
|
||||
}
|
||||
|
||||
JOW(jboolean, MediaStream_nativeAddVideoTrack)(
|
||||
JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
|
||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
||||
return stream->AddTrack(track);
|
||||
return reinterpret_cast<MediaStreamInterface*>(pointer)
|
||||
->AddTrack(reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
||||
}
|
||||
|
||||
JOW(jboolean, MediaStream_nativeRemoveAudioTrack)(
|
||||
JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
|
||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
||||
talk_base::scoped_refptr<AudioTrackInterface> track(
|
||||
return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
|
||||
reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
|
||||
return stream->RemoveTrack(track);
|
||||
}
|
||||
|
||||
JOW(jboolean, MediaStream_nativeRemoveVideoTrack)(
|
||||
JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
|
||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
||||
return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
|
||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
||||
return stream->RemoveTrack(track);
|
||||
}
|
||||
|
||||
JOW(jstring, MediaStream_nativeLabel)(JNIEnv* jni, jclass, jlong j_p) {
|
||||
@ -1162,7 +1191,7 @@ JOW(jstring, MediaStream_nativeLabel)(JNIEnv* jni, jclass, jlong j_p) {
|
||||
}
|
||||
|
||||
JOW(void, MediaStream_free)(JNIEnv*, jclass, jlong j_p) {
|
||||
reinterpret_cast<MediaStreamInterface*>(j_p)->Release();
|
||||
CHECK_RELEASE(reinterpret_cast<MediaStreamInterface*>(j_p));
|
||||
}
|
||||
|
||||
JOW(jlong, PeerConnectionFactory_nativeCreateObserver)(
|
||||
@ -1183,13 +1212,15 @@ JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)(
|
||||
|
||||
JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)(
|
||||
JNIEnv* jni, jclass) {
|
||||
webrtc::Trace::CreateTrace();
|
||||
talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
|
||||
webrtc::CreatePeerConnectionFactory());
|
||||
return (jlong)factory.release();
|
||||
}
|
||||
|
||||
JOW(void, PeerConnectionFactory_freeFactory)(JNIEnv*, jclass, jlong j_p) {
|
||||
reinterpret_cast<PeerConnectionFactoryInterface*>(j_p)->Release();
|
||||
CHECK_RELEASE(reinterpret_cast<PeerConnectionFactoryInterface*>(j_p));
|
||||
webrtc::Trace::ReturnTrace();
|
||||
}
|
||||
|
||||
JOW(jlong, PeerConnectionFactory_nativeCreateLocalMediaStream)(
|
||||
@ -1322,7 +1353,8 @@ JOW(jobject, PeerConnection_createDataChannel)(
|
||||
j_data_channel_class, j_data_channel_ctor, channel.get());
|
||||
CHECK_EXCEPTION(jni, "error during NewObject");
|
||||
// Channel is now owned by Java object, and will be freed from there.
|
||||
channel->AddRef();
|
||||
int bumped_count = channel->AddRef();
|
||||
CHECK(bumped_count == 2, "Unexpected refcount");
|
||||
return j_channel;
|
||||
}
|
||||
|
||||
@ -1493,59 +1525,50 @@ JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)(
|
||||
}
|
||||
|
||||
JOW(jstring, MediaStreamTrack_nativeId)(JNIEnv* jni, jclass, jlong j_p) {
|
||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
||||
return JavaStringFromStdString(jni, p->id());
|
||||
return JavaStringFromStdString(
|
||||
jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->id());
|
||||
}
|
||||
|
||||
JOW(jstring, MediaStreamTrack_nativeKind)(JNIEnv* jni, jclass, jlong j_p) {
|
||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
||||
return JavaStringFromStdString(jni, p->kind());
|
||||
return JavaStringFromStdString(
|
||||
jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->kind());
|
||||
}
|
||||
|
||||
JOW(jboolean, MediaStreamTrack_nativeEnabled)(JNIEnv* jni, jclass, jlong j_p) {
|
||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
||||
return p->enabled();
|
||||
return reinterpret_cast<MediaStreamTrackInterface*>(j_p)->enabled();
|
||||
}
|
||||
|
||||
JOW(jobject, MediaStreamTrack_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
|
||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
||||
return JavaEnumFromIndex(jni, "MediaStreamTrack$State", p->state());
|
||||
return JavaEnumFromIndex(
|
||||
jni,
|
||||
"MediaStreamTrack$State",
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p)->state());
|
||||
}
|
||||
|
||||
JOW(jboolean, MediaStreamTrack_nativeSetState)(
|
||||
JNIEnv* jni, jclass, jlong j_p, jint j_new_state) {
|
||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
||||
MediaStreamTrackInterface::TrackState new_state =
|
||||
(MediaStreamTrackInterface::TrackState)j_new_state;
|
||||
return p->set_state(new_state);
|
||||
return reinterpret_cast<MediaStreamTrackInterface*>(j_p)
|
||||
->set_state(new_state);
|
||||
}
|
||||
|
||||
JOW(jboolean, MediaStreamTrack_nativeSetEnabled)(
|
||||
JNIEnv* jni, jclass, jlong j_p, jboolean enabled) {
|
||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
||||
return p->set_enabled(enabled);
|
||||
return reinterpret_cast<MediaStreamTrackInterface*>(j_p)
|
||||
->set_enabled(enabled);
|
||||
}
|
||||
|
||||
JOW(void, VideoTrack_nativeAddRenderer)(
|
||||
JNIEnv* jni, jclass,
|
||||
jlong j_video_track_pointer, jlong j_renderer_pointer) {
|
||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
||||
track->AddRenderer(
|
||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->AddRenderer(
|
||||
reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
|
||||
}
|
||||
|
||||
JOW(void, VideoTrack_nativeRemoveRenderer)(
|
||||
JNIEnv* jni, jclass,
|
||||
jlong j_video_track_pointer, jlong j_renderer_pointer) {
|
||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
||||
track->RemoveRenderer(
|
||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->RemoveRenderer(
|
||||
reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public class MediaSource {
|
||||
return nativeState(nativeSource);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
public void dispose() {
|
||||
free(nativeSource);
|
||||
}
|
||||
|
||||
|
@ -28,13 +28,12 @@
|
||||
package org.webrtc;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/** Java wrapper for a C++ MediaStreamInterface. */
|
||||
public class MediaStream {
|
||||
public final List<AudioTrack> audioTracks;
|
||||
public final List<VideoTrack> videoTracks;
|
||||
// Package-protected for LocalMediaStream and PeerConnection.
|
||||
public final LinkedList<AudioTrack> audioTracks;
|
||||
public final LinkedList<VideoTrack> videoTracks;
|
||||
// Package-protected for PeerConnection.
|
||||
final long nativeStream;
|
||||
|
||||
public MediaStream(long nativeStream) {
|
||||
@ -76,14 +75,16 @@ public class MediaStream {
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
for (AudioTrack track : audioTracks) {
|
||||
while (!audioTracks.isEmpty()) {
|
||||
AudioTrack track = audioTracks.getFirst();
|
||||
removeTrack(track);
|
||||
track.dispose();
|
||||
}
|
||||
audioTracks.clear();
|
||||
for (VideoTrack track : videoTracks) {
|
||||
while (!videoTracks.isEmpty()) {
|
||||
VideoTrack track = videoTracks.getFirst();
|
||||
removeTrack(track);
|
||||
track.dispose();
|
||||
}
|
||||
videoTracks.clear();
|
||||
free(nativeStream);
|
||||
}
|
||||
|
||||
|
@ -176,6 +176,7 @@ public class PeerConnection {
|
||||
public void dispose() {
|
||||
close();
|
||||
for (MediaStream stream : localStreams) {
|
||||
nativeRemoveLocalStream(stream.nativeStream);
|
||||
stream.dispose();
|
||||
}
|
||||
localStreams.clear();
|
||||
|
@ -77,7 +77,7 @@ public class PeerConnectionFactory {
|
||||
public VideoSource createVideoSource(
|
||||
VideoCapturer capturer, MediaConstraints constraints) {
|
||||
return new VideoSource(nativeCreateVideoSource(
|
||||
nativeFactory, capturer.nativeVideoCapturer, constraints));
|
||||
nativeFactory, capturer.takeNativeVideoCapturer(), constraints));
|
||||
}
|
||||
|
||||
public VideoTrack createVideoTrack(String id, VideoSource source) {
|
||||
|
@ -27,9 +27,9 @@
|
||||
|
||||
package org.webrtc;
|
||||
|
||||
/** Java version of VideoCapturerInterface. */
|
||||
/** Java version of cricket::VideoCapturer. */
|
||||
public class VideoCapturer {
|
||||
final long nativeVideoCapturer;
|
||||
private long nativeVideoCapturer;
|
||||
|
||||
private VideoCapturer(long nativeVideoCapturer) {
|
||||
this.nativeVideoCapturer = nativeVideoCapturer;
|
||||
@ -43,8 +43,22 @@ public class VideoCapturer {
|
||||
return new VideoCapturer(nativeVideoCapturer);
|
||||
}
|
||||
|
||||
// Package-visible for PeerConnectionFactory.
|
||||
long takeNativeVideoCapturer() {
|
||||
if (nativeVideoCapturer == 0) {
|
||||
throw new RuntimeException("Capturer can only be taken once!");
|
||||
}
|
||||
long ret = nativeVideoCapturer;
|
||||
nativeVideoCapturer = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
free(nativeVideoCapturer);
|
||||
// No-op iff this capturer is owned by a source (see comment on
|
||||
// PeerConnectionFactoryInterface::CreateVideoSource()).
|
||||
if (nativeVideoCapturer != 0) {
|
||||
free(nativeVideoCapturer);
|
||||
}
|
||||
}
|
||||
|
||||
private static native long nativeCreateVideoCapturer(String deviceName);
|
||||
|
@ -31,11 +31,11 @@ import java.util.LinkedList;
|
||||
|
||||
/** Java version of VideoTrackInterface. */
|
||||
public class VideoTrack extends MediaStreamTrack {
|
||||
private final LinkedList<VideoRenderer> renderers;
|
||||
private final LinkedList<VideoRenderer> renderers =
|
||||
new LinkedList<VideoRenderer>();
|
||||
|
||||
public VideoTrack(long nativeTrack) {
|
||||
super(nativeTrack);
|
||||
renderers = new LinkedList<VideoRenderer>();
|
||||
}
|
||||
|
||||
public void addRenderer(VideoRenderer renderer) {
|
||||
@ -55,8 +55,11 @@ public class VideoTrack extends MediaStreamTrack {
|
||||
while (!renderers.isEmpty()) {
|
||||
removeRenderer(renderers.getFirst());
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private static native void free(long nativeTrack);
|
||||
|
||||
private static native void nativeAddRenderer(
|
||||
long nativeTrack, long nativeRenderer);
|
||||
|
||||
|
@ -34,6 +34,7 @@ import org.webrtc.PeerConnection.IceConnectionState;
|
||||
import org.webrtc.PeerConnection.IceGatheringState;
|
||||
import org.webrtc.PeerConnection.SignalingState;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
@ -194,6 +195,7 @@ public class PeerConnectionTest extends TestCase {
|
||||
expectedAddStreamLabels.add(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onAddStream(MediaStream stream) {
|
||||
assertEquals(expectedAddStreamLabels.removeFirst(), stream.label());
|
||||
assertEquals(1, stream.videoTracks.size());
|
||||
@ -212,6 +214,7 @@ public class PeerConnectionTest extends TestCase {
|
||||
expectedRemoveStreamLabels.add(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onRemoveStream(MediaStream stream) {
|
||||
assertEquals(expectedRemoveStreamLabels.removeFirst(), stream.label());
|
||||
WeakReference<VideoRenderer> renderer = renderers.remove(stream);
|
||||
@ -477,11 +480,13 @@ public class PeerConnectionTest extends TestCase {
|
||||
public void testCompleteSession() throws Exception {
|
||||
// Uncomment to get ALL WebRTC tracing and SENSITIVE libjingle logging.
|
||||
// Logging.enableTracing(
|
||||
// "/tmp/AMI-nope.txt",
|
||||
// "/tmp/PeerConnectionTest-log.txt",
|
||||
// EnumSet.of(Logging.TraceLevel.TRACE_ALL),
|
||||
// Logging.Severity.LS_SENSITIVE);
|
||||
|
||||
CountDownLatch testDone = new CountDownLatch(1);
|
||||
System.gc(); // Encourage any GC-related threads to start up.
|
||||
TreeSet<String> threadsBeforeTest = allThreads();
|
||||
|
||||
PeerConnectionFactory factory = new PeerConnectionFactory();
|
||||
MediaConstraints pcConstraints = new MediaConstraints();
|
||||
@ -688,7 +693,11 @@ public class PeerConnectionTest extends TestCase {
|
||||
offeringPC = null;
|
||||
shutdownPC(answeringPC, answeringExpectations);
|
||||
answeringPC = null;
|
||||
videoSource.dispose();
|
||||
factory.dispose();
|
||||
System.gc();
|
||||
TreeSet<String> threadsAfterTest = allThreads();
|
||||
assertEquals(threadsBeforeTest, threadsAfterTest);
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
@ -720,4 +729,28 @@ public class PeerConnectionTest extends TestCase {
|
||||
|
||||
pc.dispose();
|
||||
}
|
||||
|
||||
// Returns a set of thread IDs belonging to this process, as Strings.
|
||||
private static TreeSet<String> allThreads() {
|
||||
TreeSet<String> threads = new TreeSet<String>();
|
||||
// This pokes at /proc instead of using the Java APIs because we're also
|
||||
// looking for libjingle/webrtc native threads, most of which won't have
|
||||
// attached to the JVM.
|
||||
for (String threadId : (new File("/proc/self/task")).list()) {
|
||||
threads.add(threadId);
|
||||
}
|
||||
return threads;
|
||||
}
|
||||
|
||||
// Return a String form of |strings| joined by |separator|.
|
||||
private static String joinStrings(String separator, TreeSet<String> strings) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String s : strings) {
|
||||
if (builder.length() > 0) {
|
||||
builder.append(separator);
|
||||
}
|
||||
builder.append(s);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +107,9 @@ class MediaStreamTrackInterface : public talk_base::RefCountInterface,
|
||||
virtual bool set_enabled(bool enable) = 0;
|
||||
// These methods should be called by implementation only.
|
||||
virtual bool set_state(TrackState new_state) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~MediaStreamTrackInterface() {}
|
||||
};
|
||||
|
||||
// Interface for rendering VideoFrames from a VideoTrack
|
||||
|
@ -354,10 +354,10 @@ bool PeerConnection::AddStream(MediaStreamInterface* local_stream,
|
||||
}
|
||||
|
||||
void PeerConnection::RemoveStream(MediaStreamInterface* local_stream) {
|
||||
mediastream_signaling_->RemoveLocalStream(local_stream);
|
||||
if (IsClosed()) {
|
||||
return;
|
||||
}
|
||||
mediastream_signaling_->RemoveLocalStream(local_stream);
|
||||
observer_->OnRenegotiationNeeded();
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,8 @@ import java.util.List;
|
||||
public class AppRTCDemoActivity extends Activity
|
||||
implements AppRTCClient.IceServersObserver {
|
||||
private static final String TAG = "AppRTCDemoActivity";
|
||||
private PeerConnectionFactory factory;
|
||||
private VideoSource videoSource;
|
||||
private PeerConnection pc;
|
||||
private final PCObserver pcObserver = new PCObserver();
|
||||
private final SDPObserver sdpObserver = new SDPObserver();
|
||||
@ -183,8 +185,7 @@ public class AppRTCDemoActivity extends Activity
|
||||
|
||||
@Override
|
||||
public void onIceServers(List<PeerConnection.IceServer> iceServers) {
|
||||
PeerConnectionFactory factory = new PeerConnectionFactory();
|
||||
|
||||
factory = new PeerConnectionFactory();
|
||||
pc = factory.createPeerConnection(
|
||||
iceServers, appRtcClient.pcConstraints(), pcObserver);
|
||||
|
||||
@ -217,7 +218,7 @@ public class AppRTCDemoActivity extends Activity
|
||||
{
|
||||
logAndToast("Creating local video source...");
|
||||
VideoCapturer capturer = getVideoCapturer();
|
||||
VideoSource videoSource = factory.createVideoSource(
|
||||
videoSource = factory.createVideoSource(
|
||||
capturer, appRtcClient.videoConstraints());
|
||||
MediaStream lMS = factory.createLocalMediaStream("ARDAMS");
|
||||
VideoTrack videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
|
||||
@ -485,6 +486,14 @@ public class AppRTCDemoActivity extends Activity
|
||||
appRtcClient.disconnect();
|
||||
appRtcClient = null;
|
||||
}
|
||||
if (videoSource != null) {
|
||||
videoSource.dispose();
|
||||
videoSource = null;
|
||||
}
|
||||
if (factory != null) {
|
||||
factory.dispose();
|
||||
factory = null;
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ bool TCPPort::Init() {
|
||||
|
||||
TCPPort::~TCPPort() {
|
||||
delete socket_;
|
||||
std::list<Incoming>::iterator it;
|
||||
for (it = incoming_.begin(); it != incoming_.end(); ++it)
|
||||
delete it->socket;
|
||||
incoming_.clear();
|
||||
}
|
||||
|
||||
Connection* TCPPort::CreateConnection(const Candidate& address,
|
||||
|
Loading…
x
Reference in New Issue
Block a user