Table of Contents |
---|
Example of Android application to publish device screen
...
The example shows how to share device screen. Device microphone or (in Android 10 or above) system audio may also be captured.
Work with example code
To analyze the code let's take the class ScreenSharingActivity.java of the screen-sharing example, which can be downloaded with build 1.1.0.1164.
1. Initialization of the API.
Flashphoner.init() code
Context object is passed to method init() for initialization.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init(this); |
2. Request permossion to use microphoneDisplay or hide a system sound capture button depending on Android version
Code Block | ||||
---|---|---|---|---|
| ||||
mMicCheckBox.setOnClickListener(new View.OnClickListener() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @OverridemAudioRadioGroup.setVisibility(View.VISIBLE); public void onClick(View v) } else { if (mMicCheckBox.isChecked()) {mAudioRadioGroup.setVisibility(View.GONE); } |
3. Request the permission to capture audio
Code Block | ||||
---|---|---|---|---|
| ||||
ActivityCompat mUseAudioCheckBox.requestPermissions(ScreenSharingActivity.this, setOnClickListener(v -> { if (mUseAudioCheckBox.isChecked()) { new String[]{Manifest.permission.RECORD_AUDIO}ActivityCompat.requestPermissions(ScreenSharingActivity.this, PUBLISH_REQUEST_CODE); new String[]{Manifest.permission.RECORD_AUDIO}, } PUBLISH_REQUEST_CODE); } }); |
34. Choose the microphone
Code Block | ||||
---|---|---|---|---|
| ||||
mMicSpinner = (Spinner) findViewById(R.id.spinner_mic); ArrayAdapter<MediaDevice> arrayAdapter = new ArrayAdapter<MediaDevice>(this, android.R.layout.simple_spinner_item, Flashphoner.getMediaDevices().getAudioList()); arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mMicSpinner.setAdapter(arrayAdapter); |
45. Session creation
Flashphoner.createSession() code
SessionOptions is passed to the method with the following parameters
...
Code Block | ||||
---|---|---|---|---|
| ||||
SessionOptions sessionOptions = new SessionOptions(url); sessionOptions.setLocalRenderer(localRender); sessionOptions.setRemoteRenderer(remoteRender); /** * Session for connection to WCS server is created with method createSession(). */ session = Flashphoner.createSession(sessionOptions); |
56. Connection to the server
Session.connect() . code
Code Block | ||||
---|---|---|---|---|
| ||||
session.connect(new Connection()); |
67. Receiving the event confirming successful connection.
session.onConnected() code
Code Block | ||||
---|---|---|---|---|
| ||||
@Override public void onConnected(final Connection connection) { runOnUiThread(new Runnable() -> { @Override mStartButton.setText(R.string.action_stop); public void run() { mStartButton.setText mStartButton.setTag(R.string.action_stop); mStartButton.setTag(R.string.action_stopmStatusView.setText(connection.getStatus()); mStartButton.setEnabled(true}); mStatusView.setText(connection.getStatus()); ... } }); } |
...
...
} |
8. Video stream creation
session.createStream() code
Code Block | ||||
---|---|---|---|---|
| ||||
StreamOptions streamOptions = new StreamOptions(streamName); VideoConstraints videoConstraints = new VideoConstraints(); DisplayMetrics metrics = getResources().getDisplayMetrics(); videoConstraints.setResolution(metrics.widthPixels, metrics.heightPixels); videoConstraints.setVideoFps(metrics.densityDpi); streamOptions.getConstraints().setVideoConstraints(videoConstraints); streamOptions.getConstraints().updateAudio(mMicCheckBoxmUseAudioCheckBox.isChecked()); /** * Stream is created with method Session.createStream(). */ publishStream = session.createStream(streamOptions); ... startScreenCapture(); |
89. Prepare to capture device screen
Code Block | ||||
---|---|---|---|---|
| ||||
private void startScreenCapture() { mMediaProjectionManager = (MediaProjectionManager) getSystemService( Context.MEDIA_PROJECTION_SERVICE); Intent permissionIntent = mMediaProjectionManager.createScreenCaptureIntent(); startActivityForResult(permissionIntent, REQUEST_CODE_CAPTURE_PERM); } |
9. Screen capture and stream publishing
setVideoCapturer(), Stream.publish10. Start foreground service
context.startForegroundService() code
Code Block | ||||
---|---|---|---|---|
| ||||
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (super.onActivityResult(requestCode, resultCode, data); if (REQUEST_CODE_CAPTURE_PERM == requestCode && resultCode == RESULT_OK) { videoCapturerthis.mediaProjectionData = new ScreenCapturerAndroid(data, new MediaProjection.Callback() { data; Context context = getApplicationContext(); @Override this.serviceIntent = new Intent(context, ScreenSharingService.class); public void onStop() { context.startForegroundService(serviceIntent); } else { runOnUiThread(() -> supermStartButton.onStopsetEnabled(false)); stop(); } Log.i(TAG, "Permission has been denied by }user"); } WebRTCMediaProvider.getInstance().setVideoCapturer(videoCapturer); } |
11. Capture device screen and publish a stream
ScreenCapturerAndroid(), Stream.publish() code
Code Block | ||||
---|---|---|---|---|
| ||||
private final BroadcastReceiver mMessageReceiver = new /**BroadcastReceiver() { @Override * Method Stream.publish() ispublic calledvoid to publish stream. onReceive(Context context, Intent intent) { */ if (intent != null) { publishStream.publish(); if (ScreenSharingService.ACTION_START.equals(intent.getAction())) { Log.i(TAG, "Permission has been granted by user"); MediaProjection mediaProjection = null; ... } if } |
10. Receiving the event confirming the successful stream publishing
StreamStatusEvent PUBLISHING code
On receiving this event preview stream is created with Session.createStream() and Stream.play() is invoked to play it.
Code Block | ||||
---|---|---|---|---|
| ||||
(mUseAudioCheckBox.isChecked() && !mUseMicRadioButton.isChecked() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, mediaProjectionData);
}
WebRTCMediaProvider.getInstance().setMediaProjection(mediaProjection);
videoCapturer = new ScreenCapturerAndroid(mediaProjection, mediaProjectionData, new MediaProjection.Callback() {
@Override
public void onStop() {
super.onStop();
handler.post(ScreenSharingActivity.this::stop);
}
});
WebRTCMediaProvider.getInstance().setVideoCapturer(videoCapturer);
publishStream.publish();
} else if (ScreenSharingService.ACTION_STOP.equals(intent.getAction())) {
handler.post(ScreenSharingActivity.this::stop);
}
}
}
}; |
12. Receiving the event confirming the successful stream publishing
StreamStatus.PUBLISHING code
On receiving this event preview stream is created with Session.createStream() and Stream.play() is invoked to play it.
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream.on(new StreamStatusEvent() { @Override public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) { runOnUiThread(new Runnable() { @Override public void run() { if (StreamStatus.PUBLISHING.equals(streamStatus)) { /** * The options for the stream to play are set. * The stream name is passed when StreamOptions object is created. publishStream.on(new StreamStatusEvent() { @Override */ public void onStreamStatus(finalStreamOptions StreamstreamOptions stream,= final StreamStatus streamStatus) { new StreamOptions(streamName); runOnUiThread(new Runnable() { streamOptions.getConstraints().updateAudio(mUseAudioCheckBox.isChecked()); @Override /** public void run() { * Stream is created with method Session.createStream(). if (StreamStatus.PUBLISHING.equals(streamStatus)) { */ /** playStream = session.createStream(streamOptions); * The options for the stream to play are set. ... * The stream name is passed when StreamOptions object is created. playStream.play(); */ } else { StreamOptions streamOptions = new StreamOptions(streamName); Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus); streamOptions.getConstraints().updateAudio(mMicCheckBox.isChecked()); } /** mStatusView.setText(streamStatus.toString()); } * Stream is created with method Session.createStream(). }); */ } playStream = session.createStream(streamOptions); }); |
13. Session disconnection.
Session.disconnect() code
Code Block | ||||
---|---|---|---|---|
| ||||
private synchronized void stop() { if (session != null) { session.disconnect(); session = null; ...} WebRTCMediaProvider.getInstance().releaseLocalMediaAccess(); if (serviceIntent != null) { stopService(serviceIntent); this.serviceIntent = null; playStream.play();} ... } |
14. Foreground service creation
Service.onCreate(), startForeground() code
Code Block | ||||
---|---|---|---|---|
| ||||
@Override public void onCreate() { super.onCreate(); NotificationChannel chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE); } else {chan.setImportance(NotificationManager.IMPORTANCE_MIN); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); manager.createNotificationChannel(chan); final int notificationId = (int) LogSystem.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatuscurrentTimeMillis(); Notification.Builder notificationBuilder = new Notification.Builder(this, CHANNEL_ID); Notification notification = notificationBuilder }.setSmallIcon(R.drawable.service_icon) .setOngoing(true) mStatusView.setText(streamStatus.toString());setShowWhen(true) .setContentTitle("ScreenSharingService is running in the foreground") } .setCategory(Notification.CATEGORY_SERVICE) .addAction(createStopAction()) }); .build(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { startForeground(notificationId, }notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION); } else { startForeground(notificationId, notification); } }); |
11. Session disconnection.
Session.disconnect15. Stopping foreground service
Service.onDestroy(), stopForeground() code
Code Block | ||||
---|---|---|---|---|
| ||||
mStartButton.setEnabled(false); /** * Connection to WCS server is closed with method Session.disconnect(). */ session.disconnect(); @Override public void onDestroy() { stopForeground(true); super.onDestroy(); } |