Versions Compared

Key

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

...

Данный пример может использоваться как стример для публикации WebRTC-видеопотока с Web Call Server и позволяет выбрать медиа-устройства и параметры для публикуемого видео

Image Modified


Пример переключения объекта для вывода изображения с камеры

Image Modified

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

Для разбора кода возьмем класс MediaDevicesActivity.java примера media-devices, который доступен для скачивания в соответствующей сборке 1.0.1.4670.

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

Flashphoner.init() код code

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

...

Flashphoner.getMediaDevices(), MediaDeviceList.getAudioList(), MediaDeviceList.getVideoList() код code

Code Block
languagejava
themeRDark
mMicSpinner = (LabelledSpinner) findViewById(R.id.microphone);
mMicSpinner.setItemsArray(Flashphoner.getMediaDevices().getAudioList());

mMicLevel = (TextView) findViewById(R.id.microphone_level);

mCameraSpinner = (LabelledSpinner) findViewById(R.id.camera);
mCameraSpinner.setItemsArray(Flashphoner.getMediaDevices().getVideoList());

...

FPSurfaceViewRenderer.setMirror() код code

При показе видео изображение выводится на объекты FPSurfaceViewRenderer:

...

Для этих объектов устанавливается позиция на экране, тип масштабирования и зеркалирование.

По умолчанию, активируется фронтальная камера устройства, поэтому для отображения видео с камеры устанавливается зеркальная ориентация при помощи метода setMirror(true). Для отображения публикуемого потока и объекта для демонстрации переключения зеркалирование отключается при помощи setMirror(false):

Code Block
languagejava
themeRDark
        remoteRenderLayout.setPosition(0, 0, 100, 100);
        remoteRender.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        remoteRender.setMirror(false);
        remoteRender.requestLayout();

        localRenderLayout.setPosition(0, 0, 100, 100);
        localRender.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        localRender.setMirror(true);
        localRender.requestLayout();

        switchRenderLayout.setPosition(0, 0, 100, 100);
        newSurfaceRenderer.setZOrderMediaOverlay(true);
        newSurfaceRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        newSurfaceRenderer.setMirror(true);
        newSurfaceRenderer.requestLayout();

Таким образомВ данном случае, при выборе фронтальной камеры изображение с камеры выглядит нормально, но публикуется зеркальным. При выборе тыловой камеры изображение с камеры будет выглядеть зеркальным, а публикуемый поток будет иметь нормальную ориентацию (см. скриншоты приложения выше).

...

AudioConstraints, VideoConstraints код code

Code Block
languagejava
themeRDark
@NonNull
private Constraints getConstraints() {
    AudioConstraints audioConstraints = null;
    if (mSendAudio.isChecked()) {
        audioConstraints = new AudioConstraints();
        if (mUseFEC.isChecked()) {
            audioConstraints.setUseFEC(true);
        }
        if (mUseStereo.isChecked()) {
            audioConstraints.setUseStereo(true);
        }
        if (!mDefaultPublishAudioBitrate.isChecked() && mDefaultPublishAudioBitrate.getText().length() > 0) {
            audioConstraints.setBitrate(Integer.parseInt(mPublishAudioBitrate.getText().toString()));
        }
    }
    VideoConstraints videoConstraints = null;
    if (mSendVideo.isChecked()) {
        videoConstraints = new VideoConstraints();
        videoConstraints.setCameraId(((MediaDevice) mCameraSpinner.getSpinner().getSelectedItem()).getId());
        if (mCameraFPS.getText().length() > 0) {
            videoConstraints.setVideoFps(Integer.parseInt(mCameraFPS.getText().toString()));
        }
        if (mWidth.getText().length() > 0 && mHeight.getText().length() > 0) {
            videoConstraints.setResolution(Integer.parseInt(mWidth.getText().toString()),
                    Integer.parseInt(mHeight.getText().toString()));
        }
        if (!mDefaultPublishVideoBitrate.isChecked() && mPublishVideoBitrate.getText().length() > 0) {
             videoConstraints.setBitrate(Integer.parseInt(mPublishVideoBitrate.getText().toString()));
        }
    }
    return new Constraints(audioConstraints, videoConstraints);
}

...

Flashphoner.getLocalMediaAccess() код code

Методу передаются:

  • настройки аудио и видео, заданные пользователем
  • локальный объект SurfaceViewRenderer localRenderer для вывода изображения с выбранной камеры

...

Flashphoner.createSession() код code

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

...

7. Подключение к серверу.

Session.connect(). код code

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

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

session.onConnected() код code

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

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

session.createStream() код code

Code Block
languagejava
themeRDark
publishStream = session.createStream(streamOptions);
if (mMuteAudio.isChecked()) {
    publishStream.muteAudio();
}
if (mMuteVideo.isChecked()) {
    publishStream.muteVideo();
}
...

ActivityCompat.requestPermissions(MediaDevicesActivity.this,
         new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA},
         PUBLISH_REQUEST_CODE);

10. Публикация потока

Stream.publish() код code

Code Block
languagejava
themeRDark
case PUBLISH_REQUEST_CODE: {
    if (grantResults.length == 0 ||
           grantResults[0] != PackageManager.PERMISSION_GRANTED ||
           grantResults[1] != PackageManager.PERMISSION_GRANTED) {
        mStartButton.setEnabled(false);
        mTestButton.setEnabled(false);
        session.disconnect();
        Log.i(TAG, "Permission has been denied by user");
    } else {
        /**
          * Method Stream.publish() is called to publish stream.
          */
        publishStream.publish();
        Log.i(TAG, "Permission has been granted by user");
    }
    break;
}

...

StreamStatusEvent PUBLISHING код code

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

...

12. Переключение камеры во время трансляции

Stream.switchCamera() код code

Code Block
languagejava
themeRDark
mSwitchCameraButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        if (publishStream != null) {
            mSwitchCameraButton.setEnabled(false);
            publishStream.switchCamera(new CameraSwitchHandler() {
                @Override
                public void onCameraSwitchDone(boolean var1) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mSwitchCameraButton.setEnabled(true);
                        }
                    });

                }

                @Override
                public void onCameraSwitchError(String var1) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mSwitchCameraButton.setEnabled(true);
                        }
                    });
                }
            });
        }
    }

});

13. Переключение объекта для отображения видеопотока во время трансляции

Stream.switchRenderer() код code

Code Block
languagejava
themeRDark
mSwitchRendererButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        if (spinner.getSelectedItemId() == 0){
            if (isSwitchRemoteRenderer) {
                playStream.switchRenderer(remoteRender);
                isSwitchRemoteRenderer = false;
            }
            if (!isSwitchLocalRenderer) {
                publishStream.switchRenderer(newSurfaceRenderer);
                isSwitchLocalRenderer = true;
            } else {
                publishStream.switchRenderer(localRender);
                isSwitchLocalRenderer = false;
            }
        } else {
            if (isSwitchLocalRenderer) {
                publishStream.switchRenderer(localRender);
                isSwitchLocalRenderer = false;
            }
            if (!isSwitchRemoteRenderer) {
                playStream.switchRenderer(newSurfaceRenderer);
                isSwitchRemoteRenderer = true;
            } else {
                playStream.switchRenderer(remoteRender);
                isSwitchRemoteRenderer = false;
            }
        }
    }
});

14. Закрытие соединения.Session.disconnect() кодУправление звуком при помощи аппаратных кнопок

Flashphoner.setVolume() code

Code Block
languagejava
themeRDark
mStartButton.setEnabled(    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        int currentVolume = Flashphoner.getVolume();
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
                if (currentVolume == 1) {
                    Flashphoner.setVolume(0);
                }
                mPlayVolume.setProgress(currentVolume-1);
                break;
            case KeyEvent.KEYCODE_VOLUME_UP:
                if (currentVolume == 0) {
                    Flashphoner.setVolume(1);
                }
                mPlayVolume.setProgress(currentVolume+1);
                break;
        }
        return super.onKeyDown(keyCode, event);
    }

15. Использование внешнего динамика телефона

Flashphoner.getAudioManager().isSpeakerphoneOn(), Flashphoner.getAudioManager().setUseSpeakerPhone() code

Code Block
languagejava
themeRDark
        mSpeakerPhone = (CheckBox) findViewById(R.id.use_speakerphone);
        mSpeakerPhone.setChecked(Flashphoner.getAudioManager().getAudioManager().isSpeakerphoneOn());
        mSpeakerPhone.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Flashphoner.getAudioManager().setUseSpeakerPhone(isChecked);
            }
        });

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

Session.disconnect() code

Code Block
languagejava
themeRDark
mStartButton.setEnabled(false);
mTestButton.setEnabled(false);

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

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

session.onDisconnection() код code

Code Block
languagejava
themeRDark
@Override
public void onDisconnection(final Connection connection) {
    runOnUiThread(new Runnable() {
       @Override
       public void run() {
           mStartButton.setText(R.string.action_start);
           mStartButton.setTag(R.string.action_start);
           mStartButton.setEnabled(true);
           mSwitchCameraButton.setEnabled(false);
           mSwitchRendererButton.setEnabled(false);
           mStatusView.setText(connection.getStatus());
           mTestButton.setEnabled(true);
      }
   });
}