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 build1.1.0.5564.
1. Initialization of the API.
Flashphoner.init() code
Context object is passed to method init() for initialization.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init(this); |
2. Request the permission to use microphoneDisplay or hide a system sound capture button depending on Android version
Code Block | ||||
---|---|---|---|---|
| ||||
if mMicCheckBox.setOnClickListener(new View.OnClickListener() (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); ActivityCompat.requestPermissions(ScreenSharingActivity.this,} |
3. Request the permission to capture audio
Code Block | ||||
---|---|---|---|---|
| ||||
mUseAudioCheckBox.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.setTag(R.string.action_stop); mStartButtonmStatusView.setText(R.string.action_stopconnection.getStatus()); }); mStartButton.setTag(R.string.action_stop); ... } |
8. Video stream creation
session.createStream() code
Code Block | ||||
---|---|---|---|---|
| ||||
mStartButton.setEnabled(true); mStatusView.setText(connection.getStatus()); ... StreamOptions } streamOptions = new }StreamOptions(streamName); } |
7. Video stream creation
session.createStream() code
Code Block | ||||
---|---|---|---|---|
| ||||
StreamOptionsVideoConstraints streamOptionsvideoConstraints = new StreamOptionsVideoConstraints(streamName); VideoConstraints videoConstraints = new VideoConstraints(); DisplayMetrics 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); } |
910. Start foreground service, capture the screen and publish the stream
startService(), setVideoCapturer(), Stream.publish() code
context.startForegroundService() code
Code Block | ||||
---|---|---|---|---|
| ||||
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (REQUEST_CODE_CAPTURE_PERM == requestCode && resultCode == RESULT_OK) { serviceIntentthis.mediaProjectionData = data; new Intent(this, ScreenSharingService.class); Context context = startServicegetApplicationContext(serviceIntent); videoCapturer this.serviceIntent = new ScreenCapturerAndroidIntent(datacontext, new MediaProjection.Callback() {ScreenSharingService.class); context.startForegroundService(serviceIntent); @Override } else { public void onStoprunOnUiThread(() { -> mStartButton.setEnabled(false)); super.onStopstop(); } }Log.i(TAG, "Permission has been denied by user"); } WebRTCMediaProvider.getInstance} |
11. Capture device screen and publish a stream
ScreenCapturerAndroid(), Stream.
...
publish(
...
) code
Code Block | ||||
---|---|---|---|---|
| ||||
private final BroadcastReceiver mMessageReceiver = new BroadcastReceiver() { /** @Override *public Methodvoid Stream.publish() is called 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 publishStream.on(new StreamStatusEvent() {= mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, mediaProjectionData); } @Override WebRTCMediaProvider.getInstance().setMediaProjection(mediaProjection); videoCapturer = new ScreenCapturerAndroid(mediaProjection, mediaProjectionData, public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) { new MediaProjection.Callback() { @Override public runOnUiThread(newvoid RunnableonStop() { super.onStop(); @Override handler.post(ScreenSharingActivity.this::stop); } public void run(}) {; WebRTCMediaProvider.getInstance().setVideoCapturer(videoCapturer); publishStream.publish(); } else if (StreamStatusScreenSharingService.PUBLISHINGACTION_STOP.equals(streamStatusintent.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 | ||||
---|---|---|---|---|
| ||||
* The options for the stream to play are set. publishStream.on(new StreamStatusEvent() { @Override * The stream name is passed when StreamOptions object is created. public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) { */ runOnUiThread(new Runnable() { StreamOptions streamOptions = new StreamOptions(streamName); @Override public void streamOptions.getConstraints().updateAudio(mMicCheckBox.isChecked()); run() { if (StreamStatus.PUBLISHING.equals(streamStatus)) { /** /** * Stream is created with method Session.createStream(). * The options for the stream to play are */set. playStream = session.createStream(streamOptions); * The stream name is passed when StreamOptions object is created. ... */ playStream.play(); StreamOptions streamOptions = new StreamOptions(streamName); } else { streamOptions.getConstraints().updateAudio(mMicCheckBox.isChecked()); Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus); /** } * Stream is created with method Session.createStream(). mStatusView.setText(streamStatus.toString());*/ } playStream = session.createStream(streamOptions); }); ... } }); |
11. Session disconnection.
Session.disconnect() code
Code Block | ||||
---|---|---|---|---|
| ||||
mStartButton.setEnabled(false);
/**
* Connection to WCS server is closed with method Session.disconnect().
*/
session.disconnect(); |
12. Starting foreground service
Service.onCreate(), startForeground() code
Code Block | ||||
---|---|---|---|---|
| ||||
@Override public void onCreate() {playStream.play(); } else { Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus); } mStatusView.setText(streamStatus.toString()); } }); } }); |
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; super.onCreate();} NotificationChannel chan =... } |
14. Foreground service creation
Service.onCreate(), startForeground() code
Code Block | ||||
---|---|---|---|---|
| ||||
@Override public void new NotificationChannel( onCreate() { super.onCreate(); NotificationChannel chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE); chan.setImportance(NotificationManager.IMPORTANCE_MIN); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); manager.createNotificationChannel(chan); final int notificationId = (int) System.currentTimeMillis(); NotificationCompat.Builder notificationBuilder = = (int) System.currentTimeMillis(); Notification.Builder notificationBuilder = new NotificationCompatNotification.Builder(this, CHANNEL_ID); Notification notification = notificationBuilder .setSmallIcon(R.drawable.service_icon) .setOngoing(true) .setShowWhen(true) .setContentTitle("ScreenSharingService is running in the foreground") .setPrioritysetCategory(NotificationManagerNotification.IMPORTANCECATEGORY_MINSERVICE) .setCategory(Notification.CATEGORY_SERVICEaddAction(createStopAction()) .build(); .build(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { NotificationManager notificationManager startForeground(notificationId, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION); } else { = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(NOTIFICATION_IDstartForeground(notificationId, notification); startForeground(notificationId, notification);} } |
1315. Stopping foreground service
Service.onDestroy(), stopForeground() code
Code Block | ||||
---|---|---|---|---|
| ||||
@Override public void onDestroy() { stopForeground(true); super.onDestroy(); } |