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.

Code Block
languagejsjava
themeRDark
Flashphoner.init(this);

...

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

Code Block
languagejsjava
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());

3. Настройка параметров аудио и видео, заданных пользователем

AudioConstraints, VideoConstraints код

...

languagejs
themeRDark

...

Управление отображением видео

FPSurfaceViewRenderer.setMirror() code

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

  • localRender для отображения видео с камеры
  • remoteRender для отображения публикуемого потока
  • newSurfaceRenderer для демонстрации переключения объекта

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

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

Code Block
languagejava
themeRDark
        remoteRenderLayout.setPosition(0, 0, 100, 100);
        remoteRender.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        audioConstraints = new AudioConstraints(remoteRender.setMirror(false);
        if (mUseFEC.isChecked()) {remoteRender.requestLayout();

        localRenderLayout.setPosition(0, 0,   audioConstraints.setUseFEC(true100, 100);
        }
        if (mUseStereo.isChecked()) {localRender.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
            audioConstraints.setUseStereolocalRender.setMirror(true);
        }localRender.requestLayout();

        if (!mDefaultPublishAudioBitrate.isChecked() && mDefaultPublishAudioBitrate.getText().length() > 0) {
    switchRenderLayout.setPosition(0, 0, 100, 100);
        newSurfaceRenderer.setZOrderMediaOverlay(true);
        audioConstraintsnewSurfaceRenderer.setBitratesetScalingType(IntegerRendererCommon.parseInt(mPublishAudioBitrate.getText().toString()))ScalingType.SCALE_ASPECT_FIT);
        }
    }
    VideoConstraints videoConstraints = null;
    if (mSendVideo.isChecked()) {newSurfaceRenderer.setMirror(true);
        videoConstraints = new VideoConstraints();
        videoConstraints.setCameraId(((MediaDevice) mCameraSpinner.getSpinner().getSelectedItem()).getId());newSurfaceRenderer.requestLayout();

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

4. Настройка параметров аудио и видео, заданных пользователем

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);
}

5. Локальное тестирование камеры и микрофона

Flashphoner.getLocalMediaAccess() code

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

  • настройки аудио и видео, заданные пользователем
  • локальный объект SurfaceViewRenderer localRenderer для вывода изображения с выбранной камеры
Code Block
languagejava
themeRDark
case TEST_REQUEST_CODE: {
    if (grantResults.length == 0 ||
          grantResults[0] != PackageManager.PERMISSION_GRANTED ||
          grantResults[1] != PackageManager.PERMISSION_GRANTED) {
        if (mCameraFPS.getText().length() > 0) {
    Log.i(TAG, "Permission has been denied by user");
    } else {
        videoConstraintsFlashphoner.setVideoFpsgetLocalMediaAccess(Integer.parseInt(mCameraFPS.getText().toString()))getConstraints(), localRender);
        }mTestButton.setText(R.string.action_release);
        if (mWidth.getText().length() > 0 && mHeight.getText().length() > 0) {mTestButton.setTag(R.string.action_release);
            videoConstraints.setResolution(Integer.parseInt(mWidth.getText().toString()),mStartButton.setEnabled(false);
        soundMeter = new SoundMeter();
         IntegersoundMeter.parseInt(mHeight.getText().toString()))start();
        }...
        if (!mDefaultPublishVideoBitrate.isChecked() && mPublishVideoBitrate.getText().length() > 0) {
             videoConstraints.setBitrate(Integer.parseInt(mPublishVideoBitrate.getText().toString()));
        }
    }
    return new Constraints(audioConstraints, videoConstraints);
}

4. Локальное тестирование камеры и микрофона

Flashphoner.getLocalMediaAccess() код

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

  • настройки аудио и видео, заданные пользователем
  • локальный объект SurfaceViewRenderer localRenderer для вывода изображения с выбранной камеры
Code Block
languagejs
themeRDark
case TEST_REQUEST_CODE: {
    if (grantResults.length == 0 ||
          grantResults[0] != PackageManager.PERMISSION_GRANTED ||
          grantResults[1] != PackageManager.PERMISSION_GRANTED Log.i(TAG, "Permission has been granted by user");
    }
break;

6. Создание сессии

Flashphoner.createSession() code

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

  • URL WCS-сервера
  • SurfaceViewRenderer localRenderer, который будет использоваться для отображения видео с камеры
  • SurfaceViewRenderer remoteRenderer, который будет использоваться для воспроизведения опубликованного видеопотока
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);

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() {
        Log.i(TAG, "Permission has been denied by user");
    } else @Override
       public void run() {
        Flashphoner.getLocalMediaAccess(getConstraints(), localRender);
        mTestButtonmStartButton.setText(R.string.action_releasestop);
          mTestButton mStartButton.setTag(R.string.action_releasestop);
           mStartButton.setEnabled(falsetrue);
        soundMeter = new SoundMetermTestButton.setEnabled(false);
         soundMeter.start()  mStatusView.setText(connection.getStatus());;
           ...
        Log.i(TAG, "Permission has been granted by user"}
   });
    }
break;

5. Создание сессии

Flashphoner.createSession() код

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

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

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

session.createStream() code

Code Block
languagejsjava
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);

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

Session.connect(). код

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

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

session.onConnected() код

Code Block
languagejs
themeRDark
@Override
public void onConnected(final Connection connection) {
   runOnUiThread(new Runnable() {
       @Override
       public void run() {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  mStartButton.setText(R.string.action_stopby user");
    } else {
     mStartButton.setTag(R.string.action_stop);   /**
          * mStartButtonMethod Stream.setEnabledpublish(true);
 is called to publish stream.
       mTestButton.setEnabled(false);
   */
        mStatusViewpublishStream.setTextpublish(connection.getStatus());;

           /**
  Log.i(TAG, "Permission has been granted by user");
    }
  * The options for the stream to publish are set.
             * The stream name is passed when StreamOptions object is created. break;
}

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

StreamStatusEvent PUBLISHING code

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

Code Block
languagejava
themeRDark
publishStream.on(new StreamStatusEvent() {
    @Override
    public         * VideoConstraints object is used to set the source camera, FPS and resolution.
    void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
        runOnUiThread(new Runnable() {
         * Stream constraints are@Override
 set with method StreamOptions.setConstraints().
        public void run() {
  */
           StreamOptions streamOptions = new StreamOptions(streamName);if (StreamStatus.PUBLISHING.equals(streamStatus)) {
           Constraints constraints = getConstraints();
      /**
     streamOptions.setConstraints(constraints);
           String[] stripCodec = {(String) mStripStreamerCodec.getSpinner().getSelectedItem()};
  * The options for the stream to play are streamOptionsset.setStripCodecs(stripCodec);


                /**
      * The stream name is passed when *StreamOptions Streamobject is created with method Session.createStream().
             */
           publishStream = session.createStream(streamOptions);*/
           ...
       }
   });
}

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

session.createStream() код

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

ActivityCompat.requestPermissions(MediaDevicesActivity.this,
StreamOptions streamOptions = new StreamOptions(streamName);

                    streamOptions.setConstraints(new Constraints(mReceiveAudio.isChecked(), mReceiveVideo.isChecked()));

          new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA},
         PUBLISH_REQUEST_CODE);

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

Stream.publish() код

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

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

StreamStatusEvent PUBLISHING код

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

Code Block
languagejs
themeRDark
publishStream.on(new StreamStatusEvent() {
   audioConstraints @Override
= new AudioConstraints();
  public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
        runOnUiThread(new Runnable() {}
            @Override
        streamOptions.setConstraints(new Constraints(audioConstraints, videoConstraints));
   public void run() {
              String[] stripCodec if= {(StreamStatus.PUBLISHING.equals(streamStatus)) {String) mStripPlayerCodec.getSpinner().getSelectedItem()};
                    /**streamOptions.setStripCodecs(stripCodec);
                    /**
  * The options for the stream to play are set.
           * Stream is created with method Session.createStream().
     *  The stream name is passed when StreamOptions object is created.
      */
                */
    playStream = session.createStream(streamOptions);
                StreamOptions streamOptions = new StreamOptions(streamName);

 ...
                    streamOptions.setConstraints(new Constraints(mReceiveAudio.isChecked(), mReceiveVideo.isChecked()));

/**
                      VideoConstraints* videoConstraints = null;
       Method Stream.play() is called to start playback of the stream.
             if (mReceiveVideo.isChecked()) {
       */
                 videoConstraints = new VideoConstraintsplayStream.play();
                    if    ...
(mSendVideo.isChecked())
                     }
   mSwitchCameraButton.setEnabled(true);
                 AudioConstraints audioConstraints = null mSwitchRendererButton.setEnabled(true);
               }     if (mReceiveAudio.isChecked()) else {
                   Log.e(TAG, "Can not publish stream audioConstraints" = new AudioConstraints+ stream.getName();
 + " " + streamStatus);
               }
                    streamOptions.setConstraints(new Constraints(audioConstraints, videoConstraintsmStatusView.setText(streamStatus.toString());
           }
       });
  String[] stripCodec = {(String) mStripPlayerCodec.getSpinner().getSelectedItem()}; }
});

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

Stream.switchCamera() code

Code Block
languagejava
themeRDark
mSwitchCameraButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        if streamOptions.setStripCodecs(stripCodec);
   (publishStream != null) {
                 /**mSwitchCameraButton.setEnabled(false);
            publishStream.switchCamera(new CameraSwitchHandler() {
        * Stream is created with method Session.createStream().
  @Override
                public void onCameraSwitchDone(boolean var1) */{
                    playStream = session.createStream(streamOptions);runOnUiThread(new Runnable() {
                    ...
    @Override
                      /**
  public void run() {
                  * Method Stream.play() is called to start playback of the streammSwitchCameraButton.setEnabled(true);
                      */  }
                    playStream.play(});

                }

    if (mSendVideo.isChecked())
            @Override
                public void mSwitchCameraButton.setEnabled(true);onCameraSwitchError(String var1) {
                    mSwitchRendererButton.setEnabled(true);runOnUiThread(new Runnable() {
               }  else {
      @Override
             Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus);           public void run() {
               }
               mStatusViewmSwitchCameraButton.setText(streamStatus.toString())setEnabled(true);
           }
             });
    }
});

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

Stream.switchCamera() код

Code Block
languagejs
themeRDark
mSwitchCameraButton.setOnClickListener(new OnClickListener() {
         @Override
    public void onClick(View view});
    {
        if (publishStream != null) {}
            mSwitchCameraButton.setEnabled(false});
            publishStream.switchCamera(new CameraSwitchHandler }
    }

});

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

Stream.switchRenderer() code

Code Block
languagejava
themeRDark
mSwitchRendererButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onCameraSwitchDoneonClick(booleanView var1v) {
                    runOnUiThread(new Runnable() {
      if (spinner.getSelectedItemId() == 0){
            if (isSwitchRemoteRenderer) {
    @Override
            playStream.switchRenderer(remoteRender);
            public void run() {
 isSwitchRemoteRenderer = false;
            }
            if mSwitchCameraButton.setEnabled(true!isSwitchLocalRenderer); {
                publishStream.switchRenderer(newSurfaceRenderer);
        }
        isSwitchLocalRenderer = true;
          });

  } else {
            }

    publishStream.switchRenderer(localRender);
            @Override
    isSwitchLocalRenderer = false;
          public void onCameraSwitchError(String var1) {
}
        } else {
            runOnUiThread(newif Runnable(isSwitchLocalRenderer) {
                publishStream.switchRenderer(localRender);
        @Override
        isSwitchLocalRenderer = false;
            }
  public void run() {
       if (!isSwitchRemoteRenderer) {
                   mSwitchCameraButton.setEnabled(trueplayStream.switchRenderer(newSurfaceRenderer);
                isSwitchRemoteRenderer = true;
            }
 else {
                  }playStream.switchRenderer(remoteRender);
                isSwitchRemoteRenderer = }false;
            });
        }
    }

});

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

Stream.switchRenderer() код14. Управление звуком при помощи аппаратных кнопок

Flashphoner.setVolume() code

Code Block
languagejsjava
themeRDark
mSwitchRendererButton.setOnClickListener(new OnClickListener(    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    @Override
    int publiccurrentVolume void= onClick(View v) {Flashphoner.getVolume();
        ifswitch (spinner.getSelectedItemId(keyCode) == 0){
            if (isSwitchRemoteRenderer) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
                if playStream.switchRenderer(remoteRender);
    (currentVolume == 1) {
            isSwitchRemoteRenderer = false;
        Flashphoner.setVolume(0);
    }
            if (!isSwitchLocalRenderer) {}
                publishStreammPlayVolume.switchRenderersetProgress(newSurfaceRenderercurrentVolume-1);
                isSwitchLocalRenderer = truebreak;
            } else {
case KeyEvent.KEYCODE_VOLUME_UP:
                if publishStream.switchRenderer(localRender);(currentVolume == 0) {
                isSwitchLocalRenderer   = false Flashphoner.setVolume(1);
            }
        }
 else  {
            if (isSwitchLocalRenderer) {mPlayVolume.setProgress(currentVolume+1);
                publishStream.switchRenderer(localRender)break;
        }
        isSwitchLocalRenderer = falsereturn super.onKeyDown(keyCode, event);
         }

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

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

Code Block
languagejava
themeRDark
   }
     mSpeakerPhone =      if (!isSwitchRemoteRenderer) {(CheckBox) findViewById(R.id.use_speakerphone);
                playStream.switchRenderer(newSurfaceRenderermSpeakerPhone.setChecked(Flashphoner.getAudioManager().getAudioManager().isSpeakerphoneOn());
                isSwitchRemoteRenderer = true;mSpeakerPhone.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            }@Override
 else {
          public void onCheckedChanged(CompoundButton buttonView, boolean  playStream.switchRenderer(remoteRender);isChecked) {
                isSwitchRemoteRenderer = falseFlashphoner.getAudioManager().setUseSpeakerPhone(isChecked);
            }
        }
    }
});

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

Session.disconnect() код code

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

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

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

session.onDisconnection() код code

Code Block
languagejsjava
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);
      }
   });
}