0
votes

I am trying to build a video call app which has a feature of screen sharing. The users can share their screen during the call. I'm using WebRTC SDK to meet my purpose, but they have a solution for screen share when the call starts but not for screen share while the call is ongoing. One can tick the screen sharing option and can start the call but cannot start screen sharing during the call.

I added a button on the CallActivity screen which on click calls MediaProjection Class of Android to cast the screen but the casted screen is not being shown remotly.

public void onScreenShare(boolean isScreenShared) {
    screencaptureEnabled = isScreenShared;
    if (screencaptureEnabled && videoWidth == 0 && videoHeight == 0) {
        DisplayMetrics displayMetrics = getDisplayMetrics();
        videoWidth = displayMetrics.widthPixels;
        videoHeight = displayMetrics.heightPixels;
    }

    if (isPemitted()) {
        startScreenCapture();
    } else {
        Log.i(TAG, "onScreenShare: not permitted");
    }

    /*if (peerConnectionClient != null) {
        peerConnectionClient.stopVideoSource();
    }*/
}

private void startScreenCapture() {
    MediaProjectionManager mediaProjectionManager =
            (MediaProjectionManager) getApplication().getSystemService(
                    Context.MEDIA_PROJECTION_SERVICE);
    startActivityForResult(
            mediaProjectionManager.createScreenCaptureIntent(), 
CAPTURE_PERMISSION_REQUEST_CODE);
    Log.d("tagged", ">>>>Method called :- ");
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) 
{
    Log.d("tagged", ">>>>Method called :- " + requestCode);
    if (requestCode != CAPTURE_PERMISSION_REQUEST_CODE)
        return;
    else {
        mediaProjectionPermissionResultCode = resultCode;
        mediaProjectionPermissionResultData = data;

if (peerConnectionParameters.videoCallEnabled) {
        videoCapturer = createVideoCapturer();
    }
    peerConnectionClient.createPeerConnection(
            localProxyVideoSink, remoteSinks, videoCapturer, 
 signalingParameters);
    }
}

private @Nullable
VideoCapturer createScreenCapturer() {
    Log.d("CheckMedia", ">>>Checking " + 
mediaProjectionPermissionResultData);
    if (mediaProjectionPermissionResultCode != Activity.RESULT_OK) {
        reportError("User didn't give permission to capture the screen.");
        return null;
    }
    return new ScreenCapturerAndroid(
            mediaProjectionPermissionResultData, new 
MediaProjection.Callback() {
        @Override
        public void onStop() {
            reportError("User revoked permission to capture the screen.");
        }
    });
}

This code starts the casting on local device but is not streaming anything on remote device.

UPDATE

 private void switchCameraInternal() {
    if (videoCapturer instanceof CameraVideoCapturer) {
        if (!isVideoCallEnabled() || isError) {
            Log.e(TAG,
                    "Failed to switch camera. Video: " + 
 isVideoCallEnabled() + ". Error : " + isError);
            return; // No video is sent or only one camera is available or 
 error happened.
        }
        Log.d(TAG, "Switch camera");
        CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) 
 videoCapturer;
        cameraVideoCapturer.switchCamera(null);

    } else {
        Log.d(TAG, "Will not switch camera, video caputurer is not a 
camera");
    }
 }

public void switchCamera() {
    executor.execute(this::switchCameraInternal);
}

private void startScreenSharing() {
    if (videoCapturer instanceof ScreenCapturerAndroid) {
        if (!isVideoCallEnabled() || isError) {
            Log.e(TAG,
                    "Failed to share screen. Video: " + isVideoCallEnabled() 
+ ". Error : " + isError);
            return; // No video is sent or only one camera is available or 
error happened.
        }
        ScreenCapturerAndroid screenCapturerAndroid = 
(ScreenCapturerAndroid) videoCapturer;
        screenCapturerAndroid.startCapture(500, 500, 30);
    }
}

public void screenSharing() {
    executor.execute(this::startScreenSharing);
}

I did the changes, and made the code look similar to switchCamera() code but I am getting an Not On Camera Thread Exception.

1
You mentioned that the sharing is working fine if you don't start video first so there is no need to change anything apart from removing the video track before adding the new one. Check my update113408
Reference Still not able to make it. Can you help me out here, actually trying this from past 5 days now.Vismay Patil
You can check my code example and fit it to your needs113408
@113408 I am stuck in the same situation as mentioned above. It will be really helpful if i could get the link your code to fit my need.Sunil kumar

1 Answers

0
votes

I'm not sure you can stream from the Camera and Screen at the same time. However what you can do is:

  1. User click the screen share button
  2. You remove your Camera video track from your PeerConnection using PeerConnection.removeTrack(RtpSender sender)
  3. You create your Screen video track using ScreenCapturerAndroid (as you already do)
  4. You add the track to your PeerConnection

If you say that the Screen share is working without call then step 4 and 5 should already be done.

Don't forget to dispose/release all resources related to the Camera when you remove its track.

Also to stop the Screen share and go back to the Camera just do the above steps for the Screen track.

For reference : PeerConnection.java

UPDATE :

Here is a part of the WebRTC Client that allows me to achieve what you are asking for (you can adapt it to your current code base):

private fun stopCameraShare(){
        videoCapturerAndroid?.stopCapture()
        localRenderer.dispose()
        localVideoView.release()
        localVideoView.clearImage()
        stream?.removeTrack(localVideoTrack)
        localVideoTrack.dispose()
    }
private fun shareScreen(){
        stopCameraShare()
        val mediaProjectionManager = activity!!.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
        startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 29)
    }

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (requestCode != 29)
            return
        initVideos()
        videoCapturerAndroid = ScreenCapturerAndroid(
            data, object : MediaProjection.Callback() {
                override fun onStop() {
                    Timber.e("User revoked permission to capture the screen.")
                }
            })
        peerConnectionFactory.setVideoHwAccelerationOptions(rootEglBase.eglBaseContext, rootEglBase.eglBaseContext)
        videoSource = peerConnectionFactory.createVideoSource(videoCapturerAndroid)
        localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource)
        videoCapturerAndroid?.startCapture(300, 300, 30)
        stream?.addTrack(localVideoTrack)
    }

PS: It's very important to peerConnectionFactory.setVideoHwAccelerationOptions(rootEglBase.eglBaseContext, rootEglBase.eglBaseContext)

Hope this will help you !