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:
@@ -56,8 +56,11 @@
|
|||||||
#undef JNIEXPORT
|
#undef JNIEXPORT
|
||||||
#define JNIEXPORT __attribute__((visibility("default")))
|
#define JNIEXPORT __attribute__((visibility("default")))
|
||||||
|
|
||||||
|
#include <asm/unistd.h>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
||||||
#include "talk/app/webrtc/peerconnectioninterface.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 {
|
namespace {
|
||||||
|
|
||||||
static JavaVM* g_jvm = NULL; // Set in JNI_OnLoad().
|
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_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
|
||||||
static pthread_key_t g_jni_ptr; // Key for per-thread JNIEnv* data.
|
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) {
|
static void ThreadDestructor(void* unused) {
|
||||||
jint status = g_jvm->DetachCurrentThread();
|
jint status = g_jvm->DetachCurrentThread();
|
||||||
CHECK(status == JNI_OK, "Failed to detach thread: " << status);
|
CHECK(status == JNI_OK, "Failed to detach thread: " << status);
|
||||||
@@ -144,7 +175,13 @@ static JNIEnv* AttachCurrentThreadIfNeeded() {
|
|||||||
#else
|
#else
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
#endif
|
#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!");
|
CHECK(env, "AttachCurrentThread handed back NULL!");
|
||||||
jni = reinterpret_cast<JNIEnv*>(env);
|
jni = reinterpret_cast<JNIEnv*>(env);
|
||||||
CHECK(!pthread_setspecific(g_jni_ptr, jni), "pthread_setspecific");
|
CHECK(!pthread_setspecific(g_jni_ptr, jni), "pthread_setspecific");
|
||||||
@@ -512,8 +549,10 @@ class PCOJava : public PeerConnectionObserver {
|
|||||||
ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
|
ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
|
||||||
*j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id));
|
*j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id));
|
||||||
CHECK_EXCEPTION(jni(), "error during NewObject");
|
CHECK_EXCEPTION(jni(), "error during NewObject");
|
||||||
jfieldID audio_tracks_id = GetFieldID(
|
jfieldID audio_tracks_id = GetFieldID(jni(),
|
||||||
jni(), *j_media_stream_class_, "audioTracks", "Ljava/util/List;");
|
*j_media_stream_class_,
|
||||||
|
"audioTracks",
|
||||||
|
"Ljava/util/LinkedList;");
|
||||||
ScopedLocalRef<jobject> audio_tracks(jni(), GetObjectField(
|
ScopedLocalRef<jobject> audio_tracks(jni(), GetObjectField(
|
||||||
jni(), *j_stream, audio_tracks_id));
|
jni(), *j_stream, audio_tracks_id));
|
||||||
jmethodID add = GetMethodID(jni(),
|
jmethodID add = GetMethodID(jni(),
|
||||||
@@ -531,8 +570,10 @@ class PCOJava : public PeerConnectionObserver {
|
|||||||
ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
|
ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
|
||||||
*j_video_track_class_, j_video_track_ctor_, (jlong)track, *id));
|
*j_video_track_class_, j_video_track_ctor_, (jlong)track, *id));
|
||||||
CHECK_EXCEPTION(jni(), "error during NewObject");
|
CHECK_EXCEPTION(jni(), "error during NewObject");
|
||||||
jfieldID video_tracks_id = GetFieldID(
|
jfieldID video_tracks_id = GetFieldID(jni(),
|
||||||
jni(), *j_media_stream_class_, "videoTracks", "Ljava/util/List;");
|
*j_media_stream_class_,
|
||||||
|
"videoTracks",
|
||||||
|
"Ljava/util/LinkedList;");
|
||||||
ScopedLocalRef<jobject> video_tracks(jni(), GetObjectField(
|
ScopedLocalRef<jobject> video_tracks(jni(), GetObjectField(
|
||||||
jni(), *j_stream, video_tracks_id));
|
jni(), *j_stream, video_tracks_id));
|
||||||
jmethodID add = GetMethodID(jni(),
|
jmethodID add = GetMethodID(jni(),
|
||||||
@@ -569,13 +610,18 @@ class PCOJava : public PeerConnectionObserver {
|
|||||||
ScopedLocalRef<jobject> j_channel(jni(), jni()->NewObject(
|
ScopedLocalRef<jobject> j_channel(jni(), jni()->NewObject(
|
||||||
*j_data_channel_class_, j_data_channel_ctor_, (jlong)channel));
|
*j_data_channel_class_, j_data_channel_ctor_, (jlong)channel));
|
||||||
CHECK_EXCEPTION(jni(), "error during NewObject");
|
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",
|
jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel",
|
||||||
"(Lorg/webrtc/DataChannel;)V");
|
"(Lorg/webrtc/DataChannel;)V");
|
||||||
jni()->CallVoidMethod(*j_observer_global_, m, *j_channel);
|
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");
|
CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1013,25 +1059,20 @@ extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
|||||||
return -1;
|
return -1;
|
||||||
g_class_reference_holder = new ClassReferenceHolder(jni);
|
g_class_reference_holder = new ClassReferenceHolder(jni);
|
||||||
|
|
||||||
webrtc::Trace::CreateTrace();
|
|
||||||
|
|
||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) {
|
extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) {
|
||||||
webrtc::Trace::ReturnTrace();
|
|
||||||
delete g_class_reference_holder;
|
delete g_class_reference_holder;
|
||||||
g_class_reference_holder = NULL;
|
g_class_reference_holder = NULL;
|
||||||
CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()");
|
CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()");
|
||||||
}
|
}
|
||||||
|
|
||||||
static talk_base::scoped_refptr<DataChannelInterface> ExtractNativeDC(
|
static DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) {
|
||||||
JNIEnv* jni, jobject j_dc) {
|
|
||||||
jfieldID native_dc_id = GetFieldID(jni,
|
jfieldID native_dc_id = GetFieldID(jni,
|
||||||
GetObjectClass(jni, j_dc), "nativeDataChannel", "J");
|
GetObjectClass(jni, j_dc), "nativeDataChannel", "J");
|
||||||
jlong j_d = GetLongField(jni, j_dc, native_dc_id);
|
jlong j_d = GetLongField(jni, j_dc, native_dc_id);
|
||||||
return talk_base::scoped_refptr<DataChannelInterface>(
|
return reinterpret_cast<DataChannelInterface*>(j_d);
|
||||||
reinterpret_cast<DataChannelInterface*>(j_d));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jlong, DataChannel_registerObserverNative)(
|
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) {
|
JOW(void, DataChannel_dispose)(JNIEnv* jni, jobject j_dc) {
|
||||||
ExtractNativeDC(jni, j_dc)->Release();
|
CHECK_RELEASE(ExtractNativeDC(jni, j_dc));
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(void, Logging_nativeEnableTracing)(
|
JOW(void, Logging_nativeEnableTracing)(
|
||||||
@@ -1096,7 +1137,7 @@ JOW(void, Logging_nativeEnableTracing)(
|
|||||||
}
|
}
|
||||||
|
|
||||||
JOW(void, PeerConnection_freePeerConnection)(JNIEnv*, jclass, jlong j_p) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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)(
|
JOW(jboolean, MediaStream_nativeAddAudioTrack)(
|
||||||
JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
|
JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
|
||||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
return reinterpret_cast<MediaStreamInterface*>(pointer)->AddTrack(
|
||||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
|
||||||
talk_base::scoped_refptr<AudioTrackInterface> track(
|
|
||||||
reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
|
reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
|
||||||
return stream->AddTrack(track);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jboolean, MediaStream_nativeAddVideoTrack)(
|
JOW(jboolean, MediaStream_nativeAddVideoTrack)(
|
||||||
JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
|
JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
|
||||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
return reinterpret_cast<MediaStreamInterface*>(pointer)
|
||||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
->AddTrack(reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
||||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
|
||||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
|
||||||
return stream->AddTrack(track);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jboolean, MediaStream_nativeRemoveAudioTrack)(
|
JOW(jboolean, MediaStream_nativeRemoveAudioTrack)(
|
||||||
JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
|
JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
|
||||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
|
||||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
|
||||||
talk_base::scoped_refptr<AudioTrackInterface> track(
|
|
||||||
reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
|
reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
|
||||||
return stream->RemoveTrack(track);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jboolean, MediaStream_nativeRemoveVideoTrack)(
|
JOW(jboolean, MediaStream_nativeRemoveVideoTrack)(
|
||||||
JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
|
JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
|
||||||
talk_base::scoped_refptr<MediaStreamInterface> stream(
|
return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
|
||||||
reinterpret_cast<MediaStreamInterface*>(pointer));
|
|
||||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
|
||||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
||||||
return stream->RemoveTrack(track);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jstring, MediaStream_nativeLabel)(JNIEnv* jni, jclass, jlong j_p) {
|
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) {
|
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)(
|
JOW(jlong, PeerConnectionFactory_nativeCreateObserver)(
|
||||||
@@ -1183,13 +1212,15 @@ JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)(
|
|||||||
|
|
||||||
JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)(
|
JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)(
|
||||||
JNIEnv* jni, jclass) {
|
JNIEnv* jni, jclass) {
|
||||||
|
webrtc::Trace::CreateTrace();
|
||||||
talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
|
talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
|
||||||
webrtc::CreatePeerConnectionFactory());
|
webrtc::CreatePeerConnectionFactory());
|
||||||
return (jlong)factory.release();
|
return (jlong)factory.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(void, PeerConnectionFactory_freeFactory)(JNIEnv*, jclass, jlong j_p) {
|
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)(
|
JOW(jlong, PeerConnectionFactory_nativeCreateLocalMediaStream)(
|
||||||
@@ -1322,7 +1353,8 @@ JOW(jobject, PeerConnection_createDataChannel)(
|
|||||||
j_data_channel_class, j_data_channel_ctor, channel.get());
|
j_data_channel_class, j_data_channel_ctor, channel.get());
|
||||||
CHECK_EXCEPTION(jni, "error during NewObject");
|
CHECK_EXCEPTION(jni, "error during NewObject");
|
||||||
// Channel is now owned by Java object, and will be freed from there.
|
// 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;
|
return j_channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1493,59 +1525,50 @@ JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)(
|
|||||||
}
|
}
|
||||||
|
|
||||||
JOW(jstring, MediaStreamTrack_nativeId)(JNIEnv* jni, jclass, jlong j_p) {
|
JOW(jstring, MediaStreamTrack_nativeId)(JNIEnv* jni, jclass, jlong j_p) {
|
||||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
return JavaStringFromStdString(
|
||||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->id());
|
||||||
return JavaStringFromStdString(jni, p->id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jstring, MediaStreamTrack_nativeKind)(JNIEnv* jni, jclass, jlong j_p) {
|
JOW(jstring, MediaStreamTrack_nativeKind)(JNIEnv* jni, jclass, jlong j_p) {
|
||||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
return JavaStringFromStdString(
|
||||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->kind());
|
||||||
return JavaStringFromStdString(jni, p->kind());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jboolean, MediaStreamTrack_nativeEnabled)(JNIEnv* jni, jclass, jlong j_p) {
|
JOW(jboolean, MediaStreamTrack_nativeEnabled)(JNIEnv* jni, jclass, jlong j_p) {
|
||||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
return reinterpret_cast<MediaStreamTrackInterface*>(j_p)->enabled();
|
||||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
|
||||||
return p->enabled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jobject, MediaStreamTrack_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
|
JOW(jobject, MediaStreamTrack_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
|
||||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
return JavaEnumFromIndex(
|
||||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
jni,
|
||||||
return JavaEnumFromIndex(jni, "MediaStreamTrack$State", p->state());
|
"MediaStreamTrack$State",
|
||||||
|
reinterpret_cast<MediaStreamTrackInterface*>(j_p)->state());
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(jboolean, MediaStreamTrack_nativeSetState)(
|
JOW(jboolean, MediaStreamTrack_nativeSetState)(
|
||||||
JNIEnv* jni, jclass, jlong j_p, jint j_new_state) {
|
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 new_state =
|
||||||
(MediaStreamTrackInterface::TrackState)j_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)(
|
JOW(jboolean, MediaStreamTrack_nativeSetEnabled)(
|
||||||
JNIEnv* jni, jclass, jlong j_p, jboolean enabled) {
|
JNIEnv* jni, jclass, jlong j_p, jboolean enabled) {
|
||||||
talk_base::scoped_refptr<MediaStreamTrackInterface> p(
|
return reinterpret_cast<MediaStreamTrackInterface*>(j_p)
|
||||||
reinterpret_cast<MediaStreamTrackInterface*>(j_p));
|
->set_enabled(enabled);
|
||||||
return p->set_enabled(enabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(void, VideoTrack_nativeAddRenderer)(
|
JOW(void, VideoTrack_nativeAddRenderer)(
|
||||||
JNIEnv* jni, jclass,
|
JNIEnv* jni, jclass,
|
||||||
jlong j_video_track_pointer, jlong j_renderer_pointer) {
|
jlong j_video_track_pointer, jlong j_renderer_pointer) {
|
||||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->AddRenderer(
|
||||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
|
||||||
track->AddRenderer(
|
|
||||||
reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
|
reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
|
||||||
}
|
}
|
||||||
|
|
||||||
JOW(void, VideoTrack_nativeRemoveRenderer)(
|
JOW(void, VideoTrack_nativeRemoveRenderer)(
|
||||||
JNIEnv* jni, jclass,
|
JNIEnv* jni, jclass,
|
||||||
jlong j_video_track_pointer, jlong j_renderer_pointer) {
|
jlong j_video_track_pointer, jlong j_renderer_pointer) {
|
||||||
talk_base::scoped_refptr<VideoTrackInterface> track(
|
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->RemoveRenderer(
|
||||||
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
|
|
||||||
track->RemoveRenderer(
|
|
||||||
reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
|
reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class MediaSource {
|
|||||||
return nativeState(nativeSource);
|
return nativeState(nativeSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
public void dispose() {
|
||||||
free(nativeSource);
|
free(nativeSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,12 @@
|
|||||||
package org.webrtc;
|
package org.webrtc;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Java wrapper for a C++ MediaStreamInterface. */
|
/** Java wrapper for a C++ MediaStreamInterface. */
|
||||||
public class MediaStream {
|
public class MediaStream {
|
||||||
public final List<AudioTrack> audioTracks;
|
public final LinkedList<AudioTrack> audioTracks;
|
||||||
public final List<VideoTrack> videoTracks;
|
public final LinkedList<VideoTrack> videoTracks;
|
||||||
// Package-protected for LocalMediaStream and PeerConnection.
|
// Package-protected for PeerConnection.
|
||||||
final long nativeStream;
|
final long nativeStream;
|
||||||
|
|
||||||
public MediaStream(long nativeStream) {
|
public MediaStream(long nativeStream) {
|
||||||
@@ -76,14 +75,16 @@ public class MediaStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
for (AudioTrack track : audioTracks) {
|
while (!audioTracks.isEmpty()) {
|
||||||
|
AudioTrack track = audioTracks.getFirst();
|
||||||
|
removeTrack(track);
|
||||||
track.dispose();
|
track.dispose();
|
||||||
}
|
}
|
||||||
audioTracks.clear();
|
while (!videoTracks.isEmpty()) {
|
||||||
for (VideoTrack track : videoTracks) {
|
VideoTrack track = videoTracks.getFirst();
|
||||||
|
removeTrack(track);
|
||||||
track.dispose();
|
track.dispose();
|
||||||
}
|
}
|
||||||
videoTracks.clear();
|
|
||||||
free(nativeStream);
|
free(nativeStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ public class PeerConnection {
|
|||||||
public void dispose() {
|
public void dispose() {
|
||||||
close();
|
close();
|
||||||
for (MediaStream stream : localStreams) {
|
for (MediaStream stream : localStreams) {
|
||||||
|
nativeRemoveLocalStream(stream.nativeStream);
|
||||||
stream.dispose();
|
stream.dispose();
|
||||||
}
|
}
|
||||||
localStreams.clear();
|
localStreams.clear();
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class PeerConnectionFactory {
|
|||||||
public VideoSource createVideoSource(
|
public VideoSource createVideoSource(
|
||||||
VideoCapturer capturer, MediaConstraints constraints) {
|
VideoCapturer capturer, MediaConstraints constraints) {
|
||||||
return new VideoSource(nativeCreateVideoSource(
|
return new VideoSource(nativeCreateVideoSource(
|
||||||
nativeFactory, capturer.nativeVideoCapturer, constraints));
|
nativeFactory, capturer.takeNativeVideoCapturer(), constraints));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VideoTrack createVideoTrack(String id, VideoSource source) {
|
public VideoTrack createVideoTrack(String id, VideoSource source) {
|
||||||
|
|||||||
@@ -27,9 +27,9 @@
|
|||||||
|
|
||||||
package org.webrtc;
|
package org.webrtc;
|
||||||
|
|
||||||
/** Java version of VideoCapturerInterface. */
|
/** Java version of cricket::VideoCapturer. */
|
||||||
public class VideoCapturer {
|
public class VideoCapturer {
|
||||||
final long nativeVideoCapturer;
|
private long nativeVideoCapturer;
|
||||||
|
|
||||||
private VideoCapturer(long nativeVideoCapturer) {
|
private VideoCapturer(long nativeVideoCapturer) {
|
||||||
this.nativeVideoCapturer = nativeVideoCapturer;
|
this.nativeVideoCapturer = nativeVideoCapturer;
|
||||||
@@ -43,8 +43,22 @@ public class VideoCapturer {
|
|||||||
return new VideoCapturer(nativeVideoCapturer);
|
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() {
|
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);
|
private static native long nativeCreateVideoCapturer(String deviceName);
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ import java.util.LinkedList;
|
|||||||
|
|
||||||
/** Java version of VideoTrackInterface. */
|
/** Java version of VideoTrackInterface. */
|
||||||
public class VideoTrack extends MediaStreamTrack {
|
public class VideoTrack extends MediaStreamTrack {
|
||||||
private final LinkedList<VideoRenderer> renderers;
|
private final LinkedList<VideoRenderer> renderers =
|
||||||
|
new LinkedList<VideoRenderer>();
|
||||||
|
|
||||||
public VideoTrack(long nativeTrack) {
|
public VideoTrack(long nativeTrack) {
|
||||||
super(nativeTrack);
|
super(nativeTrack);
|
||||||
renderers = new LinkedList<VideoRenderer>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRenderer(VideoRenderer renderer) {
|
public void addRenderer(VideoRenderer renderer) {
|
||||||
@@ -55,8 +55,11 @@ public class VideoTrack extends MediaStreamTrack {
|
|||||||
while (!renderers.isEmpty()) {
|
while (!renderers.isEmpty()) {
|
||||||
removeRenderer(renderers.getFirst());
|
removeRenderer(renderers.getFirst());
|
||||||
}
|
}
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static native void free(long nativeTrack);
|
||||||
|
|
||||||
private static native void nativeAddRenderer(
|
private static native void nativeAddRenderer(
|
||||||
long nativeTrack, long nativeRenderer);
|
long nativeTrack, long nativeRenderer);
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.webrtc.PeerConnection.IceConnectionState;
|
|||||||
import org.webrtc.PeerConnection.IceGatheringState;
|
import org.webrtc.PeerConnection.IceGatheringState;
|
||||||
import org.webrtc.PeerConnection.SignalingState;
|
import org.webrtc.PeerConnection.SignalingState;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@@ -194,6 +195,7 @@ public class PeerConnectionTest extends TestCase {
|
|||||||
expectedAddStreamLabels.add(label);
|
expectedAddStreamLabels.add(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public synchronized void onAddStream(MediaStream stream) {
|
public synchronized void onAddStream(MediaStream stream) {
|
||||||
assertEquals(expectedAddStreamLabels.removeFirst(), stream.label());
|
assertEquals(expectedAddStreamLabels.removeFirst(), stream.label());
|
||||||
assertEquals(1, stream.videoTracks.size());
|
assertEquals(1, stream.videoTracks.size());
|
||||||
@@ -212,6 +214,7 @@ public class PeerConnectionTest extends TestCase {
|
|||||||
expectedRemoveStreamLabels.add(label);
|
expectedRemoveStreamLabels.add(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public synchronized void onRemoveStream(MediaStream stream) {
|
public synchronized void onRemoveStream(MediaStream stream) {
|
||||||
assertEquals(expectedRemoveStreamLabels.removeFirst(), stream.label());
|
assertEquals(expectedRemoveStreamLabels.removeFirst(), stream.label());
|
||||||
WeakReference<VideoRenderer> renderer = renderers.remove(stream);
|
WeakReference<VideoRenderer> renderer = renderers.remove(stream);
|
||||||
@@ -477,11 +480,13 @@ public class PeerConnectionTest extends TestCase {
|
|||||||
public void testCompleteSession() throws Exception {
|
public void testCompleteSession() throws Exception {
|
||||||
// Uncomment to get ALL WebRTC tracing and SENSITIVE libjingle logging.
|
// Uncomment to get ALL WebRTC tracing and SENSITIVE libjingle logging.
|
||||||
// Logging.enableTracing(
|
// Logging.enableTracing(
|
||||||
// "/tmp/AMI-nope.txt",
|
// "/tmp/PeerConnectionTest-log.txt",
|
||||||
// EnumSet.of(Logging.TraceLevel.TRACE_ALL),
|
// EnumSet.of(Logging.TraceLevel.TRACE_ALL),
|
||||||
// Logging.Severity.LS_SENSITIVE);
|
// Logging.Severity.LS_SENSITIVE);
|
||||||
|
|
||||||
CountDownLatch testDone = new CountDownLatch(1);
|
CountDownLatch testDone = new CountDownLatch(1);
|
||||||
|
System.gc(); // Encourage any GC-related threads to start up.
|
||||||
|
TreeSet<String> threadsBeforeTest = allThreads();
|
||||||
|
|
||||||
PeerConnectionFactory factory = new PeerConnectionFactory();
|
PeerConnectionFactory factory = new PeerConnectionFactory();
|
||||||
MediaConstraints pcConstraints = new MediaConstraints();
|
MediaConstraints pcConstraints = new MediaConstraints();
|
||||||
@@ -688,7 +693,11 @@ public class PeerConnectionTest extends TestCase {
|
|||||||
offeringPC = null;
|
offeringPC = null;
|
||||||
shutdownPC(answeringPC, answeringExpectations);
|
shutdownPC(answeringPC, answeringExpectations);
|
||||||
answeringPC = null;
|
answeringPC = null;
|
||||||
|
videoSource.dispose();
|
||||||
|
factory.dispose();
|
||||||
System.gc();
|
System.gc();
|
||||||
|
TreeSet<String> threadsAfterTest = allThreads();
|
||||||
|
assertEquals(threadsBeforeTest, threadsAfterTest);
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,4 +729,28 @@ public class PeerConnectionTest extends TestCase {
|
|||||||
|
|
||||||
pc.dispose();
|
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;
|
virtual bool set_enabled(bool enable) = 0;
|
||||||
// These methods should be called by implementation only.
|
// These methods should be called by implementation only.
|
||||||
virtual bool set_state(TrackState new_state) = 0;
|
virtual bool set_state(TrackState new_state) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~MediaStreamTrackInterface() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Interface for rendering VideoFrames from a VideoTrack
|
// Interface for rendering VideoFrames from a VideoTrack
|
||||||
|
|||||||
@@ -354,10 +354,10 @@ bool PeerConnection::AddStream(MediaStreamInterface* local_stream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::RemoveStream(MediaStreamInterface* local_stream) {
|
void PeerConnection::RemoveStream(MediaStreamInterface* local_stream) {
|
||||||
|
mediastream_signaling_->RemoveLocalStream(local_stream);
|
||||||
if (IsClosed()) {
|
if (IsClosed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mediastream_signaling_->RemoveLocalStream(local_stream);
|
|
||||||
observer_->OnRenegotiationNeeded();
|
observer_->OnRenegotiationNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ import java.util.List;
|
|||||||
public class AppRTCDemoActivity extends Activity
|
public class AppRTCDemoActivity extends Activity
|
||||||
implements AppRTCClient.IceServersObserver {
|
implements AppRTCClient.IceServersObserver {
|
||||||
private static final String TAG = "AppRTCDemoActivity";
|
private static final String TAG = "AppRTCDemoActivity";
|
||||||
|
private PeerConnectionFactory factory;
|
||||||
|
private VideoSource videoSource;
|
||||||
private PeerConnection pc;
|
private PeerConnection pc;
|
||||||
private final PCObserver pcObserver = new PCObserver();
|
private final PCObserver pcObserver = new PCObserver();
|
||||||
private final SDPObserver sdpObserver = new SDPObserver();
|
private final SDPObserver sdpObserver = new SDPObserver();
|
||||||
@@ -183,8 +185,7 @@ public class AppRTCDemoActivity extends Activity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIceServers(List<PeerConnection.IceServer> iceServers) {
|
public void onIceServers(List<PeerConnection.IceServer> iceServers) {
|
||||||
PeerConnectionFactory factory = new PeerConnectionFactory();
|
factory = new PeerConnectionFactory();
|
||||||
|
|
||||||
pc = factory.createPeerConnection(
|
pc = factory.createPeerConnection(
|
||||||
iceServers, appRtcClient.pcConstraints(), pcObserver);
|
iceServers, appRtcClient.pcConstraints(), pcObserver);
|
||||||
|
|
||||||
@@ -217,7 +218,7 @@ public class AppRTCDemoActivity extends Activity
|
|||||||
{
|
{
|
||||||
logAndToast("Creating local video source...");
|
logAndToast("Creating local video source...");
|
||||||
VideoCapturer capturer = getVideoCapturer();
|
VideoCapturer capturer = getVideoCapturer();
|
||||||
VideoSource videoSource = factory.createVideoSource(
|
videoSource = factory.createVideoSource(
|
||||||
capturer, appRtcClient.videoConstraints());
|
capturer, appRtcClient.videoConstraints());
|
||||||
MediaStream lMS = factory.createLocalMediaStream("ARDAMS");
|
MediaStream lMS = factory.createLocalMediaStream("ARDAMS");
|
||||||
VideoTrack videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
|
VideoTrack videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
|
||||||
@@ -485,6 +486,14 @@ public class AppRTCDemoActivity extends Activity
|
|||||||
appRtcClient.disconnect();
|
appRtcClient.disconnect();
|
||||||
appRtcClient = null;
|
appRtcClient = null;
|
||||||
}
|
}
|
||||||
|
if (videoSource != null) {
|
||||||
|
videoSource.dispose();
|
||||||
|
videoSource = null;
|
||||||
|
}
|
||||||
|
if (factory != null) {
|
||||||
|
factory.dispose();
|
||||||
|
factory = null;
|
||||||
|
}
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ bool TCPPort::Init() {
|
|||||||
|
|
||||||
TCPPort::~TCPPort() {
|
TCPPort::~TCPPort() {
|
||||||
delete socket_;
|
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,
|
Connection* TCPPort::CreateConnection(const Candidate& address,
|
||||||
|
|||||||
Reference in New Issue
Block a user