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:
fischman@webrtc.org 2013-08-12 23:26:21 +00:00
parent a5506690b4
commit 32001ef124
12 changed files with 174 additions and 83 deletions

View File

@ -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));
}

View File

@ -45,7 +45,7 @@ public class MediaSource {
return nativeState(nativeSource);
}
void dispose() {
public void dispose() {
free(nativeSource);
}

View File

@ -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);
}

View File

@ -176,6 +176,7 @@ public class PeerConnection {
public void dispose() {
close();
for (MediaStream stream : localStreams) {
nativeRemoveLocalStream(stream.nativeStream);
stream.dispose();
}
localStreams.clear();

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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,