Add screenshare support for Android
This commit is contained in:
parent
987f011769
commit
ccd8601c75
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,3 +19,4 @@ local.properties
|
||||
!.cargo/audit.toml
|
||||
target
|
||||
.spr.yml
|
||||
src/android/.settings
|
||||
|
||||
@ -401,7 +401,10 @@ public class CallManager {
|
||||
|
||||
/**
|
||||
*
|
||||
* Indication from application to proceed with call
|
||||
* Indication from application to proceed with call. Defaults call to:
|
||||
* - videoEnabled == false
|
||||
* - audioEnabled == false
|
||||
* - isScreenshare == false
|
||||
*
|
||||
* @param callId callId for the call
|
||||
* @param context Call service context
|
||||
@ -832,17 +835,39 @@ public class CallManager {
|
||||
* @throws CallException for native code failures
|
||||
*
|
||||
*/
|
||||
public void setVideoEnable(boolean enable)
|
||||
public void setVideoEnable(boolean enable, boolean isScreenShare)
|
||||
throws CallException
|
||||
{
|
||||
checkCallManagerExists();
|
||||
|
||||
CallContext callContext = ringrtcGetActiveCallContext(nativeCallManager);
|
||||
callContext.setVideoEnabled(enable);
|
||||
callContext.setOutgoingVideoIsScreenShare(isScreenShare);
|
||||
|
||||
ringrtcSetVideoEnable(nativeCallManager, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Notification from application to indicate whether the outgoing video
|
||||
* is a screen share.
|
||||
*
|
||||
* @param isScreenShare if true, the outgoing video is a screen share
|
||||
*
|
||||
* @throws CallException for native code failures
|
||||
*
|
||||
*/
|
||||
public void setOutgoingVideoIsScreenShare(boolean isScreenShare)
|
||||
throws CallException
|
||||
{
|
||||
checkCallManagerExists();
|
||||
|
||||
CallContext callContext = ringrtcGetActiveCallContext(nativeCallManager);
|
||||
callContext.setOutgoingVideoIsScreenShare(isScreenShare);
|
||||
|
||||
ringrtcSetOutgoingVideoIsScreenShare(nativeCallManager, isScreenShare);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets a data mode, allowing the client to limit the media bandwidth used.
|
||||
@ -1243,7 +1268,10 @@ public class CallManager {
|
||||
|
||||
/**
|
||||
*
|
||||
* Creates and returns a GroupCall object.
|
||||
* Creates and returns a GroupCall object. Defaults call to:
|
||||
* - videoEnabled == false
|
||||
* - audioEnabled == false
|
||||
* - isScreenshare == false
|
||||
*
|
||||
* If there is any error when allocating resources for the object,
|
||||
* null is returned.
|
||||
@ -1289,7 +1317,11 @@ public class CallManager {
|
||||
|
||||
/**
|
||||
*
|
||||
* Creates and returns a GroupCall object for a call link call.
|
||||
* Creates and returns a GroupCall object for a call link call. Defaults call to:
|
||||
* - videoEnabled == false
|
||||
* - audioEnabled == false
|
||||
* - isScreenshare == false
|
||||
*
|
||||
*
|
||||
* If there is any error when allocating resources for the object,
|
||||
* null is returned.
|
||||
@ -1883,6 +1915,14 @@ public class CallManager {
|
||||
*/
|
||||
static class CallContext {
|
||||
|
||||
public static final int CAMERA_MAX_WIDTH = 1280;
|
||||
|
||||
public static final int CAMERA_MAX_HEIGHT = 720;
|
||||
|
||||
public static final int CAMERA_MAX_FPS = 30;
|
||||
|
||||
public static final int SCREENSHARE_MAX_FPS = 30;
|
||||
|
||||
@NonNull private final String TAG = CallManager.CallContext.class.getSimpleName();
|
||||
/** CallId */
|
||||
@NonNull public final CallId callId;
|
||||
@ -1943,6 +1983,20 @@ public class CallManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setOutgoingVideoIsScreenShare(boolean isScreenShare) {
|
||||
if (this.videoSource != null) {
|
||||
this.videoSource.setIsScreencast(isScreenShare);
|
||||
|
||||
if (isScreenShare) {
|
||||
this.videoSource.adaptOutputFormat(VideoSource.AspectRatio.UNDEFINED, null, VideoSource.AspectRatio.UNDEFINED, null, SCREENSHARE_MAX_FPS);
|
||||
} else {
|
||||
this.videoSource.adaptOutputFormat(CAMERA_MAX_WIDTH, CAMERA_MAX_HEIGHT, CAMERA_MAX_FPS);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "setOutgoingVideoIsScreenShare(): tried to set isScreenshare but there is no VideoSource");
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
Log.i(TAG, "dispose(): " + callId);
|
||||
|
||||
@ -2620,6 +2674,10 @@ public class CallManager {
|
||||
void ringrtcSetVideoEnable(long nativeCallManager, boolean enable)
|
||||
throws CallException;
|
||||
|
||||
private native
|
||||
void ringrtcSetOutgoingVideoIsScreenShare(long nativeCallManager, boolean isScreenShare)
|
||||
throws CallException;
|
||||
|
||||
private native
|
||||
void ringrtcUpdateDataMode(long nativeCallManager, int dataMode)
|
||||
throws CallException;
|
||||
|
||||
@ -40,6 +40,14 @@ public final class GroupCall {
|
||||
|
||||
public static final int INVALID_CLIENT_ID = 0;
|
||||
|
||||
public static final int CAMERA_MAX_WIDTH = 640;
|
||||
|
||||
public static final int CAMERA_MAX_HEIGHT = 360;
|
||||
|
||||
public static final int CAMERA_MAX_FPS = 30;
|
||||
|
||||
public static final int SCREENSHARE_MAX_FPS = 30;
|
||||
|
||||
@NonNull private static final String TAG = GroupCall.class.getSimpleName();
|
||||
|
||||
@NonNull private Kind kind;
|
||||
@ -101,7 +109,7 @@ public final class GroupCall {
|
||||
this.outgoingAudioTrack.setEnabled(false);
|
||||
}
|
||||
|
||||
this.outgoingVideoSource = factory.createVideoSource(false);
|
||||
this.outgoingVideoSource = factory.createVideoSource(/* isScreencast */false);
|
||||
if (this.outgoingVideoSource == null) {
|
||||
return;
|
||||
}
|
||||
@ -114,14 +122,17 @@ public final class GroupCall {
|
||||
this.outgoingVideoTrack.setEnabled(false);
|
||||
}
|
||||
|
||||
// Define maximum output video format for group calls.
|
||||
this.outgoingVideoSource.adaptOutputFormat(640, 360, 30);
|
||||
// Define maximum camera video format for group calls.
|
||||
this.outgoingVideoSource.adaptOutputFormat(CAMERA_MAX_WIDTH, CAMERA_MAX_HEIGHT, CAMERA_MAX_FPS);
|
||||
|
||||
this.incomingVideoTracks = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GroupCall object.
|
||||
* Creates a GroupCall object. Defaults call to:
|
||||
* - videoEnabled == false
|
||||
* - audioEnabled == false
|
||||
* - isScreenshare == false
|
||||
*
|
||||
* Will return null on failure. Should only be accessed via the CallManager.createGroupCall().
|
||||
*/
|
||||
@ -436,17 +447,62 @@ public final class GroupCall {
|
||||
* @throws CallException for native code failures
|
||||
*
|
||||
*/
|
||||
public void setOutgoingVideoMuted(boolean muted)
|
||||
public void setOutgoingVideoMuted(boolean muted, boolean isScreenShare)
|
||||
throws CallException
|
||||
{
|
||||
Log.i(TAG, "setOutgoingVideoMuted():");
|
||||
|
||||
this.localDeviceState.videoMuted = muted;
|
||||
this.outgoingVideoTrack.setEnabled(!this.localDeviceState.videoMuted);
|
||||
this.setOutgoingVideoIsScreenShare(isScreenShare);
|
||||
|
||||
ringrtcSetOutgoingVideoMuted(nativeCallManager, this.clientId, muted);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Indicates whether the outgoing video is a screen share.
|
||||
* This updates the local device state and sends the status to the SFU.
|
||||
*
|
||||
* @param isScreenShare true if the outgoing video is a screen share
|
||||
*
|
||||
* @throws CallException for native code failures
|
||||
*
|
||||
*/
|
||||
public void setOutgoingVideoIsScreenShare(boolean isScreenShare)
|
||||
throws CallException
|
||||
{
|
||||
Log.i(TAG, String.format("setOutgoingVideoIsScreenShare(): %b", isScreenShare));
|
||||
|
||||
this.outgoingVideoSource.setIsScreencast(isScreenShare);
|
||||
if (isScreenShare) {
|
||||
this.outgoingVideoSource.adaptOutputFormat(VideoSource.AspectRatio.UNDEFINED, null, VideoSource.AspectRatio.UNDEFINED, null, SCREENSHARE_MAX_FPS);
|
||||
} else {
|
||||
this.outgoingVideoSource.adaptOutputFormat(CAMERA_MAX_WIDTH, CAMERA_MAX_HEIGHT, CAMERA_MAX_FPS);
|
||||
}
|
||||
|
||||
this.localDeviceState.sharingScreen = isScreenShare;
|
||||
ringrtcSetOutgoingVideoIsScreenShare(nativeCallManager, this.clientId, isScreenShare);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Indicates whether the user is presenting.
|
||||
* This updates the local device state and sends the status to the SFU.
|
||||
*
|
||||
* @param isPresenting true if the outgoing video is a screen share
|
||||
*
|
||||
* @throws CallException for native code failures
|
||||
*
|
||||
*/
|
||||
public void setPresenting(boolean isPresenting)
|
||||
throws CallException
|
||||
{
|
||||
Log.i(TAG, "setPresenting():");
|
||||
this.localDeviceState.presenting = isPresenting;
|
||||
ringrtcSetPresenting(nativeCallManager, this.clientId, isPresenting);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Links the camera to the outgoing video track.
|
||||
@ -990,6 +1046,8 @@ public final class GroupCall {
|
||||
JoinState joinState;
|
||||
boolean audioMuted;
|
||||
boolean videoMuted;
|
||||
boolean presenting;
|
||||
boolean sharingScreen;
|
||||
NetworkRoute networkRoute;
|
||||
int audioLevel;
|
||||
@Nullable Long demuxId;
|
||||
@ -999,6 +1057,8 @@ public final class GroupCall {
|
||||
this.joinState = JoinState.NOT_JOINED;
|
||||
this.audioMuted = true;
|
||||
this.videoMuted = true;
|
||||
this.presenting = false;
|
||||
this.sharingScreen = false;
|
||||
this.networkRoute = new NetworkRoute();
|
||||
this.audioLevel = 0;
|
||||
}
|
||||
@ -1008,6 +1068,8 @@ public final class GroupCall {
|
||||
this.joinState = localDeviceState.joinState;
|
||||
this.audioMuted = localDeviceState.audioMuted;
|
||||
this.videoMuted = localDeviceState.videoMuted;
|
||||
this.presenting = localDeviceState.presenting;
|
||||
this.sharingScreen = localDeviceState.sharingScreen;
|
||||
this.networkRoute = localDeviceState.networkRoute;
|
||||
this.audioLevel = localDeviceState.audioLevel;
|
||||
this.demuxId = localDeviceState.demuxId;
|
||||
@ -1029,6 +1091,14 @@ public final class GroupCall {
|
||||
return videoMuted;
|
||||
}
|
||||
|
||||
public boolean getPresenting() {
|
||||
return presenting;
|
||||
}
|
||||
|
||||
public boolean getSharingScreen() {
|
||||
return sharingScreen;
|
||||
}
|
||||
|
||||
public NetworkRoute getNetworkRoute() {
|
||||
return networkRoute;
|
||||
}
|
||||
@ -1375,6 +1445,18 @@ public final class GroupCall {
|
||||
boolean muted)
|
||||
throws CallException;
|
||||
|
||||
private native
|
||||
void ringrtcSetPresenting(long nativeCallManager,
|
||||
long clientId,
|
||||
boolean isPresenting)
|
||||
throws CallException;
|
||||
|
||||
private native
|
||||
void ringrtcSetOutgoingVideoIsScreenShare(long nativeCallManager,
|
||||
long clientId,
|
||||
boolean isScreenShare)
|
||||
throws CallException;
|
||||
|
||||
private native
|
||||
void ringrtcRing( long nativeCallManager,
|
||||
long clientId,
|
||||
|
||||
@ -561,6 +561,25 @@ pub unsafe extern "C" fn Java_org_signal_ringrtc_CallManager_ringrtcSetVideoEnab
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_org_signal_ringrtc_CallManager_ringrtcSetOutgoingVideoIsScreenShare(
|
||||
mut env: JNIEnv,
|
||||
_object: JObject,
|
||||
call_manager: jlong,
|
||||
is_screenshare: jboolean,
|
||||
) {
|
||||
match call_manager::set_outgoing_video_is_screenshare(
|
||||
call_manager as *mut AndroidCallManager,
|
||||
is_screenshare != 0,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error::throw_error(&mut env, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[allow(non_snake_case)]
|
||||
pub extern "C" fn Java_org_signal_ringrtc_CallManager_ringrtcUpdateDataMode(
|
||||
@ -1057,6 +1076,48 @@ pub unsafe extern "C" fn Java_org_signal_ringrtc_GroupCall_ringrtcSetOutgoingVid
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_org_signal_ringrtc_GroupCall_ringrtcSetPresenting(
|
||||
mut env: JNIEnv,
|
||||
_object: JObject,
|
||||
call_manager: jlong,
|
||||
client_id: jlong,
|
||||
presenting: jboolean,
|
||||
) {
|
||||
match call_manager::set_presenting(
|
||||
call_manager as *mut AndroidCallManager,
|
||||
client_id as group_call::ClientId,
|
||||
presenting != 0,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error::throw_error(&mut env, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_org_signal_ringrtc_GroupCall_ringrtcSetOutgoingVideoIsScreenShare(
|
||||
mut env: JNIEnv,
|
||||
_object: JObject,
|
||||
call_manager: jlong,
|
||||
client_id: jlong,
|
||||
is_screenshare: jboolean,
|
||||
) {
|
||||
match call_manager::set_outgoing_group_call_video_is_screenshare(
|
||||
call_manager as *mut AndroidCallManager,
|
||||
client_id as group_call::ClientId,
|
||||
is_screenshare != 0,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error::throw_error(&mut env, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_org_signal_ringrtc_GroupCall_ringrtcRing(
|
||||
|
||||
@ -558,6 +558,20 @@ pub fn set_video_enable(call_manager: *mut AndroidCallManager, enable: bool) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// CMI request to set whether the outgoing video is a screen share
|
||||
pub fn set_outgoing_video_is_screenshare(
|
||||
call_manager: *mut AndroidCallManager,
|
||||
is_screenshare: bool,
|
||||
) -> Result<()> {
|
||||
let call_manager = unsafe { ptr_as_mut(call_manager)? };
|
||||
|
||||
if let Ok(mut active_connection) = call_manager.active_connection() {
|
||||
active_connection.send_is_screenshare_update(is_screenshare)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Request to update the data mode on the direct connection
|
||||
pub fn update_data_mode(call_manager: *mut AndroidCallManager, data_mode: DataMode) -> Result<()> {
|
||||
let call_manager = unsafe { ptr_as_mut(call_manager)? };
|
||||
@ -1051,6 +1065,26 @@ pub fn set_outgoing_video_muted(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_presenting(
|
||||
call_manager: *mut AndroidCallManager,
|
||||
client_id: group_call::ClientId,
|
||||
presenting: bool,
|
||||
) -> Result<()> {
|
||||
let call_manager = unsafe { ptr_as_mut(call_manager)? };
|
||||
call_manager.set_presenting(client_id, presenting);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_outgoing_group_call_video_is_screenshare(
|
||||
call_manager: *mut AndroidCallManager,
|
||||
client_id: group_call::ClientId,
|
||||
is_screenshare: bool,
|
||||
) -> Result<()> {
|
||||
let call_manager = unsafe { ptr_as_mut(call_manager)? };
|
||||
call_manager.set_sharing_screen(client_id, is_screenshare);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn group_ring(
|
||||
env: &JNIEnv,
|
||||
call_manager: *mut AndroidCallManager,
|
||||
|
||||
@ -1606,6 +1606,13 @@ where
|
||||
call.connect_incoming_media(incoming_media)
|
||||
}
|
||||
|
||||
pub fn send_is_screenshare_update(&mut self, is_screenshare: bool) -> Result<()> {
|
||||
self.update_sender_status(signaling::SenderStatus {
|
||||
sharing_screen: Some(is_screenshare),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
/// Send a ConnectionEvent to the internal FSM.
|
||||
fn inject_event(&mut self, event: ConnectionEvent) -> Result<()> {
|
||||
self.fsm_sender
|
||||
|
||||
@ -1376,10 +1376,7 @@ fn setOutgoingVideoIsScreenShare(mut cx: FunctionContext) -> JsResult<JsValue> {
|
||||
.adapt_output_format(width, height, fps);
|
||||
|
||||
if let Ok(mut active_connection) = endpoint.call_manager.active_connection() {
|
||||
active_connection.update_sender_status(signaling::SenderStatus {
|
||||
sharing_screen: Some(is_screenshare),
|
||||
..Default::default()
|
||||
})?;
|
||||
active_connection.send_is_screenshare_update(is_screenshare)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user