Make PeerConnectionClient non-singleton.

Ownership of EglBase is moved to PeerConnectionClient.

BUG=webrtc:8135

Review-Url: https://codereview.webrtc.org/3007893002
Cr-Commit-Position: refs/heads/master@{#19634}
This commit is contained in:
sakal
2017-08-31 08:03:46 -07:00
committed by Commit Bot
parent da194e79c4
commit 85d7650ab6
3 changed files with 46 additions and 102 deletions

View File

@ -45,7 +45,6 @@ import org.appspot.apprtc.PeerConnectionClient.PeerConnectionParameters;
import org.webrtc.Camera1Enumerator; import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator; import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator; import org.webrtc.CameraEnumerator;
import org.webrtc.EglBase;
import org.webrtc.FileVideoCapturer; import org.webrtc.FileVideoCapturer;
import org.webrtc.IceCandidate; import org.webrtc.IceCandidate;
import org.webrtc.Logging; import org.webrtc.Logging;
@ -162,7 +161,6 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
private AppRTCClient appRtcClient; private AppRTCClient appRtcClient;
private SignalingParameters signalingParameters; private SignalingParameters signalingParameters;
private AppRTCAudioManager audioManager = null; private AppRTCAudioManager audioManager = null;
private EglBase rootEglBase;
private SurfaceViewRenderer pipRenderer; private SurfaceViewRenderer pipRenderer;
private SurfaceViewRenderer fullscreenRenderer; private SurfaceViewRenderer fullscreenRenderer;
private VideoFileRenderer videoFileRenderer; private VideoFileRenderer videoFileRenderer;
@ -234,9 +232,11 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
final Intent intent = getIntent(); final Intent intent = getIntent();
// Create peer connection client.
peerConnectionClient = new PeerConnectionClient();
// Create video renderers. // Create video renderers.
rootEglBase = EglBase.create(); pipRenderer.init(peerConnectionClient.getRenderContext(), null);
pipRenderer.init(rootEglBase.getEglBaseContext(), null);
pipRenderer.setScalingType(ScalingType.SCALE_ASPECT_FIT); pipRenderer.setScalingType(ScalingType.SCALE_ASPECT_FIT);
String saveRemoteVideoToFile = intent.getStringExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE); String saveRemoteVideoToFile = intent.getStringExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE);
@ -245,15 +245,15 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
int videoOutWidth = intent.getIntExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_WIDTH, 0); int videoOutWidth = intent.getIntExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_WIDTH, 0);
int videoOutHeight = intent.getIntExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT, 0); int videoOutHeight = intent.getIntExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT, 0);
try { try {
videoFileRenderer = new VideoFileRenderer( videoFileRenderer = new VideoFileRenderer(saveRemoteVideoToFile, videoOutWidth,
saveRemoteVideoToFile, videoOutWidth, videoOutHeight, rootEglBase.getEglBaseContext()); videoOutHeight, peerConnectionClient.getRenderContext());
remoteRenderers.add(videoFileRenderer); remoteRenderers.add(videoFileRenderer);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(
"Failed to open video file for output: " + saveRemoteVideoToFile, e); "Failed to open video file for output: " + saveRemoteVideoToFile, e);
} }
} }
fullscreenRenderer.init(rootEglBase.getEglBaseContext(), null); fullscreenRenderer.init(peerConnectionClient.getRenderContext(), null);
fullscreenRenderer.setScalingType(ScalingType.SCALE_ASPECT_FILL); fullscreenRenderer.setScalingType(ScalingType.SCALE_ASPECT_FILL);
pipRenderer.setZOrderMediaOverlay(true); pipRenderer.setZOrderMediaOverlay(true);
@ -368,7 +368,6 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
}, runTimeMs); }, runTimeMs);
} }
peerConnectionClient = PeerConnectionClient.getInstance();
if (loopback) { if (loopback) {
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.networkIgnoreMask = 0; options.networkIgnoreMask = 0;
@ -507,7 +506,6 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
logToast.cancel(); logToast.cancel();
} }
activityRunning = false; activityRunning = false;
rootEglBase.release();
super.onDestroy(); super.onDestroy();
} }
@ -623,10 +621,6 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
appRtcClient.disconnectFromRoom(); appRtcClient.disconnectFromRoom();
appRtcClient = null; appRtcClient = null;
} }
if (peerConnectionClient != null) {
peerConnectionClient.close();
peerConnectionClient = null;
}
if (pipRenderer != null) { if (pipRenderer != null) {
pipRenderer.release(); pipRenderer.release();
pipRenderer = null; pipRenderer = null;
@ -639,6 +633,10 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
fullscreenRenderer.release(); fullscreenRenderer.release();
fullscreenRenderer = null; fullscreenRenderer = null;
} }
if (peerConnectionClient != null) {
peerConnectionClient.close();
peerConnectionClient = null;
}
if (audioManager != null) { if (audioManager != null) {
audioManager.stop(); audioManager.stop();
audioManager = null; audioManager = null;
@ -747,8 +745,8 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven
if (peerConnectionParameters.videoCallEnabled) { if (peerConnectionParameters.videoCallEnabled) {
videoCapturer = createVideoCapturer(); videoCapturer = createVideoCapturer();
} }
peerConnectionClient.createPeerConnection(rootEglBase.getEglBaseContext(), localProxyRenderer, peerConnectionClient.createPeerConnection(
remoteRenderers, videoCapturer, signalingParameters); localProxyRenderer, remoteRenderers, videoCapturer, signalingParameters);
if (signalingParameters.initiator) { if (signalingParameters.initiator) {
logAndToast("Creating OFFER..."); logAndToast("Creating OFFER...");

View File

@ -103,11 +103,15 @@ public class PeerConnectionClient {
private static final int HD_VIDEO_HEIGHT = 720; private static final int HD_VIDEO_HEIGHT = 720;
private static final int BPS_IN_KBPS = 1000; private static final int BPS_IN_KBPS = 1000;
private static final PeerConnectionClient instance = new PeerConnectionClient(); // Executor thread is started once in private ctor and is used for all
// peer connection API calls to ensure new peer connection factory is
// created on the same thread as previously destroyed factory.
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
private final PCObserver pcObserver = new PCObserver(); private final PCObserver pcObserver = new PCObserver();
private final SDPObserver sdpObserver = new SDPObserver(); private final SDPObserver sdpObserver = new SDPObserver();
private final ExecutorService executor;
private final EglBase rootEglBase;
private PeerConnectionFactory factory; private PeerConnectionFactory factory;
private PeerConnection peerConnection; private PeerConnection peerConnection;
PeerConnectionFactory.Options options = null; PeerConnectionFactory.Options options = null;
@ -288,15 +292,8 @@ public class PeerConnectionClient {
void onPeerConnectionError(final String description); void onPeerConnectionError(final String description);
} }
private PeerConnectionClient() { public PeerConnectionClient() {
// Executor thread is started once in private ctor and is used for all rootEglBase = EglBase.create();
// peer connection API calls to ensure new peer connection factory is
// created on the same thread as previously destroyed factory.
executor = Executors.newSingleThreadExecutor();
}
public static PeerConnectionClient getInstance() {
return instance;
} }
public void setPeerConnectionFactoryOptions(PeerConnectionFactory.Options options) { public void setPeerConnectionFactoryOptions(PeerConnectionFactory.Options options) {
@ -335,15 +332,15 @@ public class PeerConnectionClient {
}); });
} }
public void createPeerConnection(final EglBase.Context renderEGLContext, public void createPeerConnection(final VideoRenderer.Callbacks localRender,
final VideoRenderer.Callbacks localRender, final VideoRenderer.Callbacks remoteRender, final VideoRenderer.Callbacks remoteRender, final VideoCapturer videoCapturer,
final VideoCapturer videoCapturer, final SignalingParameters signalingParameters) { final SignalingParameters signalingParameters) {
createPeerConnection(renderEGLContext, localRender, Collections.singletonList(remoteRender), createPeerConnection(
videoCapturer, signalingParameters); localRender, Collections.singletonList(remoteRender), videoCapturer, signalingParameters);
} }
public void createPeerConnection(final EglBase.Context renderEGLContext, public void createPeerConnection(final VideoRenderer.Callbacks localRender,
final VideoRenderer.Callbacks localRender, final List<VideoRenderer.Callbacks> remoteRenders, final List<VideoRenderer.Callbacks> remoteRenders, final VideoCapturer videoCapturer,
final VideoCapturer videoCapturer, final SignalingParameters signalingParameters) { final SignalingParameters signalingParameters) {
if (peerConnectionParameters == null) { if (peerConnectionParameters == null) {
Log.e(TAG, "Creating peer connection without initializing factory."); Log.e(TAG, "Creating peer connection without initializing factory.");
return; return;
@ -357,7 +354,7 @@ public class PeerConnectionClient {
public void run() { public void run() {
try { try {
createMediaConstraintsInternal(); createMediaConstraintsInternal();
createPeerConnectionInternal(renderEGLContext); createPeerConnectionInternal();
} catch (Exception e) { } catch (Exception e) {
reportError("Failed to create peer connection: " + e.getMessage()); reportError("Failed to create peer connection: " + e.getMessage());
throw e; throw e;
@ -583,7 +580,7 @@ public class PeerConnectionClient {
} }
} }
private void createPeerConnectionInternal(EglBase.Context renderEGLContext) { private void createPeerConnectionInternal() {
if (factory == null || isError) { if (factory == null || isError) {
Log.e(TAG, "Peerconnection factory is not created"); Log.e(TAG, "Peerconnection factory is not created");
return; return;
@ -594,8 +591,8 @@ public class PeerConnectionClient {
queuedRemoteCandidates = new LinkedList<IceCandidate>(); queuedRemoteCandidates = new LinkedList<IceCandidate>();
if (videoCallEnabled) { if (videoCallEnabled) {
Log.d(TAG, "EGLContext: " + renderEGLContext); factory.setVideoHwAccelerationOptions(
factory.setVideoHwAccelerationOptions(renderEGLContext, renderEGLContext); rootEglBase.getEglBaseContext(), rootEglBase.getEglBaseContext());
} }
PeerConnection.RTCConfiguration rtcConfig = PeerConnection.RTCConfiguration rtcConfig =
@ -698,6 +695,7 @@ public class PeerConnectionClient {
factory = null; factory = null;
} }
options = null; options = null;
rootEglBase.release();
Log.d(TAG, "Closing peer connection done."); Log.d(TAG, "Closing peer connection done.");
events.onPeerConnectionClosed(); events.onPeerConnectionClosed();
PeerConnectionFactory.stopInternalTracingCapture(); PeerConnectionFactory.stopInternalTracingCapture();
@ -713,6 +711,10 @@ public class PeerConnectionClient {
return videoWidth * videoHeight >= 1280 * 720; return videoWidth * videoHeight >= 1280 * 720;
} }
public EglBase.Context getRenderContext() {
return rootEglBase.getEglBaseContext();
}
private void getStats() { private void getStats() {
if (peerConnection == null || isError) { if (peerConnection == null || isError) {
return; return;

View File

@ -35,7 +35,6 @@ import org.junit.runner.RunWith;
import org.webrtc.Camera1Enumerator; import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator; import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator; import org.webrtc.CameraEnumerator;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate; import org.webrtc.IceCandidate;
import org.webrtc.MediaCodecVideoEncoder; import org.webrtc.MediaCodecVideoEncoder;
import org.webrtc.PeerConnection; import org.webrtc.PeerConnection;
@ -73,9 +72,6 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
private volatile PeerConnectionClient pcClient; private volatile PeerConnectionClient pcClient;
private volatile boolean loopback; private volatile boolean loopback;
// EGL context that can be used by hardware video decoders to decode to a texture.
private EglBase eglBase;
// These are protected by their respective event objects. // These are protected by their respective event objects.
private ExecutorService signalingExecutor; private ExecutorService signalingExecutor;
private boolean isClosed; private boolean isClosed;
@ -242,22 +238,21 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
PeerConnectionClient createPeerConnectionClient(MockRenderer localRenderer, PeerConnectionClient createPeerConnectionClient(MockRenderer localRenderer,
MockRenderer remoteRenderer, PeerConnectionParameters peerConnectionParameters, MockRenderer remoteRenderer, PeerConnectionParameters peerConnectionParameters,
VideoCapturer videoCapturer, EglBase.Context eglContext) { VideoCapturer videoCapturer) {
List<PeerConnection.IceServer> iceServers = new LinkedList<PeerConnection.IceServer>(); List<PeerConnection.IceServer> iceServers = new LinkedList<PeerConnection.IceServer>();
SignalingParameters signalingParameters = SignalingParameters signalingParameters =
new SignalingParameters(iceServers, true, // iceServers, initiator. new SignalingParameters(iceServers, true, // iceServers, initiator.
null, null, null, // clientId, wssUrl, wssPostUrl. null, null, null, // clientId, wssUrl, wssPostUrl.
null, null); // offerSdp, iceCandidates. null, null); // offerSdp, iceCandidates.
PeerConnectionClient client = PeerConnectionClient.getInstance(); PeerConnectionClient client = new PeerConnectionClient();
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.networkIgnoreMask = 0; options.networkIgnoreMask = 0;
options.disableNetworkMonitor = true; options.disableNetworkMonitor = true;
client.setPeerConnectionFactoryOptions(options); client.setPeerConnectionFactoryOptions(options);
client.createPeerConnectionFactory( client.createPeerConnectionFactory(
InstrumentationRegistry.getTargetContext(), peerConnectionParameters, this); InstrumentationRegistry.getTargetContext(), peerConnectionParameters, this);
client.createPeerConnection( client.createPeerConnection(localRenderer, remoteRenderer, videoCapturer, signalingParameters);
eglContext, localRenderer, remoteRenderer, videoCapturer, signalingParameters);
client.createOffer(); client.createOffer();
return client; return client;
} }
@ -327,17 +322,11 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
@Before @Before
public void setUp() { public void setUp() {
signalingExecutor = Executors.newSingleThreadExecutor(); signalingExecutor = Executors.newSingleThreadExecutor();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
eglBase = EglBase.create();
}
} }
@After @After
public void tearDown() { public void tearDown() {
signalingExecutor.shutdown(); signalingExecutor.shutdown();
if (eglBase != null) {
eglBase.release();
}
} }
@Test @Test
@ -347,7 +336,7 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
MockRenderer localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME); MockRenderer localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
pcClient = createPeerConnectionClient(localRenderer, new MockRenderer(0, null), pcClient = createPeerConnectionClient(localRenderer, new MockRenderer(0, null),
createParametersForVideoCall(VIDEO_CODEC_VP8), createParametersForVideoCall(VIDEO_CODEC_VP8),
createCameraCapturer(false /* captureToTexture */), null); createCameraCapturer(false /* captureToTexture */));
// Wait for local SDP and ice candidates set events. // Wait for local SDP and ice candidates set events.
assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT)); assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT));
@ -375,8 +364,7 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
} else { } else {
Log.d(TAG, "testLoopback for audio."); Log.d(TAG, "testLoopback for audio.");
} }
pcClient = createPeerConnectionClient(localRenderer, remoteRenderer, parameters, videoCapturer, pcClient = createPeerConnectionClient(localRenderer, remoteRenderer, parameters, videoCapturer);
decodeToTexture ? eglBase.getEglBaseContext() : null);
// Wait for local SDP, rename it to answer and set as remote SDP. // Wait for local SDP, rename it to answer and set as remote SDP.
assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT)); assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT));
@ -481,50 +469,6 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
createCameraCapturer(true /* captureToTexture */), true /* decodeToTexture */); createCameraCapturer(true /* captureToTexture */), true /* decodeToTexture */);
} }
// Test that a call can be setup even if the EGL context used during initialization is
// released before the Video codecs are created. The HW encoder and decoder is setup to use
// textures.
@Test
@SmallTest
public void testLoopbackEglContextReleasedAfterCreatingPc() throws InterruptedException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Log.i(TAG, "Decode to textures is not supported. Requires SDK version 19");
return;
}
loopback = true;
PeerConnectionParameters parameters = createParametersForVideoCall(VIDEO_CODEC_VP8);
MockRenderer localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
MockRenderer remoteRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, REMOTE_RENDERER_NAME);
pcClient = createPeerConnectionClient(localRenderer, remoteRenderer, parameters,
createCameraCapturer(true /* captureToTexture */), eglBase.getEglBaseContext());
// Wait for local SDP, rename it to answer and set as remote SDP.
assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT));
// Release the EGL context used for creating the PeerConnectionClient.
// Since createPeerConnectionClient is asynchronous, we must wait for the local
// SessionDescription.
eglBase.release();
eglBase = null;
SessionDescription remoteSdp = new SessionDescription(
SessionDescription.Type.fromCanonicalForm("answer"), localSdp.description);
pcClient.setRemoteDescription(remoteSdp);
// Wait for ICE connection.
assertTrue("ICE connection failure.", waitForIceConnected(ICE_CONNECTION_WAIT_TIMEOUT));
// Check that local and remote video frames were rendered.
assertTrue(
"Local video frames were not rendered.", localRenderer.waitForFramesRendered(WAIT_TIMEOUT));
assertTrue("Remote video frames were not rendered.",
remoteRenderer.waitForFramesRendered(WAIT_TIMEOUT));
pcClient.close();
assertTrue(waitForPeerConnectionClosed(WAIT_TIMEOUT));
Log.d(TAG, "testLoopback done.");
}
@Test @Test
@SmallTest @SmallTest
public void testLoopbackH264CaptureToTexture() throws InterruptedException { public void testLoopbackH264CaptureToTexture() throws InterruptedException {
@ -555,7 +499,7 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
pcClient = createPeerConnectionClient(localRenderer, remoteRenderer, pcClient = createPeerConnectionClient(localRenderer, remoteRenderer,
createParametersForVideoCall(VIDEO_CODEC_VP8), createParametersForVideoCall(VIDEO_CODEC_VP8),
createCameraCapturer(false /* captureToTexture */), null); createCameraCapturer(false /* captureToTexture */));
// Wait for local SDP, rename it to answer and set as remote SDP. // Wait for local SDP, rename it to answer and set as remote SDP.
assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT)); assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT));
@ -603,7 +547,7 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
pcClient = createPeerConnectionClient(localRenderer, remoteRenderer, pcClient = createPeerConnectionClient(localRenderer, remoteRenderer,
createParametersForVideoCall(VIDEO_CODEC_VP8), createParametersForVideoCall(VIDEO_CODEC_VP8),
createCameraCapturer(false /* captureToTexture */), null); createCameraCapturer(false /* captureToTexture */));
// Wait for local SDP, rename it to answer and set as remote SDP. // Wait for local SDP, rename it to answer and set as remote SDP.
assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT)); assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT));
@ -652,7 +596,7 @@ public class PeerConnectionClientTest implements PeerConnectionEvents {
pcClient = createPeerConnectionClient(localRenderer, remoteRenderer, pcClient = createPeerConnectionClient(localRenderer, remoteRenderer,
createParametersForVideoCall(VIDEO_CODEC_VP8), createParametersForVideoCall(VIDEO_CODEC_VP8),
createCameraCapturer(false /* captureToTexture */), null); createCameraCapturer(false /* captureToTexture */));
// Wait for local SDP, rename it to answer and set as remote SDP. // Wait for local SDP, rename it to answer and set as remote SDP.
assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT)); assertTrue("Local SDP was not set.", waitForLocalSDP(WAIT_TIMEOUT));