Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

Пример Android приложения для демонстрации экрана устройства

Пример демонстрирует возможность трансляции экрана устройства. К видео может быть добавлено аудио с микрофона устройства

...

Image Removedили (для Android 10 и выше) системный звук.

Image Added

Работа с кодом примера

Для разбора кода возьмем класс ScreenSharingActivity.java примера screen-sharing, который доступен для скачивания в соответствующей сборке1.1.0.5564.

1. Инициализация API.

Flashphoner.init() code

При инициализации методу init() передается объект Сontext.

Code Block
languagejava
themeRDark
Flashphoner.init(this);

2. Разрешение на использование микрофонаСкрытие или отображение захвата системного звука в зависимости от версии Android

code

Code Block
languagejava
themeRDark
        mMicCheckBox.setOnClickListener(new View.OnClickListener() {
            @Overrideif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            public void onClickmAudioRadioGroup.setVisibility(View v) {.VISIBLE);
        }        if (mMicCheckBox.isChecked()) else {
            mAudioRadioGroup.setVisibility(View.GONE);

        ActivityCompat.requestPermissions(ScreenSharingActivity.this,
}

3. Разрешение на захват звука

code

Code Block
languagejava
themeRDark
        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. Выбор микрофона

code

Code Block
languagejava
themeRDark
        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. Создание сессии

Flashphoner.createSession() code

Методу передается объект SessionOptions со следующими параметрами

...

Code Block
languagejava
themeRDark
                    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. Подключение к серверу.

Session.connect() . code

Code Block
languagejava
themeRDark
session.connect(new Connection());

67. Получение от сервера события, подтверждающего успешное соединение.

session.onConnected() code

Code Block
languagejava
themeRDark
@Override
public void onConnected(final Connection connection) {
    runOnUiThread(new Runnable() -> {
       @Override mStartButton.setText(R.string.action_stop);
       public void run() {
           mStartButton.setText(R.string.action_stop);
           mStartButton.setTag(R.string.action_stop);
           mStartButton.setEnabled(true);
           mStatusView.setText(connection.getStatus());
    });
       ....
       }
   });
}

78. Создание потока и подготовка к публикации

session.createStream() code

Code Block
languagejava
themeRDark
                                    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. Подготовка захвата экрана

code

Code Block
languagejava
themeRDark
    private void startScreenCapture() {
        mMediaProjectionManager = (MediaProjectionManager) getSystemService(
                Context.MEDIA_PROJECTION_SERVICE);
        Intent permissionIntent = mMediaProjectionManager.createScreenCaptureIntent();
        startActivityForResult(permissionIntent, REQUEST_CODE_CAPTURE_PERM);
    }

910. Запуск сервиса, захват экрана и публикация потокаstartService(), setVideoCapturer(), Stream.publish

context.startForegroundService() code

Code Block
languagejava
themeRDark
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (REQUEST_CODE_CAPTURE_PERM == requestCode super.onActivityResult(requestCode, resultCode, data);
        if (REQUEST_CODE_CAPTURE_PERM == requestCode && resultCode == RESULT_OK) {

            serviceIntentthis.mediaProjectionData = new Intent(this, ScreenSharingService.class);
data;

            Context context = startServicegetApplicationContext(serviceIntent);
             videoCapturer = new ScreenCapturerAndroid(data, new MediaProjection.Callback() {this.serviceIntent = new Intent(context, ScreenSharingService.class);
            context.startForegroundService(serviceIntent);
    @Override
    } else {
          public void onStoprunOnUiThread(() {-> mStartButton.setEnabled(false));
            stop();
        super.onStop();
    Log.i(TAG, "Permission has been denied by user");
        }
    }

11. Захват экрана и публикация потока

ScreenCapturerAndroid(), Stream.publish() code

Code Block
languagejava
themeRDark
    private final BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
                if (ScreenSharingService.ACTION_START.equals(intent.getAction())) {
                    MediaProjection mediaProjection = null;
                    if (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. Получение от сервера события, подтверждающего успешную публикацию потока

StreamStatus.PUBLISHING code

При получении данного события создается превью-видеопоток при помощи Session.createStream() и вызывается Stream.play() для его воспроизведения.

Code Block
languagejava
themeRDark
                }
            });
            WebRTCMediaProviderpublishStream.getInstance().setVideoCapturer(videoCapturer);

            /**on(new StreamStatusEvent() {
             * Method Stream.publish() is called to publish stream.
             */
            publishStream.publish();@Override
            Log.i(TAG, "Permission has been granted by user");
            ...
        }
  public  }

10. Получение от сервера события, подтверждающего успешную публикацию потока

StreamStatusEvent PUBLISHING code

При получении данного события создается превью-видеопоток при помощи Session.createStream() и вызывается Stream.play() для его воспроизведения.

Code Block
languagejava
themeRDark
   void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
                                 publishStream.on(new StreamStatusEvent() {
         runOnUiThread(new Runnable() {
                             @Override
                   @Override
                     public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
                   public void run() {
                      runOnUiThread(new Runnable() {
                            if (StreamStatus.PUBLISHING.equals(streamStatus)) {

                  @Override
                                      /**
          public void run() {
                                            * The options for the stream to play ifare (StreamStatus.PUBLISHING.equals(streamStatus)) {

set.
                                                         /**
 The stream name    is passed when StreamOptions object is created.
                                            * The options for the stream to play are set.
    */
                                                     * The stream nameStreamOptions isstreamOptions passed= whennew StreamOptions(streamName);
 object is created.
                                                     streamOptions.getConstraints().updateAudio(mUseAudioCheckBox.isChecked());

    */
                                                     /**
   StreamOptions streamOptions = new StreamOptions(streamName);
                                                  * Stream is created with method streamOptionsSession.getConstraintscreateStream().updateAudio(mMicCheckBox.isChecked());


                                                         */**
                                                         * Stream is created with method Session.createStream().
playStream = session.createStream(streamOptions);
                                                        ...
        */
                                                playStream.play();
        playStream = session.createStream(streamOptions);
                                          } else {
            ...
                                            Log.e(TAG, "Can not publish stream " + stream.getName() + " " + playStream.play(streamStatus);
                                                    } else {

                                                    mStatusView.setText(streamStatus.toString());
            Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus);
                                    }
                              }
              });
                                      mStatusView.setText(streamStatus.toString());
  }
                                    });

13. Закрытие соединения.

Session.disconnect() code

Code Block
languagejava
themeRDark
    private synchronized void    }stop() {
        if (session != null) {
            session.disconnect();
            session        })= null;
        }

        WebRTCMediaProvider.getInstance().releaseLocalMediaAccess();

        if                }(serviceIntent != null) {
            stopService(serviceIntent);
            this.serviceIntent =   null;
        });

11. Закрытие соединения.

Session.disconnect() code

Code Block
languagejava
themeRDark
mStartButton.setEnabled(false);

/**
  * Connection to WCS server is closed with method Session.disconnect()...
  */
session.disconnect();

...

  }

14. Создание сервиса

Service.onCreate(), startForeground() code

Code Block
languagejava
themeRDark
    @Override
    public void onCreate() {
        super.onCreate();

        NotificationChannel chan =
                new NotificationChannel(
   {
        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. Остановка сервиса

Service.onDestroy(), stopForeground() code

Code Block
languagejava
themeRDark
    @Override
    public void onDestroy() {
        stopForeground(true);
        super.onDestroy();
    }