diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index fa3ad1b451..4e55e4774f 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -56,8 +56,11 @@ #undef JNIEXPORT #define JNIEXPORT __attribute__((visibility("default"))) +#include #include #include +#include +#include #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(env); CHECK(!pthread_setspecific(g_jni_ptr, jni), "pthread_setspecific"); @@ -512,8 +549,10 @@ class PCOJava : public PeerConnectionObserver { ScopedLocalRef 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 audio_tracks(jni(), GetObjectField( jni(), *j_stream, audio_tracks_id)); jmethodID add = GetMethodID(jni(), @@ -531,8 +570,10 @@ class PCOJava : public PeerConnectionObserver { ScopedLocalRef 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 video_tracks(jni(), GetObjectField( jni(), *j_stream, video_tracks_id)); jmethodID add = GetMethodID(jni(), @@ -569,13 +610,18 @@ class PCOJava : public PeerConnectionObserver { ScopedLocalRef 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 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( - reinterpret_cast(j_d)); + return reinterpret_cast(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(j_p)->Release(); + CHECK_RELEASE(reinterpret_cast(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(j_p)->Release(); + CHECK_RELEASE(reinterpret_cast(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(j_p)->Release(); + CHECK_RELEASE(reinterpret_cast(j_p)); } JOW(jboolean, MediaStream_nativeAddAudioTrack)( JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) { - talk_base::scoped_refptr stream( - reinterpret_cast(pointer)); - talk_base::scoped_refptr track( + return reinterpret_cast(pointer)->AddTrack( reinterpret_cast(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 stream( - reinterpret_cast(pointer)); - talk_base::scoped_refptr track( - reinterpret_cast(j_video_track_pointer)); - return stream->AddTrack(track); + return reinterpret_cast(pointer) + ->AddTrack(reinterpret_cast(j_video_track_pointer)); } JOW(jboolean, MediaStream_nativeRemoveAudioTrack)( JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) { - talk_base::scoped_refptr stream( - reinterpret_cast(pointer)); - talk_base::scoped_refptr track( + return reinterpret_cast(pointer)->RemoveTrack( reinterpret_cast(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 stream( - reinterpret_cast(pointer)); - talk_base::scoped_refptr track( + return reinterpret_cast(pointer)->RemoveTrack( reinterpret_cast(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(j_p)->Release(); + CHECK_RELEASE(reinterpret_cast(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 factory( webrtc::CreatePeerConnectionFactory()); return (jlong)factory.release(); } JOW(void, PeerConnectionFactory_freeFactory)(JNIEnv*, jclass, jlong j_p) { - reinterpret_cast(j_p)->Release(); + CHECK_RELEASE(reinterpret_cast(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 p( - reinterpret_cast(j_p)); - return JavaStringFromStdString(jni, p->id()); + return JavaStringFromStdString( + jni, reinterpret_cast(j_p)->id()); } JOW(jstring, MediaStreamTrack_nativeKind)(JNIEnv* jni, jclass, jlong j_p) { - talk_base::scoped_refptr p( - reinterpret_cast(j_p)); - return JavaStringFromStdString(jni, p->kind()); + return JavaStringFromStdString( + jni, reinterpret_cast(j_p)->kind()); } JOW(jboolean, MediaStreamTrack_nativeEnabled)(JNIEnv* jni, jclass, jlong j_p) { - talk_base::scoped_refptr p( - reinterpret_cast(j_p)); - return p->enabled(); + return reinterpret_cast(j_p)->enabled(); } JOW(jobject, MediaStreamTrack_nativeState)(JNIEnv* jni, jclass, jlong j_p) { - talk_base::scoped_refptr p( - reinterpret_cast(j_p)); - return JavaEnumFromIndex(jni, "MediaStreamTrack$State", p->state()); + return JavaEnumFromIndex( + jni, + "MediaStreamTrack$State", + reinterpret_cast(j_p)->state()); } JOW(jboolean, MediaStreamTrack_nativeSetState)( JNIEnv* jni, jclass, jlong j_p, jint j_new_state) { - talk_base::scoped_refptr p( - reinterpret_cast(j_p)); MediaStreamTrackInterface::TrackState new_state = (MediaStreamTrackInterface::TrackState)j_new_state; - return p->set_state(new_state); + return reinterpret_cast(j_p) + ->set_state(new_state); } JOW(jboolean, MediaStreamTrack_nativeSetEnabled)( JNIEnv* jni, jclass, jlong j_p, jboolean enabled) { - talk_base::scoped_refptr p( - reinterpret_cast(j_p)); - return p->set_enabled(enabled); + return reinterpret_cast(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 track( - reinterpret_cast(j_video_track_pointer)); - track->AddRenderer( + reinterpret_cast(j_video_track_pointer)->AddRenderer( reinterpret_cast(j_renderer_pointer)); } JOW(void, VideoTrack_nativeRemoveRenderer)( JNIEnv* jni, jclass, jlong j_video_track_pointer, jlong j_renderer_pointer) { - talk_base::scoped_refptr track( - reinterpret_cast(j_video_track_pointer)); - track->RemoveRenderer( + reinterpret_cast(j_video_track_pointer)->RemoveRenderer( reinterpret_cast(j_renderer_pointer)); } diff --git a/talk/app/webrtc/java/src/org/webrtc/MediaSource.java b/talk/app/webrtc/java/src/org/webrtc/MediaSource.java index 2949049000..aba7b87ef3 100644 --- a/talk/app/webrtc/java/src/org/webrtc/MediaSource.java +++ b/talk/app/webrtc/java/src/org/webrtc/MediaSource.java @@ -45,7 +45,7 @@ public class MediaSource { return nativeState(nativeSource); } - void dispose() { + public void dispose() { free(nativeSource); } diff --git a/talk/app/webrtc/java/src/org/webrtc/MediaStream.java b/talk/app/webrtc/java/src/org/webrtc/MediaStream.java index 431c561582..6b3c001a7c 100644 --- a/talk/app/webrtc/java/src/org/webrtc/MediaStream.java +++ b/talk/app/webrtc/java/src/org/webrtc/MediaStream.java @@ -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 audioTracks; - public final List videoTracks; - // Package-protected for LocalMediaStream and PeerConnection. + public final LinkedList audioTracks; + public final LinkedList 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); } diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java index 0a0a7f636e..1cd4dc5add 100644 --- a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java +++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java @@ -176,6 +176,7 @@ public class PeerConnection { public void dispose() { close(); for (MediaStream stream : localStreams) { + nativeRemoveLocalStream(stream.nativeStream); stream.dispose(); } localStreams.clear(); diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java index 03ed03f8e8..3125cfc11a 100644 --- a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java +++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java @@ -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) { diff --git a/talk/app/webrtc/java/src/org/webrtc/VideoCapturer.java b/talk/app/webrtc/java/src/org/webrtc/VideoCapturer.java index eab5797bfd..4b3585cc2c 100644 --- a/talk/app/webrtc/java/src/org/webrtc/VideoCapturer.java +++ b/talk/app/webrtc/java/src/org/webrtc/VideoCapturer.java @@ -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); diff --git a/talk/app/webrtc/java/src/org/webrtc/VideoTrack.java b/talk/app/webrtc/java/src/org/webrtc/VideoTrack.java index 90e5c95658..403590fa0e 100644 --- a/talk/app/webrtc/java/src/org/webrtc/VideoTrack.java +++ b/talk/app/webrtc/java/src/org/webrtc/VideoTrack.java @@ -31,11 +31,11 @@ import java.util.LinkedList; /** Java version of VideoTrackInterface. */ public class VideoTrack extends MediaStreamTrack { - private final LinkedList renderers; + private final LinkedList renderers = + new LinkedList(); public VideoTrack(long nativeTrack) { super(nativeTrack); - renderers = new LinkedList(); } 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); diff --git a/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java b/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java index dd4ef487fb..3d3e6c44ef 100644 --- a/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java +++ b/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java @@ -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 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 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 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 allThreads() { + TreeSet threads = new TreeSet(); + // 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 strings) { + StringBuilder builder = new StringBuilder(); + for (String s : strings) { + if (builder.length() > 0) { + builder.append(separator); + } + builder.append(s); + } + return builder.toString(); + } } diff --git a/talk/app/webrtc/mediastreaminterface.h b/talk/app/webrtc/mediastreaminterface.h index 0d3e39d892..c16ac9d600 100644 --- a/talk/app/webrtc/mediastreaminterface.h +++ b/talk/app/webrtc/mediastreaminterface.h @@ -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 diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc index 2be76f439a..77a5da65eb 100644 --- a/talk/app/webrtc/peerconnection.cc +++ b/talk/app/webrtc/peerconnection.cc @@ -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(); } diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java index 66187538df..43f5f55acb 100644 --- a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java @@ -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 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(); } } diff --git a/talk/p2p/base/tcpport.cc b/talk/p2p/base/tcpport.cc index 037abeccc8..5f4ccc48ab 100644 --- a/talk/p2p/base/tcpport.cc +++ b/talk/p2p/base/tcpport.cc @@ -67,6 +67,10 @@ bool TCPPort::Init() { TCPPort::~TCPPort() { delete socket_; + std::list::iterator it; + for (it = incoming_.begin(); it != incoming_.end(); ++it) + delete it->socket; + incoming_.clear(); } Connection* TCPPort::CreateConnection(const Candidate& address,